RAILS3: Pass arbitrary parameters via button_to? - ruby-on-rails

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

Related

How to make a custom route in Rails? By custom I mean one that reacts to params

So essentially I've setup a route to match "products/:product", which seems to respond to a page like baseurl/products/toaster and displays the toaster product. My problem is I can't seem to use link_to to generate this path, and by that I mean I don't know how. Any help on this?
There are several solutions on this one :
<%= link_to 'Toaster', { :controller => 'products', :action => 'whatever', :product => 'toaster' } %>
But it's not really Rails Way, for that you need to add :as => :product at the end of your route. This will create the product_path helper that can be used this way :
<%= link_to 'Toaster', product_path(:product => 'toaster') %>
Within your routes file you can do something like:
match "products/:product" => "products#show", :as => :product
Where the controller is ProductsController and the view is show
within the Products controller your have
def show
#product = Hub.find_by_name(params[:product])
respond_to do |format|
format.html # show.html.erb
end
end
Where whatever is in the products/:product section will be available via params.
Then, since we used :as in your routes you can do this with link_to:
<%= link_to product(#product) %>
Where #product is an instance of a product or a string. This is just an example and the param can be anything you want, the same goes for controller/action. For more info you should check out this.
Hope this helps!

Rails: link_to calls custom method in controller

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').

Rails 3: ":method => :post" doesn't work... seems to be 'GET' when it should 'POST'

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 :)

ruby-on-rails: passing params through a route & link_to

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

Rails Restful actions Index Put

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!

Resources