I have an app structure with nested routes in which a proposal belongs to a request and a request has many proposals.
When I execute a send_proposal method, I am trying to get it to update the status of the request to which that proposal belongs, but I am getting an error that says undefined method 'request' for true:TrueClass.
My route for this method (not that I think it matters) is:
put "proposal/:id/send_proposal" => "proposals#send_proposal", as: "send_proposal"
Here is my send_proposal method, as found in my proposals_controller:
def send_proposal
#proposal = Proposal.find(params[:id])
ProposalMailer.send_proposal_to_client(#proposal, #proposal.request.user).deliver_now
#proposal = #proposal.update_attributes(status: "Sent to Client")
#proposal.request = #proposal.request.update_attributes(archived: "Proposal Sent to Client") <<<<<<<<<ERROR CALLED ON THIS LINE
flash[:notice] = "Your proposal has been sent to the client!"
end
I have looked at many SO posts for other TrueClass errors, but can't seem to find one with a problem like this. Can anyone see what I'm doing wrong or help me conceptualize what TrueClass errors are generally about?
update_attributes is an alias for update:
update(attributes)
Updates the attributes of the model from the passed-in hash and saves the record, all wrapped in a transaction. If the object is invalid, the saving will fail and false will be returned.
and update returns true or false (the documentation could be a lot more explicit about this) not the updated model instance. So this:
#proposal = #proposal.update_attributes(status: "Sent to Client")
will leave #proposal as true or false and neither of those have an update_attributes method.
Your controller method should look more like this:
def send_proposal
#...
#proposal.update(status: "Sent to Client"))
#proposal.request.update(archived: "Proposal Sent to Client")
#...
end
You probably want to do some error checking on those two update calls too.
Related
I've got an app where users submit weeks which can be approved or denied, and in my weeks controller I have the following lines meant to iterate over the selected weeks, find their corresponding users and send each user an email:
elsif params[:commit] == "Reject selected weeks"
user_week = Week.where(id: params[:weeks_ids])
user_week.update_all(approved?: false)
# fetch the set of user_emails by converting the user_weeks to user_ids
users = User.find(user_week.pluck(:user_id))
users.each do |user|
#iterate over the users and send each one an email
UserMailer.send_rejection(user).deliver
end
flash[:info] = "Selected weeks were Rejected."
end
redirect_to weeks_path
When I attempt to reject a week, I receive the following error message:
undefined method `send_rejection' for UserMailer:Class
I'm adding on to pre-existing code and have little knowledge of MVC, so the only issues I can think of would be with placing the mailer method in the wrong file or sending an incorrect type of arg to the mailer method.
Here is "send_rejection", the mailer contained in my user model.
def send_rejection(user)
UserMailer.reject_timesheet(user).deliver_now
end
The corresponding method in my user_mailer.rb file:
def reject_timesheet(user)
#greeting = "Hi"
mail to: user.email, subject: "Rejected Timesheet"
end
New to rails and not sure where I'm going wrong.
This is not a problem of MVC, one question I'd probably ask is why are you not calling the reject_timesheet directly instead of send_rejection.
You're getting the error because as you said the method is defined in the user model, so in order to call the method, you'd need to do:
user.send_rejection
In which case I doubt you'd be needing to pass a user argument to the send_rejection, as you could just do:
class User
def send_rejection
UserMailer.reject_timesheet(self).deliver_now
end
end
then in your controller:
...
users.each do |user|
#iterate over the users and send each one an email
user.send_rejection
end
...
I believe you could also clean up your codebase a bit and possibly refactor some logic, but basically this approach should resolve your errors.
Let me know if that helps
I'm trying to update the attributes of an Object, but often the Object i try to update doesn't exist anymore.
E.g.: I'm post-processing a CSV file to get the attributes:
array.each do |a|
player = Player.find_by_grepo_id(a[:grepo_id])
player.update_attributes(a)
end
That will throw an error when a Player is not found.
What i learned from previous experiences:
ActiveRecord::Base.find always throws an exception if it does not find a record, this is intentional.
I should only use find if I'm absolutely expect to have whatever it is I'm looking for.
If I'm rending a show action and can't find the article, I should rescue that exception and render a 404 (Not found)
instead of redirecting to index (technically).
If I want to find something by it's id attribute without forcing an exception, I should use the dynamic finder
find_by_id (In my case find_by_grepo_id) which will return false if it doesn't find a record with that id.
But upon running the task which contains the above code i get
NoMethodError: undefined method `update_attributes' for nil:NilClass
That's because a Player with that specific id doesn't exist anymore. If i wrap the update_attributes call in a .present? method it works.
What am i missing? Shouldn't the find_by_id method NOT throw an error and just skip it ?
If you want to do it in one call instead of two, you can use the update_all method like this:
Player.where(:grepo_id => a[:grepo_id]).update_all(a)
This will result in the following SQL:
UPDATE players SET ... = ..., ... = ... WHERE players.grepo_id = ...
Also works if the grepo_id doesn't exist: nothing will get updated. Note however that this just runs the SQL; any validations or callbacks on your model are ignored.
This is due to you are doing update_attributes even if it does not find the record by grepo_id. find_by_grepo_id returns nil if it does not find any record. So you need to add a condition to get rid of this error.
array.each do |a|
player = Player.find_by_grepo_id(a[:grepo_id])
player.update_attributes(a) if player.present?
end
Rails has a try method (check the docs) and you can use it here:
array.each do |a|
player = Player.find_by_grepo_id(a[:grepo_id])
player.try do |p|
p.update_attributes(a)
end
end
This should work fine and updating attribute or silently failing (without throwing exceptions) when no record is found.
I'm just throwing this out there because I really can't figure this out. When I call for instance user.articles.create! { title: 'blah' } nil is returned but the object is created. I've not seen anything like this before and was wondering if someone else has?
I've tried rails 3.2.13 and 3.2.12 and they both do the same thing.
EDIT
In active record both create and create! ends up IN THIS METHOD that is supposed to return the record or throw an exception.
def create_record(attributes, options, raise = false, &block)
unless owner.persisted?
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
end
if attributes.is_a?(Array)
attributes.collect { |attr| create_record(attr, options, raise, &block) }
else
transaction do
add_to_target(build_record(attributes, options)) do |record|
yield(record) if block_given?
insert_record(record, true, raise)
end
end
end
end
If I'm not mistaken Factory Girl mimic the actual object you're dealing with through your predefined factory. Therefor User#articles might not return what you think it is when called on a factory.
Changing
user.articles.create! { title: 'blah' }
to
create(:article, user: user, title: 'blah')
should enforce the association through Factory Girl's interface.
I believe there is something going on with your attr_accessible or attr_accessor in your Article class. I you might have not included the user_id or something else...
There is also a similar question here: rails Model.create(:attr=>"value") returns model with uninitialized fields
I had the same symptom, and this question is the only relevant hit that I could find. I'll throw my solution into the mix in case it helps anyone else.
The code worked in real life, and only failed under rspec. All the troubleshooting I did made no sense, pointing to create! being broken, which I never believed.
As it turns out, I was mocking create! so it never got called. Adding .and_call_original to my mock solved the problem.
My model was something like this: (not really...but compatible with this answer)
class Flight < ApplicationRecord
has_many :seats
def create_seats(seat_count)
seat_count.times { Seat.create!(flight: self) }
seats.each(&:raise_seatback_and_lock_tray)
end
And my test was:
it 'creates enough empty seats' do
expect(LicenseFile).to receive(:create!).twice
flight.create_seats(2)
end
The expectation was met (confirmed manually), but an error was raised:
NoMethodError:
undefined method `raise_seatback_and_lock_tray=' for nil:NilClass
Changing my mock to allow create! to actually be called solved the problem:
it 'creates a LicenseFile for each destination rule' do
expect(LicenseFile).to receive(:create!).twice.and_call_original
flight.create_seats(2)
end
This now passed:
creates enough empty seats
1 example, 0 failures
If you are expecting the object to be returned use
user.articles.create { title: 'blah' }
Why some methods have bang (!), you can read this topic
Why are exclamation marks used in Ruby methods?
I have a custom validation on a model and I'm getting undefined method which makes no sense.
Here's the code
validate :unique_seo_url_from_title
def unique_seo_url_from_title
url = "#{title.parameterize}-#{region.parameterize}-#{country}"
errors.add(:title, "already in use") if SeoMapping.find_by_seo_url(url)
end
Strange thing is if I output the url ie raise url it prints it perfectly so its working.
Hope someone can advise!
Since you didn't pass parameters to the function, it seems that title, region and country are attributes of model for which validation is used. So use self for the object
validate :unique_seo_url_from_title
def unique_seo_url_from_title
url = "#{self.title.parameterize}-#{self.region.parameterize}-#{self.country}"
errors.add(:title, "already in use") if SeoMapping.find_by_seo_url(url)
end
Thanks
undefined method `parameterize' for nil:NilClass
The message means exactly what it says. You have an object which is nil, and you call parameterize on it -- which fails because that method is defined on String, not NilClass.
Check that the values for title and region are present. If not, there's your problem.
My validations were working for a while, or so I thought. Now I come back to them after a while doing something else and I am getting the error above. I think it means I am creating a nil object with the .new method but I am just lost. It seemed to be a problem with the creation of a new object by the controller because even if I # out the validation the next validation in the tree on another attribute of #thing throws up the same error. However even if I create the object in the console and test it's there the .save method throws up the error still - undefined method 'user_id' for nil:NilClass
ThingsController:
def create
#thing = Thing.new(params[:thing])
#thing.user_id = #currentuser_id
if #thing.save
flash[:notice] = "Successfully created thing."
redirect_to #thing
else
#flash[:notice] = "Your thing did not get created."
render 'otherthings/show'
end
end
Thing.rb
validate :user_valid
def user_valid
errors.add("you must be logged in to add a thing") unless #thing.user_id?
end
I'm a bit of a ruby on rails noob (i.e. <8weeks) and this is my first stackoverflow question, so go easy on me if this is stupidly obvious. I've tried params["thing"] too, as that worked in the console after manually creating params to match the log files (whereas params [:thing] didn't) but that doesn't change anything in terms of the error message I get.
When you are calling unless #thing.user_id?, it's flipping out because there's no #thing (it doesn't get passed from your controller to your model as an instance variable).
I don't remember how exactly it works, but I'm pretty sure that when calling validate :user_valid, it will pass along the record to be validated for you. If that's indeed the case you could try:
def user_valid
errors.add("you must be logged in to add a thing") unless user_id?
end