is there a way to specify in ActiveAdmin's index page of a model what actions are allowed, things like:
index do
actions :edit
end
index do
actions only: :edit
end
do not work. What's the correct syntax?
Appreciated.
bundle show activeadmin
/home/muichkine/.rvm/gems/ruby-2.1.2/bundler/gems/active_admin-9cfc45330e5a
Add whatever actions you want to be available by using actions (it is usually put under model definition):
ActiveAdmin.register YourModel do
actions :index, :show, :create, :edit, :update
If you want to specify the method for certain action, you can do
action_item only: :show do
link_to 'Edit', action: :edit # so link will only be available on show action
end
Example how to play with the action column. In this example I just re-implemented the default one, but you can do powerful coding here:
column :actions do |item|
links = []
links << link_to('Show', item_path(item))
links << link_to('Edit', edit_item_path(item))
links << link_to('Delete', item_path(item), method: :delete, confirm: 'Are you sure?')
links.join(' ').html_safe
end
Do this way,
ActiveAdmin.register Foobar do
actions :all, :except => [:destroy]
end
or
ActiveAdmin.register Foobar do
actions :only => :edit
end
Need to be specified at resource level not in method definition
According to source code, https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/views/index_as_table.rb#L80
if one want to change the actions in the index he should go with
actions defaults: false do |sample|
link_to t('active_admin.edit'), admin_sample_path(sample)
end
where you can replace the link title and the path for the action
For Example:
actions defaults: false do |user|
link_to t('active_admin.view'), admin_user_path(user)
end
Note:
Keep in mind that add the path correctly like for show it should be admin_user_path(:id) and for index it should be admin_users_path :)
Related
Routes
resources :favorites, only: [ :index, :create, :destroy ] , param: :listing_id
Rake routes
favorites GET /favorites(.:format) favorites#index
favorites POST /favorites(.:format) favorites#create
favorite DELETE /favorites/:listing_id(.:format) favorites#destroy
Notice the (s) in favorites, why is it not all favorite or favorites?
I create one favorite and destroy one favorite, so I think it should be singular in both.
I need
favorite POST /favorites/:listing_id(.:format) favorites#create
I tried this in my routes:
resources :favorites, only: [ :index, :destroy
] , param: :listing_id
post 'favorites/:listing_id' => 'favorite#create', as: :favorite
but get this error:
ArgumentError: Invalid route name, already in use: 'favorite' You may
have defined two routes with the same name using the :as option, or
you may be overriding a route already defined by a resource with the
same naming. For the latter, you can restrict the routes created with
resources as explained here:
http://guides.rubyonrails.org/routing.html#restricting-the-routes-created
How do I modify this one?
How do I keep it consistent as I need create path and destroy path consistent in my view for a number of reasons.
My controller
class FavoritesController < ApplicationController
before_action :load_listing, only: [:create, :destroy]
def index
#favorites = current_user.favorites.map{|i| i.id} || []
#listings = ListingsQuery::Search.call(:favorited_ids=> current_user.favorites.map{|i| i.id} )
respond_to do |format|
format.html {}
format.js {}
end
end
def create
if current_user.favorite!(#listing)
format.js {}
end
end
def destroy
if current_user.unfavorite!(#listing)
format.js {}
end
end
private
def load_listing
#listing_id = favorite_params[:listing_id]
#listing = Listing.find(#listing_id)
end
def favorite_params
params.permit(:listing_id)
end
end
view
<% if listing.is_favorited == true %>
<%= link_to favorite_path(:listing_id => listing.listing_id), method: :delete, remote: true do%>
<i id='i-<%= listing.listing_id %>' class=" fa fa-heart"></i>
<% end %>
<% else %>
<%= link_to favorite_path(:listing_id => listing.listing_id), method: :post, remote: true do %>
<i id='i-<%= listing.listing_id %>' class="fa fa-heart-o"></i>
<% end %>
<% end %>
create.js
(function(){
$("#i-<%= #listing_id %>").removeClass('fa-heart-o');
$("#i-<%= #listing_id %>").addClass('fa-heart');
$("#i-<%= #listing_id %>").parent().attr("data-method",'delete');
})();
Why is this...
resources :favorite do
collection do
post "for_lisiting/:listing_id", action: :create_for_listing
delete "for_listing/:listing_id", action: :delete_for_listing
end
end
preferred over this..
match 'favorite' => 'favorites#create', via: :post
match 'favorite' => 'favorites#destroy', via: :delete
It seems to me, but maybe I am wrong. that
/favorite/for_lisiting/:listing_id(.:format)
is unnecessarily long compared to
/favorite/:listing_id(.:format)
however, I am a novice, so value your reasoning.
When using rails resource helper, it creates some REST endpoints. You have listings that can be favorited, you are mixing both resources. Your resource is the Listing and favorite/unfavorite/favorites are actions on the resource.
Try something like this:
resources :listings do
member do
post :favorite, action: :create_favorite
delete :favorite, action: :delete_favorite
get :favorites
end
end
That will give you two route: /listings/:id/favorite (both for create -POST- and delete -DELETE-) and /listings/:id/favorites (GET). Both create and delete will be the same favorite_listing_path(listing) (or similar, check rake routes).
Now, on your ListingsController, define those actions:
class ListingsController < ApplicationController
def create_favorite
Listing.find(:id).favorites.create(user: current_user)
redirect_to :something, notice: 'Favorited'
end
def delete_favorite
Listing.find(id).favorites.where(user: current_user).destroy_all
redirect_to :something, notice: 'Unfavorited'
end
def favorites
#favorites = Listing.find(id).favorites
end
# of course, you could add a before_action to DRY it, I just wanted to be explicit on which is the actual resource
end
When creating objects in ActiveAdmin I typically need to add multiple, and wish there was an option to add another object on the show page (which appears after submitting the new object).
I have been doing this model by model:
ActiveAdmin.register Color do
action_item :add, only: :show do
link_to "New", new_administration_color_path
end
end
Add this to admin/administration_color.rb
controller do
def create
create! do |format|
format.html { redirect_to admin_administration_color_path(resource, add_more: true) }
end
end
end
and some modified your code
action_item :add, only: :show do
link_to "New", new_administration_color_path if params['add_more'] == true
end
Consider the example in the note for this guide on routing and singular resources Both of these would be directed to 'photos#index', but are different contexts.
/users/1/photos (might list a user's photos)
/photos (list all users' photos)
I want to give the user different options depending on which route was followed to access.
There are two ways to assign this issue.
1st way
In the users_controller.rb
before_action :set_user, only: [:photos]
def photos
#photos = #user.photos
render "photos/index"
end
private
#user = User.find(params[:id])
In routes.rb you need to add this route,
resources :users do
get "photos", on: :member
end
2nd way
In photos_controller.rb
before_action :set_user, only: [:photos]
def index
unless #user.nil?
#photos = #user.photots
else
#photos = Photo.all
end
end
private
def set_user
if params[:user_id].present?
#user = User.where(params[:user_id]).first
end
end
Well first of all, I'd make /users/1/photos an action off users controller instead. However, if you really want to do what you say there, you could check for presence of the user_id param on the photos controller index and fashion your finder appropriately.
For example you can validate route match with rspec, something like the following:
expect(:get => "/users/1/photos").to route_to(
:controller => "photos",
:user_id => "1",
)
I am trying to create a button to change a model record attribute from false to true. I'm using a form_tag as follows:
=form_tag edit_goal_path(goal), method: :post do
=hidden_field_tag :purchased, value: true
=submit_tag "Purchase"
It's haml, but feel free to post suggestions with ERB. I'm getting the following error:
No route matches [POST] "/goals/4/edit"
Rails.root: /home/ben/rails_projects/hartwig
However, I already have the following route from resources:
PUT /goals/:id(.:format) goals#update
My controller looks as following:
def edit
#goal = Goal.find(params[:id])
end
def update
#goal = Goal.find(params[:id])
if #goal.update_attributes(goal_params)
redirect_to '/goals', notice: "Update successful!"
else
render '/'
end
end
def goal_params
params.require(:goal).permit(:item, :description, :picture, :purchased)
end
How do I get this to work? Or is there a better way to solve this?
Your question says:
I am trying to create a button to change a model record attribute from false to true
so why are you using a form for it? I think a better approach would be to create a link or button that will call an ajax method or a normal method with post route and update your attribute. You can achieve it by following these steps:
a. Create a route for your custom action where you'll update your attribute:
post 'purchase_update/:id' => "goal#update_purchase", as: update_purchase #post as you want to send your goal id
b. create your custom method inside your controller:
def update_purchase
#goal = Goal.find(params[:id])
#goal.update_attribute(:purchased, true)
respond_to do |format|
format.html {redirect_to your_path, notice: 'purchase updated'}
format.js {} #if you want to do something by ajax
end
end
c. Create your link that will call this method:
=link_to "Purchase", update_purchase_path(#goal), method: post
and if you want to do it by ajax then
=link_to "Purchase", update_purchase_path(#goal), method: post, remote: true
Another solution to your problem could be adding a new method to the Goal Controller:
in goals_controller.rb
def purchase
#goal.update_attribute(:purchased, true)
end
and also add on top (just add :purchase)
before_action :set_goal, only: [:show, :edit, :update, :destroy, :purchase]
in routes.rb change to
resources :goals do
member do
post 'purchase'
end
end
to add a new post routes to your goals
now you will have a purchase_goal_path that you can use in your view like this:
link_to 'Purchase', purchase_goal_path(#goal), method: :post
I set up my current_user in the Controller so User's Cant Edit other User's Entries.
But i can get it working for the Destroy Action.
My only solution is to check if the User is the Actual Uploader and only then show him the Destroy link.
<% if current_user == shop.user %>
<td><%= link_to 'Destroy', shop, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% end %>
But isn't his a Bad Practice as someone can easily hack this ?
If someone could enlighten me on this...
Thank You :)
You can use the CanCan gem. Once installed, CanCan will generate an 'Ability' file where you can define what users can or cannot do. in you case for example, you might have
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :read, :all
end
end
Then in your views, you can do a simple check to see whether or not a user has the ability to modify that object. For instance:
<% if can? :destroy, shop %>
<td><%= link_to 'Destroy', shop, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% end %>
In your controller you can authorize actions like so:
class ShopsController < ActionController::Base
load_and_authorize_resource
def destroy
....
end
end
The "load_and_authorize_resource" line will automatically add authorization into each RESTful action. It would add this to your destroy action, for instance:
#shop = Shop.find(params[:id])
authorize! :destroy, #shop
It's a super useful gem and very well documented
Another approach:
Instead of passing a shop id to edit, update and destroy, you could use a singular resource(http://guides.rubyonrails.org/routing.html#singular-resources) instead of a plural.
routes.rb:
resources :shops, only: :show, :index
resource :shop, only: [:new, :create, edit, :update, :destroy]
You will also need to change the before_action so it fetches the shop of the current user instead of querying by the id passed as parameter:
def set_shop
#shop = current_user.shop
end
Because you are no longer fetching the shop to be destroyed/edited using an id provided by the web user, there is no way for him to make a custom request with a fake id.
You should hide it on the view, but you should also have control of it on your controller.
In your controller you must do something like a before_action (read about it here: http://guides.rubyonrails.org/action_controller_overview.html)