role_model 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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 an User class with a roles_mask attribute
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 an role-based authorization solution, such as
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.1
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
- # add class methods
5
- base.instance_eval do
6
- def roles_attribute(name)
7
- @@roles_attribute = name.to_sym
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
- def roles(*roles)
12
- @@valid_roles = Array[*roles].flatten.map { |r| r.to_sym }
13
- end
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
- # add instance methods
17
- base.class_eval do
18
- def roles=(*roles)
19
- self.send("#{@@roles_attribute}=", (Array[*roles].flatten.map { |r| r.to_sym } & @@valid_roles).map { |r| 2**@@valid_roles.index(r) }.inject { |sum, bitvalue| sum + bitvalue })
20
- end
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
- def roles
23
- @@valid_roles.reject { |r| ((self.send(@@roles_attribute) || 0) & 2**@@valid_roles.index(r)).zero? }
24
- end
25
- alias_method :role_symbols, :roles
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
- def has_role?(role)
28
- roles.include?(role.to_sym)
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
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{role_model}
8
- s.version = "0.1.1"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Martin Rehfeld"]
@@ -13,19 +13,21 @@ describe RoleModel do
13
13
  end
14
14
  end
15
15
 
16
- describe ".roles_attribute" do
17
- before(:each) do
18
- model_class.instance_eval do
19
- roles_attribute :custom_roles_mask
20
- roles :sample
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
- end
23
- subject { model_class.new }
24
+ subject { model_class.new }
24
25
 
25
- it "should change the bitmask attribute used to store the assigned roles" do
26
- subject.roles = [:sample]
27
- subject.roles_mask.should be_nil
28
- subject.custom_roles_mask.should == 1
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
- describe "#roles" do
51
- subject { model_class.new }
51
+ [:roles, :role_symbols].each do |role_query_method|
52
+ describe "##{role_query_method}" do
53
+ subject { model_class.new }
52
54
 
53
- it "should return the assigned roles as symbols" do
54
- subject.roles = [:foo, :bar]
55
- subject.roles.should include(:foo)
56
- subject.roles.should include(:bar)
57
- subject.roles.should have(2).elements
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
- it "should be an alias to roles" do
69
- subject.method(:role_symbols).should == subject.method(:roles)
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.roles.should include(:bar)
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.roles.should have(1).element
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.roles.should include(:bar)
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.roles.should have(1).element
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.roles.should have(1).element
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.roles.should have(1).element
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.roles.should include(:bar)
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.roles.should include(:bar)
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
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
8
- - 1
9
- version: 0.1.1
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Martin Rehfeld