Skip to content

CanCan: remove associated action buttons in forms

inbreed edited this page Aug 9, 2012 · 1 revision

Help: i dont feel too confident about my ruby-skills. Please help improving this article by improving the source where needed, thanks

In order to remove the "Add new"/"Edit" buttons for associated Models in the form without removing the ability to manage those associated models, cancan helps out well.

Example: a User can :manage Authors and Books, but must not be able to :manage authors while in Books-Edit form

The idea is pretty simple:

   cannot [:create,:update], Model unless current_model == Model

The default CanCan AuthorizationAdapter however does not inform the Ability-Class about the current view/model, that is why it needs to be overwritten.


In your Rails-App root:

mkdir -p lib/rails_admin/extensions/cancan/ 
cd  lib/rails_admin/extensions/cancan/;
wget https://raw.github.com/gist/3302673/7d013f23bba67a7c7317003c56117b2a88503d39/authorization_adapter.rb

This file, contains the ControllerExtension-Module, cloned from RailsAdmins original CanCan AuthorizationAdapter, except for the 2nd Parameter in @ability.new, which represents the current displayed model you want to edit.

This gist contains:

module RailsAdmin
  module Extensions
    module CanCan
      class AuthorizationAdapter
        private
        module ControllerExtension
          def current_ability
            @current_ability ||= @ability.new(_current_user, self.params["model_name"])
          end
        end
      end
    end
  end
end

config/initializers/rails_admin.rb

the CanCan configuration will remain the same, except for loading the updated authorization_adapter.rb.

   require Rails.root.join('lib/rails_admin/extensions/cancan/authorization_adapter')
   RailsAdmin.config do |config|
   ....

I load the authorization_adapter here, since i find it belongs to rails_admins configuration, which i dont want to have "all over the app".


Ability-Class

The only mandatory update is to add a further parameter to the initialize method, named like request_model. It is the Model-Class you are viewing,editing in RailsAdmin.

My Example from above was: "a User can :manage Authors and Books, but should not be able to manage Autors while in Books-Edit form"

this can result in something like:

class Ability
  include CanCan::Ability

  def initialize(user, request_model)

    if user
      can :access, :rails_admin
      can :dashboard
      can :manage, :all

      # For all Models we have, forbid create and update
      # but dont forbid :manage, in order to keep it in 
      # navigation if not excluded by rails_admin config
      ActiveRecord::Base.descendants.each do |m|
        cannot [:create, :update], m unless request_model == m.name.underscore
      end if request_model

      # a manual way can look like this :
      # cannot [:create, :update], Author if request_model == "books"
      # cannot [:create, :update], Book   if request_model == "author"

    else
      cannot :access, :rails_admin
    end
  end
end

Thats it, now the Edit/Create associated Model-buttons are gone, but the Model remains in the Navigation. This is quite usefull if a User may create associations with models which are excluded via rails_admin config

Clone this wiki locally