This is the error:
NoMethodError in VideosController#update
undefined method `each' for #<Topic:0x1032ee330>
This is the application trace:
app/models/video.rb:19:in `assign_topics'
app/controllers/videos_controller.rb:41:in `update'
app/controllers/videos_controller.rb:40:in `update'
this is my assign_topics method:
def assign_topics
if #topic_names
self.topics = Topic.find_or_create_by_name(#topic_names)
end
end
Note that I'm following this: http://media.railscasts.com/videos/167_more_on_virtual_attributes.mov
Here's the video controller's update method:
def update
#video = current_user.videos.find(params[:id])
respond_to do |format|
if #video.update_attributes(params[:video])
format.html { redirect_to(#video, :notice => 'Video was successfully updated.') }
else
format.html { render :action => "edit" }
end
end
end
I'd guess that your assign_topics method is at fault. Topic.find_or_create_by_name will return a single Topic instance, then you assign that to self.topics and self.topics is probably expecting an Array (or some other Enumerble); then later, the update process will try to loop through self.topics using each and you get your error.
You mention, in a comment, that you tried something like this:
self.topics = #topic_names.each { |n| Topic.find_or_create_by_name(n) }
But that won't work because each returns the original array and so the above is equivalent to this:
#topic_names.each { |n| Topic.find_or_create_by_name(n) }
self.topics = #topic_names
and all the Topic instances that you found/created are simply thrown away.
So, you might have better luck using collect like this:
def assign_topics
if #topic_names
self.topics = #topic_names.collect { |n| Topic.find_or_create_by_name(n) }
end
end
You are getting a NoMethodError Exception because somewhere in your code you are trying to loop, via .each(), on something that is not an array/enumerable.
According to your exception, you are calling .each() on a Model Object(Topic), which would make sense to not have the .each() method.
Related
I have a rails form which takes the rails model FlsCenter and orders them alphabetically to be displayed in a drop down. When none of the fields of the form are filled out, and the form is submitted, the page gets a 500 Server Error in which the following Rails error is displayed:
undefined method `map' for nil:NilClass
Where the map function is in the following view code:
= f.select(:fls_center, #fls_centers.map{|p| [p.name, p.id]}, prompt: "Select a Program Center", selected:#school_application.fls_center)
Where #fls_centers is defined in the new method of my SchoolApplicationController here:
#fls_centers = FlsCenter.order('name ASC')
Here are the relevant controller methods from SchoolApplicationsController
def create
#school_application = SchoolApplication.new(school_application_params)
if #school_application.save_with_payment
redirect_to pay_path(id: #school_application.id)
else
#school_application.errors.full_messages.each do |msg|
flash.now[:error] = msg
end
render action: "new"
end
end
And here is my new method:
def new
if application_params
#school_application = SchoolApplication.new(application_params)
else
#school_application = SchoolApplication.new()
end
Rails.logger.debug(#fls_centers)
#fls_centers = FlsCenter.order('name ASC')
end
The only thing I can imagine that is going wrong is that render action: "new" does not execute the variable instantiation inside the new method. I do not know how to amerliorate this situation. Thank you
You are executing Rails.logger.debug(#fls_centers) before defining the #fls_centers, So make changes like shown below:
def new
#fls_centers = FlsCenter.order('name ASC')
if application_params
#school_application = SchoolApplication.new(application_params)
else
#school_application = SchoolApplication.new()
end
Rails.logger.debug(#fls_centers)
end
Hope it helps!
You guessed right. The actual new method does not get executed when you call
render action: "new"
You will have to call the variable instantiations all over again:
def create
#school_application = SchoolApplication.new(school_application_params)
if #school_application.save_with_payment
redirect_to pay_path(id: #school_application.id)
else
#school_application.errors.full_messages.each do |msg|
flash.now[:error] = msg
end
#fls_centers = FlsCenter.order('name ASC')
render action: "new"
end
end
If you have a lot of them it might be better to refactor them in a method and call that method in the new method as well as before the render call.
i try to test method create in my controller:
def create
#fund = Fund.new({started_at: Date.strptime(params[:fund].delete(:started_at), '%m/%d/%Y')}.merge(fund_params))
if #fund.save
flash[:alert] = "Fund #{#fund.name} saved!"
redirect_to funds_path
else
flash[:error] = "Fund #{#fund.name} could not be saved"
render :edit
end
end
file spec:
it 'Can create a new fund' do
fund = FactoryGirl.create(:fund)
post :create
expect(response).to redirect_to(funds_path)
end
And show this error:
NoMethodError: undefined method `delete' for nil:NilClass
The error is in this line, for the method 'delete':
#fund = Fund.new({started_at: Date.strptime(params[:fund].delete(:started_at),...
i don't know how solve this problem, Thanks.
As the error message says, that's because params[:fund] is nil. Try to send the proper parameters to the create action:
post :create, fund: { add_here_needed_params_for_this_action }
Example:
post :create, fund: { started_at: Date.today }
I have this code in my update action of Registration controller. And I get undefined method 'errors'. I can not use flash message here for some reason.
if subjects_selected.blank?
#registration = Registration.where(:student_id => params[:registration][:student_id], :semester_id => params[:registration][:semester_id] )
redirect_to editreg_registrations_path(#registration.first.id, params[:registration][:student_id], params[:registration][:semester_id]), #registration.errors.add(:You_have_to_register_for_at_least_one_subject) and return
end
How can I access error method here?
you can take the method in Registration model for error displaying the errors
like
error_array = Registration.validate_subjects(params[:registration][:student_id],params[:registration][:semester_id])
then in Registration model
def validate_subjects(student_id, semester_id)
is_registration = self.where(:student_id=>student_id,:semester_id =>semester_id)
error_array=[]
if !is_registration
//RIGHT THE CODE
error_array << "You_have_to_register_for_at_least_one_subjec"
end
error_array
end
if subjects_selected.blank?
#registration = Registration.where(:student_id => params[:registration][:student_id], :semester_id => params[:registration][:semester_id] )
if !#registration.errors
redirect_to editreg_registrations_path(#registration.first.id, params[:registration][:student_id], params[:registration][:semester_id])
else
#registration.errors.add(:You_have_to_register_for_at_least_one_subject)
end
end
I want to sort array from controller, that doesn't works, but throws no errors.
def my_published
#tests=Test.where(:user_id => current_user.id, :state=>'saved')
#tests=#tests.sort { |p1, p2| p1.rating <=> p2.rating }
respond_to do |format|
format.html
format.js{#tests}
end
end
Rating is an integer.
P.S. To display array I use each method.
Try this construction:
#test = Test.where(:user_id=>current_user.id, :state=>'saved').order('rating')
You can add the direction of order:
order('rating DESC') or order('rating ASC')
I've looked at similar posts but can't seem to quite figure it out.
I have the following function which works just fine. The Listing model has a foreign key called price_id which maps to the Price model and its price_range column. Price_id is returned as part of the message object in the JSON response.
How can I return the corresponding price_range value from the association instead of the price_id value (as part of the message obj, and keep the other attributes)?
def update
#listing = Listing.find(params[:listing][:id])
#if params were passed in for updating
if #listing.update_attributes(params[:listing])
#should we return the whole thing or just what's needed?
json_response = {
"success" => #listing.save, #save to DB and assign true/false based on success...
"message" => #listing.attributes #USE attributes to show output the content of the #message obj, and not another object called "message"
}
respond_to do |format|
#json response
format.html { render:json => json_response }
format.xml { render :xml => #listing }
#normal response. Consider leaving this for now?
#format.html { render :action => "detail" } #refresh this page, with new data in it. Consider trying to use redirect instead?
#format.xml { head :ok }
end
end #end if
end
add a method in your Listing model with the price_range and call it in serializable_hash
class Listing
def price_range
price.price_range
end
end
Like explain on comment you can use delegate instead this method :
class Listing
delegate :prince_range, :to => price
end
In you controller you can now do :
json_response = {
"success" => #listing.save, #save to DB and assign true/false based on success...
"message" => #listing.serializable_hash(:methods => [:price_range])
}
Based on what I read in this article, you should be able to do this:
class Listing
def as_json
super(:include => :price)
end
end
Then in your controller:
json_response = {
"success" => #listing.save,
"message" => #listing.as_json
}
If I understand correctly, you want to add #listing.price.price_range value to the "message" ?
If so, try this:
"message" => #listing.attributes[:price_range] = #listing.price.price_range