NoMethodError: undefined method `each' for #<Menu:0x512be78> - ruby-on-rails

irb(main):001:0> hotel=Hotel.find(1)
←[1m←[36mHotel Load (1.0ms)←[0m ←[1mSELECT `hotels`.* FROM `hotels` WHERE `hotels`.`hotel_Id` = 1 LIMIT 1←[0m
=> #<Hotel hotel_Id: 1, hotel_Name: "Hotel Swosti", hotel_address: nil, hotel_location: "Bhubaneswar", hotel_contactNo: nil, crea
ted_at: nil, updated_at: nil>
irb(main):002:0> hotel.menus
←[1m←[35mMenu Load (1.0ms)←[0m SELECT `menus`.* FROM `menus` WHERE `menus`.`hotel_id` = 1
=> #<ActiveRecord::Associations::CollectionProxy []>
irb(main):003:0> first_menu=Menu.new(:menu_item_name=>'Rajma',:price=>30,:item_type=>'Half')
=> #<Menu hotel_Id: nil, menu_item_id: nil, menu_item_name: "Rajma", price: 30, item_type: "Half", created_at: nil, updated_at: n
il>
irb(main):004:0> first_menu.hotel
=> nil
irb(main):005:0> hotel.menus=first_menu
NoMethodError: undefined method `each' for #<Menu:0x512be78>
migration:
create_table :menus,:id=>false do |t|
t.integer 'hotel_Id'
t.primary_key 'menu_item_id'
t.string 'menu_item_name'
t.integer 'price'
t.string 'item_type'
end
add_index("menus","hotel_Id")
end
end

If you want to add first_menu to hotel.menus association, you should do:
hotel.menus << first_menu
The error occurs because Hotel#menus= setter expects collection of Menu objects as parameter.

hotel.menus is a relation. The association that you used returns an array of hotel menus.
To get the first member, a single menu, you could use hotel.menus.first.
If you want to create a new menu for a hotel, you'll probably be better off using:
hotel.menus.build(menu_item_name: 'Rajma', price: 30, item_type: 'Half')
hotel.save!
or the create form - depending on what else you want to do with the hotel or the menu, before you save.

Related

Find Non Orphan records in Rails

Following up on the question : Want to find records with no associated records in Rails
I am wondering how I can get all the NON orphan records returned as an AssociationRelation instead of an Array. When trying to subtract the total records of the table from the rails 6 .missing ones, the result is correct, but it's in the form of an array.
Here is a console example :
p = ProductResearch.first
(Product.all - p.products.where.missing(:keywords)).class
=> Array
How do I get the association ?
( With the help of #max below I found a query, without missing, that returns the expected result as an association. It's like :
irb(main):206:0> p.products.includes(:keywords).where.not(keywords: { id: nil }).class
=> Product::ActiveRecord_AssociationRelation
and it does return the non orphan ones only.
Given:
class Post < ApplicationRecord
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :post
end
class CreateComments < ActiveRecord::Migration[6.0]
def change
create_table :comments do |t|
# Referential integrity is for wusses! YOLO!
t.belongs_to :post, null: true, foreign_key: false
t.timestamps
end
end
end
p1 = Post.create!(title: 'Foo')
3.times { p1.comments.create! }
p2 = Post.create!(title: 'Bar')
3.times { p2.comments.create! }
p2.destroy! # orphans the comments
If you do an INNER JOIN on posts you will only get rows with at least one match in the join table:
irb(main):014:0> Comment.joins(:post)
Comment Load (0.3ms) SELECT "comments".* FROM "comments" INNER JOIN "posts" ON "posts"."id" = "comments"."post_id" LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Comment id: 1, post_id: 1, created_at: "2021-05-11 08:59:04", updated_at: "2021-05-11 08:59:04">, #<Comment id: 2, post_id: 1, created_at: "2021-05-11 08:59:04", updated_at: "2021-05-11 08:59:04">, #<Comment id: 3, post_id: 1, created_at: "2021-05-11 08:59:04", updated_at: "2021-05-11 08:59:04">]>
This gives you the "non-orphaned" posts.
The opposite is of course an OUTER JOIN:
irb(main):016:0> Comment.left_joins(:post).where(posts: { id: nil })
Comment Load (0.3ms) SELECT "comments".* FROM "comments" LEFT OUTER JOIN "posts" ON "posts"."id" = "comments"."post_id" WHERE "posts"."id" IS NULL LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Comment id: 4, post_id: 2, created_at: "2021-05-11 08:59:26", updated_at: "2021-05-11 08:59:26">, #<Comment id: 5, post_id: 2, created_at: "2021-05-11 08:59:26", updated_at: "2021-05-11 08:59:26">, #<Comment id: 6, post_id: 2, created_at: "2021-05-11 08:59:26", updated_at: "2021-05-11 08:59:26">]>
Rails 6.1 added the .missing query method which is a shortcut for the above query:
Comment.where.missing(:post)

NameError (uninitialized constant Many-To-Many) Mono-transitive association in rails

My many to many associations seem not to be working.
An example is this: where TasksTestMethods is the JoinTable
TasksTestMethods.all
TasksTestMethods Load (0.5ms) SELECT "tasks_test_methods".* FROM "tasks_test_methods" LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<TasksTestMethods task_id: 2, test_method_id: 1>]>
The join table in schema.rb looks like this
create_table "tasks_test_methods", id: false, force: :cascade do |t|
t.bigint "task_id", null: false
t.bigint "test_method_id", null: false
t.index ["task_id", "test_method_id"], name: "index_tasks_test_methods_on_task_id_and_test_method_id"
t.index ["test_method_id", "task_id"], name: "index_tasks_test_methods_on_test_method_id_and_task_id"
end
Now when I want to query for TestMethods for task with id of 2 as seen above.
task = Task.last
Task Load (0.9ms) SELECT "tasks".* FROM "tasks" ORDER BY "tasks"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<Task id: 2, name: "", description: "", start_date: "2018-05-15", end_date: nil, order_number: "", material_type: nil, client_id: 1, drawing_number: nil, task_status_field: nil, invoice_number: nil, offshore_flag: 0, client_ref: "", contact_person: "", contact_person_phone: "", contact_person_email: "", testsite: "", information_to_operator: nil, client_online_order: nil, start_date_asap: nil, thickness: nil, testextent: nil, groove: nil, material_quality: nil, class_society: nil, welder_id: nil, project_id: nil, operator_id: nil, task_manager_id: 1, department_id: 5, created_at: "2018-05-14 22:13:08", updated_at: "2018-05-14 22:18:30", language_id: 2>
I get the following error when I run it like this
task.test_methods
Traceback (most recent call last):
1: from (irb):6
NameError (uninitialized constant Task::TasksTestMethod)
Same when I try for TestMethod
test_method = TestMethod.last
TestMethod Load (0.4ms) SELECT "test_methods".* FROM "test_methods" ORDER BY "test_methods"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<TestMethod id: 1, name: "Visual Testing (VT)", code: nil, description: nil, category_id: 1, procedure_id: 1, created_at: "2018-05-15 07:06:51", updated_at: "2018-05-15 07:06:51">
I get this error:
test_method.tasks
Traceback (most recent call last):
1: from (irb):9
NameError (uninitialized constant TestMethod::TasksTestMethod)
I'm getting this same error for all JoinTables in my app.
What I'm I doing wrong? And my models are as follows:
class Task < ApplicationRecord
has_many :tasks_test_methods, :dependent => :destroy
has_many :test_methods, :through => :tasks_test_methods
end
class TestMethod < ApplicationRecord
belongs_to :category
belongs_to :procedure
has_many :tasks_test_methods
has_many :tasks, :through => :tasks_test_methods
end
class TasksTestMethods < ApplicationRecord
belongs_to :task
belongs_to :test_method
end
I also tried to rename the model name from TasksTestMethods to TasksTestMethod based on this error NameError (uninitialized constant Task::TasksTestMethod) but that didn't help.
Is there anything I'm missing?
Name your rb file containing this class properly, i.e. tasks_test_method.rb.

Rails undefined method in model

Somehow this one is really perplexing me, everything is really there, but somehow it doesn't work:
neither one of the self.status_transition.update do not work
require 'state_machine/core'
class V1::Order < ActiveRecord::Base
extend StateMachine::MacroMethods
has_one :status_transition
after_create :create_status
def create_status
self.create_status_transition if self.status_transition.nil?
end
state_machine initial: :draft do
state :draft, value: 0
state :placed, value: 1
state :paid, value: 2
state :canceled, value: 3
after_transition any => any do |t|
self.status_transition.update(event: t.event,
from: t.from, to: t.to)
#self.status_transition.update(event: "dsa", from: "ds", to: "dsd")
end
event :place do
transition :draft => :placed
end
ERROR:
2.1.1 :004 > o.place
(0.1ms) begin transaction
SQL (0.9ms) UPDATE "v1_orders" SET "state" = ?, "updated_at" = ? WHERE "v1_orders"."id" = 19
[["state", 1], ["updated_at", "2014-11-28 12:48:40.256820"]]
(91.7ms) rollback transaction
NoMethodError: undefined method `status_transition' for #<StateMachine::Machine:0x00000107aa78e8>
o = V1::Order.create # => #<V1::Order id: 22, state: 1, user_id: nil, created_at: "2014-11-28 13:29:03", updated_at: "2014-11-28 13:29:03", vat: #<BigDecimal
:101c09fe8,'0.2E2',9(18)>>
o.status_transitions # IS CREATED
o.status_transition # => #<V1::StatusTransition id: 23, event: nil, from: nil, to: nil, order_id: 28, created_at: "2014-11-28 13:25:55", updated_at: "2014-1
1-28 13:25:55">
o.place returns the above error .
According the documentation block shell be passed as value for :do key to ::after_transition callback, so:
class V1::Order
after_transition any => any do |order, transition|
p order # disable this as needed
order.status_transition.update(event: transition.event, from: transition.from, to: transition.to)
end
end

Rails model create and new returns nil in test environment

I have a model spec that is failing with "undefined method 'save' for nil:NilClass'." This occurs in the class method 'create_and_send_self_eval'. The method is creating a new Evaluation, but it always returns nil in the test environment. I've also tried using 'create', 'create!' and they also return nil. However, this only occurs in the test environment. In the development environment, it returns the correct object. I'm using rspec 3.1.5, rails 4.1.6, and ruby 2.1.2.
I've included the code for the class and my debug output. Any suggestions?
Evaluation.rb
class Evaluation < ActiveRecord::Base
has_one :evaluator
validates_uniqueness_of :access_key
validates_presence_of :participant_id
before_validation :set_access_key, on: :create
def send_invite
return true
end
def self.create_and_send_self_eval(participant)
evaluation = self.new do |e|
e.participant_id = participant.id
e.evaluator = participant
end
if evaluation.nil?
binding.pry
end
evaluation.save
end
private
def set_access_key
return if access_key.present?
begin
self.access_key = SecureRandom.hex(8)
end while self.class.exists?(access_key: self.access_key)
end
end
Debug output using pry in the test environment
[1] pry(Evaluation)> participant
=> #<Participant id: 167, first_name: "Puff", last_name: "Daddy", evaluation_url: nil, created_at: "2014-10-07 19:43:47", updated_at: "2014-10-07 19:43:47">
[2] pry(Evaluation)> Evaluation.new
=> nil
[3] pry(Evaluation)> Evaluation.create(participant_id: participant.id)
NoMethodError: undefined method `save' for nil:NilClass
from /Users/diyahm/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.6/lib/active_record/persistence.rb:34:in `create'
[4] pry(Evaluation)> Evaluation.create!(participant_id: participant.id)
NoMethodError: undefined method `save!' for nil:NilClass
from /Users/diyahm/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.6/lib/active_record/validations.rb:41:in `create!'
Debug output in rails console
2.1.2 :005 > p = Participant.last
SQL (0.9ms) SELECT "participants"."id" AS t0_r0, "participants"."first_name" AS t0_r1, "participants"."last_name" AS t0_r2, "participants"."evaluation_url" AS t0_r3, "participants"."created_at" AS t0_r4, "participants"."updated_at" AS t0_r5, "evaluators"."id" AS t1_r0, "evaluators"."email" AS t1_r1, "evaluators"."created_at" AS t1_r2, "evaluators"."updated_at" AS t1_r3, "evaluators"."actable_id" AS t1_r4, "evaluators"."actable_type" AS t1_r5, "evaluators"."evaluation_id" AS t1_r6 FROM "participants" LEFT OUTER JOIN "evaluators" ON "evaluators"."actable_id" = "participants"."id" AND "evaluators"."actable_type" = 'Participant' ORDER BY "participants"."id" DESC LIMIT 1
=> #<Participant id: 3, first_name: "Puff", last_name: "Daddy", evaluation_url: nil, created_at: "2014-10-06 06:32:40", updated_at: "2014-10-06 06:32:40">
2.1.2 :006 > Evaluation.new
=> #<Evaluation id: nil, participant_id: nil, access_key: nil, created_at: nil, updated_at: nil>
2.1.2 :007 > Evaluation.create(participant_id: p.id)
(0.2ms) BEGIN
Evaluation Exists (2.1ms) SELECT 1 AS one FROM "evaluations" WHERE "evaluations"."access_key" = 'c688b05ee4625c60' LIMIT 1
Evaluation Exists (0.3ms) SELECT 1 AS one FROM "evaluations" WHERE "evaluations"."access_key" = 'c688b05ee4625c60' LIMIT 1
SQL (1.7ms) INSERT INTO "evaluations" ("access_key", "created_at", "participant_id", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["access_key", "c688b05ee4625c60"], ["created_at", "2014-10-07 19:47:15.877706"], ["participant_id", 3], ["updated_at", "2014-10-07 19:47:15.877706"]]
(2.3ms) COMMIT
=> #<Evaluation id: 4, participant_id: 3, access_key: "c688b05ee4625c60", created_at: "2014-10-07 19:47:15", updated_at: "2014-10-07 19:47:15">
pry debug output at beginning of method
[1] pry(Evaluation)> self
=> Evaluation(id: integer, participant_id: integer, access_key: string, created_at: datetime, updated_at: datetime)
[2] pry(Evaluation)> self.class
=> Class
[3] pry(Evaluation)> self.connection
=> #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter:0x007f8158eb8ee0
[4] pry(Evaluation)> Evaluation
=> Evaluation(id: integer, participant_id: integer, access_key: string, created_at: datetime, updated_at: datetime)
[5] pry(Evaluation)> Evaluation.class
=> Class
[6] pry(Evaluation)> Evaluation.connection
=> #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter:0x007f8158eb8ee0
I didn't show the entire output for self.connection or Evaluation.connection. But connection is returning correctly.
The answer to this question had to do with how the tests were written. In my spec, I'm checking to see if "new" is called on Evaluation. Since, I'm using rspec-mocks, Evaluation is not actually being created. Fixed this by changing the test to test the output results.
Try doing this instead:
evaluation = self.new.tap do |e|
e.participant_id = participant.id
e.evaluator = participant
end
Using Object#tap should guarantee that you set evaluation to the object rather than to the return value of the block.

state_machine only works for new records

I can't seem to get the state_machine gem (http://github.com/pluginaweek/state_machine/) to work on existing records (it works correctly on new records).
Here's my model:
class Comment < ActiveRecord::Base
state_machine :state, :initial => :pending do
event :publish do
transition all => :published
end
end
end
and here's an IRB session that demonstrates the issue (I did ActiveRecord::Base.logger = Logger.new(STDOUT) to make it easier to read):
>> c = Comment.new
=> #<Comment id: nil, song_id: nil, author: nil, body: nil, created_at: nil, updated_at: nil, state: "pending">
>> c.state
=> "pending"
>> c.publish
Comment Create (0.6ms) INSERT INTO "comments" ("updated_at", "body", "author", "song_id", "created_at", "state") VALUES('2009-11-02 02:44:37', NULL, NULL, NULL, '2009-11-02 02:44:37', 'published')
=> true
>> Comment.last.state
Comment Load (0.4ms) SELECT * FROM "comments" ORDER BY comments.id DESC LIMIT 1
=> "published"
>> c = Comment.create
Comment Create (0.5ms) INSERT INTO "comments" ("updated_at", "body", "author", "song_id", "created_at", "state") VALUES('2009-11-02 02:44:47', NULL, NULL, NULL, '2009-11-02 02:44:47', 'pending')
=> #<Comment id: 4, song_id: nil, author: nil, body: nil, created_at: "2009-11-02 02:44:47", updated_at: "2009-11-02 02:44:47", state: "pending">
>> c.publish
=> true
>> c.save
=> true
>> Comment.last.state
Comment Load (0.4ms) SELECT * FROM "comments" ORDER BY comments.id DESC LIMIT 1
=> "pending"
I.e., everything works fine when I publish an unsaved comment, but when I try to publish a comment that's already saved, nothing happens.
Another Edit: Perhaps the root of the problem?
=> true
>> a = Comment.last
Comment Load (1.3ms) SELECT * FROM "comments" ORDER BY comments.id DESC LIMIT 1
=> #<Comment id: 3, song_id: nil, author: nil, body: nil, created_at: "2009-11-03 03:03:54", updated_at: "2009-11-03 03:03:54", state: "pending">
>> a.state
=> "pending"
>> a.publish
=> true
>> a.state
=> "published"
>> a.state_changed?
=> false
I.e., even though the state has actually changed, state_changed? is returning false and therefore Rails won't update the corresponding database row when I call save.
It works when I turn off partial updates, but not when I try state_will_change!:
>> Comment.partial_updates = false
=> false
>> c = Comment.create
Comment Create (0.5ms) INSERT INTO "comments" ("updated_at", "body", "author", "song_id", "created_at", "state") VALUES('2009-11-07 05:06:49', NULL, NULL, NULL, '2009-11-07 05:06:49', 'pending')
=> #<Comment id: 7, song_id: nil, author: nil, body: nil, created_at: "2009-11-07 05:06:49", updated_at: "2009-11-07 05:06:49", state: "pending">
>> c.publish
Comment Update (0.9ms) UPDATE "comments" SET "created_at" = '2009-11-07 05:06:49', "author" = NULL, "state" = 'published', "body" = NULL, "song_id" = NULL, "updated_at" = '2009-11-07 05:06:53' WHERE "id" = 7
=> true
>> Comment.last.state
Comment Load (0.5ms) SELECT * FROM "comments" ORDER BY comments.id DESC LIMIT 1
=> "published"
>> Comment.partial_updates = true
=> true
>> c = Comment.create
Comment Create (0.8ms) INSERT INTO "comments" ("updated_at", "body", "author", "song_id", "created_at", "state") VALUES('2009-11-07 05:07:21', NULL, NULL, NULL, '2009-11-07 05:07:21', 'pending')
=> #<Comment id: 8, song_id: nil, author: nil, body: nil, created_at: "2009-11-07 05:07:21", updated_at: "2009-11-07 05:07:21", state: "pending">
>> c.state_will_change!
=> "pending"
>> c.publish
=> true
>> c.save
=> true
>> Comment.last.state
Comment Load (0.5ms) SELECT * FROM "comments" ORDER BY comments.id DESC LIMIT 1
=> "pending"
EDIT:
More weirdness:
>> a = Comment.last
Comment Load (1.2ms) SELECT * FROM "comments" ORDER BY comments.id DESC LIMIT 1
=> #<Comment id: 5, song_id: nil, author: nil, body: nil, created_at: "2009-11-02 06:33:19", updated_at: "2009-11-02 06:33:19", state: "pending">
>> a.state
=> "pending"
>> a.publish
=> true
>> a.state
=> "published"
>> a.save
=> true
>> a.id
=> 5
>> Comment.find(5).state
Comment Load (0.3ms) SELECT * FROM "comments" WHERE ("comments"."id" = 5)
=> "pending"
Compare to:
>> a = Comment.last
Comment Load (0.3ms) SELECT * FROM "comments" ORDER BY comments.id DESC LIMIT 1
=> #<Comment id: 5, song_id: nil, author: nil, body: nil, created_at: "2009-11-02 06:33:19", updated_at: "2009-11-02 06:33:19", state: "pending">
>> a.state = "published"
=> "published"
>> a.save
Comment Update (0.6ms) UPDATE "comments" SET "state" = 'published', "updated_at" = '2009-11-02 08:29:34' WHERE "id" = 5
=> true
>> a.id
=> 5
>> Comment.find(5).state
Comment Load (0.4ms) SELECT * FROM "comments" WHERE ("comments"."id" = 5)
=> "published"
I came over the same issue 3 years after so it worth answering here to save other folks's time.
You need to have column called 'state' in your table, so state_machine will able to make the state persistant.
Just add it to your migration - t.string :state
Can you please retry your state transitions with publish**!** instead of publish
Not contributing anything useful, but I just wanted to say I'm struggling with this error as well, in multiple state_machines throughout my application. And I can't switch to AASM, because I need to have more than one state_machine in the same model... So frustrating!
Anyway, you're not alone, it definitely still needs a solution.
Does this still happen with partial updates turned off? Comment.partial_updates = false
If so, then we know the issue is with identifying dirty objects. You should be able to call c.state_will_change! before you call c.publish
Does the model call super when it's initialized?
The state_machine documentation says it's required for states to get initialized
def initialize
#seatbelt_on = false
super() # NOTE: This *must* be called, otherwise states won't get initialized
end
Again, not a real answer to your question, but here I tried to simulate your session:
>> c = Comment.new
=> #<Comment id: nil, body: nil, created_at: nil, updated_at: nil, state: "pending">
>> c.state
=> "pending"
>> c.publish
=> true
>> Comment.last.state
=> "published"
>> c = Comment.create
=> #<Comment id: 4, body: nil, created_at: "2009-11-05 07:12:53", updated_at: "2009-11-05 07:12:53", state: "pending">
>> c.publish
=> true
>> c.save
=> true
>> Comment.last.state
=> "published"
As you can see, it works as expected for me. Checked it twice.
(I created a model with body and state attributes and put your code in it.)
Try to remove :state from definition:
FROM:
state_machine :state , :initial => :pending do
TO
state_machine :initial => :pending do

Resources