Custom action_item with custom method in Active Admin - ruby-on-rails

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

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.

Get the actual id of current user without deleting the session? rails will_paginate dilema

I want to get the real id of a User due to a problem in the url for will_paginate in that I need to set it manually, so I want to get the target link for will paginate by a custom renderer method in the view like this,
<%= will_paginate #friends, :renderer => WillPaginateHelper::MyLinkRenderer %>
And the helper is something like,
module WillPaginateHelper
class MyLinkRenderer < WillPaginate::ActionView::LinkRenderer
include SessionsHelper
protected
def link(text, target, attributes = {})
if target.is_a?(Integer)
attributes[:rel] = rel_value(target)
target = "/users" + current_user.id + "/friends?page=#{target}"
end
attributes[:href] = target
tag(:a, text, attributes)
end
end
end
The line target = "/users" + current_user.id + "/friends?page=#{target}" is the important one where I need to set the url for will_paginate anchor links with the current user id.
Problem is that when the helper I am using is ran in the view to set the url, I get a error undefined local variable or method session..., you cannot use sessions hash in helpers so how to get the real id of the current_user to insert into variable. Do I delete/destroy the session get the id and create a new one? The problem is once I delete the session how to get the id once I delete the session and the user reference is deleted.
Reason
I have a friends#index action that is rendered in a div and upon initial call to url the pagination attaches correctly being the url users/:id/friends so each pagination request goes to the correct user and action. But this index view has "Unfriend" form attached to each displayed friend so you can destroy the friendship which goes to the destroy action of the friends controller so markup is eg <form... action="friends/177"...> and on full view reload of the index action from destroy action will paginate attaches to the last known link unless overriden. So when index action is fully rendered again pagination links are friends/177 which give a server error anyway and makes no sense as that record just got destroyed.
I have the current_user variables and id in my destroy action but I can not find a way to get them to my helper method, or simply get the current user id from the session.
Ok, so thanks to max and everyone for helping me. found this post and this answer here which you can do something like,
In your routes
get 'users/:cu_id/friends', to: 'users#index', as: :my_friends
And for the view
<%= will_paginate #friends, params: { controller: :users, :cu_id => current_user.id } %>
This will produce users/:cu_id/friends?page=2... of course :cu_id will be the actual id number, for pagination.
EDIT: Order is important here to if you have any other routes going to the same url eg, users/:user_id/friends when you nest resources, you need to put get 'users/:cu_id/friends',... below this, as rails will build the correct route for you for will paginate but come back in through the friends#index action when paginated and match eg users/1/friends through users/:user_id/friends as it is first in order in routes.rb. here in rails guide it explains how it works.

ActiveAdmin - custom page actions

I need to create a custom page with a controller action in ActiveAdmin. I need a form on this page that checks if the phone number exists and output a list of records with this telephone.
I have the following:
ActiveAdmin.register_page 'Phone' do
page_action :check, method: :post do
#resources = Telephone.where(number: params[:number]).
joins(:phoneable).map(&:phoneable)
end
# A form for #check action
end
But no idea what to do next. How to render the form properly (I would prefer to use AA DSL, rather than rendering a partial)? How to define routes?

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