code reusability issue in active admin rails - ruby-on-rails

How to avoid common code used in active admin action item and controller action.
I have seen people used to write the same code in the controller and active admin action item.
Is there any way to avoid it?
Example is like i want to cancel a user and it requires 3-4 steps to do it.
So i have writtent this code in users controller.
Now i have resource user in active admin and i want to delete the user from active admin. I have created an action item and again written the same code of deleting the user in the member action.
Is there any way to avoid above.

If you want to share code between different controllers, you should look to the rails concerns https://api.rubyonrails.org/v5.2.2/classes/ActiveSupport/Concern.html

Assuming this is in the User register block (probably at app/admin/users.rb) this might help you. The action_item only contains a link (actually a form that posts) to the actual member_action. This is just example code:
action_item :cancel_user, :only => :edit do
link_to 'Cancel user', do_cancel_user_admin_user_path(resource), method => :post
end
member_action :do_cancel_user, :method => :post do
flash.notice = "User will be canceled"
resource.cancel # I guess this would the 4 lines of code that you are repeating
redirect_to edit_admin_user_path(resource) and return
end
Let me know if this did/did not confuse you. Good luck!
P.S. A few weeks ago someone asked something similar, this might also be of help: How to reset user´s password by Devise authentication token directly from edit_page in Active Admin?

Related

Best way to reset ActiveRecord Attribute with a link

I have a counter in my model that I want to give the user the ability to
reset it, I'm wondering what's the best way to achieve this. I can think of
two ways:
By a custom controller action.
Simple and easy but I can't decide which HTTP verb to use. I can make the
case that it should be a GET because the user clicks a link that reset
the counter and the result are always the same, i.e. counter
becomes 0. But it could also be a POST/PATCH since we are modifying
something on the server but POST/PATCH requires a form which leads to
the other way.
By a link that submits an edit form with the counter reset to 0 without
the user seeing the form.
I like this solution because it can be done with RESTful controller
methods. But I have no idea how to do that with Rails, or even if it's
possible.
So which is "Rails Way" to do this? and how do I do it?
Rather than creating a custom action, another approach is to create a well-named controller and stick to the RESTful controller method names.
config/routes.rb
resource :counter_reset, only: [:create]
app/controllers/counter_reset_controller.rb
class CounterResetController < ApplicationController
def create
# reset your counter
end
end
Then POST to counter_reset_path in your view
Personally, I would use button_to — this generates a single button that submits to the URL; it performs a POST operation by default. If you don't like the button style, you can switch to using link_to; however, keep in mind that if a user has JavaScript disabled, the request will fallback to using GET.
<%= button_to "Reset counter!", counter_reset_path %>
<%= link_to "Reset counter!", counter_reset_path, method: :post %>
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
Update:
If you prefer not to create a new controller, you can create a new route that maps to a custom action in your existing controller:
config/routes.rb
resources :counters do
post :reset, to: "counters#reset"
end
app/controllers/counters_controller.rb
class CountersController < ApplicationController
def reset
# reset your counter
end
end
In your view:
<%= button_to "Reset counter!", counter_reset_path %>
Actually you don't need a form, for me i would add a new action, it would look something like this ( of course depends on how your current routing looks like )
/user/:id/counter/reset # with action = post
And the link is very simple, you just create a link_to and add a method: :post which will add a data-method: :post in the html, the rest will be handled by the unobtrusive js.
The reason I don't recommend the form method, is users might use it to update different attributes that you might not want to update, or at least even change the counter to whatever number they want, I prefer the specific number to be defined in the controller not the view/form.

Rails multiple create/update forms

Due to business logic, I want certain resources in my Rails app to be able to be created and updated from various forms. So for example, I have a resource Business, and depending on whether the user has just signed up or has been on the site for a while, a Business can be created from /account_setup (first time user) or /businesses/new (existing users).
I want to have both forms post to the same :create method and I want the method to be smart enough to figure out which page did the posting so it can render that page if it fails a validation. Just for clarity:
GET /account_setup => POST /businesses => businesses#create => {fail} => render 'account_setup'
GET /businesses_new => POST /businesses => businesses#create => {fail} => render 'new'
Since both are looking to create a Business, it's pointless to have two separate controller methods, and I'm assuming there's a Rails-y way to solve this. What's a good way to detect which of my two forms did the post?
Use request.original_fullpath in businesses#create or embed values specific to the pages(controllers) that show the forms using hidden_field.
You can put hidden fields in the form which will store the type of the form. Keep in mind, if it's a new user then you will have to create the user before creating business assuming the models are associated.

How to do browser-based new user approval by Admin in Rails 3.2 using Devise

I created a simple directory using Rails 3.2 and devise where new users need approval before they can use the site. I followed the instructions in "How To: Require admin to activate account before sign_in" and admin users can now see lists of different index pages of approved versus non-approved users. So far so good.
My problem is the user approval process. Currently I approve users in the Rails console. I'd like for admin users to be able to approve users through their browser. I'm at a loss. I know I need to put "approve" and "don't approve" links next to each unapproved user but then what?
In the abstract I know that clicking those links should activate a method in the User model or controller and then redirect with flash but I've strayed beyond what I know from my beginner tutorials.
I'm not sure what to put in my views and routes. Do I need a special hidden form that only changes 'approved' from false to true when when the "approve" submit button is clicked and the button is the only visible element?
If anyone can get me started in the right direction I can probably figure it out from there.
#sas1ni69's comment lead me to Ruby on Rails link_to With put Method which allowed me to find a solution.
To my view I added:
<%= link_to "approve", approve_user_path(user.id) %>
To my routes I added:
match 'users/:id/approve'=> 'users#approve_user', as: 'approve_user'
To my user controller I added:
def approve_user
user = User.find(params[:id])
user.approved = true
if user.save
flash[:notice] = "#{user.full_name} approved"
else
flash[:alert] = "#{user.full_name} approval failure"
end
redirect_to :back
end
Seems to be working like a charm! Thanks everybody!

Keeping a query-string in devise sign up

I am using rails with devise for signing up. Also I added an invite code, so not everybody can sign up.
The invite code gets transmitted via query-string like "/users/sign_up?invite_code=wajdpapojapsd" and gets added to a hidden field of the sign-up form with "f.hidden_field :invite_code, :value => params[:invite_code]".
This works pretty well. The only problem is that if the sign up doesn't get validated and rejected, devise redirects to "/users" and loses the query string with the invite_code in it.
As the email stays in the sign up form after the failed attempt, I believe that this should also work for the invite code. As a worst case solution redirecting :back after a failed sign up and losing the email, but keeping the invite code, would be better than the way it works now.
EDIT: By now I ve set up a registration controller for devise, but don't have any idea how to get the desired behavior.
Any help on how to keep the query string or just the invite code would be awesome.
I found a working solution by now.
I used jstr's answer to set up the controller for devise and added the "session" line.
class MyRegistrationsController < Devise::RegistrationsController
prepend_view_path "app/views/devise"
def create
super
session[:invite_code] = resource.invite_code
end
def update
super
end
end
Afterwards I added following to the devise/registrations/new.html.erb
<% if params[:invite_code]
#invite_code = params[:invite_code]
else
#invite_code = session[:invite_code]
end %>
And changed the hidden_field to
<%= f.hidden_field :invite_code, :value => #invite_code %>
You might need to make your own subclassed Devise controllers to get this to work.
This answer has a good description of how to to this.
The basics:
Install the Devise views if you haven't already with rails generate devise:views
Create a subclassed Devise::RegistrationsController
Update your Devise routes declaration to get Devise to use your subclassed controller
#James Lever 's solution has one disadvantage. invite_code remains in session. In some solutions it can cause problems. The alternative soultion would be:
def create
# set invite code to session:
session[:invite_code] = resource.invite_code
# here the form is rendered and invite code from session would be used:
super
# delete invite code from session:
session.delete('invite_code')
end
why don't use instance variable such
#invite_code = params[:invite_code]
Then when a sign up didn't pass validation, you can use that variable in the view which will be displayed for failed sign up.
I mean before, it's redirected, you should keep the invite code parameters with instance variable. sorry if i'm mistaken.

How to setup routes when the controller only has edit and update?

I can't seem to figure out how to get my routes setup properly.
In my app, I have a view that lets site owners update their address information. The new and create actions are part of the signup process and are located in the signups_controller. The edit and update actions are in the settings_controller.
When the user goes into the settings area, he/she sees only the edit form. When filled out, the user is then returned to the same form with a flash message, or error message. Here is what the controller looks like:
class SettingsController < ApplicationController
def edit
#account = current_account
#account.companies.first
#account.companies.first.addresses.first
#account.companies.first.phones.first
end
def update
#account = current_account
if #account.update_attributes(params[:account])
redirect_to edit_setting_path
flash[:notice] = "Success!"
else
render :edit
end
end
end
In my routes, I simply have:
resources :settings
The link to this area of the site is a basic RESTful named linke, with the parameter options:
edit_setting_path(:id => current_account.id)
When the user arrives to this page, they see the following URL:
http://domainname.com/settings/1/edit
When they submit the form and get errors, the URL changes to:
http://domainname.com/settings/1
Why is the URL changing -- I'd rather it not? Is there a way to make it stay the same as the initial edit view? I've tried doing a redirect on a failed update, but then I don't get the error messages.
Any ideas?
To answer your "why" question: The URL is changing because it's reflecting the URL of the failed request - which in this case is a PUT request to that URL (/settings/1). You've submitted the form and the submission of that form (correctly) points to that URL. This is a result of the RESTful routes that the helper gives you. Since the logic in your action, falls through to the render :action, there is no redirect and the form simply re-renders on the page using the same data available in this action (which is why you can see the errors).
If you want to redirect back to the edit page, yes, you will lose the errors that have been set in the #account instance variable since the redirect will reset (re-query for) the account.
You could add a route that matches a PUT to /settings/1/edit and point it to your update action and change your form etc. In short, I wouldn't recommend this, but it should work.
completely untested but attemptable:
routes.rb
put "/settings/:id/edit", :to=>"settings#update", :as=>"update_setting"
resources :settings, :except=>:update
your form would also have to submit to the update_setting_path (which also means it's not reusable for a new object... ew)
First you should read up on The Rails Guides for Routing. They will help a lot to understand why its working like that.
Secondly, to accomplish what you are trying to do, you will need to add manual routes via the match call. You'll need something like this.
match '/settings/:id/edit' => "settings#edit"

Resources