roles_generic 0.1.1

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