ActionController::UrlGenerationError using link_to - ruby-on-rails

I have a controller Posts in which I have a method:
def report_user
...
end
I have a view where I would like a link that will perform some logic (it should NOT actually take the user to a different page, only perform the logic and possibly show a dialog box after completion). The logic is contained in the report_user action in the Posts controller:
<%= link_to "Report User", :controller => :Posts, :action => :report_user %>
I would ultimately like to pass some variables also to the report_user action, however I haven't gotten that far as I've come across this error:
No route matches {:action=>"report_user", :controller=>"Posts"}
message << " missing required keys: #{missing_keys.sort.inspect}" unless missing_keys.empty?
raise ActionController::UrlGenerationError, message
end
I'm not sure what the issue is. There is definitely an action in the Posts controller called report_user and it is not private. I'm not sure what the missing required keys means either. I've seen on SO other people with that error, but they all have routes defined that require parameters. I do not have any routes defined for this. Possibly I"m going about the entire thing in the wrong way?

As Nils suggested, you need an entry in routes.rb. Assuming that this is a member route using a GET request, that entry would look like this.
resources :posts do
get :report_user, on: :member
end
Next, you need to update your link to use the routing helpers that Rails provides.
<%= link_to "Report User", report_user_post_path(#post), remote: true %>
I included the remote: true option b/c you mentioned that clicking the link shouldn't reload the page. The default response for this request will be app/views/posts/report_user.js.erb.
I would encourage you to read up on Rails routing at http://guides.rubyonrails.org/routing.html.

Related

Custom action_item on show page only

I'm struggling to create an action_item for a show page (not the index) of a resource. I need to add a button on the show page, which when the user clicks it, updates a single attribute on the resource (in this case 'status' of a SupportSession). I have created a method on my SupportSession model called approval_confirmed which accepts a parameter called support_session_id - I'd like this method to be called.
(BTW I'm not sure which HTTP verb to use (patch, put, get) though I've settled on patch).
Here's how my resource is set-up:
ActiveAdmin.register SupportSession, as: "SUSupportSession" do
(I've given this resource a custom name/alias because different users use this resource in different ways; so for simplicity I keep them in separate files.)
Here is my action_item code:
action_item only: [:show] do
link_to 'Approve session', approve_su_support_session_path, method: :patch
end
Here is the corresponding member_action (which I understand is called by the action_item?):
member_action :approve, method: :patch do
SupportSession.with(support_session_id: resource.id).approval_confirmed
redirect_to su_support_session_path, notice: "Your support session has been approved!"
end
This creates a new route (output of rails routes below):
approve_su_support_session PATCH /su_support_sessions/:id/approve(.:format) su_support_sessions#approve
However, when I try to open the index page (to select a record from the table and move to its show page) I get this error:
ActionController::UrlGenerationError in SuSupportSessions#index
No route matches {:action=>"approve", :controller=>"su_support_sessions"}, missing required keys: [:id]
I understand this is something to do with the fact that there is no id parameter on the index page (although I don't understand why it's getting called on the index page because I only need this button on the show page - where the id will appear once a user clicks 'view' on any item listed in a table).
Thanks for your help. I think I'm getting terribly mixed up with how ActiveAdmin controllers connect to the underlying model.
As #max says in comment above your have to pass an argument to your path, resource or resource.id works.
Regarding your current error I guess it could be caused because you have not mentionned the action you want to do.
action_item :approve, only: [:show] do
link_to 'Approve session', approve_su_support_session_path(resource), method: :patch
end
I hope it helps. Let me know.

understanding passing rails parameters between controllers

I am just starting to wrap my head around parameters in rails. I am currently working on a project that isn't accessible to the public, so keeping params secure isn't exactly a priority in this case.
I have a link_to to a different controller action that requires an object id to fulfil the controller action.
=link_to "Barcode", print_barcode_label_admin_items_path(:item_to_print => { :article_id => article.id })
Then in the relevant controller
def print_barcode_label
if params[:item_to_print][:article_id].present?
return if force_format :pdf
..........
private
def params_document
params.require(:document).permit!
end
As I was writing the code for this controller I am certain the parameters were being passed (I am using the better-errors gem to debug along the way so I could see them being passed in the request parameters hash). But now, not sure what I have done, but i get the error
undefined method `[]' for nil:NilClass
failing at line two in my above controller action. I am sure there is something really basic I am missing. What is it? Is there a more favourable way of doing this?
Update
So I started playing with other possible solutions, and one is naming a route that specifically carries the parameter
get 'print_barcode_label/:article_id', to: 'documents#print_barcode_label', as: 'print_barcode_label'
This seems a more robust and sensible approach. Howeever, despite passing the variable in the link, like this
=link_to "Barcode", print_barcode_label_admin_items_path(article.id)
Gives a no route matches error
No route matches {:action=>"print_barcode_label", :controller=>"admin/documents"} missing required keys: [:article_id]
It is hard to answer this question without seeing more code with some context. But if you want to do rails way you should propably create custom action on document resource.
In your routes.rb:
namespace :admin do
resources :documents
get :print_barcode_label, :on => :member
end
end
And then you can create link to this action:
= link_to 'Barcode', print_barcode_label_admin_document_path(article)

link_to custom action but wrong method?

all, I'm trying to get a custom action to work with a put method: in the
in _post.html.erb i have a link_to statement:
<%= link_to 'End now', post, :method => :put, :action => endnow %>
routes.rb contains:
resources :posts do
member do
put :endnow
end
and posts_controller.rb looks like:
class PostsController < ApplicationController
helper_method :endnow
[.. code for create, edit, destroy, etc ..]
def endnow
puts params
end
end
rake routes's relevant line looks like:
endnow_post PUT /posts/:id/endnow(.:format) posts#endnow
However, the action endnow helper doesn't run when clicking on this link.
Strangely, it does run with an index action (which i can tell from the puts command.
Of course, eventually the code for endnow will update #post, but for now, it just doesn't run properly.
Maybe i'm going about this the wrong way - all I'm trying to achieve is to update #post upon clicking the link to that post, and before showing it.
Any ideas / Alternatives?
Why not use the route helper method provided to you? Change your link to
<%= link_to 'End now', endnow_post_path(#post), method: :put %>
Things you're doing wrong:
If you want to specify the :action, use the Symbol for the action (you're missing a colon). :action => endnow should be action: :endnow
I will assume you have a #post instance variable you're passing from your controller to your action. You should be using that instead of post (unless you do in fact have a local post variable you're omitting from your code)
You are using endnow as an action; you should remove the helper_method :endnow line in your controller because it's not something you want to/should be accessing from your view.
This can all be avoided by using the route helper (for endnow_post you'd append _path to get the local route path: endnow_post_path), and pass in your #post as an argument.
Because you're trying to do a PUT request, you must make sure you have something like jquery-ujs included in your asset pipeline to convert these links to form submissions behind the scenes; browsers don't support PUT via the click of a link on their own.
As for why you're getting the template error when you get your link_to working, Rails is telling you that you need to create a app/views/posts/endnow.html.erb file. Your action has only puts params which does not terminate execution, leaving Rails to assume you still are trying to render some endnow.html.erb template.
Are there other ways to do what you're trying to do (change a single attribute of a specific model)? Sure. Are there better ways? That's pretty subjective; it may not be the most RESTful way, but it's arguably easier to deal with (if for example there are very specific authorization rules to check before updating the attribute you are modifying in endnow. Does the way you've started fleshing out work? Absolutely.
Finally, as a bump in the right direction, after you fix your link_to and remove the the helper_method as I have described above, your endnow action might look like this:
def endnow
post = Post.find!(params[:id])
post.some_attribute_here = some_new_value_here
post.save
redirect_to :root and return # <- this line sets a redirect back to your homepage and terminates execution, telling rails to do the redirect and **not** to render some endnow.html.erb file
end

System call from method (ruby on rails)

I need make system call from method of Ruby of Rails , but I want it to stay on the same page.
Right now for some reason it does not execute , but shows :
Routing Error
No route matches [POST] "/devices/22918"
Try running rake routes for more information on available routes.
This is the button:
<%= link_to image_tag("/images/glossy_green_button.png"), device , :method => :turnon, :confirm => "Are you sure?" %>
This is method:
def turnon
#device = Device.find(params[:id])
result = `/perl/toggle.pl #device.infodot on`
end
please let me know what I am doing wrong,
thank you
D
You're simply not using the method correctly. You're using it to target the action of the controller you want to execute (note that I explicitly said action and not method for clarity).
Available actions in a controller are defined by your routes.rb file.You should eventually read or re read that
In your case, let's say you have a resource device (I guess this is what you have), you'll first create a new action in your routes.rb file
resources :devices do
put :turnon, on: :member
end
You can read doc about this syntax here but basically I'm making the action turnon available via the HTTP PUT method on each devices, meaning that it will be accessible through the URL /devices/1/turnon or via the url_helper : turnon_device_path (or turnon_device_url)
I assume that your turnon action will modify existing things, not creating new things, that's why I'm using the PUT verb
Then the link will look something like :
<%= link_to image_tag("/images/glossy_green_button.png"), turnon_device_path(device) , :method => :put, :confirm => "Are you sure?" %>
see that the method is the HTTP method corresponding to the new route I created.
I also assume that you put the turnon method in the DevicesController.
Finally as you want to do that in ajax, you can have a look a the remote: true option

Ruby on Rails: How to print out a string and where does it display at?

I know this is a trivial question. But I have search all over google but cannot find a simple answer to this question.
Basically I have a line that says <%= link_to 'Run it', :method => 'doIt' %> in the view, then in the corresponding controller, I have the doIt method as follows:
def doIt
puts "Just do it"
end
I just want to check that if i click on Run it, it will output the string "Just do it". I ran this on localhost and there is no errors, but I can't find the output "Just do it" anywhere. It is not displayed in the rails console or rails server log. I just want to know where does puts output the string to , where to find it ?
Round 2: So this is what I tried ....
Added this line in the index.html.erb (which is the root)
<%= link_to 'Run it', :method => 'do_it' %>
and in the url, it is just basically http://localhost:3000/ (since i route controller#index as root)
The display is just an underlined 'Run it' that links to 'do_it' method in the controller.
In the controller, i include this method
def do_it
logger.debug "Just do it"
end
when i click on 'Run it', the url change to http://localhost:3000/gollum_starters?method=do_it and in the development.log, the following is written into it:
Started GET "/gollum_starters?method=do_it" for 127.0.0.1 at 2011-08-25 15:27:49 -0700
Processing by GollumStartersController#index as HTML
Parameters: {"method"=>"do_it"}
[1m[35mGollumStarter Load (0.3ms)[0m SELECT "gollum_starters".* FROM "gollum_starters"
Rendered gollum_starters/index.html.erb within layouts/application (3.6ms)
Completed 200 OK in 16ms (Views: 7.7ms | ActiveRecord: 0.3ms)
Additionally, i tried all the logger.error/info/fatal/etc ... and Rails.logger.error/info/fatal/etc, all did not print out the line "Just do it" in the development log
#Paul: I did not touch the environment folder or file, i assume by default when a new rails app is created, it is in development ?
#Maz: Yes you are right, I am just trying to test if the do_it method is getting called. To do that, I just want to print something out in the controller. Can't think of any way simpler that just print a string out, but this problem is making me miserable. I am just using textmate, no IDE.
Round 3:
#Paul thx alot, but i encountered error
My routes files is now:
resources :gollum_starters
root :to => "gollum_starters#index"
match 'gollum_starters/do_it' => 'gollum_starters#do_it', :as => 'do_it'
My index.html.erb is now:
<%= link_to "Do it", do_it_path %>
My gollum_starters_controller.rb
def do_it
logger.debug 'Just do it'
end
I am getting this error:
Couldn't find GollumStarter with ID=do_it
the error is in here, 2nd line:
def show
#gollum_starter = GollumStarter.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #gollum_starter }
end
end
I wonder why does it route to show ? When i click do_it, it actually goes to localhost:3000/gollum_starters/do_it which is correct, but apparently the error points to the show method ?
Round 4:
#Paul, i shifted resources :gollum_starters down:
root :to => "gollum_starters#index"
match 'gollum_starters/do_it' => 'gollum_starters#do_it', :as => 'do_it'
resources :gollum_starters
but got this error (omg i wanna kill myself),
Template is missing
Missing template gollum_starters/do_it with {:handlers=>[:erb, :rjs,
:builder, :rhtml, :rxml], :formats=>[:html], :locale=>[:en, :en]} in
view paths "~/project_name/app/views"
:/
---------- Answer to Round 4 ------------
Basically as the error explains, there is no template(i.e a webpage) to show hence error thrown. The solution is to add a redirect_to , in this case I redirect to root_url.
def do_it
logger.debug 'Just do it'
redirect_to(root_url)
end
Everything works now, "Just do it" finally outputs to development.log and the rails server console.
Thank you Maz and Paul and Andrew for helping me out. Learn a lot.
That link_to does not do what you think it does the value for :method is referring to the HTTP verbs.
Taken from the docs for ActionView::Helpers::UrlHelper
:method - Symbol of HTTP verb. Supported verbs are :post, :get, :delete and :put. By default it will be :post.
You would need to define a route in your routes.rb file that uses your method
# The order of routes is important as the first matched will be used
# therefore the match needs to be above 'resources :controller'
match 'controller/do_it' => 'controller#do_it', :as => 'do_it'
resources :gollum_starters # <--- This needs to be below the match or this will catch first
The controller/do_it is the route to be matched
The controller#do_it is the controller followed by the action to be used (separated by #)
The value for :as creates the path do_it_path that can be used in your link_to
Your link_to may look something like
<%= link_to "Do it", do_it_path %>
And to complete the lifecycle of a request you will need to add a view to be rendered
app/views/gollum_startes/do_it.html.erb # <-- Add file
Summary
Doing all of this creates a bit of a mess just to print something out to the logs, but it should help you understand the whole lifecycle a bit better now. Plus this answers serves as a document to help you rewind this mess.
You are not understanding what "method" means in the context of a link.
The "method" here refers to the request method, which means the kind of request you are asking the browser to make. From the perspective of a RESTful application like Rails there are four relevant request types: GET, POST, PUT, and DELETE. These request types affect how the controller responds to a request.
GET => INDEX or SHOW
POST => CREATE
PUT => UPDATE
DELETE => DESTROY
There are two other "standard" rails actions, NEW and EDIT. These are GET requests to present an interface to the user. NEW gives you a form to POST (CREATE) a new object, and EDIT gives you a form to PUT (UPDATE) and existing one.
See the rails guide for more on how HTTP Verbs relate to CRUD operations.
The important, basic thing to understand is that links, by default, are GET requests, and forms, by default, are POST requests.
So, when your link looks like this:
<%= link_to 'Run it', :method => 'do_it' %>
...it is bogus. There is no such HTTP method as "do_it", so you're not triggering anything. Because there is no such method, Rails actually passes this on as a parameter of the URL. Hence if you click that you should see your url bar now says ?method=do_it at the end.
There are several problems with what you're trying to do. First of all, the link_to helper expects at least two arguments: 1, the text for the link, and 2 the HREF for the link. So, you really need to use:
link_to 'Run it', url
Second, you need to know what URL to pass to get your controller action.
Please be familiar with the following rails convention: When referring to a controller action you can abbreviate it using the form: controller_name#controller_action. e.g. pages#show or articles#index.
Assuming your controller is called ExamplesController, you can manually trigger the seven standard controller actions as follows:
link_to 'examples#index', '/examples'
link_to 'examples#show', '/examples/123' # 123 is the id of a specific example
link_to 'examples#new', '/examples/new'
link_to 'examples#create', '/examples', :method => :post
link_to 'examples#edit', '/examples/123/edit'
link_to 'examples#update', '/examples/123', :method => :put
link_to 'examples#destroy', '/examples/123', :method => :delete
Note that in the above, INDEX, SHOW, NEW, and EDIT are all GET requests. You could specify :method => :get but that is unnecessary
To abstract this away and take care of assigning the ID when required Rails provides path helpers.
So, to repeat the above using the path helpers you could use:
link_to 'examples#index', examples_path
link_to 'examples#show', example_path( #example )
link_to 'examples#new', new_example_path
link_to 'examples#create', examples_path, :method => :post
link_to 'examples#edit', edit_example_path( #example )
link_to 'examples#update', example_path( #example ), :method => :put
link_to 'examples#destroy', example_path( #example ), :method => :delete
Now, you get these path helpers from the router, and they are defined in your routes.rb file. Within that file if you define:
resources :examples
...then you will get all the path_helpers above.
If you are using a normal RESTful controller and you want to add a custom action, then you need to make one decision: does the action operate on the entire set of objects handled by that controller (like index) or just a single specific one (like show). The reason this is important is this tells the router whether the new action you're defining needs to receive a record ID as part of the request.
If you want to act on the entire collection of objects, you define:
resources :examples do
collection do
get 'do_it'
end
end
If you want to act on just a single member of the collection you define:
resources :examples do
member do
get 'do_it'
end
end
Where I wrote 'get' in the examples above you can use any of the four verbs -- GET is normally what you do if you want to show a page, and POST is normally what I'd use if you're submitting a form. You can also write this shorthand like so:
resources :examples do
get 'do_it', :on => :collection
post 'something', :on => :member
end
For more on defining custom controller actions, see the rails guide.
Now that you've defined a route, you should run rake routes in terminal to see the name of the new path helper. Let's assume you added do_it as a collection method, your path helper would be: do_it_examples_path.
Now then, back to your link, if you put:
<%= link_to 'Do it.', do_it_examples_path %>
... then you would trigger the do_it action. When the action is triggered your puts should normally render to the server log (assuming you're running rails s in a terminal window you should see it right after started GET on examples#do_it...).
Now, in the browser you would get a missing template error as a GET request is going to expect to render a view, but that's a subject for another question. Basically, now you should understand what the controller actions are, how you get to them. If you want to learn more about what to do with your controller action, see the guide :)
I hope you understand what's going on now. Feel free to ask questions.
You want to use the Rails logging mechanism:
http://guides.rubyonrails.org/debugging_rails_applications.html#sending-messages
This means that even if you don't launch the server using rails s the output will still go to the right place.

Resources