How to pass attribute name as a secondary parameter through link_to? - ruby-on-rails-3

IN my application Controller
class FeedEntriesController < ApplicationController
def index
#search = FeedEntry.search(params[:search])
#feed_entries = #search.page(params[:page])
#app_keys = AppKey.all
end
def update
#feed_article = FeedEntry.find(params[:id])
#feed_article.toggle!(#saad)
redirect_to feed_entries_path
end
end
And in my Views/feed_entries/index.html.erb
<div id="content">
<%= paginate #feed_entries, :theme => 'twitter-bootstrap'%>
<p><%= render(#feed_entries) || 'There is no Feeds'%><p>
<%= paginate #feed_entries, :theme => 'twitter-bootstrap'%>
</div>
And in _feed_entry.html.erb partial.
<%= link_to "Make UnStar |", feed_entry_path(:id => feed_entry.id, #saad => :is_to_read), method: :put %> <%= link_to feed_entry.name, feed_entry.url %>
But the above code is not working, I just want to pass attribute name (:is_to_read) as a secondary parameter through link_to into the controller's update action.
:is_to_read is boolean attribute of feedentries table.
Please someone help me to resolve this issue.

A parameter is a pair of name and value. So it should be: :saad => ... and not #saad =>
Further you need to call the is_to_read attribute on the object. So:
<%= link_to "Make UnStar |", feed_entry_path(:id => feed_entry.id, :saad => #feedentry.is_to_read), method: :put %>

Related

undefined method `model_name' in a partial rendered by different controller

I'm trying to render this form:
<form class="form-inline">
<%= simple_form_for #prospect,
:url => url_for(:action => 'create', :controller => 'prospects'),
:method => 'post' do |f| %>
<%= f.error_notification %>
<%= f.input :name, placeholder: 'Name', label: false %>
<%= f.input :email, placeholder: 'Email', label: false %>
<%= f.input :interests, placeholder: 'Tell us what you were searching for', label: false, value: params[:search] %>
<%= f.error :base %>
<%= f.button :submit, "Submit", :class=> "btn" %>
<% end %>
Using this partial:
<%= render partial: 'prospects/novideo_capture' %>
The partial is in a view controlled by Videos#index controller, and I keep getting this error: 'undefined method `model_name' for NilClass:Class'
This is my prospects controller:
class ProspectsController < ApplicationController
def index
#prospects = Prospect.all
end
def new
#prospect = Prospect.new
end
def create
#prospect = Prospect.new(params[:prospect])
if #prospect.save
render "thanks_for_interest"
else
render "novideo_capture"
end
end
I'm not sure what I'm going wrong, although I'm pretty sure it's a simple solution. I've seen a lot of similar questions around SO and tried all their answers, but none of them seem to work for this situation.
Thanks for any help...
EDIT: Adding
#prospect = Prospect.new
to the videos index controller stops the error occurring, but I don't feel it's the right way to do this. It also doesn't actually make the form use the prospects controller.
EDIT2: I now have the partial rendering correctly (I think), and my videos#index calls the partial like this:
<%= render partial: 'prospects/novideo_capture', :prospect => #prospect %>
Then simple_form in the partial looks like this:
<form class="form-inline">
<%= simple_form_for :prospect,
:url => url_for(:action => 'create', :controller => 'prospects'),
:method => 'post' do |f| %>
...
<% end %>
However it's not actually submitting the form with the prospects controller. Any ideas why?
Check your markup. You're wrapping a simple_form inside another form. Since the first form tag has no action associated with it (<form class="form-inline">), that form will submit against the current URL, which is the video#index.
You're going to want something like this:
<%= simple_form_for :prospect, :url => etc, :method => 'post', :class => "form-inline" do |f|
...
<% end %>
Losing the leading (redundant) form-inline form tag and you'll be fine.

No Method Error for "comments", can't find and correct the nil value to make the comment post

I'm relatively new to rails and am trying to pull off my first polymorphic association with comments.
I am running rails 3.2.3
Edit - When I try to post a comment, my log is returning this error:
Started POST "/comments" for 127.0.0.1 at 2012-05-20 13:17:38 -0700
Processing by CommentsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"SOLcF71+WpfNLtpBFpz2qOZVaqcVCHL2AVZWwM2w0C4=", "comment"=>{"text"=>"Test this comment"}, "commit"=>"Create Comment"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 101 LIMIT 1
Completed 500 Internal Server Error in 126ms
NoMethodError (undefined method `Comment' for nil:NilClass):
app/controllers/comments_controller.rb:13:in `create'
I have tried out many different solutions offered on SO and elsewhere, including the answer from Jordan below, due, I'm sure, to my own inexperience, but have been unable to resolve the error.
The trace calls out line 13 in the Comments Controller and I commented after that line below to mark the error:
class CommentsController < ApplicationController
def index
#commentable = find_commentable
#comments = #commentable.comments
end
def new
#post = Post.find(params[:post_id])
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment]) #<<<<LINE 13
if #comment.save
flash[:notice] = "Successfully created comment."
redirect_to :id => nil
else
render :action => 'new'
end
end
private
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
end
Posts Controller:
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #post }
end
end
Comment template (in post show)
<ul id="comments">
<% if #comments %>
<h2>Comments</h2>
<% #comments.each do |comment| %>
<li><%= comment.text %></li>
<% end %>
<% else %>
<h2>Comment:</h2>
<% end %>
</ul>
<%= simple_form_for [#commentable,Comment.new], :html => { :class => 'form-horizontal', :multipart => true } do |f| %>
<fieldset>
<%= f.input :text %>
Upload Photo <%= f.file_field :photo %>
</fieldset>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
</div>
<% end %>
Post show:
<p id="notice"><%= notice %></p>
<div class="row">
<div class="span2 offset1">
<%= image_tag #post.photo.url(:show) %>
</div>
<div class="span5">
<h1><%= #post.title %></h1>
<p><%= #post.index_text.html_safe %></p>
<p><%= #post.show_text.html_safe %></p>
<%= render "comments/comment" %>
<%= render "comments/form" %>
<% if can? :update, #course %>
<%= link_to 'Edit Post', edit_post_path(#post), :class => 'btn btn-mini' %>
<%= link_to 'Delete Post', #post,
confirm: 'Are you sure?',
method: :delete,
:class => 'btn btn-mini' %>
<%= link_to 'New Post', new_post_path, :class => 'btn btn-mini' %>
<% end %>
</div>
<nav class="span2 offset1">
<ul class="well">
<li>Category 1</li>
<li>Category 2</li>
</ul>
</nav>
</div>
<div class="row offset2">
<%= link_to 'Back to Posts', posts_path, :class => 'btn btn-mini' %>
</div>
Routes:
resources :posts, :has_many => :comments
resources :comments
It is probably something obvious that someone with more experience can resolve. Let me know if anything comes to mind. Brian
The problem is that #commentable is nil, which means that CommentsController#find_commentable is returning nil. I think your regular expression is sound, so that means one of two things is happening in find_commentable:
There aren't any keys in params that match your regex.
Your regex is matching but there aren't any records in the resulting table with the id in value.
Debug this as usual by inspecting params and the records in your database to make sure they look like you expect them to look.
The problem is your find_commentable method.
Here are the params passed to your CommentsController#create:
Started POST "/comments" for 127.0.0.1 at 2012-05-20 13:17:38 -0700
Processing by CommentsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"SOLcF71+WpfNLtpBFpz2qOZVaqcVCHL2AVZWwM2w0C4=", "comment"=>{"text"=>"Test this comment"}, "commit"=>"Create Comment"}
Here is your CommentsController#create:
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment]) #<<<<LINE 13
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
As you can see, find_commentable expects a param like xx_id (for example, comments_id) which it uses to search for an appropriate class (in case of comments_id, it will be Comment), otherwise it returns nil. Refer classify and constantize here.
Your params do not contain any such param. So, you always get a nil object.
Your find_commentable needs some rework. I think in case of nested_fields, it should be an expression like
/(.+)_attributes$/
instead of
/(.+)_id$/.
And you need to have
:accepts_nested_attributes_for :commentable
in your Comment model class.
I tried both of the above answers, but the problem continued.
I ended up consulting with a friend who suggested the following solution, which I like because it's more elegant than my original attempt and easier to read (for later, when I or someone else need to return to the code):
def find_commentable
if params[:post_id]
Post.find(params[:post_id])
#elsif params[:other_id]
# Other.find(params[:other_id])
else
# error out?
end
end
The commented out section will refer to other associations once I get them up and running.

How can I update this vote counter dynamically with Ajax/JavaScript (Rails)?

I built a simple voting system:
votes_controller.rb:
class VotesController < ApplicationController
def vote_up
#post = Post.find(params[:id])
#vote = #post.votes.create(:user_id => current_user.id, :polarity => 1)
end
end
(If there is any bad practice here, please let me know)
views/show.html.erb:
<h3><%= #post.votes.count %> votes</h3><br />
<%= link_to "Vote Up", vote_up_path(#post), :remote => true %>
(I will put this in a partial of course)
routes.rb:
get 'votes/:id/vote_up' => 'votes#vote_up', as: 'vote_up'
When I click the "Vote Up" link the vote is added to the post but I have to refresh the page in order to see the changes. How can I refresh #post.votes.count and vote.user.username dynamically with Ajax/JavaScript?
First put make a wrapper div for show.html.erb
<div class='post-<%=#post.id%>' >
<h3><%= #post.votes.count %> votes</h3><br />
<%= link_to "Vote Up", vote_up_path(#post), :remote => true %>
</div>
and in vote_up.js.erb
$("post-<%=#post.id%>").html('<%=escape_javascript #post.votes.count %>');
or something like this

Rails passing local parameters through link_to into js then load in partial

I am trying to pass along a local variable to a partial using link_to. I have tried many different things, but it doesn't seem to work. The js file loads the partial fine, it just doesn't have the locals. This is what I have, thanks for any direction!
_health.html.erb (this is a partial on index.html of Contacts model)
<% #comments = Comment.find_all_by_api(#api) %>
<%= link_to 'Read Comments', comments_path(:comments => #comments), :action => 'comments', :remote => true %>
comments.js.erb
$("#comments").html("<%= escape_javascript(render(:partial => 'comment', :locals => {:comments => :comments})) %>");
comment.html.erb
<% unless #comments.blank? %>
<% #comments.each do |c| %>
<%= c %><br />
<% end %>
<% end %>
contacts_controller.rb
def comments
respond_to do | format |
format.js {render :layout => false}
end
end
The partial does not know about the comments because you never set them. The comments action in the controller needs to look like this:
def comments
#comments = Comment.find(params[:id])
respond_to do | format |
(replace params[:id] with the appropriate parameter from your route)
You're doing an AJAX request and since http is stateless, the comments action does not know anything about any previous requests - which means that the comments from _health.html.erb have ceased to exist for the comments action in the controller.

undefined method `to_key' for #<Class:0x17a6408> -rails-3

I am experiencing problems with undefined method `to_key' for, on a form for polymorphic upload.
This is the form partial:
<%= form_for [#parent, Upload], :html => { :multipart => true } do |f| %>
<div class="field">
<%= f.label :document %><br />
<%= f.file_field :document %>
</div>
<div class="actions">
<%= f.submit "Upload"%>
</div>
<% end %>
This is the controller:
class UploadsController < ApplicationController
before_filter :find_parent
respond_to :html, :js
def index
#uploads = #parent.uploads.all unless #uploads.blank?
respond_with([#parent, #uploads])
end
def new
#upload = #parent.uploads.new unless #uploads.blank?
end
def show
#upload = #parent.upload.find(params[:upload_id])
end
def create
# Associate the correct MIME type for the file since Flash will change it
if params[:Filedata]
#upload.document = params[:Filedata]
#upload.content_type = MIME::Types.type_for(#upload.original_filename).to_s
#upload = #parent.uploads.build(params[:upload])
if #upload.save
flash[:notice] = "suceessfully saved upload"
redirect_to [#parent, :uploads]
else
render :action => 'new'
end
end
end
def edit
#upload = Upload.where(params[:id])
end
private
def find_parent
classes ||= []
params.each do |name ,value|
if name =~ /(.*?)_id/
#parent = classes << $1.pluralize.classify.constantize.find(value)
end
end
return unless classes.blank?
end
end
If i change
<%= form_for [#parent, Upload], :html => { :multipart => true } do |f| %>
to
<%= form_for [parent, Upload], :html => { :multipart => true } do |f| %>
I get a new error: undefined local variable or method `parent' for #<#:0x21a30e0>
This is the error trace:
ActionView::Template::Error (undefined method `to_key' for #<Class:0x2205e88>):
1: <%= render :partial => "uploads/uploadify" %>
2:
3: <%= form_for [#parent, Upload], :html => { :multipart => true } do |f| %>
4:
5:
6: <div class="field">
The "uploads/uploadify" partial is in this gist: https://gist.github.com/911635
Any pointers will be helpful. Thanks
From what I can see, your form_for should be something along the lines of
<%= form_for [#parent, #upload], :html => { :multipart => true } do |f| %>
as I'm assuming your upload object is nested within another object, similar to the following:
resources :posts do
resources :uploads
end
What form_for does when passed an array like this is construct the relevant path based on the class of the given objects and whether they are new records.
In your case, you create a new upload object in the new action of your controller, so form_for will inspect the array, get the class and id of #parent, then get the class and id of #upload. However, because #upload has no id, it will POST to /parent_class/parent_id/upload instead of PUTting to parent_class/parent_id/upload/upload_id.
Let me know if that doesn't work and we'll figure it out further :)
-- EDIT - after comments --
This means that one of #parent or #upload is nil. To check, you can put the following in your view
<%= debug #parent %>
and the same for #upload and see which is nil. However, I'm guessing that #upload is nil, because of this line in your controller:
# UploadsController#new
#upload = #parent.uploads.new unless #uploads.blank?
specifically the unless #uploads.blank? part. Unless you initialize it in the ApplicationController, #uploads is always nil, which means #uploads.blank? will always be true, which in turn means #upload will never be initialized. Change the line to read
#upload = #parent.uploads.new
and the problem will hopefully be resolved. The same is true of the other methods where you have used unless #uploads.blank?.
On a semi-related note, in UploadsController#find_parent, you have this line
classes ||= []
because the variable is local to the find_parent method, you can be assured that it is not initialized, and should rather write classes = [].
Also, you have this line of code
return unless classes.blank?
right before the end of the method. Did you add that in so that you return from the method once #parent has been initialized? If so, that line should be inside the each block.
Further, since classes isn't used outside of the method, why define it at all? The code could read as follows and still have the same behaviour
def find_parent
params.each do |name ,value|
#parent = $1.pluralize.classify.constantize.find(value) if name =~ /(.*?)_id/
return if #parent
end
end
Amongst other things, you'll see that this does a few things:
Avoids initializing a variable that is not needed.
Inlines the if statement, which helps readability for single line conditionals
Changes use of unless variable.blank to if variable. Unless your variable is a boolean, this accomplishes the same thing, but reduces the cognitive load, as the former is essentially a double negative which your brain has to parse.
-- EDIT - from email exchange about the issue --
You are correct - if #parent will return true if parent is initialized. As I mentioned on SO however, the exception to this is if #parent is initialized and set to false. Essentially what it means is that in Ruby, all values except nil and false are considered true. When an instance variable has not been initialized, it's default value is nil, which is why that line of code works. Does that make sense?
In terms of setting #parent in each action that renders form in the UsersController, which of these is the correct way to do this on the index action. I have tried all 3 but got errors
Remember that both #parent and #upload must be instances of ActiveRecord (AR) objects. In the first case, you set #parent to User.all, which is an array of AR objects, which will not work. Also, you try to call #parent.uploads before #parent is initialized, which will give a no method error. However, even if you were to swap the two lines around, you are calling #parent.uploads when parent is an array. Remember that the uploads method is defined on individual AR objects, and not on an array of them. Since all three of your implementations of index do similar things, the above caveats apply to all of them in various forms.
users_controller.rb
def index
#upload = #parent.uploads
#parent = #user = User.all
end
or
def index
# #user = #parent.user.all
#parent = #user = User.all
end
or
def index
#parent = #upload = #parent.uploads
#users = User.all
end
I'll quickly walk you through the changes I made. Before I start, I should explain that this
<%= render "partial_name", :variable1 => a_variable, :variable2 => another_variable %>
is equivalent to doing this
<%= render :partial => "partial_name", :locals => {:variable1 => a_variable, :variable2 => another_variable} %>
and is just a shorter (and somewhat cleaner) way of rendering. Likewise, in a controller, you can do
render "new"
instead of
render :action => "new"
You can read more about this at http://guides.rubyonrails.org/layouts_and_rendering.html Now on to the code.
#app/views/users/_form.html.erb
<%= render :partial => "uploads/uploadify" %>
<%= form_for [parent, upload], :html => { :multipart => true } do |f| %>
<div class="field">
<%= f.label :document %><br />
<%= f.file_field :document %>
</div>
<div class="actions">
<%= f.submit "Upload"%>
</div>
<%end%>
On the uploads form, you'll see that I changed #parent and #upload to parent and upload. This means you need to pass the variables in when you render the form instead of the form looking for instance variable set by the controller. You'll see that this allows us to do the following:
#app/views/users/index.html.erb
<h1>Users</h1>
<table>
<% #users.each do |user| %>
<tr>
<td><%= link_to user.email %></td>
<td><%= render 'uploads/form', :parent => user, :upload => user.uploads.new %></td>
</tr>
<% end %>
</table>
Add an upload form for each user in UsersController#index. You'll notice that because we now explicitly pass in parent and upload, we can have multiple upload forms on the same page. This is a much cleaner and more extensible approach to embedding partials, as it becomes immediately obvious what parent and upload are being set to. With the instance variable approach, people unfamiliar with the code base might struggle to determine where #parent and #upload are being set, etc.
#app/views/users/show.html.erb
<div>
<% #user.email %>
<h3 id="photos_count"><%= pluralize(#user.uploads.size, "Photo")%></h3>
<div id="uploads">
<%= image_tag #user.upload.document.url(:small)%>
<em>on <%= #user.upload.created_at.strftime('%b %d, %Y at %H:%M') %></em>
</div>
<h3>Upload a Photo</h3>
<%= render "upload/form", :parent => #user, :upload => user.uploads.new %>
</div>
This is similar to the changes above, where we pass in the parent and upload objects.
#config/routes.rb
Uploader::Application.routes.draw do
resources :users do
resources :uploads
end
devise_for :users
resources :posts do
resources :uploads
end
root :to => 'users#index'
end
You'll see that I removed uploads as a top level resources in the routes. This is because uploads requires a parent of some sort, and so cannot be top level.
#app/views/uploads/new.html.erb
<%= render 'form', :parent => #parent, :upload => #upload %>
I made the same changes as above, passing parent and upload through explicitly. You'll obviously need to do this wherever you render the form.
#app/controllers/users_controller.rb
class UsersController < ApplicationController
respond_to :html, :js
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to users_path
else
render :action => 'new'
end
end
def update
#user = User.find_by_id(params[:id])
#user.update_attributes(params[:user])
respond_with(#user)
end
def destroy
#user = User.find_by_id(params[:id])
#user.destroy
respond_with(#user)
end
end
I've removed any mention of #parent from the user controller, as we pass it through explicitly.
Hopefully that all makes sense. You can extrapolate from these examples and pass through the parent and upload object wherever you want to render an upload form.
[#parent, Upload] => [#parent, :upload]
<%= form_for [#parent, :upload], :html => { :multipart => true } do |f| %>
UPD
You should change places :upload and #parent
<%= form_for [:upload, #parent], :html => { :multipart => true } do |f| %>