Module: SimonSays::Roleable::ClassMethods

Defined in:
lib/simon_says/roleable.rb

Instance Method Summary collapse

Instance Method Details

#has_roles(*roles) ⇒ Object

Provides a declarative method to introduce role based access controller through a give integer mask.

By default it'll use an attributed named role_mask. You can use the :as option to change the prefix for the _mask attribute. This will also alter the names of the dynamically generated methods.

Several methods are dynamically genreated when calling has_roles. The methods generated include a setter, a getter and a predicate method

Examples:

Detailed example:

class User < ActiveRecord::Base
  include SimonSays::Roleable

  has_roles :read, :write, :delete
end

class Editor < ActiveRecord::Base
  include SimonSays::Roleable

  has_roles :create, :update, :publish, as: :access
end

User.new.roles
=> []

User.new(roles: :read).roles
=> [:read]

User.new.tap { |u| u.roles = :write, :read }.roles
=> [:read, :write]

User.new(roles: [:read, :write]).has_roles? :read, :write
=> true

User.new(roles: :read).has_role? :read
=> true

Editor.new(access: %w[create update publish]).access
=> [:create, :update, :publish]

Editor.new(access: :publish).has_access? :create
=> false

Parameters:

  • roles (Array<Symbol, String>)

    array of role symbols or strings

  • opts (Hash)

    options hash

  • opts (Symbol)

    :as alternative prefix name instead of “role”



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/simon_says/roleable.rb', line 61

def has_roles *roles
  options = roles.extract_options!

  name = (options[:as] || :roles).to_s
  singular = name.singularize
  const = name.upcase

  Roleable.registry[model_name.to_s.downcase.to_sym] ||= name

  roles.map!(&:to_sym)

  class_eval <<-RUBY_EVAL, __FILE__, __LINE__
    #{const} = %i[#{roles * ' '}]

    def #{name}=(args)
      args = [args] unless Array === args

      args.compact!
      args.map!(&:to_sym)

      self[:#{name}_mask] = (args & #{const}).map { |i| 2 ** #{const}.index(i) }.sum
    end

    def #{name}
      #{const}.reject { |i| ((#{name}_mask || 0) & 2 ** #{const}.index(i)).zero? }
    end

    def has_#{name}?(*args)
      (#{name} & args).size > 0
    end
  RUBY_EVAL

  if name != singular
    class_eval <<-RUBY_EVAL
      alias has_#{singular}? has_#{name}?
    RUBY_EVAL
  end

  # Declare a scope for finding records with a given role set
  # TODO support an array roles (must match ALL)
  scope "with_#{name}", ->(role) {
    where("(#{name}_mask & ?) > 0", 2**roles.index(role.to_sym))
  }
end