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!










share|improve this question
























  • 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















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!










share|improve this question
























  • 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













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!










share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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


















  • 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












3 Answers
3






active

oldest

votes

















up vote
2
down vote



accepted
+50










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.






share|improve this answer























  • 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


















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.






share|improve this answer





















  • .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


















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





share|improve this answer























  • 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










  • 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










  • I've edited my answer to include some details regarding my previous comment.
    – Julien
    Nov 11 at 19:18











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















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

























3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
2
down vote



accepted
+50










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.






share|improve this answer























  • 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















up vote
2
down vote



accepted
+50










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.






share|improve this answer























  • 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













up vote
2
down vote



accepted
+50







up vote
2
down vote



accepted
+50




+50




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.






share|improve this answer














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.







share|improve this answer














share|improve this answer



share|improve this answer








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 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
















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












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.






share|improve this answer





















  • .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















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.






share|improve this answer





















  • .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













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.






share|improve this answer












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.







share|improve this answer












share|improve this answer



share|improve this answer










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


















  • .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










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





share|improve this answer























  • 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










  • 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










  • I've edited my answer to include some details regarding my previous comment.
    – Julien
    Nov 11 at 19:18















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





share|improve this answer























  • 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










  • 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










  • I've edited my answer to include some details regarding my previous comment.
    – Julien
    Nov 11 at 19:18













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





share|improve this answer














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






share|improve this answer














share|improve this answer



share|improve this answer








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 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












  • 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


















  • 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










  • 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










  • 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


















draft saved

draft discarded




















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

List item for chat from Array inside array React Native

Thiostrepton

Caerphilly