I have a name route:
map.up_vote 'up_vote', :controller => 'rep', :action => 'up_vote
But up_vote requires two arguments to be passed in, postID and posterID and I can't seem to figure how to do that in a partial, but in an integration test I have no issues.
Partial:
link_to 'Up Vote', up_vote_path, {:postID => session[:user_post_id], :postersID => session[:poster_id]}
Integration test:
post up_vote_path,{:postID => #latest.id,:postersID => users(:bob).id} (this works ok)
1) What is going on the in the partial?
2) What changes can I make to my tests to catch this?
A question: why are you passing your session variables in a link? You can get them directly from the session...
I don't know if there are any special reasons to put :user_post_id and :poster_id in the session but I recommend you two things:
1) Pass your variables in urls, sessions can be evil (try hitting back, refresh and forward on your browser)
2) Use resources in your URLs / controller actions logic.
Example (valid only if I got it right and you're voting an user's post):
routes:
map.resources :users do |user|
user.resources :posts do |post|
post.resource :vote
end
end
So you can have this url:
/users/:id/posts/:post_id/vote
And the link path:
link_to "Up", user_post_vote_path(#user, #post), :method => :create
I putting #user and #post instead of the integers because path methods accept them and you can build a shorter version with:
link_to "Up", [#user, #post, :vote] # or [:vote, #post, #user]
Implementing:
class VoteController ....
def create
# do your stuff here
end
end
This way it will be easier and RESTful.
Ryan Bates got a great episode on resources, it definately worths a look.
You want to pass your params in the ..._path
link_to "Up Vote", up_vote_path(:postID => session[:user_post_id], :postersID => session[:poster_id])
The integration test is written out differently than the link_to since your testing the act.
post "to" up_vote_path, "with these" {params}
Also since your doing a POST, you will want to add the appropriate :method option to the link_to
Related
I have an Approved column in a database which is false by default and might become true on "Approve" button click.
That's what this button look like at the moment:
<%= link_to('Approve It', #comment_path, method: :update) %>
But it raises an exception:
No route matches [POST] "/books/4/comments/6
# app/controllers/comments_controller.rb
def update
#comment = Comment.find(params[:id])
#comment.approve = true
redirect_to '/dashboard'
end
# config/routes.rb
resources :books do
resources :comments
end
How can I fix it?
link_to has to point to an existing route/action, with a proper method name. There is no :update HTTP method.
FYI: Approve action doesn't seem like it belongs to the #update method/action. You might want to extract it to a separate route like so:
resources :books do
resources :comments do
post :approve, on: :member
end
end
this is more idiomatic/common approach in Ruby because #update is usually preserved for more general object updates.
For this you will need to change :method argument value to :post and update your route/#comment_path.
Rails-ujs event handlers - this link might be useful for understanding how it works behind the scenes.
Controller Namespaces and Routing
Post / Update actions require forms
You're using a link_to. This is good for GET requests, but is no good for POST/PATCH/UPDATE requests. For that you'll have to use a form in HTML. Luckily Rails offers some short cut. You can use something like button_to:
<%= button_to "Approve", { controller: "comments", action: "update" }, remote: false, form: { "id" => #comment.id, "approved" => true } %>
This creates a form for you. Which will come with CSRF protection automatically. You can style the button however you like.
or you could use a link to:
<%= link_to comment_approved_path(#comment), method: :put %>
but then you would need to create a separate "approved" action in your controller, and a separate route to reach it.
(The above code has not been tested).
#html
<%= link_to "Approve It", book_comment_path(#comment), method: 'put' %>
# app/controllers/comments_controller.rb
def update
#comment = Comment.find(params[:id])
#comment.approve = true
#comment.save
redirect_to '/dashboard'
end
I am looking to use link_to to call a method in my controller. However, for some odd reason the route looks for the show method.
In my view:
<% #beverages.each do |beverage| %>
..
<%= link_to 'Archive', beverages_archive_path(:id => beverage.id) %>
..
<% end %>
In my config/routes.rb
match 'beverages/archive' => 'beverages#archive'
In my beverages_controller.rb
def archive
beverage = Beverage.find(params[:id])
respond_to do |format|
# format.html # show.html.erb
format.json { render json: beverage }
end
# beverage.update_attribute('archive', true)
end
When I click on the archive link in the view, the URL does change to: http://localhost:3000/beverages/archive?id=11, however I get the following error.
The error I get:
ActiveRecord::RecordNotFound (Couldn't find Beverage with id=archive):
app/controllers/beverages_controller.rb:46:in `show'
Any idea on what I am doing wrong? Your help is much appreciated!
PS. I also looked at Rails 3 link_to delete destory method calls show method?
but nothing seemed to work.
Have you tried this in your routes?
match 'beverages/:id/archive' => 'beverages#archive', as: :beverages_archive
This will create the beverages_archive_path method for you. Also, as you are looking for a specific beverage, use :id inside the route so that it knows where to take the id parameter from.
Apart from that, you can always tell a link specifically which controller and action to link to by doing:
link_to "Label", :controller => :my_controller, :action => :index
Taken from here: Ruby on rails 3 link_to controller and action
Use the other notation (not match) instead.
resources :beverages do
collection do
get :archive
end
end
Try this one out and let me know if something went wrong.
There's not quite enough information here to know why beverages_archive_path works in your app -- one problem is that your routes file does not define a name for your beverages#archive route. What you want is something like:
match 'beverages/archive' => 'beverages#archive', :as => :beverages_archive
or better yet use resourceful routing conventions like so:
resources :beverages do
collection do
get :archive
end
end
What's happening is that you have a beverages#show route that matches /beverages/:id, and this is what /beverages/archive matches (with :id => 'archive').
I'm trying to do something very simple in my first Rails app (Rails 3) and I'm not sure what I'm doing wrong, or if there's a better approach. Can't find anything on the web or here that has solved it for me despite much searching.
In the app I have WorkRequests and Articles. When viewing an Article, I want a button to create a WorkRequest and, when the new WorkRequest form appears, have the article filled in. Essentially, I'm trying to pass the Article.id to the new WorkRequest.
Works in link_to just by adding the parameter, but I want it to be a button. While it shows up in the Article form's HTML as a query parameter, it never gets to the WorkRequest.new method. This article from 2010 explains the problem in some detail, but the solution does not work for me (see my comment at the end of the page.)
This seems like it should be a fairly easy and common thing to do (once I figure it out, there are several other places in my own app where I want to do the same thing) but I've been banging my head against this particular wall for a few days now. I am new to Rails--this is my first app--so I hope someone more experienced can help!
Thanks in advance.
Just circling back to finish this up. Ultimately I solved this by using link_to but using jQuery to make it look like a button. #kishie, if you're saying you made this work with button_to I'd like to see the code, but as I like jQuery it's solved as far as I'm concerned (for this app, anyway.)
Here's the code in Article#show view. (The class is what makes it look like a button via jQuery.)
<%= link_to "New Request", new_work_request_path(:article_id => #article.id), :class => "ui-button" %>
Here's the code in Work_Request controller's new method:
if !params[:article_id].blank?
#work_request.article = Article.find(params[:article_id])
end
Then the Work_Request#new view renders it properly.
Add this line of code in your routes.rb file.
resources :work_requests do
member do
post 'new'
end
end
It shouldn't be the GET method because you're sending information to the server, via :module_id. This will then work.
<%= button_to("Add WorkRequest", {:controller => "work_request", :action => "new", :article_id => #article.id})%>
I just hit a similar issue and I got it to work by passing the parameter as follows:
<%= button_to("Add WorkRequest", new_work_request_path(:article_id => #article.id), :action => "new", :method => :get)%>
In article#show
<%= button_to("Add WorkRequest", {:controller => "work_request", :action => "new", :article_id => #article.id})%>
In work_requests#new
<%= f.text_field :article_id, :value => params[:article_id]%>
If you nest your resources for :work_requests within :articles in your routes.rb, then pass your params[:id] which would be your article_id and add :method => :get to the button_to call, you should be okay.
# config/routes.rb
resources :articles do
resources :work_requests
end
# app/views/articles/show.html.erb
<%= button_to "Add Work Request", new_article_work_request_path(params[:id]),
:method => :get %>
# app/controllers/work_requests_controller.rb
class WorkRequestsController < ApplicationController
...
def new
#work_item = WorkItem.new
#article = Article.find(params[:article_id])
...
end
...
end
I'm trying to implement the "Friendship" in my Rails 3 app as described in Railscast 163:Self Referential Assosication
I have everything set up as described. I am using a basic user model that logis in with Authlogic which works fine. However when I try to add a friend using the following link:
<% for user in #users %>
<%=h user.username %>
<%= link_to "Add Friend", friendships_path(:friend_id => user), :method => :post %>
<% end %>
I get a redirect to http://localhost:3000/friendships?friend_id=2 and a Unknown action The action 'index' could not be found for FriendshipsController error with no further explanation. This is expecially strange since I have a hard coded redirect back to the "User#show" method for my current user (i.e. redirect back to profile after adding friend).
If it helps, here is my "friendships#create" method:
def create
#friendship = current_user.friendships.build(:friend_id => params[:friend_id])
if #friendship.save
flash[:notice] = "Added friend."
redirect_to :controller => 'users', :action => 'show', :id =>'current_user'
else
flash[:notice] = "Unable to add friend."
redirect_to :controller => 'users', :action => 'show', :id =>'current_user'
end
end
Any idea what could be causing this? I found someone having a similar problem here but I couldn't find a definite fix: Rails 3 and Friendship Models
Thanks in advance for your help!
~Dan
I think that link_to put the arguments into the query string as it is creating with html link even if you put :method => :post if js is disabled.
you could simulte a post with javascript :onclik event.
aniway , use a link_to with method :post is generaly a bad idea.
in rails you could use button_to helper for this pourpose and style it like a link.
edit:
In Rails3 doc seems that link_to have to simulate the same behaviur of button_to when called with params :method=>:post
(dynamically create an HTML form and
immediately submit the form ).
but it's not true for me in Rails 3.0.3 even if javascript is enabled.
I will investigate.
anyway you should be using buttons and forms for anything that isn't a GET; hyperlinks intentionally don't allow for methods other than GET
edit2:
ok,
rails3 don't create a inline form for simulate the post request via link. in Rails3 a data-method=post attribute is added to the tag for manipulate it via javascript function. this way the request gracefully degradate in a get call if js is disabled.
It's a late answer but it's a solution to this problem (at least it works for me):
<%= link_to "Add Friend", {:controller => :friendships, :action => :create, :friend_id => user_id }, :method => :post %>
I know it's long overdue for your problem, but it may help someone :)
I have frequently run into the situation where I want to update many records at once - like GMail does with setting many messages "read" or "unread".
Rails encourages this with the 'update' method on an ActiveRecord class - Comment.update(keys, values)
Example - http://snippets.dzone.com/posts/show/7495
This is great functionality, but hard to map to a restful route. In a sense, I'd like to see a :put action on a collection. In routes, we might add something like
map.resources :comments, :collection => { :update_many => :put }
And then in the form, you'd do this...
<% form_for #comments do |f| %>
...
This doesn't work on many levels. If you do this: :collection => { :update_many => :put }, rails will submit a post to the index action (CommentsController#index), I want it to go to the 'update_many' action. Instead, you can do a :collection => { :update_many => :post }. This will at least go to the correct action in the controller.
And, instead of <% form for #comments ... you have to do the following:
<% form_for :comments, :url => { :controller => :comments, :action => :update_many } do |f| %>
It will work OK this way
Still not perfect - feels a little like we're not doing it the 'Rails way'. It also seems like :post, and :delete would also make sense on a collection controller.
I'm posting here to see if there's anything I missed on setting this up. Any other thoughts on how to restfully do a collection level :post, :put, :delete?
I've run into a few situations like you describe. The first couple of times I've implemented form almost identical to the one you suggest.
About the third time I hit this problem I realized that every item I'm updating has a common belongs_to relationship with something else. Usually a user. That's exactly the epiphany you need to make sense of this RESTfully. It will also help you clean clean up the form/controller.
Don't think of it as updating a bunch of messages, think of it as updating one user.
Here's some example code I've used in the past to highlight the difference. Assuming that we we want bulk operations on messages that belong to the current_user...
As of rails 2.3 we can add
accepts_nested_attributes_for :messages
to the user model. Ensure messages_attributes is part of attr_accessible, or is not attr_protected.
Then create the route:
map.resources :users, :member => {:bulk_message_update, :method => :put}
Then add the action to the controller. With AJAX capabilities ;)
def bulk_message_update
#user = User.find(params[:id])
#user.update_attributes(params[:user])
if #user.save
respond_to do |format|
format.html {redirect}
format.js {render :update do |page|
...
}
end
else
....
end
Then your form will look like this:
<% form_for current_user, bulk_message_update_user_url(current_user),
:html => {:method => :put} do |f| %>
<% f.fields_for :messages do |message| %>
form for each message
<% end %>
<%= sumbit_tag %>
<% end %>
I often add collection-based update_multiple and destroy_multiple actions to an otherwise RESTful controller.
Check out this Railscast on Updating Through Checkboxes. It should give you a good idea how to approach it, come back and add to this question if you run into troubles!