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 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