rails validation error nested object undefined method… for nil:NilClass
up vote
0
down vote
favorite
I've made a scheduling app where people are assigned to rooms each day. On Thursdays someone must be assigned for 'pager pickup' and I'm having problems with the validation to check for that.
Model
class Schedule < ActiveRecord::Base
has_many :rooms
...
validate :thursday_schedule_must_have_pager_pickup
...
def add_rooms
return unless self.rooms.count == 0
n = 1
tomorrow = DateTime.tomorrow
Schedule.site_list.each do |site|
Schedule.const_get(site).each do |room|
self.rooms.build(order: n,
site: site.to_s,
name: room,
start_hour: get_start_hour(tomorrow),
start_minute: get_start_minute(tomorrow, site.to_s))
n += 1
end
end
self.add_pager_pickup(n, tomorrow) if true # self.for_thursday?
self.add_today_call_data(n) if no_call_data
end
...
def add_pager_pickup(order, tomorrow)
self.rooms.build(order: order,
site: "TSH",
name: "Pager Pickup",
start_hour: 7,
start_minute: get_start_minute(tomorrow, "TSH"))
end
end
class Room < ActiveRecord::Base
belongs_to :schedule
...
end
The code I'd like to write is:
def thursday_schedule_needs_pager_pickup
if self.for_thursday? && self.rooms.where(name: "Pager Pickup").first.initials.blank?
errors.add(:rooms, "'Pager Pickup' can't be empty. Select '-- late start' if no one should come in early to pick up pager.")
end
end
That generates the following errors:
NoMethodError in SchedulesController#create
undefined method `initials' for nil:NilClass
By adding the "Pager Pickup" room to the schedule last I can hack a validation with the following code:
... self.rooms.last.initials.blank?
But that's brittle and preventing me from adding a 2nd, optional, pager pickup person, "2nd Pager Pickup", after the first.
Per Julien's points:
Schedule Controller
class SchedulesController < ApplicationController
...
def new
s = current_user.schedules.new
s.add_rooms
@schedule = s
end
def create
@schedule = current_user.schedules.build(schedule_params)
if @schedule.save
flash.now[:success] = "Draft Schedule Saved! Now Confirm or Edit."
render :show
else
render :new
end
...
end
Anyone have any thoughts?
Thanks in advance!
ruby-on-rails ruby-on-rails-4 rails-activerecord
add a comment |
up vote
0
down vote
favorite
I've made a scheduling app where people are assigned to rooms each day. On Thursdays someone must be assigned for 'pager pickup' and I'm having problems with the validation to check for that.
Model
class Schedule < ActiveRecord::Base
has_many :rooms
...
validate :thursday_schedule_must_have_pager_pickup
...
def add_rooms
return unless self.rooms.count == 0
n = 1
tomorrow = DateTime.tomorrow
Schedule.site_list.each do |site|
Schedule.const_get(site).each do |room|
self.rooms.build(order: n,
site: site.to_s,
name: room,
start_hour: get_start_hour(tomorrow),
start_minute: get_start_minute(tomorrow, site.to_s))
n += 1
end
end
self.add_pager_pickup(n, tomorrow) if true # self.for_thursday?
self.add_today_call_data(n) if no_call_data
end
...
def add_pager_pickup(order, tomorrow)
self.rooms.build(order: order,
site: "TSH",
name: "Pager Pickup",
start_hour: 7,
start_minute: get_start_minute(tomorrow, "TSH"))
end
end
class Room < ActiveRecord::Base
belongs_to :schedule
...
end
The code I'd like to write is:
def thursday_schedule_needs_pager_pickup
if self.for_thursday? && self.rooms.where(name: "Pager Pickup").first.initials.blank?
errors.add(:rooms, "'Pager Pickup' can't be empty. Select '-- late start' if no one should come in early to pick up pager.")
end
end
That generates the following errors:
NoMethodError in SchedulesController#create
undefined method `initials' for nil:NilClass
By adding the "Pager Pickup" room to the schedule last I can hack a validation with the following code:
... self.rooms.last.initials.blank?
But that's brittle and preventing me from adding a 2nd, optional, pager pickup person, "2nd Pager Pickup", after the first.
Per Julien's points:
Schedule Controller
class SchedulesController < ApplicationController
...
def new
s = current_user.schedules.new
s.add_rooms
@schedule = s
end
def create
@schedule = current_user.schedules.build(schedule_params)
if @schedule.save
flash.now[:success] = "Draft Schedule Saved! Now Confirm or Edit."
render :show
else
render :new
end
...
end
Anyone have any thoughts?
Thanks in advance!
ruby-on-rails ruby-on-rails-4 rails-activerecord
Do you have a room record with the name Pager Pickup?
– Pavan
Nov 8 at 13:00
Good question, @Pavan. Yes, I'm pretty confident I've been careful to make sure that a typo like a spelling or case error isn't preventing a room named "Pager Pickup" from existing.
– BenU
Nov 8 at 13:04
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
I've made a scheduling app where people are assigned to rooms each day. On Thursdays someone must be assigned for 'pager pickup' and I'm having problems with the validation to check for that.
Model
class Schedule < ActiveRecord::Base
has_many :rooms
...
validate :thursday_schedule_must_have_pager_pickup
...
def add_rooms
return unless self.rooms.count == 0
n = 1
tomorrow = DateTime.tomorrow
Schedule.site_list.each do |site|
Schedule.const_get(site).each do |room|
self.rooms.build(order: n,
site: site.to_s,
name: room,
start_hour: get_start_hour(tomorrow),
start_minute: get_start_minute(tomorrow, site.to_s))
n += 1
end
end
self.add_pager_pickup(n, tomorrow) if true # self.for_thursday?
self.add_today_call_data(n) if no_call_data
end
...
def add_pager_pickup(order, tomorrow)
self.rooms.build(order: order,
site: "TSH",
name: "Pager Pickup",
start_hour: 7,
start_minute: get_start_minute(tomorrow, "TSH"))
end
end
class Room < ActiveRecord::Base
belongs_to :schedule
...
end
The code I'd like to write is:
def thursday_schedule_needs_pager_pickup
if self.for_thursday? && self.rooms.where(name: "Pager Pickup").first.initials.blank?
errors.add(:rooms, "'Pager Pickup' can't be empty. Select '-- late start' if no one should come in early to pick up pager.")
end
end
That generates the following errors:
NoMethodError in SchedulesController#create
undefined method `initials' for nil:NilClass
By adding the "Pager Pickup" room to the schedule last I can hack a validation with the following code:
... self.rooms.last.initials.blank?
But that's brittle and preventing me from adding a 2nd, optional, pager pickup person, "2nd Pager Pickup", after the first.
Per Julien's points:
Schedule Controller
class SchedulesController < ApplicationController
...
def new
s = current_user.schedules.new
s.add_rooms
@schedule = s
end
def create
@schedule = current_user.schedules.build(schedule_params)
if @schedule.save
flash.now[:success] = "Draft Schedule Saved! Now Confirm or Edit."
render :show
else
render :new
end
...
end
Anyone have any thoughts?
Thanks in advance!
ruby-on-rails ruby-on-rails-4 rails-activerecord
I've made a scheduling app where people are assigned to rooms each day. On Thursdays someone must be assigned for 'pager pickup' and I'm having problems with the validation to check for that.
Model
class Schedule < ActiveRecord::Base
has_many :rooms
...
validate :thursday_schedule_must_have_pager_pickup
...
def add_rooms
return unless self.rooms.count == 0
n = 1
tomorrow = DateTime.tomorrow
Schedule.site_list.each do |site|
Schedule.const_get(site).each do |room|
self.rooms.build(order: n,
site: site.to_s,
name: room,
start_hour: get_start_hour(tomorrow),
start_minute: get_start_minute(tomorrow, site.to_s))
n += 1
end
end
self.add_pager_pickup(n, tomorrow) if true # self.for_thursday?
self.add_today_call_data(n) if no_call_data
end
...
def add_pager_pickup(order, tomorrow)
self.rooms.build(order: order,
site: "TSH",
name: "Pager Pickup",
start_hour: 7,
start_minute: get_start_minute(tomorrow, "TSH"))
end
end
class Room < ActiveRecord::Base
belongs_to :schedule
...
end
The code I'd like to write is:
def thursday_schedule_needs_pager_pickup
if self.for_thursday? && self.rooms.where(name: "Pager Pickup").first.initials.blank?
errors.add(:rooms, "'Pager Pickup' can't be empty. Select '-- late start' if no one should come in early to pick up pager.")
end
end
That generates the following errors:
NoMethodError in SchedulesController#create
undefined method `initials' for nil:NilClass
By adding the "Pager Pickup" room to the schedule last I can hack a validation with the following code:
... self.rooms.last.initials.blank?
But that's brittle and preventing me from adding a 2nd, optional, pager pickup person, "2nd Pager Pickup", after the first.
Per Julien's points:
Schedule Controller
class SchedulesController < ApplicationController
...
def new
s = current_user.schedules.new
s.add_rooms
@schedule = s
end
def create
@schedule = current_user.schedules.build(schedule_params)
if @schedule.save
flash.now[:success] = "Draft Schedule Saved! Now Confirm or Edit."
render :show
else
render :new
end
...
end
Anyone have any thoughts?
Thanks in advance!
ruby-on-rails ruby-on-rails-4 rails-activerecord
ruby-on-rails ruby-on-rails-4 rails-activerecord
edited Nov 10 at 12:07
asked Nov 8 at 12:56
BenU
1,5961533
1,5961533
Do you have a room record with the name Pager Pickup?
– Pavan
Nov 8 at 13:00
Good question, @Pavan. Yes, I'm pretty confident I've been careful to make sure that a typo like a spelling or case error isn't preventing a room named "Pager Pickup" from existing.
– BenU
Nov 8 at 13:04
add a comment |
Do you have a room record with the name Pager Pickup?
– Pavan
Nov 8 at 13:00
Good question, @Pavan. Yes, I'm pretty confident I've been careful to make sure that a typo like a spelling or case error isn't preventing a room named "Pager Pickup" from existing.
– BenU
Nov 8 at 13:04
Do you have a room record with the name Pager Pickup?
– Pavan
Nov 8 at 13:00
Do you have a room record with the name Pager Pickup?
– Pavan
Nov 8 at 13:00
Good question, @Pavan. Yes, I'm pretty confident I've been careful to make sure that a typo like a spelling or case error isn't preventing a room named "Pager Pickup" from existing.
– BenU
Nov 8 at 13:04
Good question, @Pavan. Yes, I'm pretty confident I've been careful to make sure that a typo like a spelling or case error isn't preventing a room named "Pager Pickup" from existing.
– BenU
Nov 8 at 13:04
add a comment |
3 Answers
3
active
oldest
votes
up vote
2
down vote
accepted
If I'm reading your code correctly, you have an unsaved object and you're trying to run this validation on it:
self.rooms.where(name: "Pager Pickup").first.initials.blank?
The problem with this approach is that .where on association will run a database query (or to be specific, it'd run the query if your objects were saved, but will do nothing for unsaved relations). This won't work for you, you haven't saved anything yet, you have to operate on objects in memory. If you change that line to:
self.rooms.detect {|r| r.name == "Pager Pickup" }.initials.blank?
it should work but it is still prone to errors if you simply leave it in your model, as in another context there might not be a room with that name and .initials will still be called on nil. I'd suggest you move such logic to factory objects, where you can strictly tie your validations to the context.
In order to fully understand the concept, you may run this in rails console:
s = Schedule.new
# => #<Schedule id: nil>
s.rooms << Room.new(foo: "bar")
# => #<ActiveRecord::Associations::CollectionProxy [#<Room id: nil, schedule_id: nil, foo: "bar">]>
s.rooms.where(foo: "bar")
# => #<ActiveRecord::AssociationRelation >
s.rooms.detect { |r| r.foo == "bar" }
# => #<Room id: nil, schedule_id: nil, foo: "bar">
Note: Your "hack" with .last worked because it operated on an array, not an ActiveRecord::Relation.
Excellent! Thank you! Problem solved. Thought that the issue lay with whetherwherewas able to act on object not yet saved to database but didn't know about detect and select. Very grateful. Tried to award +50 points but SO says must wait 22 hours.... Will do so when able, though.
– BenU
Nov 23 at 15:56
add a comment |
up vote
2
down vote
The problem that you are facing is that you are trying to do a query on a model that is not yet saved into the database. It does not have an id and the associated models rooms do not yet have an id themselves either.
Your validation throws an error because .where calls the database and it can't find the models, as they currently live only in memory.
self.rooms.where(name: "Pager Pickup").first.initials.blank?
However, you have the information on both models to do the validation correctly, you are just looking for it in the wrong place.
If you debug your app so that your instance of Schedule is built (not saved yet) and you add some rooms to it (not yet saved either), you will see the following behavior:
@schedule.rooms.length # It will be some value bigger than 0
@schedule.rooms.count # It will be zero
Why? Because .length works on the object as an array and .count searches for it in the database. As I mentioned before, your models do not exist on the DB so it wont find them, but they are in memory so you can measure the length of it.
The change you need to do in this problem is to imitate what .where does but in memory, with a simple .select:
self.rooms.select { |r| r.name == 'Pager Pickup' }.first.initials.blank?
That is the only change you need to do, but you should understand why.
.detect { }is a bit nicer than.select { }.first, but still a valid answer.
– Marcin Kołodziej
Nov 23 at 14:39
This works and thank you for your answer and explanation. Selected @MarcinKołodziej because submitted a couple minutes before yours.... Very grateful, though.
– BenU
Nov 23 at 15:57
Yeah I noticed the difference in time. We're glad it worked!
– byrdEmmanuel
Nov 23 at 16:04
add a comment |
up vote
0
down vote
Ok so first of all
self.rooms.where(name: "Pager Pickup")
is potentially going to return multiple objects so it doesn't give you a Room object but probably an ActiveRecord::Relation object so you would need to add something like .first before .initials to get a Room like so:
self.rooms.where(name: "Pager Pickup").first.initials.blank?
But anyway the error states that it is not finding anything so it seems that at the time of validation the rooms relationship for this schedule doesn't exist or is "empty" since it is claiming nil is returned from that query instead of an empty array so i'm guessing that your validation occurs before the room is actually created/saved.
Maybe show us the create action of the SchedulesController to see if something is wrong there.
UPDATE
After seeing your extra code, the problem is that you are calling add_rooms to a different schedule object than the one you are creating, I'm guessing your are on the impression that instance variables (variables starting with @) are persisted between requests but they are not, so the @schedule object in your create action is not the same one as in your new action therefore it doesn't have any rooms yet, update your create action to populate the rooms of that object doing something like this:
def create
@schedule = current_user.schedules.build(schedule_params)
@schedule.add_rooms # <-- Add this line
if @schedule.save
flash.now[:success] = "Draft Schedule Saved! Now Confirm or Edit."
render :show
else
render :new
end
Thanks, @Julien. Added the .first and as you predicted it still doesn't work. I edited my answer to include the SchedulesController#Create and other methods in the Schedule Model that may include my error. Thanks so much for your insight.
– BenU
Nov 9 at 14:58
when isadd_roomscalled? i don't see it in thecreateaction and i'm guessing its not in theschedule_paramsso where do you call it?
– Julien
Nov 10 at 2:18
add_roomsis called in the SchedulesController#New action. I added it.
– BenU
Nov 10 at 12:14
well there's your problem, you are adding the rooms on a different schedule object than the one you are creating, you need to add the rooms to your@scheduleobject in thecreateaction before callingsaveon it.
– Julien
Nov 11 at 19:01
I've edited my answer to include some details regarding my previous comment.
– Julien
Nov 11 at 19:18
|
show 2 more comments
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
If I'm reading your code correctly, you have an unsaved object and you're trying to run this validation on it:
self.rooms.where(name: "Pager Pickup").first.initials.blank?
The problem with this approach is that .where on association will run a database query (or to be specific, it'd run the query if your objects were saved, but will do nothing for unsaved relations). This won't work for you, you haven't saved anything yet, you have to operate on objects in memory. If you change that line to:
self.rooms.detect {|r| r.name == "Pager Pickup" }.initials.blank?
it should work but it is still prone to errors if you simply leave it in your model, as in another context there might not be a room with that name and .initials will still be called on nil. I'd suggest you move such logic to factory objects, where you can strictly tie your validations to the context.
In order to fully understand the concept, you may run this in rails console:
s = Schedule.new
# => #<Schedule id: nil>
s.rooms << Room.new(foo: "bar")
# => #<ActiveRecord::Associations::CollectionProxy [#<Room id: nil, schedule_id: nil, foo: "bar">]>
s.rooms.where(foo: "bar")
# => #<ActiveRecord::AssociationRelation >
s.rooms.detect { |r| r.foo == "bar" }
# => #<Room id: nil, schedule_id: nil, foo: "bar">
Note: Your "hack" with .last worked because it operated on an array, not an ActiveRecord::Relation.
Excellent! Thank you! Problem solved. Thought that the issue lay with whetherwherewas able to act on object not yet saved to database but didn't know about detect and select. Very grateful. Tried to award +50 points but SO says must wait 22 hours.... Will do so when able, though.
– BenU
Nov 23 at 15:56
add a comment |
up vote
2
down vote
accepted
If I'm reading your code correctly, you have an unsaved object and you're trying to run this validation on it:
self.rooms.where(name: "Pager Pickup").first.initials.blank?
The problem with this approach is that .where on association will run a database query (or to be specific, it'd run the query if your objects were saved, but will do nothing for unsaved relations). This won't work for you, you haven't saved anything yet, you have to operate on objects in memory. If you change that line to:
self.rooms.detect {|r| r.name == "Pager Pickup" }.initials.blank?
it should work but it is still prone to errors if you simply leave it in your model, as in another context there might not be a room with that name and .initials will still be called on nil. I'd suggest you move such logic to factory objects, where you can strictly tie your validations to the context.
In order to fully understand the concept, you may run this in rails console:
s = Schedule.new
# => #<Schedule id: nil>
s.rooms << Room.new(foo: "bar")
# => #<ActiveRecord::Associations::CollectionProxy [#<Room id: nil, schedule_id: nil, foo: "bar">]>
s.rooms.where(foo: "bar")
# => #<ActiveRecord::AssociationRelation >
s.rooms.detect { |r| r.foo == "bar" }
# => #<Room id: nil, schedule_id: nil, foo: "bar">
Note: Your "hack" with .last worked because it operated on an array, not an ActiveRecord::Relation.
Excellent! Thank you! Problem solved. Thought that the issue lay with whetherwherewas able to act on object not yet saved to database but didn't know about detect and select. Very grateful. Tried to award +50 points but SO says must wait 22 hours.... Will do so when able, though.
– BenU
Nov 23 at 15:56
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
If I'm reading your code correctly, you have an unsaved object and you're trying to run this validation on it:
self.rooms.where(name: "Pager Pickup").first.initials.blank?
The problem with this approach is that .where on association will run a database query (or to be specific, it'd run the query if your objects were saved, but will do nothing for unsaved relations). This won't work for you, you haven't saved anything yet, you have to operate on objects in memory. If you change that line to:
self.rooms.detect {|r| r.name == "Pager Pickup" }.initials.blank?
it should work but it is still prone to errors if you simply leave it in your model, as in another context there might not be a room with that name and .initials will still be called on nil. I'd suggest you move such logic to factory objects, where you can strictly tie your validations to the context.
In order to fully understand the concept, you may run this in rails console:
s = Schedule.new
# => #<Schedule id: nil>
s.rooms << Room.new(foo: "bar")
# => #<ActiveRecord::Associations::CollectionProxy [#<Room id: nil, schedule_id: nil, foo: "bar">]>
s.rooms.where(foo: "bar")
# => #<ActiveRecord::AssociationRelation >
s.rooms.detect { |r| r.foo == "bar" }
# => #<Room id: nil, schedule_id: nil, foo: "bar">
Note: Your "hack" with .last worked because it operated on an array, not an ActiveRecord::Relation.
If I'm reading your code correctly, you have an unsaved object and you're trying to run this validation on it:
self.rooms.where(name: "Pager Pickup").first.initials.blank?
The problem with this approach is that .where on association will run a database query (or to be specific, it'd run the query if your objects were saved, but will do nothing for unsaved relations). This won't work for you, you haven't saved anything yet, you have to operate on objects in memory. If you change that line to:
self.rooms.detect {|r| r.name == "Pager Pickup" }.initials.blank?
it should work but it is still prone to errors if you simply leave it in your model, as in another context there might not be a room with that name and .initials will still be called on nil. I'd suggest you move such logic to factory objects, where you can strictly tie your validations to the context.
In order to fully understand the concept, you may run this in rails console:
s = Schedule.new
# => #<Schedule id: nil>
s.rooms << Room.new(foo: "bar")
# => #<ActiveRecord::Associations::CollectionProxy [#<Room id: nil, schedule_id: nil, foo: "bar">]>
s.rooms.where(foo: "bar")
# => #<ActiveRecord::AssociationRelation >
s.rooms.detect { |r| r.foo == "bar" }
# => #<Room id: nil, schedule_id: nil, foo: "bar">
Note: Your "hack" with .last worked because it operated on an array, not an ActiveRecord::Relation.
edited Nov 23 at 14:43
answered Nov 23 at 14:27
Marcin Kołodziej
4,067315
4,067315
Excellent! Thank you! Problem solved. Thought that the issue lay with whetherwherewas able to act on object not yet saved to database but didn't know about detect and select. Very grateful. Tried to award +50 points but SO says must wait 22 hours.... Will do so when able, though.
– BenU
Nov 23 at 15:56
add a comment |
Excellent! Thank you! Problem solved. Thought that the issue lay with whetherwherewas able to act on object not yet saved to database but didn't know about detect and select. Very grateful. Tried to award +50 points but SO says must wait 22 hours.... Will do so when able, though.
– BenU
Nov 23 at 15:56
Excellent! Thank you! Problem solved. Thought that the issue lay with whether
where was able to act on object not yet saved to database but didn't know about detect and select. Very grateful. Tried to award +50 points but SO says must wait 22 hours.... Will do so when able, though.– BenU
Nov 23 at 15:56
Excellent! Thank you! Problem solved. Thought that the issue lay with whether
where was able to act on object not yet saved to database but didn't know about detect and select. Very grateful. Tried to award +50 points but SO says must wait 22 hours.... Will do so when able, though.– BenU
Nov 23 at 15:56
add a comment |
up vote
2
down vote
The problem that you are facing is that you are trying to do a query on a model that is not yet saved into the database. It does not have an id and the associated models rooms do not yet have an id themselves either.
Your validation throws an error because .where calls the database and it can't find the models, as they currently live only in memory.
self.rooms.where(name: "Pager Pickup").first.initials.blank?
However, you have the information on both models to do the validation correctly, you are just looking for it in the wrong place.
If you debug your app so that your instance of Schedule is built (not saved yet) and you add some rooms to it (not yet saved either), you will see the following behavior:
@schedule.rooms.length # It will be some value bigger than 0
@schedule.rooms.count # It will be zero
Why? Because .length works on the object as an array and .count searches for it in the database. As I mentioned before, your models do not exist on the DB so it wont find them, but they are in memory so you can measure the length of it.
The change you need to do in this problem is to imitate what .where does but in memory, with a simple .select:
self.rooms.select { |r| r.name == 'Pager Pickup' }.first.initials.blank?
That is the only change you need to do, but you should understand why.
.detect { }is a bit nicer than.select { }.first, but still a valid answer.
– Marcin Kołodziej
Nov 23 at 14:39
This works and thank you for your answer and explanation. Selected @MarcinKołodziej because submitted a couple minutes before yours.... Very grateful, though.
– BenU
Nov 23 at 15:57
Yeah I noticed the difference in time. We're glad it worked!
– byrdEmmanuel
Nov 23 at 16:04
add a comment |
up vote
2
down vote
The problem that you are facing is that you are trying to do a query on a model that is not yet saved into the database. It does not have an id and the associated models rooms do not yet have an id themselves either.
Your validation throws an error because .where calls the database and it can't find the models, as they currently live only in memory.
self.rooms.where(name: "Pager Pickup").first.initials.blank?
However, you have the information on both models to do the validation correctly, you are just looking for it in the wrong place.
If you debug your app so that your instance of Schedule is built (not saved yet) and you add some rooms to it (not yet saved either), you will see the following behavior:
@schedule.rooms.length # It will be some value bigger than 0
@schedule.rooms.count # It will be zero
Why? Because .length works on the object as an array and .count searches for it in the database. As I mentioned before, your models do not exist on the DB so it wont find them, but they are in memory so you can measure the length of it.
The change you need to do in this problem is to imitate what .where does but in memory, with a simple .select:
self.rooms.select { |r| r.name == 'Pager Pickup' }.first.initials.blank?
That is the only change you need to do, but you should understand why.
.detect { }is a bit nicer than.select { }.first, but still a valid answer.
– Marcin Kołodziej
Nov 23 at 14:39
This works and thank you for your answer and explanation. Selected @MarcinKołodziej because submitted a couple minutes before yours.... Very grateful, though.
– BenU
Nov 23 at 15:57
Yeah I noticed the difference in time. We're glad it worked!
– byrdEmmanuel
Nov 23 at 16:04
add a comment |
up vote
2
down vote
up vote
2
down vote
The problem that you are facing is that you are trying to do a query on a model that is not yet saved into the database. It does not have an id and the associated models rooms do not yet have an id themselves either.
Your validation throws an error because .where calls the database and it can't find the models, as they currently live only in memory.
self.rooms.where(name: "Pager Pickup").first.initials.blank?
However, you have the information on both models to do the validation correctly, you are just looking for it in the wrong place.
If you debug your app so that your instance of Schedule is built (not saved yet) and you add some rooms to it (not yet saved either), you will see the following behavior:
@schedule.rooms.length # It will be some value bigger than 0
@schedule.rooms.count # It will be zero
Why? Because .length works on the object as an array and .count searches for it in the database. As I mentioned before, your models do not exist on the DB so it wont find them, but they are in memory so you can measure the length of it.
The change you need to do in this problem is to imitate what .where does but in memory, with a simple .select:
self.rooms.select { |r| r.name == 'Pager Pickup' }.first.initials.blank?
That is the only change you need to do, but you should understand why.
The problem that you are facing is that you are trying to do a query on a model that is not yet saved into the database. It does not have an id and the associated models rooms do not yet have an id themselves either.
Your validation throws an error because .where calls the database and it can't find the models, as they currently live only in memory.
self.rooms.where(name: "Pager Pickup").first.initials.blank?
However, you have the information on both models to do the validation correctly, you are just looking for it in the wrong place.
If you debug your app so that your instance of Schedule is built (not saved yet) and you add some rooms to it (not yet saved either), you will see the following behavior:
@schedule.rooms.length # It will be some value bigger than 0
@schedule.rooms.count # It will be zero
Why? Because .length works on the object as an array and .count searches for it in the database. As I mentioned before, your models do not exist on the DB so it wont find them, but they are in memory so you can measure the length of it.
The change you need to do in this problem is to imitate what .where does but in memory, with a simple .select:
self.rooms.select { |r| r.name == 'Pager Pickup' }.first.initials.blank?
That is the only change you need to do, but you should understand why.
answered Nov 23 at 14:31
byrdEmmanuel
807215
807215
.detect { }is a bit nicer than.select { }.first, but still a valid answer.
– Marcin Kołodziej
Nov 23 at 14:39
This works and thank you for your answer and explanation. Selected @MarcinKołodziej because submitted a couple minutes before yours.... Very grateful, though.
– BenU
Nov 23 at 15:57
Yeah I noticed the difference in time. We're glad it worked!
– byrdEmmanuel
Nov 23 at 16:04
add a comment |
.detect { }is a bit nicer than.select { }.first, but still a valid answer.
– Marcin Kołodziej
Nov 23 at 14:39
This works and thank you for your answer and explanation. Selected @MarcinKołodziej because submitted a couple minutes before yours.... Very grateful, though.
– BenU
Nov 23 at 15:57
Yeah I noticed the difference in time. We're glad it worked!
– byrdEmmanuel
Nov 23 at 16:04
.detect { } is a bit nicer than .select { }.first, but still a valid answer.– Marcin Kołodziej
Nov 23 at 14:39
.detect { } is a bit nicer than .select { }.first, but still a valid answer.– Marcin Kołodziej
Nov 23 at 14:39
This works and thank you for your answer and explanation. Selected @MarcinKołodziej because submitted a couple minutes before yours.... Very grateful, though.
– BenU
Nov 23 at 15:57
This works and thank you for your answer and explanation. Selected @MarcinKołodziej because submitted a couple minutes before yours.... Very grateful, though.
– BenU
Nov 23 at 15:57
Yeah I noticed the difference in time. We're glad it worked!
– byrdEmmanuel
Nov 23 at 16:04
Yeah I noticed the difference in time. We're glad it worked!
– byrdEmmanuel
Nov 23 at 16:04
add a comment |
up vote
0
down vote
Ok so first of all
self.rooms.where(name: "Pager Pickup")
is potentially going to return multiple objects so it doesn't give you a Room object but probably an ActiveRecord::Relation object so you would need to add something like .first before .initials to get a Room like so:
self.rooms.where(name: "Pager Pickup").first.initials.blank?
But anyway the error states that it is not finding anything so it seems that at the time of validation the rooms relationship for this schedule doesn't exist or is "empty" since it is claiming nil is returned from that query instead of an empty array so i'm guessing that your validation occurs before the room is actually created/saved.
Maybe show us the create action of the SchedulesController to see if something is wrong there.
UPDATE
After seeing your extra code, the problem is that you are calling add_rooms to a different schedule object than the one you are creating, I'm guessing your are on the impression that instance variables (variables starting with @) are persisted between requests but they are not, so the @schedule object in your create action is not the same one as in your new action therefore it doesn't have any rooms yet, update your create action to populate the rooms of that object doing something like this:
def create
@schedule = current_user.schedules.build(schedule_params)
@schedule.add_rooms # <-- Add this line
if @schedule.save
flash.now[:success] = "Draft Schedule Saved! Now Confirm or Edit."
render :show
else
render :new
end
Thanks, @Julien. Added the .first and as you predicted it still doesn't work. I edited my answer to include the SchedulesController#Create and other methods in the Schedule Model that may include my error. Thanks so much for your insight.
– BenU
Nov 9 at 14:58
when isadd_roomscalled? i don't see it in thecreateaction and i'm guessing its not in theschedule_paramsso where do you call it?
– Julien
Nov 10 at 2:18
add_roomsis called in the SchedulesController#New action. I added it.
– BenU
Nov 10 at 12:14
well there's your problem, you are adding the rooms on a different schedule object than the one you are creating, you need to add the rooms to your@scheduleobject in thecreateaction before callingsaveon it.
– Julien
Nov 11 at 19:01
I've edited my answer to include some details regarding my previous comment.
– Julien
Nov 11 at 19:18
|
show 2 more comments
up vote
0
down vote
Ok so first of all
self.rooms.where(name: "Pager Pickup")
is potentially going to return multiple objects so it doesn't give you a Room object but probably an ActiveRecord::Relation object so you would need to add something like .first before .initials to get a Room like so:
self.rooms.where(name: "Pager Pickup").first.initials.blank?
But anyway the error states that it is not finding anything so it seems that at the time of validation the rooms relationship for this schedule doesn't exist or is "empty" since it is claiming nil is returned from that query instead of an empty array so i'm guessing that your validation occurs before the room is actually created/saved.
Maybe show us the create action of the SchedulesController to see if something is wrong there.
UPDATE
After seeing your extra code, the problem is that you are calling add_rooms to a different schedule object than the one you are creating, I'm guessing your are on the impression that instance variables (variables starting with @) are persisted between requests but they are not, so the @schedule object in your create action is not the same one as in your new action therefore it doesn't have any rooms yet, update your create action to populate the rooms of that object doing something like this:
def create
@schedule = current_user.schedules.build(schedule_params)
@schedule.add_rooms # <-- Add this line
if @schedule.save
flash.now[:success] = "Draft Schedule Saved! Now Confirm or Edit."
render :show
else
render :new
end
Thanks, @Julien. Added the .first and as you predicted it still doesn't work. I edited my answer to include the SchedulesController#Create and other methods in the Schedule Model that may include my error. Thanks so much for your insight.
– BenU
Nov 9 at 14:58
when isadd_roomscalled? i don't see it in thecreateaction and i'm guessing its not in theschedule_paramsso where do you call it?
– Julien
Nov 10 at 2:18
add_roomsis called in the SchedulesController#New action. I added it.
– BenU
Nov 10 at 12:14
well there's your problem, you are adding the rooms on a different schedule object than the one you are creating, you need to add the rooms to your@scheduleobject in thecreateaction before callingsaveon it.
– Julien
Nov 11 at 19:01
I've edited my answer to include some details regarding my previous comment.
– Julien
Nov 11 at 19:18
|
show 2 more comments
up vote
0
down vote
up vote
0
down vote
Ok so first of all
self.rooms.where(name: "Pager Pickup")
is potentially going to return multiple objects so it doesn't give you a Room object but probably an ActiveRecord::Relation object so you would need to add something like .first before .initials to get a Room like so:
self.rooms.where(name: "Pager Pickup").first.initials.blank?
But anyway the error states that it is not finding anything so it seems that at the time of validation the rooms relationship for this schedule doesn't exist or is "empty" since it is claiming nil is returned from that query instead of an empty array so i'm guessing that your validation occurs before the room is actually created/saved.
Maybe show us the create action of the SchedulesController to see if something is wrong there.
UPDATE
After seeing your extra code, the problem is that you are calling add_rooms to a different schedule object than the one you are creating, I'm guessing your are on the impression that instance variables (variables starting with @) are persisted between requests but they are not, so the @schedule object in your create action is not the same one as in your new action therefore it doesn't have any rooms yet, update your create action to populate the rooms of that object doing something like this:
def create
@schedule = current_user.schedules.build(schedule_params)
@schedule.add_rooms # <-- Add this line
if @schedule.save
flash.now[:success] = "Draft Schedule Saved! Now Confirm or Edit."
render :show
else
render :new
end
Ok so first of all
self.rooms.where(name: "Pager Pickup")
is potentially going to return multiple objects so it doesn't give you a Room object but probably an ActiveRecord::Relation object so you would need to add something like .first before .initials to get a Room like so:
self.rooms.where(name: "Pager Pickup").first.initials.blank?
But anyway the error states that it is not finding anything so it seems that at the time of validation the rooms relationship for this schedule doesn't exist or is "empty" since it is claiming nil is returned from that query instead of an empty array so i'm guessing that your validation occurs before the room is actually created/saved.
Maybe show us the create action of the SchedulesController to see if something is wrong there.
UPDATE
After seeing your extra code, the problem is that you are calling add_rooms to a different schedule object than the one you are creating, I'm guessing your are on the impression that instance variables (variables starting with @) are persisted between requests but they are not, so the @schedule object in your create action is not the same one as in your new action therefore it doesn't have any rooms yet, update your create action to populate the rooms of that object doing something like this:
def create
@schedule = current_user.schedules.build(schedule_params)
@schedule.add_rooms # <-- Add this line
if @schedule.save
flash.now[:success] = "Draft Schedule Saved! Now Confirm or Edit."
render :show
else
render :new
end
edited Nov 11 at 19:18
answered Nov 8 at 22:32
Julien
8061028
8061028
Thanks, @Julien. Added the .first and as you predicted it still doesn't work. I edited my answer to include the SchedulesController#Create and other methods in the Schedule Model that may include my error. Thanks so much for your insight.
– BenU
Nov 9 at 14:58
when isadd_roomscalled? i don't see it in thecreateaction and i'm guessing its not in theschedule_paramsso where do you call it?
– Julien
Nov 10 at 2:18
add_roomsis called in the SchedulesController#New action. I added it.
– BenU
Nov 10 at 12:14
well there's your problem, you are adding the rooms on a different schedule object than the one you are creating, you need to add the rooms to your@scheduleobject in thecreateaction before callingsaveon it.
– Julien
Nov 11 at 19:01
I've edited my answer to include some details regarding my previous comment.
– Julien
Nov 11 at 19:18
|
show 2 more comments
Thanks, @Julien. Added the .first and as you predicted it still doesn't work. I edited my answer to include the SchedulesController#Create and other methods in the Schedule Model that may include my error. Thanks so much for your insight.
– BenU
Nov 9 at 14:58
when isadd_roomscalled? i don't see it in thecreateaction and i'm guessing its not in theschedule_paramsso where do you call it?
– Julien
Nov 10 at 2:18
add_roomsis called in the SchedulesController#New action. I added it.
– BenU
Nov 10 at 12:14
well there's your problem, you are adding the rooms on a different schedule object than the one you are creating, you need to add the rooms to your@scheduleobject in thecreateaction before callingsaveon it.
– Julien
Nov 11 at 19:01
I've edited my answer to include some details regarding my previous comment.
– Julien
Nov 11 at 19:18
Thanks, @Julien. Added the .first and as you predicted it still doesn't work. I edited my answer to include the SchedulesController#Create and other methods in the Schedule Model that may include my error. Thanks so much for your insight.
– BenU
Nov 9 at 14:58
Thanks, @Julien. Added the .first and as you predicted it still doesn't work. I edited my answer to include the SchedulesController#Create and other methods in the Schedule Model that may include my error. Thanks so much for your insight.
– BenU
Nov 9 at 14:58
when is
add_rooms called? i don't see it in the create action and i'm guessing its not in the schedule_params so where do you call it?– Julien
Nov 10 at 2:18
when is
add_rooms called? i don't see it in the create action and i'm guessing its not in the schedule_params so where do you call it?– Julien
Nov 10 at 2:18
add_rooms is called in the SchedulesController#New action. I added it.– BenU
Nov 10 at 12:14
add_rooms is called in the SchedulesController#New action. I added it.– BenU
Nov 10 at 12:14
well there's your problem, you are adding the rooms on a different schedule object than the one you are creating, you need to add the rooms to your
@schedule object in the create action before calling save on it.– Julien
Nov 11 at 19:01
well there's your problem, you are adding the rooms on a different schedule object than the one you are creating, you need to add the rooms to your
@schedule object in the create action before calling save on it.– Julien
Nov 11 at 19:01
I've edited my answer to include some details regarding my previous comment.
– Julien
Nov 11 at 19:18
I've edited my answer to include some details regarding my previous comment.
– Julien
Nov 11 at 19:18
|
show 2 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53208209%2frails-validation-error-nested-object-undefined-method-for-nilnilclass%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Do you have a room record with the name Pager Pickup?
– Pavan
Nov 8 at 13:00
Good question, @Pavan. Yes, I'm pretty confident I've been careful to make sure that a typo like a spelling or case error isn't preventing a room named "Pager Pickup" from existing.
– BenU
Nov 8 at 13:04