Search code examples
ruby-on-railscancancancancan

Where does CanCanCan define flash "%{subject}", and how to modify to be consistent with Rails localization conventions?


CanCanCan displays localised flash messages if a resource is not authorised.

https://github.com/CanCanCommunity/cancancan/wiki/Translations-(i18n)

# en.yml
en:
  unauthorized:
    manage:
      all: "You do not have access to %{action} %{subject}!"

I have dug through the GitHub repo, but I can't figure out how the subject variable is defined.

From the output, I would guess that subject is defined as something like object.class.name.underscore.

I want to alter this to use object.model_name.human.

This would make it much more consistent with Rails conventions, and easier to localize.

en:
  activerecord:
    models:
      mymodel: MyLocalizedSubjectName

Can someone point me to code that defines subject, or suggest how I can patch CanCanCan to use localized model names?


Solution

  • It looks to me like the message is coming from the unauthorized_message method in ability.rb. In particular these lines:

      variables[:subject] = (subject.class == Class ? subject : subject.class).to_s.underscore.humanize.downcase
      message = I18n.translate(keys.shift, variables.merge(scope: :unauthorized, default: keys + ['']))
    

    So you could redefine that method to do what you want (although it is already using humanize).

    EDIT: Including a note about i18n keys when using human and namespaced models. Given a locale file like this:

    en:
      activerecord:
        models:
          user: xxxx
          base: aaaa
          activerecord/base: bbbb
          active_record/base: cccc
    

    I get these results:

    2.4.1 :001 > User.model_name.human
     => "xxxx" 
    2.4.1 :002 > ActiveRecord::Base.model_name.human
     => "cccc" 
    2.4.1 :003 > 
    

    You can also say ActiveRecord::Base.model_name.i18n_key to avoid some trial-and-error.