permissive 0.0.1 → 0.2.0.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemspec +19 -1
- data/.gitignore +3 -1
- data/CHANGELOG +0 -0
- data/README.markdown +37 -29
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/generators/permissive_migration/templates/permissive_migration.rb +0 -2
- data/lib/permissive.rb +14 -12
- data/lib/permissive/errors.rb +4 -0
- data/lib/permissive/has_permissions.rb +153 -0
- data/lib/permissive/permission.rb +12 -30
- data/lib/permissive/permission_definition.rb +94 -0
- data/spec/has_permissions_spec.rb +326 -0
- data/spec/rcov.opts +2 -1
- data/spec/spec_helper.rb +2 -22
- metadata +23 -16
- data/README.markdown.html +0 -191
- data/lib/permissive/acts_as_permissive.rb +0 -134
- data/lib/permissive/permissions.rb +0 -29
- data/spec/acts_as_permissive_spec.rb +0 -192
- data/spec/permissions_spec.rb +0 -44
data/.gemspec
CHANGED
@@ -19,7 +19,25 @@ Gem::Specification.new do |s|
|
|
19
19
|
"README.markdown"
|
20
20
|
]
|
21
21
|
s.files = [
|
22
|
-
"
|
22
|
+
".gemspec",
|
23
|
+
".gitignore",
|
24
|
+
"MIT-LICENSE",
|
25
|
+
"README.markdown",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"generators/permissive_migration/USAGE",
|
29
|
+
"generators/permissive_migration/permissive_migration_generator.rb",
|
30
|
+
"generators/permissive_migration/templates/permissive_migration.rb",
|
31
|
+
"lib/permissive.rb",
|
32
|
+
"lib/permissive/acts_as_permissive.rb",
|
33
|
+
"lib/permissive/permission.rb",
|
34
|
+
"lib/permissive/permissions.rb",
|
35
|
+
"rails/init.rb",
|
36
|
+
"spec/acts_as_permissive_spec.rb",
|
37
|
+
"spec/permissions_spec.rb",
|
38
|
+
"spec/rcov.opts",
|
39
|
+
"spec/spec.opts",
|
40
|
+
"spec/spec_helper.rb"
|
23
41
|
]
|
24
42
|
s.homepage = %q{http://github.com/flipsasser/permissive}
|
25
43
|
s.rdoc_options = ["--charset=UTF-8"]
|
data/.gitignore
CHANGED
data/CHANGELOG
ADDED
File without changes
|
data/README.markdown
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
Permissive gives your ActiveRecord models granular permission support
|
2
2
|
=
|
3
|
-
Permissive
|
4
|
-
|
5
|
-
|
3
|
+
Permissive makes it trivial to add complex permission granting and checking
|
4
|
+
to your applications using ActiveRecord. It combines a model-based permissions
|
5
|
+
system with bitmasking to create a flexible approach to maintaining permissions
|
6
|
+
on your models. It supports an easy-to-use set of methods for accessing and
|
6
7
|
determining permissions, including some fun metaprogramming.
|
7
8
|
|
8
9
|
Installation
|
@@ -25,21 +26,30 @@ Installation
|
|
25
26
|
Usage
|
26
27
|
-
|
27
28
|
|
28
|
-
First, define a few permissions
|
29
|
+
First, define a few permissions on an ActiveRecord::Base subclass. You define them using the following simple, block-based API:
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
class User < ActiveRecord::Base
|
32
|
+
has_permissions do
|
33
|
+
to :manage_games, 0
|
34
|
+
to :control_rides, 1
|
35
|
+
to :punch, 2
|
36
|
+
end
|
36
37
|
end
|
37
38
|
|
39
|
+
The best practice is to name them in a verb format that follows this pattern: "Object can `do_action_name`".
|
40
|
+
|
41
|
+
Permission values (the second argument in `to`) need to be int values counting up from zero. We use ints because Permissive uses bit
|
42
|
+
masking to keep permissions data compact and performant.
|
43
|
+
|
38
44
|
And that's all it takes to configure permissions! Now that we have them, let's grant them to a model or two:
|
39
45
|
|
40
46
|
class Employee < ActiveRecord::Base
|
41
|
-
|
42
|
-
|
47
|
+
has_permissions, :on => :companies do
|
48
|
+
to :manage_games, 0
|
49
|
+
to :control_rides, 1
|
50
|
+
to :punch, 2
|
51
|
+
end
|
52
|
+
validates_presence_of :first_name, :last_name
|
43
53
|
end
|
44
54
|
|
45
55
|
class Company < ActiveRecord::Base
|
@@ -59,8 +69,9 @@ Easy-peasy, right? Let's try granting a few permissions:
|
|
59
69
|
@james.can?(:manage_games, :on => @adventureland) #=> true
|
60
70
|
|
61
71
|
# We can also use the metaprogramming syntax:
|
62
|
-
@james.
|
63
|
-
@james.
|
72
|
+
@james.can_manage_games_in! @adventureland
|
73
|
+
@james.can_manage_games_in? @adventureland #=> true
|
74
|
+
@james.can_control_rides_in? @adventureland #=> false
|
64
75
|
|
65
76
|
# We can check for multiple permissions, too:
|
66
77
|
@james.can?(:manage_games, :control_rides) #=> false
|
@@ -69,17 +80,20 @@ Easy-peasy, right? Let's try granting a few permissions:
|
|
69
80
|
|
70
81
|
# Scoping can be done through any object
|
71
82
|
@frigo.can!(:punch, :on => @james)
|
72
|
-
@frigo.
|
83
|
+
@frigo.can_punch? @james #=> true
|
73
84
|
|
74
85
|
# And the permissions aren't reciprocal
|
75
|
-
@james.
|
86
|
+
@james.can_punch? @frigo #=> false
|
76
87
|
|
77
88
|
# Of course, we can grant global (non-scoped) permissions, too:
|
78
|
-
@frigo.
|
89
|
+
@frigo.can_control_rides!
|
79
90
|
@frigo.can_control_rides? #=> true
|
80
91
|
|
92
|
+
# And we can grant permissions global to a class:
|
93
|
+
@frigo.can_control_rides_in! Company
|
94
|
+
|
81
95
|
# BUT! Global permissions don't override scoped permissions.
|
82
|
-
@frigo.
|
96
|
+
@frigo.can_control_rides_in?(@adventureland) #=> false
|
83
97
|
|
84
98
|
# Likewise, scoped permissions don't bubble up globally:
|
85
99
|
@james.can_manage_games? #=> false
|
@@ -90,6 +104,10 @@ Easy-peasy, right? Let's try granting a few permissions:
|
|
90
104
|
# We can revoke all permissions, in any scope, too:
|
91
105
|
@frigo.revoke(:all)
|
92
106
|
|
107
|
+
# And revoking does the fun meta thing, too:
|
108
|
+
@frigo.cannot_punch!(@james)
|
109
|
+
@frigo.can_punch? @james #=> flase
|
110
|
+
|
93
111
|
And that's it!
|
94
112
|
|
95
113
|
Scoping
|
@@ -128,17 +146,7 @@ which might yield something like
|
|
128
146
|
# and
|
129
147
|
@employee.can_control_rides_in_company @adventureland
|
130
148
|
|
131
|
-
|
132
|
-
|
133
|
-
@james.can_punch? @frigo
|
134
|
-
@frigo.can!(:control_rides, :in => @adventureland)
|
135
|
-
|
136
|
-
Meta-programmed methods for granting and revoking would be cool, too:
|
137
|
-
|
138
|
-
@james.can_punch! @frigo
|
139
|
-
@frigo.cannot_control_rides_in! @adventureland
|
140
|
-
|
141
|
-
And while we're on the subject of metaprogramming, let's add some OR-ing to the whole thing:
|
149
|
+
Let's add some OR-ing to the whole thing:
|
142
150
|
|
143
151
|
@james.can_control_rides_or_manage_games_in? @adventureland
|
144
152
|
|
data/Rakefile
CHANGED
@@ -15,7 +15,7 @@ namespace :spec do
|
|
15
15
|
Spec::Rake::SpecTask.new(:rcov) do |t|
|
16
16
|
t.spec_files = FileList['spec/**/*.rb']
|
17
17
|
t.rcov = true
|
18
|
-
t.rcov_opts = ['--exclude', '
|
18
|
+
t.rcov_opts = ['--exclude', 'spec/*,gems/*']
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -30,7 +30,7 @@ begin
|
|
30
30
|
determining permissions, including some fun metaprogramming.}
|
31
31
|
gemspec.email = "flip@x451.com"
|
32
32
|
gemspec.homepage = "http://github.com/flipsasser/permissive"
|
33
|
-
gemspec.authors = ["Flip Sasser"
|
33
|
+
gemspec.authors = ["Flip Sasser"]
|
34
34
|
end
|
35
35
|
rescue LoadError
|
36
36
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.2.0.alpha
|
@@ -6,12 +6,10 @@ class InstallPermissive < ActiveRecord::Migration
|
|
6
6
|
t.integer :scoped_object_id
|
7
7
|
t.string :scoped_object_type, :limit => 32
|
8
8
|
t.integer :mask, :default => 0
|
9
|
-
t.integer :grant_mask, :default => 0
|
10
9
|
end
|
11
10
|
add_index :permissive_permissions, [:permitted_object_id, :permitted_object_type], :name => 'permissive_permitted'
|
12
11
|
add_index :permissive_permissions, [:scoped_object_id, :scoped_object_type], :name => 'permissive_scoped'
|
13
12
|
add_index :permissive_permissions, :mask, :name => 'permissive_masks'
|
14
|
-
add_index :permissive_permissions, :grant_mask, :name => 'permissive_grant_masks'
|
15
13
|
end
|
16
14
|
|
17
15
|
def self.down
|
data/lib/permissive.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
-
require 'permissive/
|
2
|
-
require 'permissive/
|
3
|
-
require 'permissive/permissions'
|
1
|
+
require 'permissive/errors'
|
2
|
+
require 'permissive/has_permissions'
|
4
3
|
|
5
4
|
module Permissive
|
6
|
-
@@strong = false
|
7
|
-
|
8
|
-
def self.strong
|
9
|
-
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.strong=(new_strong)
|
13
|
-
|
14
|
-
end
|
5
|
+
# @@strong = false
|
6
|
+
#
|
7
|
+
# def self.strong
|
8
|
+
# @@strong
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# def self.strong=(new_strong)
|
12
|
+
# @@strong = !!new_strong
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
autoload(:Permission, 'permissive/permission')
|
16
|
+
autoload(:PermissionDefinition, 'permissive/permission_definition')
|
15
17
|
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Permissive
|
2
|
+
module HasPermissions
|
3
|
+
module ClassMethods
|
4
|
+
def self.extended(base)
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
def has_permissions(options = {}, &block)
|
9
|
+
options.assert_valid_keys(:on)
|
10
|
+
|
11
|
+
# Define a permissions method. This will track scoped or global
|
12
|
+
# permission levels, depending on how you define them.
|
13
|
+
class_eval do
|
14
|
+
def self.permissions
|
15
|
+
@permissions ||= {}
|
16
|
+
end
|
17
|
+
end unless respond_to?(:permissions)
|
18
|
+
|
19
|
+
include InstanceMethods
|
20
|
+
|
21
|
+
has_many :permissions, :class_name => 'Permissive::Permission', :as => :permitted_object do
|
22
|
+
def can!(*args)
|
23
|
+
options = args.extract_options!
|
24
|
+
options.assert_valid_keys(:on, :reset)
|
25
|
+
permission_matcher = case options[:on]
|
26
|
+
when ActiveRecord::Base
|
27
|
+
permission = proxy_owner.permissions.find_or_initialize_by_scoped_object_id_and_scoped_object_type(options[:on].id, options[:on].class.name)
|
28
|
+
when Class
|
29
|
+
permission = proxy_owner.permissions.find_or_initialize_by_scoped_object_id_and_scoped_object_type(nil, options[:on].name)
|
30
|
+
when String, Symbol
|
31
|
+
class_name = Permissive::PermissionDefinition.interpolate_scope(proxy_owner.class, options[:on])
|
32
|
+
permission = proxy_owner.permissions.find_or_initialize_by_scoped_object_id_and_scoped_object_type(nil, class_name)
|
33
|
+
else
|
34
|
+
permission = Permissive::Permission.find_or_initialize_by_permitted_object_id_and_permitted_object_type_and_scoped_object_id_and_scoped_object_type(proxy_owner.id, proxy_owner.class.to_s, nil, nil)
|
35
|
+
end
|
36
|
+
if options[:reset]
|
37
|
+
permission.mask = 0
|
38
|
+
end
|
39
|
+
bits_for(options[:on], args).each do |bit|
|
40
|
+
unless permission.mask & bit != 0
|
41
|
+
permission.mask = permission.mask | bit
|
42
|
+
end
|
43
|
+
end
|
44
|
+
permission.save!
|
45
|
+
permission
|
46
|
+
end
|
47
|
+
|
48
|
+
def can?(*args)
|
49
|
+
options = args.extract_options!
|
50
|
+
options.assert_valid_keys(:in, :on)
|
51
|
+
options[:on] ||= options.delete(:in)
|
52
|
+
!on(options[:on]).granted(bits_for(options[:on], args)).empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
def revoke(*args)
|
56
|
+
options = args.extract_options!
|
57
|
+
if args.length == 1 && args.first == :all
|
58
|
+
on(options[:on]).destroy_all
|
59
|
+
else
|
60
|
+
bits = bits_for(options[:on], args)
|
61
|
+
on(options[:on]).each do |permission|
|
62
|
+
bits.each do |bit|
|
63
|
+
if permission.mask & bit
|
64
|
+
permission.mask = permission.mask ^ bit
|
65
|
+
end
|
66
|
+
end
|
67
|
+
permission.save!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def bits_for(scope, permissions)
|
73
|
+
on = PermissionDefinition.normalize_scope(proxy_owner.class, scope)
|
74
|
+
permissions.map do |permission|
|
75
|
+
proxy_owner.class.permissions[on].try(:permissions).try(:[], permission.to_s.underscore.gsub('/', '_').to_sym) || raise(Permissive::InvalidPermissionError.new("#{proxy_owner.class.name} does not have a#{'n' if permission.to_s[0, 1].downcase =~ /[aeiou]/} #{permission} permission#{" on #{on}" if on}"))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
private :bits_for
|
79
|
+
end
|
80
|
+
|
81
|
+
delegate :can!, :can?, :revoke, :to => :permissions
|
82
|
+
|
83
|
+
permission_definition = Permissive::PermissionDefinition.define(self, options, &block)
|
84
|
+
|
85
|
+
permission_setter = options[:on].nil? || options[:on] == :global ? 'permissions=' : "#{options[:on].to_s.singularize}_permissions="
|
86
|
+
class_eval <<-eoc
|
87
|
+
def #{permission_setter}(values)
|
88
|
+
if values.all? {|value| value.is_a?(String) || value.is_a?(Symbol)}
|
89
|
+
can!(values, :reset => true, :on => #{options[:on].inspect})
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
eoc
|
95
|
+
|
96
|
+
|
97
|
+
# Oh that's right, it'll return an object.
|
98
|
+
permission_definition
|
99
|
+
end
|
100
|
+
alias :has_permission :has_permissions
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
module InstanceMethods
|
105
|
+
def method_missing(method, *args)
|
106
|
+
if method.to_s =~ /^can(not){0,1}_([^\?]+)(\?|!)$/
|
107
|
+
revoke = $1 == "not"
|
108
|
+
permissions = $2
|
109
|
+
setter = $3 == "!"
|
110
|
+
options = args.extract_options!
|
111
|
+
options[:on] ||= args.pop
|
112
|
+
if permissions =~ /_(on|in)$/
|
113
|
+
permissions.chomp!("_#{$1}")
|
114
|
+
end
|
115
|
+
if options[:on]
|
116
|
+
scope = Permissive::PermissionDefinition.normalize_scope(self.class, options[:on])
|
117
|
+
else
|
118
|
+
scope = :global
|
119
|
+
end
|
120
|
+
permissions = permissions.split('_and_')
|
121
|
+
if permissions.all? {|permission| self.class.permissions[scope].permissions.has_key?(permission.downcase.to_sym) }
|
122
|
+
if revoke
|
123
|
+
class_eval <<-end_eval
|
124
|
+
def #{method}(scope = nil)
|
125
|
+
revoke(#{[permissions, args].flatten.join(', ').inspect}, :on => scope)
|
126
|
+
end
|
127
|
+
end_eval
|
128
|
+
return can!(*[permissions, options].flatten)
|
129
|
+
elsif setter
|
130
|
+
class_eval <<-end_eval
|
131
|
+
def #{method}(scope = nil)
|
132
|
+
can!(#{[permissions, args].flatten.join(', ').inspect}, :on => scope)
|
133
|
+
end
|
134
|
+
end_eval
|
135
|
+
return can!(*[permissions, options].flatten)
|
136
|
+
else
|
137
|
+
class_eval <<-end_eval
|
138
|
+
def #{method}(scope = nil)
|
139
|
+
can?(#{[permissions, args].flatten.join(', ').inspect}, :on => scope)
|
140
|
+
end
|
141
|
+
end_eval
|
142
|
+
return can?(*[permissions, options].flatten)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
super
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
if defined?(ActiveRecord::Base)
|
152
|
+
ActiveRecord::Base.extend Permissive::HasPermissions::ClassMethods
|
153
|
+
end
|
@@ -4,40 +4,22 @@ module Permissive
|
|
4
4
|
attr_writer :grant_template, :template
|
5
5
|
belongs_to :permitted_object, :polymorphic => true
|
6
6
|
belongs_to :scoped_object, :polymorphic => true
|
7
|
+
named_scope :granted, lambda {|permissions|
|
8
|
+
{:conditions => permissions.map{|bit| "mask & #{bit}"}.join(' AND ')}
|
9
|
+
}
|
7
10
|
named_scope :on, lambda {|scoped_object|
|
8
|
-
|
9
|
-
|
11
|
+
case scoped_object
|
12
|
+
when ActiveRecord::Base
|
13
|
+
{:conditions => {:scoped_object_id => scoped_object.id, :scoped_object_type => scoped_object.class.to_s}}
|
14
|
+
when Class
|
15
|
+
{:conditions => {:scoped_object_id => nil, :scoped_object_type => scoped_object.name}}
|
16
|
+
when Symbol
|
17
|
+
{:conditions => {:scoped_object_id => nil, :scoped_object_type => scoped_object.to_s.classify}}
|
10
18
|
else
|
11
|
-
{:conditions =>
|
19
|
+
{:conditions => {:scoped_object_id => nil, :scoped_object_type => nil}}
|
12
20
|
end
|
13
21
|
}
|
14
22
|
set_table_name :permissive_permissions
|
15
|
-
validates_presence_of :
|
16
|
-
|
17
|
-
class << self
|
18
|
-
# Use this anywhere!
|
19
|
-
def bit_for(permission)
|
20
|
-
Permissive::Permissions.hash[permission.to_s.downcase.to_sym] || 0
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
protected
|
25
|
-
def before_save
|
26
|
-
# Save permission templates or "Roles"
|
27
|
-
if @grant_template
|
28
|
-
grant_mask = @grant_template
|
29
|
-
end
|
30
|
-
if @template
|
31
|
-
mask = @template
|
32
|
-
end
|
33
|
-
|
34
|
-
# If Permissive is set to be seriously intense about who can grant what to
|
35
|
-
# whom, it makes sure no bits on the grant_mask exceed those of the
|
36
|
-
# permission mask
|
37
|
-
# TODO: You know ... this.
|
38
|
-
# if grant_mask && Permissive.strong
|
39
|
-
# grant_mask = grant_mask & mask
|
40
|
-
# end
|
41
|
-
end
|
23
|
+
validates_presence_of :mask, :permitted_object
|
42
24
|
end
|
43
25
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# TODO: Abstract this module more later
|
2
|
+
module Permissive
|
3
|
+
class PermissionDefinition
|
4
|
+
class << self
|
5
|
+
def define(model, options, &block)
|
6
|
+
options.assert_valid_keys(:on)
|
7
|
+
options = {:on => :global}.merge(options)
|
8
|
+
|
9
|
+
unless options[:on] == :global
|
10
|
+
options[:on] = normalize_scope(model, options[:on])
|
11
|
+
end
|
12
|
+
permission_definition = model.permissions[options[:on]] ||= Permissive::PermissionDefinition.new(model, options)
|
13
|
+
if block_given?
|
14
|
+
permission_definition.instance_eval(&block)
|
15
|
+
end
|
16
|
+
permission_definition
|
17
|
+
end
|
18
|
+
|
19
|
+
def interpolate_scope(model, scope)
|
20
|
+
attempted_scope = scope.to_s.singularize.classify
|
21
|
+
model_module = model.name.to_s.split('::')
|
22
|
+
model_module.pop
|
23
|
+
model_module = model_module.join('::')
|
24
|
+
if (model_module.blank? ? Object : Object.const_get(model_module)).const_defined?(attempted_scope)
|
25
|
+
[model_module, attempted_scope].join('::')
|
26
|
+
else
|
27
|
+
scope.to_s.classify
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def normalize_scope(model, scope)
|
32
|
+
case scope
|
33
|
+
when Class
|
34
|
+
scope.name.tableize
|
35
|
+
when String, Symbol
|
36
|
+
interpolate_scope(model, scope).to_s.tableize
|
37
|
+
else
|
38
|
+
:global
|
39
|
+
end.to_s.gsub('/', '_').to_sym
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def can(name, value = nil)
|
44
|
+
if value
|
45
|
+
to(name, value)
|
46
|
+
end
|
47
|
+
name = name.to_s.downcase.to_sym
|
48
|
+
roles[@role].push(name) unless roles[@role].include?(name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(model, options = {})
|
52
|
+
options.assert_valid_keys(:on)
|
53
|
+
@options = options
|
54
|
+
@model = model.name
|
55
|
+
end
|
56
|
+
|
57
|
+
def model
|
58
|
+
@model.constantize
|
59
|
+
end
|
60
|
+
|
61
|
+
def on(class_name, &block)
|
62
|
+
Permissive::PermissionDefinition.define(model, @options.merge(:on => class_name), &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def permission(name, value)
|
66
|
+
unless value.is_a?(Numeric)
|
67
|
+
raise Permissive::PermissionError.new("Permissions must be integers or longs. Strings, symbols, and floats are not currently supported.")
|
68
|
+
end
|
69
|
+
permissions[name.to_s.downcase.to_sym] = 2 ** value
|
70
|
+
end
|
71
|
+
alias :to :permission
|
72
|
+
|
73
|
+
def permissions
|
74
|
+
@permissions ||= {}
|
75
|
+
end
|
76
|
+
|
77
|
+
def role(name, &block)
|
78
|
+
@role = name.to_s.to_sym
|
79
|
+
roles[@role] ||= []
|
80
|
+
instance_eval(&block)
|
81
|
+
unless model.instance_methods.include?('role=')
|
82
|
+
model.class_eval do
|
83
|
+
def role=(role_name)
|
84
|
+
self.permissions = self.class.permissions[:global].roles[role_name.to_s.downcase.to_sym]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def roles
|
91
|
+
@roles ||= {}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|