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 "\#{const} = %i[\#{roles * ' '}]\n\ndef \#{name}=(args)\nargs = [args] unless Array === args\n\nargs.compact!\nargs.map!(&:to_sym)\n\nself[:\#{name}_mask] = (args & \#{const}).map { |i| 2 ** \#{const}.index(i) }.sum\nend\n\ndef \#{name}\n\#{const}.reject { |i| ((\#{name}_mask || 0) & 2 ** \#{const}.index(i)).zero? }\nend\n\ndef has_\#{name}?(*args)\n(\#{name} & args).size > 0\nend\n", __FILE__, __LINE__

  if name != singular
    class_eval "alias has_\#{singular}? has_\#{name}?\n"
  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