roles_generic 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/.rspec +1 -0
  4. data/LICENSE +20 -0
  5. data/README.markdown +308 -0
  6. data/Rakefile +45 -0
  7. data/VERSION +1 -0
  8. data/lib/generators/roles_model/roles/roles_generator.rb +65 -0
  9. data/lib/roles_generic/admin_flag.rb +40 -0
  10. data/lib/roles_generic/base.rb +45 -0
  11. data/lib/roles_generic/extensions/core_ext.rb +19 -0
  12. data/lib/roles_generic/generic/class_methods.rb +33 -0
  13. data/lib/roles_generic/generic/implementation.rb +26 -0
  14. data/lib/roles_generic/generic/util.rb +39 -0
  15. data/lib/roles_generic/generic.rb +77 -0
  16. data/lib/roles_generic/many_roles.rb +39 -0
  17. data/lib/roles_generic/one_role.rb +40 -0
  18. data/lib/roles_generic/role/class_methods.rb +20 -0
  19. data/lib/roles_generic/role_string.rb +29 -0
  20. data/lib/roles_generic/role_strings.rb +23 -0
  21. data/lib/roles_generic/roles_mask.rb +48 -0
  22. data/lib/roles_generic/roles_string.rb +26 -0
  23. data/lib/roles_generic.rb +2 -0
  24. data/spec/generator_spec_helper.rb +12 -0
  25. data/spec/generators/admin_flag_generator_spec.rb +77 -0
  26. data/spec/generators/many_roles_generator_spec.rb +39 -0
  27. data/spec/generators/one_role_generator_spec.rb +39 -0
  28. data/spec/generators/role_string_generator_spec.rb +39 -0
  29. data/spec/generators/role_strings_generator_spec.rb +39 -0
  30. data/spec/generators/roles_mask_generator_spec.rb +39 -0
  31. data/spec/generators/roles_string_generator_spec.rb +39 -0
  32. data/spec/model/role.rb +26 -0
  33. data/spec/roles_generic/admin_flag_spec.rb +66 -0
  34. data/spec/roles_generic/many_roles_spec.rb +63 -0
  35. data/spec/roles_generic/one_role_spec.rb +70 -0
  36. data/spec/roles_generic/role_string_spec.rb +65 -0
  37. data/spec/roles_generic/role_strings_spec.rb +58 -0
  38. data/spec/roles_generic/roles_mask_spec.rb +57 -0
  39. data/spec/roles_generic/roles_string_spec.rb +58 -0
  40. data/spec/spec_helper.rb +9 -0
  41. metadata +182 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Kristian Mandrup
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,308 @@
1
+ # Roles Model base
2
+
3
+ Generic role strategies that share the same API and are easy to insert in any existing User model.
4
+
5
+ *Update: Now with a Rails 3 generator to instantly populate your user model of choice with a role strategy!*
6
+
7
+ # Install
8
+
9
+ <code>gem install roles_generic</code>
10
+
11
+ # Usage
12
+
13
+ The library comes with the following role models built-in:
14
+
15
+ * admin_flag (Boolean flag - 'admin' or not)
16
+ * role_string (String)
17
+ * roles_string (Comma separated String - note: no role name must have a comma in its name!)
18
+ * role_strings (Set of Strings)
19
+ * roles_mask (Integer mask)
20
+ * one_role (relation to a Role model instance)
21
+ * many_roles(Set of Role relationships)
22
+
23
+ Note: The following examples use RSpec to demonstrate usage scenarios.
24
+
25
+ ## Example : admin_flag
26
+
27
+ Creates and uses a binary field 'admin_flag', which when true signals that this user is an administrator and otherwise a normal user.
28
+
29
+ <pre>
30
+ class User
31
+ include RoleModels::Generic
32
+
33
+ attr_accessor :name, :admin_flag
34
+
35
+ role_strategy :admin_flag, :default
36
+
37
+ roles :admin, :user
38
+
39
+ def initialize name, *new_roles
40
+ self.name = name
41
+ self.roles = new_roles
42
+ end
43
+ end
44
+ </pre>
45
+
46
+ ## Example: Using an ORM
47
+
48
+ Data Mapper with persistent attributes :name and :admin_flag
49
+
50
+ <pre>
51
+ class User
52
+ include RoleModels::Generic
53
+ include DataMapper::Resource
54
+
55
+ property :name, Boolean
56
+ property :admin_flag, Boolean
57
+
58
+ role_strategy :admin_flag, :default
59
+
60
+ roles :admin, :user
61
+
62
+ def initialize name, *new_roles
63
+ self.name = name
64
+ self.roles = new_roles
65
+ end
66
+ end
67
+ </pre>
68
+
69
+
70
+ ## Example : role_string
71
+
72
+ Creates and uses a single role name, a string
73
+
74
+ <pre>
75
+ class User
76
+ include RoleModels::Generic
77
+
78
+ attr_accessor :name, :role_string
79
+
80
+ role_strategy :role_string, :default
81
+
82
+ roles :admin, :user
83
+
84
+ def initialize name, *new_roles
85
+ self.name = name
86
+ self.roles = new_roles
87
+ end
88
+ end
89
+ </pre>
90
+
91
+ ## Example : roles_string
92
+
93
+ Creates and uses single comma separated String of role names
94
+
95
+ <pre>
96
+ class User
97
+ include RoleModels::Generic
98
+
99
+ attr_accessor :name, :roles_string
100
+
101
+ role_strategy :roles_string, :default
102
+
103
+ roles :admin, :user
104
+
105
+ def initialize name, *new_roles
106
+ self.name = name
107
+ self.roles = new_roles
108
+ end
109
+ end
110
+ </pre>
111
+
112
+ ## Example : role_strings
113
+
114
+ Creates and uses an Set of role names as strings
115
+
116
+ <pre>
117
+ class User
118
+ include RoleModels::Generic
119
+
120
+ attr_accessor :name, :role_strings
121
+
122
+ role_strategy :role_strings, :default
123
+
124
+ roles :admin, :user
125
+
126
+ def initialize name, *new_roles
127
+ self.name = name
128
+ self.roles = new_roles
129
+ end
130
+ end
131
+ </pre>
132
+
133
+ ## Example : roles_mask
134
+
135
+ Creates and uses an Integer field where each on bit signifies a role
136
+
137
+ <pre>
138
+ class User
139
+ include RoleModels::Generic
140
+
141
+ attr_accessor :name, :roles_mask
142
+
143
+ role_strategy :roles_mask, :default
144
+
145
+ roles :admin, :user
146
+
147
+ def initialize name, *new_roles
148
+ self.name = name
149
+ self.roles = new_roles
150
+ end
151
+ end
152
+ </pre>
153
+
154
+ ## Example : one_role
155
+
156
+ Creates and uses a single relation to a Role model for each user
157
+
158
+ <pre>
159
+ class Role
160
+ attr_accessor :name
161
+
162
+ def self.find_role role_name
163
+ roles.to_a.select{|r| r.name == role_name}.first
164
+ end
165
+
166
+ class << self
167
+ attr_accessor :roles
168
+ end
169
+
170
+ def initialize name
171
+ @name = name
172
+ self.class.roles ||= Set.new
173
+ self.class.roles << self
174
+ end
175
+
176
+ end
177
+
178
+ class User
179
+ include RoleModels::Generic
180
+ role_strategy :one_role, :default
181
+
182
+ role_class :role
183
+
184
+ attr_accessor :name, :one_role
185
+
186
+ roles :admin, :user
187
+
188
+ def initialize name, *new_roles
189
+ self.name = name
190
+ self.roles = new_roles
191
+ end
192
+ end
193
+ </pre>
194
+
195
+ ## Example : many_roles
196
+
197
+ Creates and uses a single relation to a Role model for each user
198
+
199
+ <pre>
200
+ class Role
201
+ attr_accessor :name
202
+
203
+ def self.find_role role_name
204
+ roles.to_a.select{|r| r.name == role_name}.first
205
+ end
206
+
207
+ def self.find_roles *role_names
208
+ result = Set.new
209
+ role_names.flatten.each do |role_name|
210
+ found_role = find_role(role_name)
211
+ result << found_role if found_role
212
+ end
213
+ result
214
+ end
215
+
216
+ class << self
217
+ attr_accessor :roles
218
+ end
219
+
220
+ def initialize name
221
+ @name = name
222
+ self.class.roles ||= Set.new
223
+ self.class.roles << self
224
+ end
225
+ end
226
+
227
+ class User
228
+ include RoleModels::Generic
229
+ role_strategy :many_roles, :default
230
+
231
+ role_class :role
232
+
233
+ attr_accessor :name, :many_roles
234
+
235
+ roles :admin, :user
236
+
237
+ def initialize name, *new_roles
238
+ self.name = name
239
+ self.roles = new_roles
240
+ end
241
+ end
242
+ </pre>
243
+
244
+ ## Usage of API
245
+
246
+ <pre>
247
+ before :each do
248
+ @admin_user = User.new 'Admin user', :admin
249
+ @user = User.new 'User', :user
250
+ end
251
+
252
+ it "user 'Admin user' should have role :admin" do
253
+ @admin_user.role.should == :admin
254
+ @admin_user.roles.should == [:admin]
255
+ @admin_user.admin?.should be_true
256
+
257
+ @admin_user.has_role?(:user).should be_false
258
+
259
+ @admin_user.has_role?(:admin).should be_true
260
+ @admin_user.is?(:admin).should be_true
261
+ @admin_user.has_roles?(:admin).should be_true
262
+ @admin_user.has?(:admin).should be_true
263
+ end
264
+
265
+ it "user 'User' should have role :user" do
266
+ @user.roles.should == [:user]
267
+ @user.admin?.should be_false
268
+
269
+ @user.has_role?(:user).should be_true
270
+ @user.has_role?(:admin).should be_false
271
+ @user.is?(:admin).should be_false
272
+
273
+ @user.has_roles?(:user).should be_true
274
+ @user.has?(:admin).should be_false
275
+ end
276
+
277
+ it "should set 'User' role to :admin using roles=" do
278
+ @user.roles = :admin
279
+ @user.role.should == :admin
280
+ @user.has_role?(:admin).should be_true
281
+ end
282
+ </pre>
283
+
284
+ ## Future (TODO)
285
+
286
+ The following in planned to be completed before the end of August 2010.
287
+
288
+ ### Clean up (DRY)
289
+
290
+ DRY up the role strategy code a lot more! There is a lot more potential for code reuse - way too much duplication now.
291
+
292
+ ### Rails generator
293
+
294
+ The library will come with a Rails 3 generator that lets you populate a user model with a given role strategy
295
+
296
+ ## Note on Patches/Pull Requests
297
+
298
+ * Fork the project.
299
+ * Make your feature addition or bug fix.
300
+ * Add tests for it. This is important so I don't break it in a
301
+ future version unintentionally.
302
+ * Commit, do not mess with rakefile, version, or history.
303
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
304
+ * Send me a pull request. Bonus points for topic branches.
305
+
306
+ ## Copyright
307
+
308
+ Copyright (c) 2010 Kristian Mandrup. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gem|
4
+ gem.name = "roles_generic"
5
+ gem.summary = %Q{Generic role strategies sharing the same API}
6
+ gem.description = %Q{Generic role strategies sharing the same API. Easy to insert in any model}
7
+ gem.email = "kmandrup@gmail.com"
8
+ gem.homepage = "http://github.com/kristianmandrup/roles_for_mm"
9
+ gem.authors = ["Kristian Mandrup"]
10
+ gem.add_development_dependency "rspec", ">= 2.0.0.beta.19"
11
+ gem.add_development_dependency "generator-spec", ">= 0.5.1"
12
+ gem.add_dependency "require_all", ">= 1.1.0"
13
+ gem.add_dependency "activesupport", ">= 3.0.0.rc"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ # require 'spec/rake/spectask'
22
+ # Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ # spec.libs << 'lib' << 'spec'
24
+ # spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ # end
26
+ #
27
+ # Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ # spec.libs << 'lib' << 'spec'
29
+ # spec.pattern = 'spec/**/*_spec.rb'
30
+ # spec.rcov = true
31
+ # end
32
+ #
33
+ # task :spec => :check_dependencies
34
+ #
35
+ # task :default => :spec
36
+ #
37
+ # require 'rake/rdoctask'
38
+ # Rake::RDocTask.new do |rdoc|
39
+ # version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+ #
41
+ # rdoc.rdoc_dir = 'rdoc'
42
+ # rdoc.title = "roles_for_mm #{version}"
43
+ # rdoc.rdoc_files.include('README*')
44
+ # rdoc.rdoc_files.include('lib/**/*.rb')
45
+ # end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,65 @@
1
+ module RolesModel
2
+ module Generators
3
+ class RolesGenerator < Rails::Generators::NamedBase
4
+ include Rails::Generators::MigrationHelper
5
+
6
+ desc "Add role strategy to a model"
7
+
8
+ class_option :strategy, :type => :string, :aliases => "-s", :default => 'role_string',
9
+ :desc => "Role strategy to use (admin_flag, role_string, roles_string, role_strings, one_role, many_roles, roles_mask)"
10
+
11
+
12
+ class_option :roles, :type => :array, :aliases => "-r", :default => [], :desc => "Valid roles"
13
+ # TODO: Should detect ORM from file content instead!
14
+ class_option :orm, :type => :string, :aliases => "-o", :default => nil, :desc => "ORM of model"
15
+
16
+
17
+ # hook_for :orm
18
+
19
+ def self.source_root
20
+ @source_root ||= File.expand_path("../../templates", __FILE__)
21
+ end
22
+
23
+ def apply_role_strategy
24
+ self.class.use_orm orm if orm
25
+ insert_into_model name do
26
+ insertion_text
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ def strategy
33
+ options[:strategy]
34
+ end
35
+
36
+ def orm
37
+ @orm ||= options[:orm].to_s.to_sym
38
+ end
39
+
40
+ def roles
41
+ @roles ||= options[:roles].map{|r| ":#{r}" }
42
+ end
43
+
44
+ def role_strategy_statement
45
+ "role_strategy :#{strategy}\n"
46
+ end
47
+
48
+ def roles_statement
49
+ roles ? "roles #{roles.join(',')}" : ''
50
+ end
51
+
52
+ def insertion_text
53
+ %Q{
54
+ include RoleModels::Generic
55
+ #{role_strategy_statement}
56
+ #{roles_statement}
57
+ }
58
+ end
59
+
60
+ def role_strategy
61
+ options[:role_strategy]
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,40 @@
1
+ module RoleModels::Generic
2
+ module AdminFlag
3
+ def self.default_role_attribute
4
+ :admin_flag
5
+ end
6
+
7
+ module Implementation
8
+ # assign roles
9
+ def roles=(*new_roles)
10
+ first_role = new_roles.flatten.first
11
+ self.send("#{strategy_class.roles_attribute_name}=", new_roles.flatten.first.admin?) if valid_role? first_role
12
+ end
13
+
14
+ # query assigned roles
15
+ def roles
16
+ role = self.send(strategy_class.roles_attribute_name) ? strategy_class.admin_role_key : strategy_class.default_role_key
17
+ [role]
18
+ end
19
+ alias_method :roles_list, :roles
20
+
21
+ end # Implementation
22
+
23
+ extend RoleModels::Generic::Base::Configuration
24
+ configure :num => :single
25
+ end
26
+ end
27
+
28
+ module AdminRoleCheck
29
+ def admin?
30
+ self.to_s.downcase.to_sym == :admin
31
+ end
32
+ end
33
+
34
+ class String
35
+ include AdminRoleCheck
36
+ end
37
+
38
+ class Symbol
39
+ include AdminRoleCheck
40
+ end
@@ -0,0 +1,45 @@
1
+ require 'require_all'
2
+ require 'active_support/inflector'
3
+
4
+ module RoleModels
5
+ module Generic
6
+ end
7
+ end
8
+
9
+ module RoleModels
10
+ module Base
11
+ attr_accessor :orm_name
12
+
13
+ def roles(*roles)
14
+ strategy_class.valid_roles = Array[*roles].flatten.map { |r| r.to_sym }
15
+ end
16
+
17
+ def role_strategy strategy, options=nil
18
+ include_strategy orm_name, strategy, options
19
+ end
20
+
21
+ def include_strategy orm, strategy, options=nil
22
+ begin
23
+ constant = "RoleModels::#{orm_name.to_s.camelize}::#{strategy.to_s.camelize}".constantize
24
+
25
+ strategy_class_method = %Q{
26
+ def strategy_class
27
+ #{constant}
28
+ end
29
+ }
30
+
31
+ class_eval do
32
+ eval strategy_class_method
33
+ end
34
+
35
+ instance_eval do
36
+ eval strategy_class_method
37
+ include constant
38
+ end
39
+ rescue
40
+ raise "No Role strategy module for ORM #{orm} found for strategy #{strategy}"
41
+ end
42
+ constant.apply_options(options) if constant.respond_to? :apply_options
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,19 @@
1
+ # to be replaced by Zucker gem
2
+
3
+ class Object
4
+ define_method :not do
5
+ Not.new(self)
6
+ end
7
+
8
+ class Not
9
+ private *instance_methods.select { |m| m !~ /(^__|^\W|^binding$)/ }
10
+
11
+ def initialize(subject)
12
+ @subject = subject
13
+ end
14
+
15
+ def method_missing(sym, *args, &blk)
16
+ !@subject.send(sym,*args,&blk)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ module RoleModels::Generic::Base
2
+ module ClassMethods
3
+ def inherited(subclass) # :nodoc:
4
+ ::RoleModels::Base::INHERITABLE_CLASS_ATTRIBUTES.each do |attribute|
5
+ instance_var = "@#{attribute}"
6
+ subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
7
+ end
8
+ super
9
+ end
10
+
11
+ # set the bitmask attribute role assignments will be stored in
12
+ def roles_attribute(name)
13
+ self.roles_attribute = name
14
+ end
15
+
16
+ # alternative method signature: set the bitmask attribute role assignments will be stored in
17
+ def roles_attribute=(name)
18
+ self.roles_attribute_name = name.to_sym
19
+ end
20
+
21
+ # :call-seq:
22
+ # roles(:role_1, ..., :role_n)
23
+ # roles('role_1', ..., 'role_n')
24
+ # roles([:role_1, ..., :role_n])
25
+ # roles(['role_1', ..., 'role_n'])
26
+ #
27
+ # declare valid roles
28
+ def roles(*roles)
29
+ puts "roles: #{roles}"
30
+ self.valid_roles = Array[*roles].flatten.map { |r| r.to_sym }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ module RoleModels::Generic::Base
2
+ module Implementation
3
+
4
+ # check if a given role has been assigned
5
+ # if a list of roles: check if ALL of the given roles have been assigned
6
+ def has_roles?(*roles)
7
+ (roles_list - roles.flatten).empty?
8
+ end
9
+
10
+ # check if any (at least ONE) of the given roles have been assigned
11
+ def has_role? *roles
12
+ (roles_list & roles.flatten).not.empty?
13
+ end
14
+
15
+ def valid_role? role
16
+ strategy_class.valid_roles.include? role.to_sym
17
+ end
18
+
19
+ def admin?
20
+ is? :admin
21
+ end
22
+
23
+ alias_method :has?, :has_role?
24
+ alias_method :is?, :has_roles?
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ module RoleModels::Generic::Base
2
+ module Configuration
3
+ def configure(options={})
4
+ numericality = options[:num]
5
+ type = options[:type]
6
+
7
+ class_eval do
8
+ include RoleModels::Generic::Base
9
+ include RoleModels::Generic::Base::SingleRole if numericality == :single
10
+ include RoleModels::Generic::Base::RoleClass::InstanceMethods if type == :role_class
11
+ include self::Implementation
12
+
13
+ alias_method :role_symbols, :roles
14
+ end
15
+ extend RoleModels::Generic::Base::ClassMethods
16
+ extend RoleModels::Generic::Base::DefaultRoleKeys
17
+ end
18
+ end
19
+
20
+ module SingleRole
21
+ def role
22
+ roles.first
23
+ end
24
+
25
+ def role= new_role
26
+ self.roles = new_role
27
+ end
28
+ end
29
+
30
+ module DefaultRoleKeys
31
+ def default_role_key
32
+ valid_roles.last || :user
33
+ end
34
+
35
+ def admin_role_key
36
+ valid_roles.first || :admin
37
+ end
38
+ end
39
+ end