role_model 0.1.1 → 0.2.0
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/README.rdoc +9 -2
- data/VERSION +1 -1
- data/lib/role_model.rb +42 -19
- data/role_model.gemspec +1 -1
- data/spec/role_model_spec.rb +109 -47
- metadata +3 -3
data/README.rdoc
CHANGED
|
@@ -10,7 +10,10 @@ are done.
|
|
|
10
10
|
Assigned roles will be stored as a bitmask in an configurable attribute
|
|
11
11
|
(default: <tt>roles_mask</tt>). Here's everything you need to know:
|
|
12
12
|
|
|
13
|
-
# given
|
|
13
|
+
# given a User class with a roles_mask attribute
|
|
14
|
+
require 'rubygems'
|
|
15
|
+
require 'role_model'
|
|
16
|
+
|
|
14
17
|
class User
|
|
15
18
|
attr_accessor :roles_mask # in real life this would be an persisted attribute / DB-column
|
|
16
19
|
|
|
@@ -43,10 +46,14 @@ Assigned roles will be stored as a bitmask in an configurable attribute
|
|
|
43
46
|
=> 1
|
|
44
47
|
|
|
45
48
|
Once you have included RoleModel, your model is perfectly fit to be used
|
|
46
|
-
together with
|
|
49
|
+
together with a role-based authorization solution, such as
|
|
47
50
|
http://github.com/ryanb/cancan or
|
|
48
51
|
http://github.com/stffn/declarative_authorization .
|
|
49
52
|
|
|
53
|
+
== Installation
|
|
54
|
+
|
|
55
|
+
gem install role_model
|
|
56
|
+
|
|
50
57
|
== Reasoning
|
|
51
58
|
|
|
52
59
|
Whenever I introduce a role-based authorization scheme into a project, the
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.2.0
|
data/lib/role_model.rb
CHANGED
|
@@ -1,33 +1,56 @@
|
|
|
1
1
|
module RoleModel
|
|
2
|
-
def self.included(base) # :nodoc:
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
INHERITABLE_CLASS_ATTRIBUTES = [:roles_attribute_name, :valid_roles]
|
|
4
|
+
|
|
5
|
+
def self.included(base) # :nodoc:
|
|
6
|
+
base.extend ClassMethods
|
|
7
|
+
base.class_eval do
|
|
8
|
+
class << self
|
|
9
|
+
attr_accessor *::RoleModel::INHERITABLE_CLASS_ATTRIBUTES
|
|
8
10
|
end
|
|
9
11
|
roles_attribute :roles_mask
|
|
12
|
+
end
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
# assign roles
|
|
15
|
+
def roles=(*roles)
|
|
16
|
+
self.send("#{self.class.roles_attribute_name}=", (Array[*roles].flatten.map { |r| r.to_sym } & self.class.valid_roles).map { |r| 2**self.class.valid_roles.index(r) }.inject { |sum, bitvalue| sum + bitvalue })
|
|
14
17
|
end
|
|
15
18
|
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
# query assigned roles
|
|
20
|
+
def roles
|
|
21
|
+
self.class.valid_roles.reject { |r| ((self.send(self.class.roles_attribute_name) || 0) & 2**self.class.valid_roles.index(r)).zero? }
|
|
22
|
+
end
|
|
23
|
+
alias role_symbols roles
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
# check if a given role has been assigned
|
|
26
|
+
def has_role?(role)
|
|
27
|
+
roles.include?(role.to_sym)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
module ClassMethods
|
|
32
|
+
def inherited(subclass) # :nodoc:
|
|
33
|
+
::RoleModel::INHERITABLE_CLASS_ATTRIBUTES.each do |attribute|
|
|
34
|
+
instance_var = "@#{attribute}"
|
|
35
|
+
subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
|
|
29
36
|
end
|
|
37
|
+
super
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# set the bitmask attribute role assignment will be stored in
|
|
41
|
+
def roles_attribute(name)
|
|
42
|
+
self.roles_attribute = name
|
|
30
43
|
end
|
|
31
44
|
|
|
45
|
+
# alternative method signature: set the bitmask attribute role assignment will be stored in
|
|
46
|
+
def roles_attribute=(name)
|
|
47
|
+
self.roles_attribute_name = name.to_sym
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# declare valid roles
|
|
51
|
+
def roles(*roles)
|
|
52
|
+
self.valid_roles = Array[*roles].flatten.map { |r| r.to_sym }
|
|
53
|
+
end
|
|
32
54
|
end
|
|
55
|
+
|
|
33
56
|
end
|
data/role_model.gemspec
CHANGED
data/spec/role_model_spec.rb
CHANGED
|
@@ -13,19 +13,21 @@ describe RoleModel do
|
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
[:roles_attribute, :roles_attribute=].each do |roles_attribute_setter_method|
|
|
17
|
+
describe ".#{roles_attribute_setter_method}" do
|
|
18
|
+
before(:each) do
|
|
19
|
+
model_class.instance_eval do
|
|
20
|
+
send(roles_attribute_setter_method, :custom_roles_mask)
|
|
21
|
+
roles :sample
|
|
22
|
+
end
|
|
21
23
|
end
|
|
22
|
-
|
|
23
|
-
subject { model_class.new }
|
|
24
|
+
subject { model_class.new }
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
it "should change the bitmask attribute used to store the assigned roles" do
|
|
27
|
+
subject.roles = [:sample]
|
|
28
|
+
subject.roles_mask.should be_nil
|
|
29
|
+
subject.custom_roles_mask.should == 1
|
|
30
|
+
end
|
|
29
31
|
end
|
|
30
32
|
end
|
|
31
33
|
|
|
@@ -34,8 +36,7 @@ describe RoleModel do
|
|
|
34
36
|
|
|
35
37
|
it "should define the valid roles" do
|
|
36
38
|
subject.roles = %w(foo bar baz)
|
|
37
|
-
subject.roles.should include(:foo)
|
|
38
|
-
subject.roles.should include(:bar)
|
|
39
|
+
subject.roles.should include(:foo, :bar)
|
|
39
40
|
subject.roles.should_not include(:baz)
|
|
40
41
|
end
|
|
41
42
|
|
|
@@ -47,26 +48,19 @@ describe RoleModel do
|
|
|
47
48
|
end
|
|
48
49
|
end
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
[:roles, :role_symbols].each do |role_query_method|
|
|
52
|
+
describe "##{role_query_method}" do
|
|
53
|
+
subject { model_class.new }
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
it "should return an empty array when no roles have been assigned" do
|
|
61
|
-
subject.roles.should be_empty
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
describe "#role_symbols" do
|
|
66
|
-
subject { model_class.new }
|
|
55
|
+
it "should return the assigned roles as symbols" do
|
|
56
|
+
subject.roles = [:foo, :bar]
|
|
57
|
+
subject.send(role_query_method).should include(:foo, :bar)
|
|
58
|
+
subject.send(role_query_method).should have(2).elements
|
|
59
|
+
end
|
|
67
60
|
|
|
68
|
-
|
|
69
|
-
|
|
61
|
+
it "should return an empty array when no roles have been assigned" do
|
|
62
|
+
subject.send(role_query_method).should be_empty
|
|
63
|
+
end
|
|
70
64
|
end
|
|
71
65
|
end
|
|
72
66
|
|
|
@@ -75,48 +69,44 @@ describe RoleModel do
|
|
|
75
69
|
|
|
76
70
|
it "should accept an array of symbols" do
|
|
77
71
|
subject.roles = [:foo, :bar]
|
|
78
|
-
subject.roles.should include(:foo)
|
|
79
|
-
subject.
|
|
80
|
-
subject.roles.should have(2).elements
|
|
72
|
+
subject.roles.should include(:foo, :bar)
|
|
73
|
+
subject.should have(2).roles
|
|
81
74
|
subject.roles = [:bar]
|
|
82
75
|
subject.roles.should include(:bar)
|
|
83
|
-
subject.
|
|
76
|
+
subject.should have(1).roles
|
|
84
77
|
end
|
|
85
78
|
|
|
86
79
|
it "should accept an array of strings" do
|
|
87
80
|
subject.roles = %w(foo bar)
|
|
88
|
-
subject.roles.should include(:foo)
|
|
89
|
-
subject.
|
|
90
|
-
subject.roles.should have(2).elements
|
|
81
|
+
subject.roles.should include(:foo, :bar)
|
|
82
|
+
subject.should have(2).roles
|
|
91
83
|
subject.roles = ['bar']
|
|
92
84
|
subject.roles.should include(:bar)
|
|
93
|
-
subject.
|
|
85
|
+
subject.should have(1).roles
|
|
94
86
|
end
|
|
95
87
|
|
|
96
88
|
it "should accept a single symbol" do
|
|
97
89
|
subject.roles = :foo
|
|
98
90
|
subject.roles.should include(:foo)
|
|
99
|
-
subject.
|
|
91
|
+
subject.should have(1).roles
|
|
100
92
|
end
|
|
101
93
|
|
|
102
94
|
it "should accept a single string" do
|
|
103
95
|
subject.roles = 'foo'
|
|
104
96
|
subject.roles.should include(:foo)
|
|
105
|
-
subject.
|
|
97
|
+
subject.should have(1).roles
|
|
106
98
|
end
|
|
107
99
|
|
|
108
100
|
it "should accept multiple arguments as symbols" do
|
|
109
101
|
subject.send(:roles=, :foo, :bar)
|
|
110
|
-
subject.roles.should include(:foo)
|
|
111
|
-
subject.
|
|
112
|
-
subject.roles.should have(2).elements
|
|
102
|
+
subject.roles.should include(:foo, :bar)
|
|
103
|
+
subject.should have(2).roles
|
|
113
104
|
end
|
|
114
105
|
|
|
115
106
|
it "should accept multiple arguments as strings" do
|
|
116
107
|
subject.send(:roles=, 'foo', 'bar')
|
|
117
|
-
subject.roles.should include(:foo)
|
|
118
|
-
subject.
|
|
119
|
-
subject.roles.should have(2).elements
|
|
108
|
+
subject.roles.should include(:foo, :bar)
|
|
109
|
+
subject.should have(2).roles
|
|
120
110
|
end
|
|
121
111
|
|
|
122
112
|
it "should silently ignore undefined roles" do
|
|
@@ -148,4 +138,76 @@ describe RoleModel do
|
|
|
148
138
|
end
|
|
149
139
|
end
|
|
150
140
|
|
|
141
|
+
context "inheritance" do
|
|
142
|
+
let(:superclass_instance) { model_class.new }
|
|
143
|
+
let(:inherited_model_class) { Class.new(model_class) }
|
|
144
|
+
subject { inherited_model_class.new }
|
|
145
|
+
|
|
146
|
+
it "should not alter the superclass behaviour" do
|
|
147
|
+
inherited_model_class.instance_eval do
|
|
148
|
+
roles :quux, :quuux
|
|
149
|
+
end
|
|
150
|
+
superclass_instance.roles = [:foo, :bar, :quux, :quuux]
|
|
151
|
+
superclass_instance.roles.should include(:foo, :bar)
|
|
152
|
+
superclass_instance.roles.should have(2).roles
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "should inherit the valid roles" do
|
|
156
|
+
subject.roles = [:foo, :bar, :quux, :quuux]
|
|
157
|
+
subject.roles.should include(:foo, :bar)
|
|
158
|
+
subject.should have(2).roles
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should not inherit the assigned roles" do
|
|
162
|
+
subject.roles.should be_empty
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "should allow overriding the valid roles for the inherited class" do
|
|
166
|
+
inherited_model_class.instance_eval do
|
|
167
|
+
roles :quux, :quuux
|
|
168
|
+
end
|
|
169
|
+
subject.roles = [:foo, :bar, :quux, :quuux]
|
|
170
|
+
subject.roles.should include(:quux, :quuux)
|
|
171
|
+
subject.should have(2).roles
|
|
172
|
+
subject.roles_mask.should == 3
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "should allow overriding the attribute to store the roles for the inherited class" do
|
|
176
|
+
inherited_model_class.instance_eval do
|
|
177
|
+
roles_attribute :custom_roles_mask
|
|
178
|
+
end
|
|
179
|
+
subject.roles = [:foo, :bar, :quux, :quuux]
|
|
180
|
+
subject.roles.should include(:foo, :bar)
|
|
181
|
+
subject.should have(2).roles
|
|
182
|
+
subject.custom_roles_mask.should == 3
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
context "independent usage" do
|
|
187
|
+
let(:model_instance) { model_class.new }
|
|
188
|
+
let(:other_model_class) { Class.new }
|
|
189
|
+
before(:each) do
|
|
190
|
+
other_model_class.instance_eval do
|
|
191
|
+
attr_accessor :roles_mask
|
|
192
|
+
include RoleModel
|
|
193
|
+
roles :quux, :quuux
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
subject { other_model_class.new }
|
|
197
|
+
|
|
198
|
+
it "should not alter the behaviour of other classes" do
|
|
199
|
+
model_instance.roles = [:foo, :bar, :quux, :quuux]
|
|
200
|
+
model_instance.roles.should include(:foo)
|
|
201
|
+
model_instance.roles.should include(:bar)
|
|
202
|
+
model_instance.should have(2).roles
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it "should allow using different roles in other models" do
|
|
206
|
+
subject.roles = [:foo, :bar, :quux, :quuux]
|
|
207
|
+
subject.roles.should include(:quux)
|
|
208
|
+
subject.roles.should include(:quuux)
|
|
209
|
+
subject.should have(2).roles
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
151
213
|
end
|