Nested routes undefined variable Rails - ruby-on-rails

I am trying to build nested routes and as mentioned here I m trying to edit my boat picture as <%= link_to "edit", edit_boat_picture_path(#boat, picture) %>. But when I try it, it throws an error undefined local variable or methodpicture' for #<#:0x007f9637811ee0>`
my picture controller is; (probably destroy is wrong also)
class PicturesController < ApplicationController
before_action :logged_in_user
before_filter :load_parent
def index
#picture = #boat.pictures.all
end
def new
#picture = #boat.pictures.new
end
def show
#pictures = #boat.pictures.find(params[:id])
end
def create
#picture = #boat.pictures.new(picture_params)
if #picture.save
#flash[:success] = "Continue from here"
render 'show'
else
render 'new'
end
end
def edit
#picture = Picture.find(params[:id])
end
def update
#picture = #boat.pictures.find(params[:id])
if #picture.update_attributes(picture_params)
flash[:notice] = "Successfully updated picture."
render 'show'
else
render 'edit'
end
end
def destroy
#picture = #boat.pictures.find(params[:id])
#picture.destroy
flash[:notice] = "Successfully destroyed picture."
redirect_to #picture.boat
end
private
def picture_params
params.require(:picture).permit(:name, :image)
end
def load_parent
#boat = Boat.find(params[:boat_id])
end
end

Presumably you should change
<%= link_to "edit", edit_boat_picture_path(#boat, picture) %>
to
<%= link_to "edit", edit_boat_picture_path(#boat, #picture) %>
The key there being changing picture to #picture. The reason to do this is that you're declaring #picture (an instance variable) in your controller, not picture (a local variable). When declaring and defining an instance variable in a method in your controller, it's also accessible in the corresponding view. However, when declaring a local variable in a method in your controller, it is not available in your view.
So even if you had declared picture rather than #picture in your controller method, it wouldn't be accessible in your view, since it's a local variable.
For more information on the five types of ruby variables, see this link.

Related

undefined method `model_name' for nil:NilClass (NoMethodError)

The error: undefined method `model_name' for nil:NilClass (NoMethodError)
I'm receiving this error when trying to render the haml below:
%section#banner
.row
.medium-12.columns
%h2 Add Testimonial
= simple_form_for(#testimonial) do |f|
.row
.large-6.columns
= f.input :text, as: :text,
placeholder: 'Use this space to write a testimonial about the event(s) you participated.'
.row
.large-6.columns
%p.description
= sanitize('Any testimonial along with your name and profile picture might be used for the promotion of codebar (website, prospectus, etc).')
.row
.large-12.columns.text-right
= f.submit 'Submit testimonial', class: 'button'
The controller is the following:
class TestimonialsController < ApplicationController
before_action :authenticate_member!
def get_testimonial
testimonial = Testimonial.where(member_id: testimonial_member_id)
invitations = current_user.workshop_invitations.accepted_or_attended
if invitations.any? and testimonial.blank?
render 'new'
else
render 'show'
end
end
def show
#testimonial = Testimonial.find(testimonial_member_id)
end
def new
#testimonial = Testimonial.new
end
def create
#testimonial = Testimonial.new(testimonial_params)
#testimonial.member_id = current_user
#testimonial.public = false
if #testimonial.save
redirect_to #testimonial
else
render 'new'
end
end
private
def testimonial_params
params.require(:testimonial).permit(:text)
end
def testimonial_member_id
params[current_user]
end
end
May someone help me see why is returning nil? If the variable is the same I'm passing on the new function?
AFAIK simple_form_for(#testimonial) will try to call #testimonial.model_name so that's where the problem most likely originates.
If you go through the get_testimonial controller, you can end up at:
render 'new'
and that will render the HAML in question. But, notice that nothing in get_testimonial initializes #testimonial so get_testimonial will end up trying to simple_form_for(nil).
Changing the bottom of get_testimonial to something more like this:
if invitations.any? && testimonial.blank?
#testimonial = Testimonial.new
render 'new'
else
render 'show'
end
Your show template presumably needs a #testimonial as well so you might want to say #testimonial = testimonial.first before render 'show' too.
Also, I've changed your and operator to && since you're generally better off pretending that and doesn't exist. The low precedence of and and or cause a lot of problems so you're better off sticking to && and ||.
I'm not sure of the logic for testimonials so you might be able to go with something more like:
def get_testimonial
#testimonial = Testimonial.find_by(member_id: testimonial_member_id)
invitations = current_user.workshop_invitations.accepted_or_attended
if invitations.any? && !#testimonial
#testimonial = Testimonial.new
render 'new'
else
render 'show'
end
end
You might want to revisit your testimonial_member_id method as well, this:
def testimonial_member_id
params[current_user]
end
looks odd, maybe it should be params[:id] instead.

Rails 4 form_for nested resources issue

I have researched similar questions however I don't feel link they have addressed my particular issue:
Rails form_for results in POST instead of PUT when trying to edit
form_for with nested resources
I'm a novice with Rails (using Rails 4.2.5) an am attempting my first application. My issue is two fold: (1) When a user goes to edit a user story the fields of the form do not populate with previously inputted data (2) When the form is resubmitted, a new entry is created, opposed to editing the old data.
I have a feeling that my form_for for user_stories/edit.html.erb is the issue. When I take out the .build method from the form I get the following error message:
undefined method `to_key' for #UserStory::ActiveRecord_Associations_CollectionProxy:0x007f456a759138>
The projects/_form.html.erb for my project's view does not have the .build method and functions correctly. However the only way I can get the `user_stories/_form.html.erb form to work is if I attach the build method.
Here is my code:
user_story.rb
class UserStory < ActiveRecord::Base
belongs_to :project
belongs_to :user
include RankedModel
ranks :row_order
end
project.rb
class Project < ActiveRecord::Base
has_many :user_stories
belongs_to :user
end
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :projects do
resources :user_stories
end
end
resources :user_stories do
post :update_row_order, on: :collection
end
root 'welcome#index'
end
user_stories/_form.html.erb
<%= form_for([#project, #user_story.build]) do |f| %>
<div class="form-group">
<p>As a ...</p>
<%= f.text_field :param1, placeholder: "type of user", class: "form-control" %>
</div>
<div class="form-group">
<p>I want ...</p>
<%= f.text_field :param2, placeholder: "desired functionality", class: "form-control" %>
</div>
<div class="form-group">
<p>so that...</p>
<%= f.text_field :param3, placeholder: "reason for desired functionality", class: "form-control" %>
</div>
<div class="actions">
<%= f.submit class: "btn btn-primary" %>
</div>
<% end %>
user_stories_controller.rb
class UserStoriesController < ApplicationController
before_action :set_project
before_action :set_user_story, except: [:create]
def index
#user_story = #project.user_stories.rank(:row_order).all
end
def update_row_order
#user_story.row_order_position = user_story_params[:row_order_position]
#user_story.save
render nothing:true # this is a POST action, updates sent via AJAX, no view rendered
end
def create
#user_story = #project.user_stories.create(user_story_params)
redirect_to #project
end
def new
end
def destroy
if #user_story.destroy
flash[:success] = "User story deleted"
else
flash[:error] = "User story could not be deletd"
end
redirect_to #project
end
def complete
user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
def update
respond_to do |format|
if #project.user_stories.update(#project, user_story_params)
format.html { redirect_to project_path(#project), notice: 'User story was successfully updated.' }
format.json { render :show, status: :ok, location: #user_story }
else
format.html { render :edit }
format.json { render json: #user_story.errors, status: :unprocessable_entity }
end
end
end
def edit
#project = Project.find(params[:project_id])
#user_story = #project.user_stories(params[:id])
end
def show
end
private
def set_project
#project = Project.find(params[:project_id])
end
def set_user_story
#user_story = #project.user_stories(params[:id])
end
def user_story_params
params[:user_story].permit(:param1, :param2, :param3, :row_order_position)
end
end
There are just a few changes needed (tweaks, really), and I'll go through them top-to-bottom.
1) before_action :set_user_story
This will use the param[:id] to find the proper #user_story model object and automatically make it available to the proper methods. In this case it's being excepted for :create, but should also exclude other methods that don't have an :id in the route. Use this instead:
before_action :set_user_story, except: [:index, :new, :create]
This will solve (or prevent) some annoying and persistent ActiveRecord failures.
2) The index action
In this method, the name of the variable is non-standard by Rails naming conventions. The variable is currently singular, but represents a list of UserAction model object, which typically uses a plural name. Use this, instead:
def index
#user_stories = #project.user_stories.rank(:row_order).all
end
This change will cause a break in the app/views/user_stories/index.html.erb view, where any use of the #user_story variable would need to be changed to #user_stories. Keeping with naming conventions has many immediate and long-term benefits, so it's worth making the extra effort to change this to be consistent.
Note: the index action typically doesn't have a singular model object to work with, as this action is used to provide a list of the model objects.
3) The new action
The new action is used to create and initialize a new model object for editing. As the before_action :set_user_story is no longer being called for the new action, the #user_story model object has to be created here. This code will do that correctly:
def new
#user_story = UserStory.new
#user_story.project = #project
# Set other important default values for display now
end
And at this point, you should be able to successfully create a new UserStory model object, ready to be edited by the user.
4) The edit action
As the before_action :set_user_story handler is already being called for the edit action, there's no need to query for #user_story from within the body of the edit action; that line can be removed:
def edit
#project = Project.find(params[:project_id])
end
This will actually fix the original issue that was reported, as this form of find will (unfortunately for this situation) return multiple records, which means that you get a collection back, and not a single record. This is the actual cause of this error message:
undefined method `to_key' for #UserStory::ActiveRecord_Associations_CollectionProxy:0x007f456a759138>
Assigning the #user_story within the edit action overwrote the value that had previously been assigned from the before_action handler, and replaced it with an improper query result.
5) The complete action
The complete action is a custom member action, which means that it depends on the :id, just like many of the other actions. The code is almost correct, except that the user_story variable used within the body of the method is actually missing the #; this is originally retrieved by the before_action handler.
def complete
#user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
It's likely that this method had not been called yet during testing, as the edit action was an upstream test that failed. This should work when you get to testing this method.
6) Teh codez
Changing those few details will finalize the UserStoriesController, which was in pretty great shape to begin with. Adding in those changes, this is the final controller code:
class UserStoriesController < ApplicationController
before_action :set_project
before_action :set_user_story, except: [:index, :new, :create]
def index
#user_stories = #project.user_stories.rank(:row_order).all
end
def update_row_order
#user_story.row_order_position = user_story_params[:row_order_position]
#user_story.save
render nothing:true # this is a POST action, updates sent via AJAX, no view rendered
end
def create
#user_story = #project.user_stories.create(user_story_params)
redirect_to #project
end
def new
#user_story = UserStory.new
#user_story.project = #project
# Set other important default values for display now
end
def destroy
if #user_story.destroy
flash[:success] = "User story deleted"
else
flash[:error] = "User story could not be deleted"
end
redirect_to #project
end
def complete
#user_story.update_attribute(completed_at, Time.now)
redirect_to #project, notice: "User story completed functionality complete"
end
def update
respond_to do |format|
if #project.user_stories.update(#project, user_story_params)
format.html { redirect_to project_path(#project), notice: 'User story was successfully updated.' }
format.json { render :show, status: :ok, location: #user_story }
else
format.html { render :edit }
format.json { render json: #user_story.errors, status: :unprocessable_entity }
end
end
end
def edit
#project = Project.find(params[:project_id])
end
def show
end
private
def set_project
#project = Project.find(params[:project_id])
end
def set_user_story
#user_story = #project.user_stories(params[:id])
end
def user_story_params
params[:user_story].permit(:param1, :param2, :param3, :row_order_position)
end
end

Ruby on rails. Passing params from view to controller

I'm having what I assume must be a simple problem but I just can't figure it out. I'm trying to update an attribute in one model when another is created.
In my view:
<%= link_to 'Click here to rate this user', new_user_review_path(:user_id => request.user.id, :gigid => request.gig.id), remote: true %>
Which passes params :gigid and :user_id
Than my controller:
def new
#review = Review.new
#gig = Gig.find(params[:gigid])
end
def create
#review = #user.reviews.new review_params
#review.reviewed_id = current_user.id
if #review.save
#gig.update(reviewed: true)
respond_to do |format|
format.html {redirect_to session.delete(:return_to), flash[:notice] = "Thankyou for your rating!"}
format.js
end
else
render 'new'
end
end
But I get undefined method 'update'for nil:NilCLass:
I know the params are passing and the 'Gig' can be updated as :
def new
#review = Review.new
Gig.find(params[:gigid]).update(reviewed: true)
end
updates the attribute fine, but when I click 'New review' not when the review is actually created.
Adding :
def create
#review = #user.reviews.new review_params
#review.reviewed_id = current_user.id
if #review.save
Gig.find(params[:gigid]).update(reviewed: true)
etc etc etc
gives me the same undefined method 'update'for nil:NilCLass:
I have tried with find_by_id instead of find which makes no difference.
EDIT:
def create
#gig = Gig.find params[:gigid]
#review = #user.reviews.new review_params
#review.reviewed_id = current_user.id
if #review.save
#gig.update(reviewed: true)
etc etc etc
Doesn't work either. I get no errors, but the gig ID is still 'nil'.
The params are passing to the 'New' action but not the 'Create' action. I feel this should be very easy but I'm just not seeing it at the moment.
But I get undefined method 'update'for nil:NilCLass:
The error is that you have not defined #gig in your create action.
Since Rails is built on HTTP, and HTTP is stateless, you have to set the "instance" variables with each new request:
def new
#review = Review.new
#gig = Gig.find params[:gigid]
end
def create
#gig = Gig.find params[:gigid]
#review = #user.reviews.new review_params
A much better pattern for you would be to use the after_create callback in your Review model:
#app/models/review.rb
class Review < ActiveRecord::Base
belongs_to :gig #-> I presume
after_create :set_gig
private
def set_gig
self.gig.update(reviewed: true)
end
end
--
If you wanted to make the Gig update within your current setup, you'll be best sending the gig_id param through the request (not the link):
#app/views/reviews/new.html.erb
<%= form_for [#user, #review] do |f| %>
<%= f.hidden_field :gig_id, #gig.id %> #-> params[:reviews][:gig_id]
...
<% end %>
This will make params[:review][:gig_id] available in the create action, with which you'll be able to use in your code.
The problem is, you never assigned a value to #gig in your create method. I can't see your form, but you need something like this in your create method:
#gig = Gig.find params[:gigid]
Assuming that you're passing the parameter :gigid to #create
In the second example you showed, I'm not sure what's going on, but you should be getting a ActiveRecord::RecordNotFound exception on the find().
Try the below code for update operation.
gig_record = Gig.find_by_id(params[:gigid])
gig_record.update_attribute(reviewed: true) unless gig_record.blank?

Web scraping information from different websites

Im using a gem called MetaInspector to scrape data from different websites. Im building a site where i can collect data from different sites but am having trouble setting up. I have a model called site with a title and a url both strings. When i create a new "site" the name will come out as example.com/"sitename" and in there i would like to have the data just from that site. I kinda have an idea to this by adding page = MetaInspector.new to the new method but cant see how i can set a url in there.
I can show my controller and other info if needed.
Controller
class Admin::SitesController < Admin::ApplicationController
def index
#sites = Site.all
end
def show
#site = Site.friendly.find(params[:id])
end
def edit
#site = Site.friendly.find(params[:id])
end
def update
#site = Site.friendly.find(params[:id])
if #site.update(site_params)
redirect_to admin_path
else
render :edit
end
end
def destroy
#site = Site.friendly.find(params[:id])
#site.destroy
if #site.destroy
redirect_to admin_path
end
end
def new
#site = Site.new
end
def create
#site = Site.new(site_params)
if #site.save
redirect_to admin_path
else
render :new
end
end
private
def site_params
params.require(:site).permit(:title, :url)
end
end
If I understand correct you want to show the metainfo for a Site you have added. You could put that code in the show action of the controller:
def show
#site = Site.friendly.find(params[:id])
#page = MetaInspector.new(#site.url)
end
And update the show.html.erb template to display info about #page, ie:
<%= #page.title %>

Clarification about using form_for

While creating a search form I am facing a problem. I am getting the following error:
undefined method `model_name' for NilClass:Class
This is my view file:
"datepicker" %>
This is my clients_controller.rb:
class ClientsController < ApplicationController
def newClients
end
end
And this is my model client.rb:
class Client < ActiveRecord::Base
# attr_accessible :title, :body
end
I am confused in using form_for parameter. Can any one explain it briefly how and why to use form_for parameter?
Edit 1
I have modified my controller as
class ClientsController < ApplicationController
def search
redirect_to root_path
end
end
Once i click submit button it showing error as
No route matches [GET] "/search"
You are missing something here. Let me explain.
In your controller you don't need to define a custom method (called newClients) since Rails conventions suggest to use the following:
class ClientsController < ApplicationController
# GET /clients
def index
#clients = Client.all
end
# GET /clients/:id
def show
#client = Client.find(params[:id])
end
# GET /clients/new
def new
#client = Client.new
end
# POST /clients
def create
#client = Client.new(params[:client])
if #client.save
redirect_to :back, success: "Successfully created..."
else
render :new
end
end
# GET /clients/:id/edit
def edit
#client = Client.find(params[:id])
end
# PUT /clients/:id
def update
#client = Client.find(params[:id])
if #client.update_attributes(params[:client])
redirect_to :back, success: "Successfully edited..."
else
render :edit
end
end
# DELETE /clients/:id
def destroy
#client = Client.find(params[:id]).destroy
redirect_to :back, success: "Successfully deleted..."
end
end
And finally, in order for your form_for to work properly, you need to pass it an instance of a class:
form_for #client
where #client is Client.new in your case.
First of all in your controller please follow Rails naming conventions. The method name should be new_clients or new.
def new
#client = Client.new
end
Your view name should be new.html.erb.
You are not defining #client in your controller, but in the view you are using it.

Resources