Custom action_item on show page only - ruby-on-rails

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.

Related

Custom action_item with custom method in Active Admin

I have a nested resource in Active Admin which shows all SupportSessions (like meetings) for a particular SupportAllocation (which defines a teacher-pupil relationship):
ActiveAdmin.register SupportSession do
belongs_to :support_allocation
On my index page, I'd like a button at the top that the user can click (like they can click 'New Support Session'), which then executes a custom method which sends an email using ApplicationMailer. There is no 'page' where the button goes to - it just redirects back to the current index page with a message indicating success or otherwise.
I can get the 'Request Approvals' button to appear on the index page with this code:
# Adds a new button
action_item only: :index do
link_to 'Request approvals', send_for_approval #custom method
end
But obviously this raises an exception:
undefined local variable or method `send_for_approval'
Because I haven't defined this custom method anywhere.
I've created my mailer class but I'm not sure how to 'connect' it to my resource. I realise this will involve a new route of some sort, or use the existing 'put' method. I'd need to hand the current SupportAllocation ID to the method, so it knows which records/data to deal with when it sends email messages.
Any tips on how do I do create a button that runs this custom method + parameter? Where do I define this new custom method?
Thank you for your help.
You should code the action first, in your file:
member_action :send_for_approval, method: :patch do
# send your email here you can access to :resource which is your record
YourMailer.with(support_session_id: resource.id).your_email.deliver_now
# redirect to your admin index or show path
end
Then rails routes will give you the correct path to it so you can pass it to action_item, it will look something like that:
action_item only: :index do
link_to 'Request approvals', send_for_approval_admin_support_session_path, method: :patch
end
References:
https://activeadmin.info/8-custom-actions.html#member-actions
https://activeadmin.info/8-custom-actions.html#action-items

How to add a button in ActiveAdmin to create a new record for another resource, with fields pre-populated with values from the current resource?

In my ActiveAdmin dashboard, I have two resources - Posts and ScrapedPosts. For a ScrapedPost, I want to add a Publish Post button, which, when clicked, goes to a page for creating a new Post (new action), where all the fields for Post are pre-filled with values from ScrapedPost (Post and ScrapedPost have identical schema).
Here is my current code inside app/admin/scraped_post.rb
ActiveAdmin.register ScrapedPost do
action_item :view, only: :show do
link_to "Publish Post", new_admin_post_path
end
end
This adds a new button called Publish Post. When I click on it, as expected, it creates a page for adding a new Post. However, I want the fields pre-filled with the values from the current ScrapedPost from where I am creating the post.
I have tried a number of things so far. The documentation for action_item is very basic and it didn't help. It looks like I can't pass parameters to the action identified by new_admin_post_path. How can I do it?
[I started learning RoR just this week, so I'll be grateful if you can explain your solution as well.]
All action_item is doing is injecting a link styled as a button onto the show page. You can pass parameters to the link target by embedding them in the link. However, new does not accept parameters so to copy a Post what you need is a member_action:
member_action :clone do
resource.clone
render :new
end
This action can be invoked so:
action_item :clone, only: :show do
link_to "Publish Post", clone_admin_post_path
end
Make sure first that works for copying a Post. If you want to create a ScrapedPost from a Post then you will need to pass the id of the Post as a parameter:
link_to "Publish Post", clone_admin_scraped_post_path(post_id: resource.id)
then the ScrapedPost member action can find it:
member_action :clone do
#scraped_post = Post.find(params[:post_id]).clone.becomes(ScrapedPost)
render :new
end
As an aside, for better or worse ActiveAdmin is an additional layer of abstraction on top of Ruby on Rails so my recommendation is always to master vanilla Rails first to ease the learning curve.

ActionController::UrlGenerationError using link_to

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.

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

How do I create a rails controller action?

My rails app has a single CustomerSelectionController, with two actions:
index: which shows a form where the user can enter customer information and
select: which just displays a static page.
class CustomerSelectionController < ApplicationController
def index
end
def select
end
end
I've created an entry in my routes.rb file:
resources :customer_selection
and the form in the index view looks like:
<h1>Customer Selection</h1>
<%= form_tag("customer_selection/select", :method => "get") do %>
<%= submit_tag("Select") %>
<% end %>
however when I click on the Select button in the browser, all I get is:
Unknown action
The action 'show' could not be found for CustomerSelectionController
I'm not sure why it is trying to perform an action called show? I haven't defined or referenced one anywhere.
I'm not sure why it is trying to perform an action called show? I haven't defined or referenced one anywhere.
Yes you have. That's what resources does. It defines the seven default RESTful routes: index, show, new, create, edit, update and destroy. When you route to /customer_selection/select, the route that matches is "/customer_action/:id", or the "show" route. Rails instantiates your controller and attempts to invoke the "show" action on it, passing in an ID of "select".
If you want to add a route in addition to those, you need to explicitly define it, and you should also explicitly state which routes you want if you don't want all seven:
resources :customer_selection, only: %w(index) do
collection { get :select }
# or
# get :select, on: :collection
end
Since you have so few routes, you can also just define them without using resources:
get "/customer_selection" => "customer_selection#index"
get "/customer_select/select"
Note that, in the second route, the "customer_select#select" is implied. In a route with only two segments, Rails will default to "/:controller/:action" if you don't specify a controller/action.

Resources