rails controller, undefined method error - based off edge guides - ruby-on-rails

I'm building a fairly simple recipe site to learn RoR, and I've been following the getting started guide, except that I've exchanged posts for recipes and comments for ingredients.
I got all the way to deleting a comment (ingredient) http://edgeguides.rubyonrails.org/getting_started.html#deleting-comments
now i'm getting an error
undefined method `recipe' for #
The line which in the partial which is causing the problem is here
<%= link_to 'Delete Ingredient', [ingredient.recipe_id, ingredient],
:confirm => 'Are you sure',
:method => :delete %>
The controller method (which I don't think has any effect, but I'm not completely sure) is
def destroy
#recipe = Recipe.find(params[:recipe_id])
#ingredient = #recipe.ingredient.find(params[:id])
#ingredient.destroy
redirect_to post_path(#recipe)
end
I use 'recipe_id' in the link_to because when I output the debug, it doesn't have a 'recipe' attribute, but has a recipe_id attribute.
The output of the debug is
--- !ruby/object:Ingredient
attributes:
id: 3
ingredient: testing
amount: 10
measure: "10"
description: "10"
recipe_id: 2
created_at: 2010-09-06 22:16:17.599217
updated_at: 2010-09-06 22:16:17.599217
attributes_cache: {}
changed_attributes: {}
destroyed: false
marked_for_destruction: false
new_record: false
previously_changed: {}
readonly: false
I'm assuming the [ingredient.recipe_id, ingredient] is simply a hash of the variables??
Is that correct? Am I coming at this from the wrong angle?

Shot in the dark, are your associations proper ? Kinda like:
class Ingredient < ActiveRecord::Base
belongs_to :recipe
...
That will give you
#ingredient.recipe
Hope that helps.

Related

Saving arrays in Rails 4.2.3

I am having some trouble saving arrays in Rails.
Rails version: 4.2.3 | Ruby version: 2.2.1 | DB: PostgreSQL
In my view, I have a collection of check boxes that shows the conferences that my member attended.
<%= f.fields_for :conferences_member do |conference| %>
<%= collection_check_boxes(:conferences_member, :conference_id, #all_conferences, :id, :name)%>
<% end %>
I put a break point (binding.pry) after the create action in my MembersController, and surprisingly, it shows the selected check boxes:
Processing by Database::MembersController#create as HTML
Parameters: {"utf8"=>"✓","authenticity_token"=>"XYZ==",
[...] "conferences_member"=> {"conference_id"=>["3", "5", ""]}, [...]
Now, if I go to rails c, and type ConferencesMember.last to check what was saved, I get:
pry(main)> ConferencesMember.last
ConferencesMember Load (0.5ms) SELECT "conferences_members".* FROM
"conferences_members" ORDER BY "conferences_members"."id" DESC LIMIT 1
=> nil
These are my associations:
#=> member.rb
has_one :conferences_member
accepts_nested_attributes_for :conferences_member, allow_destroy: true, reject_if: :all_blank
#=> conferences_member.rb
serialize :conference_id, Array
belongs_to :member
#=> members_controller.rb
params.require(:member).permit( [...]
:conference_member_attributes => [:id, :member_id, :conference_id => []],
[...])
I want to thank you in advance. I've tried almost everything here on StackOverflow, but I don't see my error.
Thank you again.
EDIT:
More of my MembersController:
def new
#member = Member.new
#member.build_conferences_member
end
def create
#member = Member.new(member_params)
binding.pry
end
The log doesn't show any error, it just shows that conferences were not saved at all.
First, your field needs to be renamed to nest the :conference_id in :conferences_member_attributes (not in :conferences_member as you do now). Take advantage of the form object yielded by fields_for:
<%= f.fields_for :conferences_member do |conference| %>
<%= conference.collection_check_boxes :conference_id, #all_conferences, :id, :name %>
<% end %>
You also need to actually save the record in the create action: Member.new builds the record but does not save it. Typically, the create action branches based on whether the record saved or did not (due to validations). So you might rewrite this method like so:
def create
#member = Member.new(member_params)
# when #member.save returns true, it saved to the db successfully
if #member.save
redirect_to members_path, notice: "Member #{#member.id} saved!"
# otherwise, it didn't save because of a validation error, so we render the error
# to the user and give them a chance to fix it
else
flash[:error] = "Member didn't save: #{#member.errors.full_messages.to_sentence}"
render :new
end
end
Lastly, to make sure your data gets through your strong parameters, check your logs for any messages that parameters were filtered out. The messages look like:
Unpermitted parameters: your_favorite_attribute

Rails 4 - trouble with two associations, same model

I'm trying to make a simple movie database using Rails 4.0.0 as a learning project. I'm particularly interested in using scaffolding as much as possible, as this is one of the features that drew me to RoR in the first place.
Yes, I do realize the potential risks (Box 1.2.Scaffolding: Quicker, easier, more seductive), but I promise I won't have my project go public before I really understand what's going on beneath the hood. Right now I'm more in "evaluating technologies for my next super duper project"-mode.
Here's what I've got so far:
rails g scaffold Person name:string
rails g scaffold Movie name:string
Now, I could've done something like
rails g scaffold Movie name:string person_id:integer
instead, but I want a movie to be associated with both a director and an actor. (Next step is to make an association that relates multiple actors to a single movie, but I'm not quite there yet.)
So, I headed over to the blog post Creating Multiple Associations With the Same Table, describing pretty much what I need. It's a somewhat old post, so things might have changed now - I don't know. Anyway. This how I changed the models:
class Person < ActiveRecord::Base
has_many :movies
end
and
class Movie < ActiveRecord::Base
belongs_to :director_id, :class_name => 'Person', :foreign_key => 'person_id'
belongs_to :actor_id, :class_name => 'Person', :foreign_key => 'actor_id'
end
and finally the magical
rake db:migrate
Starting the WEBrick by running rails s in the console, I open my browser and start registering people
The time has come to start registering movies. According to previous questions here and here I have to make a migration script in order to create the necessary database fields. So this is what I did:
rails g migration AddPersonIdsToMovies director_id:integer actor_id:integer
I also updated the app/views/movies/_form.html.erb to
<%= form_for(#movie) do |f| %>
<% if #movie.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#movie.errors.count, "error") %> prohibited this movie from being saved:</h2>
<ul>
<% #movie.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :director_id %><br>
<%= f.select :director_id, Person.all.collect {|x| [x.name, x.id]}, {}, :multiple => false %>
</div>
<div class="field">
<%= f.label :actor_id %><br>
<%= f.select :actor_id, Person.all.collect {|x| [x.name, x.id]}, {}, :multiple => false %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
When I create a new movie, the view shows up fine and the select inputs works fine. However, the data in the director and actor field isn't persisted. I ran rails console and looked at the newly created movie:
irb(main):004:0> mov = Movie.first
Movie Load (0.2ms) SELECT "movies".* FROM "movies" ORDER BY "movies"."id"
ASC LIMIT 1 => #<Movie id: 1, name: "No such movie", created_at:
"2013-08-02 17:02:12", updated_at: "2013-08-02 17:02:12",
director_id: nil, actor_id: nil>
which is kind'a disappointing with no director or actor info.
Update
Based on #Mattherick's suggesition, I edited the private part of the movies_controller.rb to this:
# Never trust parameters from the scary internet, only allow the white list through.
def movie_params
params.require(:movie).permit(:name, :director_id, :actor_id)
end
Unfortunately, when I post a new movie I get
Person(#70319935588740) expected, got String(#70319918738480)
Extracted source:
# POST /movies.json
def create
#movie = Movie.new(movie_params)
respond_to do |format|
if #movie.save
and the request data goes as
{"utf8"=>"✓",
"authenticity_token"=>"???",
"movie"=>{"name"=>"Romantic Comedy",
"director_id"=>"2",
"actor_id"=>"1"},
"commit"=>"Create Movie"}
Update 2
I tried to create a new Movie in the rails console, like this:
irb(main):001:0> movie = Movie.new(name: "My fine movie", director_id: "1", actor_id: "2")
ActiveRecord::AssociationTypeMismatch: Person(#70311109773080) expected, got String(#70311102311480)
which is what you'd expect from the POST to the controller. This made me test what happened if I excluded the quotation marks for director_id and actor_id. So I did
irb(main):005:0> movie = Movie.new(name: "My fine movie", director_id: 1, actor_id: 2)
ActiveRecord::AssociationTypeMismatch: Person(#70282707507880) expected, got Fixnum(#70282677499540)
Still using the console, I decided to create an actor and a director
director = Person.new(name: "Woody Allen")
director.save
actor = Person.new(name: "Arnold Schwarzenegger")
actor.save
and then I did
movie = Movie.new(name: "I'd like to see that", director_id: director, actor_id: actor)
movie.save
which worked like a charm (output omitted for brevity). So the whole question boils down to "How can I pass a Person as the argument to director_id and actor_id through the web interface?"
If I had a single field in Movies called person_id: integer, I believe that rails would've inferred that I'm not trying to pass a string containing the id of a person, but rather I'm trying to pass an entire person object.
Update 3
I tested my suspicion that rails understands how to deal with posts when the foreign key column is named after the pattern [table]_id. So I created a new project with a Continent model and a Country model, where rails g scaffold Country name:string continent_id:integer. I changed my Country view to include
<div class="field">
<%= f.label :continent_id %><br>
<%= f.select :continent_id, Continent.all.collect {|x| [x.name, x.id]} %>
</div>
instead of the default numeric field. The continent_id is still posted a string:
Started POST "/countries" for 127.0.0.1 at 2013-08-03 10:40:40 +0200
Processing by CountriesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"???", "country"=>{"name"=>"Uganda", "continent_id"=>"4"}, "commit"=>"Create Country"}
yet rails understood that continent_id was the identifier of an entry in the Continent table.
Sadly, the inferrer mechanism doesn't work in my original case, as I have two associations from Movie to the Person table. I need to somehow make sure rails understand that there is a mapping from director_id to a Person.
Update 4
According to some sources, it seems as I need to refine the Person model further. So I did
class Person < ActiveRecord::Base
has_many :directed_movies, :class_name => 'Movie', :foreign_key => 'director_id'
has_many :acted_movies, :class_name => 'Movie', :foreign_key => 'actor_id'
end
but still not fixing my problems.
I'm kind'a lost. Can anyone give me a few pointers on what I'm missing here? Can anyone help me map from director_id to person_id? Thanks!
Ok, so I finally got it. I don't think this is the correct way to go about this, but at least it solved the problem. As I mentioned in update 2, I was able to create a Movie object in the irb, and so I asked the question "How can I pass a Person as the argument to director_id and actor_id through the web interface?"
According to the sources here and elsewhere, rails should've understood the has_many and belongs_to class methods. However, I just can't seem to get it to work.
So I hacked the create method in movies_controller.rb to read like this:
def create
director = Person.where(:id => movie_params[:director_id]).first
actor = Person.where(:id => movie_params[:actor_id]).first
#movie = Movie.new(name: movie_params[:name], director_id: director, actor_id: actor)
respond_to do |format|
if #movie.save
format.html { redirect_to #movie, notice: 'Movie was successfully created.' }
format.json { render action: 'show', status: :created, location: #movie }
else
format.html { render action: 'new' }
format.json { render json: #movie.errors, status: :unprocessable_entity }
end
end
end
This is certainly not as elegant as I'd like it to be, and I don't think it is the RoR way to do things. Unfortunately it is the only thing I got working so far, but if anyone else can make a multiple association to the same model using rails 4, please do give me a heads up! :)

Creating new child in nested routes

This one is driving me crazy. I've got a nested relationship between two models in my project, and I decided I did not want it to be shallow, since the child object (years) has no meaning outside the context of the parent (festivals).
So I sort of de-shallowed the relationship wherever I could find a reference to it, but I find myself unable to access the page to create a new child object.
Here's the url as I understand it should be: /festivals/1/years/new
from routes.rb:
resources :festivals do
resources :years
end
From years_controller.rb:
# GET festivals/1/years/new
# GET festivals/1/years/new.json
def new
#festival = Festival.find(params[:festival_id])
#year = #festival.years.build
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #year }
end
end
And the button users press to get to the New page (on the Show page for the parent object):
<%= link_to 'Add Year', new_festival_year_path(#festival), :class => 'btn' %>
That takes the user to the correct URL, but I get:
No route matches {:action=>"show", :controller=>"years", :festival_id=>#<Festival id: 7, name: "Improganza", founded: nil, logo: "", mission: "This is that one that people spend a lot of money t...", city: "Honolulu", state_code: "HI", country_code: "US", created_at: "2013-07-26 14:49:19", updated_at: "2013-07-26 14:49:19">}
I created a new Rails project and set up scaffolds using Akria Matsuda's nested_scaffold gem, just to compare that output with my code... the resulting files look as I've shown here. I have no idea what I might be missing.
Just for good measure, the output of my rake routes:
festival_years GET /festivals/:festival_id/years(.:format) years#index
POST /festivals/:festival_id/years(.:format) years#create
new_festival_year GET /festivals/:festival_id/years/new(.:format) years#new
edit_festival_year GET /festivals/:festival_id/years/:id/edit(.:format) years#edit
festival_year GET /festivals/:festival_id/years/:id(.:format) years#show
PUT /festivals/:festival_id/years/:id(.:format) years#update
DELETE /festivals/:festival_id/years/:id(.:format) years#destroy
festivals GET /festivals(.:format) festivals#index
POST /festivals(.:format) festivals#create
new_festival GET /festivals/new(.:format) festivals#new
edit_festival GET /festivals/:id/edit(.:format) festivals#edit
festival GET /festivals/:id(.:format) festivals#show
PUT /festivals/:id(.:format) festivals#update
DELETE /festivals/:id(.:format) festivals#destroy
GET /festivals(.:format) festivals#index
POST /festivals(.:format) festivals#create
GET /festivals/new(.:format) festivals#new
GET /festivals/:id/edit(.:format) festivals#edit
GET /festivals/:id(.:format) festivals#show
PUT /festivals/:id(.:format) festivals#update
DELETE /festivals/:id(.:format) festivals#destroy
Try this:
<%= link_to 'Add Year', new_festival_year_path(#festival.id, :class => 'btn' %>
or
<%= link_to 'Add Year', new_festival_year_path({festival_id: #festival.id}, :class => 'btn' %>
according to the error you're getting
:festival_id=>#<Festival id: 7, name: "Improganza", founded: nil, logo: "", mission: "This is that one that people spend a lot of money t...", city: "Honolulu", state_code: "HI", country_code: "US", created_at: "2013-07-26 14:49:19", updated_at: "2013-07-26 14:49:19">}
the router is getting your whole festival param as the input for :festival_id
I think you are merging together the #new and #year actions in the years_controller and that might be causing some problems.
# GET festivals/1/years/new
# GET festivals/1/years/new.json
def new
#festival = Festival.find(params[:festival_id])
#year = #festival.years.build
end
def create
#festival = Festival.find(params[:festival_id])
#year = #festival.years.create(...)
#...fill in the rest of the method...
end
You also should update your link:
<%= link_to 'Add Year', new_festival_year_path(festival_id: #festival), :class => 'btn' %>
I created a short quiz on nested resources that might be helpful.
The answer was fairly silly. In my Rails server log (which I need to train myself to pay more attention to), I saw the some lines indicating a problem in line 63 of my _form.html.erb partial.
That line was:
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
festival_year_path(#festival), :class => 'btn' %>
Oops. Why I ever decided the "Cancel" button should take you to a year (that, of course, would not exist) is beyond me. I changed it to festival_path(#festival) and it's all good.
Thanks, everyone, for your help. I'm a newcomer to StackOverflow and to Rails in general. It really makes me feel welcome that I got such quick responses!

Rails not generating validation fail messages

I have a number of standard rails validations within my model:
validates_presence_of :url_string
validates_uniqueness_of :url_string
validates_presence_of :stream_source
validates_presence_of :width
validates_presence_of :height
validates_presence_of :name
validates_uniqueness_of :name
validates_presence_of :customer_name
validates_presence_of :iframe_background_color
If I don't fill out one of these fields within my form then I am taken back to the form as expected but the odd thing is no error messages are displayed. I am using the code below to display the error messages:
<% #camera.errors.full_messages.each do |error| %>
<p><%= error %></p>
<% end %
I also attempted to print out the #camera.errors object and this is what is shown:
#<ActiveModel::Errors:0x12db19bc #base=#<Camera id: 1, stream_source: "test", width: 640, height: 360, active: true, name: "test", url_string: "CAYD19Vp", customer_name: "test", iframe_background_color: "#FFFFFF", online: true, created_at: "2011-08-30 15:54:16", updated_at: "2011-09-06 15:52:48", audio: true, iframe_text_color: "#FF00FF", iframe_link_color: "#FF0000", notes: "Some notes!", offline_image_file_name: "Cake.jpg", offline_image_content_type: "image/jpeg", offline_image_file_size: 196591, offline_image_updated_at: "2011-09-06 12:12:38", pull_stream_url: "test", bitrate: "300-500", show_branding: false>, #messages={}>
#
As you can see the messages hash is empty. I tried setting the validation error message manually by doing the following:
validates_presence_of :name, :message => "No name present"
but it did not populate the messages hash either.
Controller update action is shown below:
def update
#camera = Camera.find(params[:id])
if #camera.update_attributes(params[:camera])
flash[:notice] = "Camera updated"
redirect_to nwcadmin_camera_path
else
redirect_to :action => :edit
end
end
I am using Ruby version ruby 1.9.2p290 and Rails version 3.1.0.
Any assistance would be great!
Thanks
Just a heads up that you'll get a Validation failed (ActiveRecord::RecordInvalid) error with an empty error message (if there are no other errors) when you have before_validation declarations and any of them returns false.
Note that before_validation callbacks must not return false (nil is okay) and this can happen by accident, e.g., if you are assigning false to a boolean attribute in the last line inside that callback method. Explicitly write return true in your callback methods to make this work (or just true at the end if your callback is a block (as noted here)).
UPDATE: This will no longer be an issue starting Rails 5.0, as return false will no longer halt the callback chain (throw :abort will now halt the callback chain).
UPDATE: You might also receive ActiveRecord::RecordNotSaved: Failed to save the record if a callback returns false.
I managed to get to the bottom of my problem. In the controller I was using:
redirect_to :action => :edit
I should have been using:
render :action => :edit
By using redirect_to I was hitting the edit action within the controller which was then getting a new camera object from the database rather than preserving the current camera object from the update action.
Unless you call #camera.save or #camera.valid?, the errors hash will not be populated with the validation errors. Please check your controller code.
You can use flash[:message] or flash[:notice] in controller code to store the error message, which can be used in view to display the errors.Link Have a look in the link,it's clearly explained, how to append the error messages and use them to display.The instance variable doestnot contains any errors as no validation runs in update.
You can use #camera_errors = #camera.save to collect the errors and then
<% #camera_errors.errors.full_messages.each do |error| %>
<p><%= error %></p>
<% end %>
I'm not sure if this is something that you might be interested in or not, but you can use this official Rails gem: dynamic_form
This gem provides you two helper methods: error_messages and error_messages_for
Refer to the following Rails guide for more: http://guides.rubyonrails.org/active_record_validations_callbacks.html#displaying-validation-errors-in-the-view

foreign key removed on new/build

hopefully someone can help me understand why this is happening. i setup the following instances...
#product = Product.find(params[:id])
#new_image = #product.images.new
when i debug #new_image, it correctly has the foreign key set.
--- !ruby/object:Image
attributes:
product_id: 1
however, when saving, the product_id was not being set. that's when i noticed that also in that debug info, was this...
changed_attributes:
product_id:
basically nulling my foreign key. same thing if i use build. why isnt that instance not holding onto the foreign key?
UPDATE:
to make things simpler, even if i just output debug Product.find(1).images.new in my view, i get:
!ruby/object:ProductImage
attributes:
created_at:
product_id: 1
updated_at:
attributes_cache: {}
changed_attributes:
product_id:
destroyed: false
marked_for_destruction: false
new_record: true
previously_changed: {}
readonly: false
The way I understand it, your product's show view contains a form to post to the images controller. When you create the #new_image variable based on #product in your view, it properly assigned the product_id to the image. However, this does NOT persist when you post your form.
You have two options. The simplest would be to just add a <%= f.hidden_field :product_id %> item to your form, that way the product_id actually gets posted to Image#create. Alternatively, you could create a nested resource and do a <%= form_for [#product, #new_image] do |f| %> instead of the <%= form_for #new_image %> that you're probably using right now, and then in your create method do:
#product = Product.find(params[:product_id])
#new_image = #product.images.new(params[:image])

Resources