rolify 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. data/CHANGELOG.rdoc +13 -1
  2. data/{README.rdoc → README.md} +69 -41
  3. data/lib/generators/rolify/role/role_generator.rb +2 -2
  4. data/lib/generators/rolify/role/templates/initializer.rb +3 -3
  5. data/lib/generators/rolify/role/templates/role-mongoid.rb +9 -0
  6. data/lib/rolify.rb +10 -7
  7. data/lib/rolify/adapters/active_record/resource_adapter.rb +17 -0
  8. data/lib/rolify/adapters/{active_record.rb → active_record/role_adapter.rb} +12 -21
  9. data/lib/rolify/adapters/base.rb +24 -22
  10. data/lib/rolify/adapters/mongoid/resource_adapter.rb +23 -0
  11. data/lib/rolify/adapters/{mongoid.rb → mongoid/role_adapter.rb} +11 -31
  12. data/lib/rolify/configure.rb +5 -4
  13. data/lib/rolify/dynamic.rb +1 -1
  14. data/lib/rolify/role.rb +11 -7
  15. data/lib/rolify/utils.rb +10 -0
  16. data/lib/rolify/version.rb +1 -1
  17. data/rolify.gemspec +1 -1
  18. data/spec/generators/rolify/role/role_generator_spec.rb +41 -19
  19. data/spec/rolify/config_spec.rb +13 -11
  20. data/spec/rolify/resource_spec.rb +28 -29
  21. data/spec/rolify/shared_contexts.rb +17 -17
  22. data/spec/rolify/shared_examples/{shared_examples_for_has_role_setter.rb → shared_examples_for_add_role.rb} +16 -16
  23. data/spec/rolify/shared_examples/shared_examples_for_dynamic.rb +6 -6
  24. data/spec/rolify/shared_examples/shared_examples_for_has_any_role.rb +6 -6
  25. data/spec/rolify/shared_examples/{shared_examples_for_has_role_getter.rb → shared_examples_for_has_role.rb} +0 -0
  26. data/spec/rolify/shared_examples/{shared_examples_for_has_no_role.rb → shared_examples_for_remove_role.rb} +37 -13
  27. data/spec/rolify/shared_examples/shared_examples_for_roles.rb +18 -20
  28. data/spec/spec_helper.rb +0 -4
  29. data/spec/support/adapters/active_record.rb +2 -0
  30. data/spec/support/adapters/mongoid.rb +2 -0
  31. metadata +33 -31
data/CHANGELOG.rdoc CHANGED
@@ -1,4 +1,16 @@
1
- = 3.0 (unreleased)
1
+ = 3.1 (Apr 6, 2012)
2
+ * Mongoid adapter optimization
3
+ * adapter code refactoring
4
+ * generator now adds the role class name to the rolify method injected in the user class
5
+ * fixed a bug on the generator when using a 2 words Camel case for the Role class name
6
+ * <b>DEPRECATION NOTICE:</b> <tt>has_role</tt> and <tt>has_no_role</tt> have been depecrated. They are replaced by <tt>add_role</tt> and <tt>remove_role</tt>
7
+ * some internals cleanup (backward compatible)
8
+ * stop requiring <tt>active_record</tt> in <tt>rolify.rb</tt> to prevent other gems ORM detection issue
9
+ * fixed a bug when removing a role to the user using Mongoid adapter
10
+ * added indexes to generator for mongoid (thanks to @stigi)
11
+ * fixed a bug regarding **with_role** method on resource classes (thanks to @nfo)
12
+
13
+ = 3.0 (Apr 2, 2012)
2
14
  * support for Mongoid
3
15
  * roles search on resources on instance level (e.g. <tt>Forum.first.roles</tt>) and class level (e.g. <tt>Forum.with_role("admin", user)</tt>)
4
16
  * heavy lifting and redesign of the library, code and specs refactoring
@@ -1,138 +1,166 @@
1
- = rolify {<img src="https://secure.travis-ci.org/EppO/rolify.png?branch=master">}[http://travis-ci.org/EppO/rolify] {<img src="https://gemnasium.com/EppO/rolify.png">}[https://gemnasium.com/EppO/rolify]
1
+ # rolify [![build status](https://secure.travis-ci.org/EppO/rolify.png?branch=master)](http://travis-ci.org/EppO/rolify) [![dependency status](https://gemnasium.com/EppO/rolify.png)](https://gemnasium.com/EppO/rolify)
2
2
 
3
3
  Very simple Roles library without any authorization enforcement supporting scope on resource object.
4
4
 
5
- Let's see an example:
5
+ Let's see an example:
6
6
 
7
- user.has_role?("moderator", Forum.first)
8
- => false # if user is moderator of another Forum
7
+ ```ruby
8
+ user.has_role?(:moderator, Forum.first)
9
+ => false # if user is moderator of another Forum
10
+ ```
9
11
 
10
- This library was intended to be used with CanCan[https://github.com/ryanb/cancan] and devise[https://github.com/plataformatec/devise/] but should be generic enough to be used by any other authentication/authorization solutions.
12
+ This library was intended to be used with [CanCan](https://github.com/ryanb/cancan) and [devise](https://github.com/plataformatec/devise) but should be generic enough to be used by any other authentication/authorization solutions.
11
13
 
12
- == Requirements
14
+ ## Requirements
13
15
 
14
16
  * >= Rails 3.1
15
17
  * ActiveRecord ORM <b>or</b> Mongoid
16
18
  * supports ruby 1.8/1.9, REE, JRuby and Rubinius
17
19
 
18
- == Installation
20
+ ## Installation
19
21
 
20
22
  In <b>Rails 3</b>, add this to your Gemfile and run the +bundle+ command.
21
23
 
24
+ ```ruby
22
25
  gem "rolify"
26
+ ```
23
27
 
24
28
  Alternatively, you can install it as a plugin.
25
29
 
30
+ ```
26
31
  rails plugin install git://github.com/EppO/rolify.git
32
+ ```
27
33
 
28
- == Getting Started
34
+ ## Getting Started
29
35
 
30
- === 1. Generate Role Model
36
+ ### 1. Generate Role Model
31
37
 
32
38
  First, create your Role model and migration file using this generator:
33
39
 
40
+ ```
34
41
  rails g rolify:role Role User
42
+ ```
35
43
 
36
44
  Role and User classes are the default. You can specify any Role class name you want. This is completly a new file so any name can do the job.
37
45
  For the User class name, you would probably use the one provided by your authentication solution. rolify just adds some class methods in an existing User class.
38
46
 
39
- If you want to use Mongoid instead of ActiveRecord, follow these instructions[https://github.com/EppO/rolify/wiki/Configuration], and skip to step #3
47
+ If you want to use Mongoid instead of ActiveRecord, follow these [instructions](https://github.com/EppO/rolify/wiki/Configuration), and skip to step #3
40
48
 
41
- === 2. Run the migration (only required when using ActiveRecord)
49
+ ### 2. Run the migration (only required when using ActiveRecord)
42
50
 
43
51
  Let's migrate !
44
52
 
53
+ ```
45
54
  rake db:migrate
55
+ ```
46
56
 
47
- === 3. Add a role to a user
57
+ ### 3. Add a role to a user
48
58
 
49
59
  To define a global role:
50
60
 
61
+ ```ruby
51
62
  user = User.find(1)
52
- user.has_role "admin"
63
+ user.add_role :admin
64
+ ```
53
65
 
54
66
  To define a role scoped to a resource instance
55
67
 
68
+ ```ruby
56
69
  user = User.find(2)
57
- user.has_role "moderator", Forum.first
70
+ user.add_role :moderator, Forum.first
71
+ ```
58
72
 
59
73
  To define a role scoped to a resource class
60
74
 
75
+ ```ruby
61
76
  user = User.find(3)
62
- user.has_role "moderator", Forum
77
+ user.add_role :moderator, Forum
78
+ ```
63
79
 
64
80
  That's it !
65
81
 
66
- === 4. Check roles
82
+ ### 4. Check roles
67
83
 
68
84
  To check if a user has a global role:
69
85
 
86
+ ```ruby
70
87
  user = User.find(1)
71
- user.has_role "admin" # sets a global role
72
- user.has_role? "admin"
88
+ user.add_role :admin # sets a global role
89
+ user.has_role? :admin
73
90
  => true
91
+ ```
74
92
 
75
93
  To check if a user has a role scoped to a resource instance:
76
94
 
95
+ ```ruby
77
96
  user = User.find(2)
78
- user.has_role "moderator", Forum.first # sets a role scoped to a resource instance
79
- user.has_role? "moderator", Forum.first
97
+ user.add_role :moderator, Forum.first # sets a role scoped to a resource instance
98
+ user.has_role? :moderator, Forum.first
80
99
  => true
81
- user.has_role? "moderator", Forum.last
100
+ user.has_role? :moderator, Forum.last
82
101
  => false
102
+ ```
83
103
 
84
104
  To check if a user has a role scoped to a resource class:
85
105
 
106
+ ```ruby
86
107
  user = User.find(3)
87
- user.has_role "moderator", Forum # sets a role scoped to a resource class
88
- user.has_role? "moderator", Forum
108
+ user.add_role :moderator, Forum # sets a role scoped to a resource class
109
+ user.has_role? :moderator, Forum
89
110
  => true
90
- user.has_role? "moderator", Forum.first
111
+ user.has_role? :moderator, Forum.first
91
112
  => true
92
- user.has_role? "moderator", Forum.last
113
+ user.has_role? :moderator, Forum.last
93
114
  => true
115
+ ```
94
116
 
95
117
  A global role overrides resource role request:
96
118
 
119
+ ```ruby
97
120
  user = User.find(4)
98
- user.has_role "moderator" # sets a global role
99
- user.has_role? "moderator", Forum.first
121
+ user.add_role :moderator # sets a global role
122
+ user.has_role? :moderator, Forum.first
100
123
  => true
101
- user.has_role? "moderator", Forum.last
124
+ user.has_role? :moderator, Forum.last
102
125
  => true
126
+ ```
103
127
 
104
- === 5. Resource roles querying
128
+ ### 5. Resource roles querying
105
129
 
106
130
  Starting from rolify 3.0, you can search roles on instance level or class level resources.
107
131
 
108
- ==== Instance level
132
+ #### Instance level
109
133
 
134
+ ```ruby
110
135
  forum = Forum.first
111
136
  forum.roles
112
137
  # => [ list of roles that are only binded to forum instance ]
113
138
  forum.applied_roles
114
139
  # => [ list of roles binded to forum instance and to the Forum class ]
140
+ ```
115
141
 
116
- ==== Class level
142
+ #### Class level
117
143
 
118
- Forum.with_role("admin")
144
+ ```ruby
145
+ Forum.with_role(:admin)
119
146
  # => [ list of Forum instances that has role "admin" binded to it ]
120
- Forum.with_role("admin", current_user)
147
+ Forum.with_role(:admin, current_user)
121
148
  # => [ list of Forum instances that has role "admin" binded to it and belongs to current_user roles ]
122
149
 
123
150
  Forum.find_roles
124
151
  # => [ list of roles that binded to any Forum instance or to the Forum class ]
125
- Forum.find_roles("admin")
152
+ Forum.find_roles(:admin)
126
153
  # => [ list of roles that binded to any Forum instance or to the Forum class with "admin" as a role name ]
127
- Forum.find_roles("admin", current_user)
154
+ Forum.find_roles(:admin, current_user)
128
155
  # => [ list of roles that binded to any Forum instance or to the Forum class with "admin" as a role name and belongs to current_user roles ]
156
+ ```
129
157
 
130
- == Resources
158
+ ## Resources
131
159
 
132
- * {Wiki}[https://github.com/EppO/rolify/wiki]
133
- * {Usage}[https://github.com/EppO/rolify/wiki/Usage]: all the available commands
134
- * {Tutorial}[https://github.com/EppO/rolify/wiki/Tutorial]: how to use {rolify}[http://eppo.github.com/rolify] with {Devise}[https://github.com/plataformatec/devise] and {CanCan}[https://github.com/ryanb/cancan].
160
+ * [Wiki](https://github.com/EppO/rolify/wiki)
161
+ * [Usage](https://github.com/EppO/rolify/wiki/Usage): all the available commands
162
+ * [Tutorial](https://github.com/EppO/rolify/wiki/Tutorial): how to use [rolify](http://eppo.github.com/rolify) with [Devise](https://github.com/plataformatec/devise) and [CanCan](https://github.com/ryanb/cancan).
135
163
 
136
- == Questions or Problems?
164
+ ## Questions or Problems?
137
165
 
138
- If you have any issue or feature request with/for rolify, please add an {issue on GitHub}[https://github.com/EppO/rolify/issues] or fork the project and send a pull request.
166
+ If you have any issue or feature request with/for rolify, please add an [issue on GitHub](https://github.com/EppO/rolify/issues) or fork the project and send a pull request.
@@ -14,9 +14,9 @@ module Rolify
14
14
  desc "Generates a model with the given NAME and a migration file."
15
15
 
16
16
  def generate_role
17
- template "role-#{orm_adapter}.rb", "app/models/#{role_cname.downcase}.rb"
17
+ template "role-#{orm_adapter}.rb", "app/models/#{role_cname.underscore}.rb"
18
18
  inject_into_class(model_path, user_cname.camelize) do
19
- "\trolify\n"
19
+ "\trolify" + (role_cname == "Role" ? "" : ":role_cname => '#{role_cname.camelize}'") + "\n"
20
20
  end
21
21
  end
22
22
 
@@ -1,7 +1,7 @@
1
- Rolify.configure do |c|
1
+ Rolify.configure do |config|
2
2
  # By default ORM adapter is ActiveRecord. uncomment to use mongoid
3
- <%= "# " if orm_adapter == "active_record" %>c.use_mongoid
3
+ <%= "# " if orm_adapter == "active_record" %>config.use_mongoid
4
4
 
5
5
  # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false
6
- <%= "# " if !options[:dynamic_shortcuts] %>c.use_dynamic_shortcuts
6
+ <%= "# " if !options[:dynamic_shortcuts] %>config.use_dynamic_shortcuts
7
7
  end
@@ -5,4 +5,13 @@ class <%= role_cname.camelize %>
5
5
  belongs_to :resource, :polymorphic => true
6
6
 
7
7
  field :name, :type => String
8
+ index :name, unique: true
9
+ index(
10
+ [
11
+ [:name, Mongo::ASCENDING],
12
+ [:resource_type, Mongo::ASCENDING],
13
+ [:resource_id, Mongo::ASCENDING]
14
+ ],
15
+ unique: true
16
+ )
8
17
  end
data/lib/rolify.rb CHANGED
@@ -1,36 +1,39 @@
1
- require 'active_record'
2
-
3
- require 'rolify/adapters/active_record' if defined?(ActiveRecord)
4
- require 'rolify/adapters/mongoid' if defined?(Mongoid)
5
1
  require 'rolify/railtie' if defined?(Rails)
2
+ require 'rolify/utils'
6
3
  require 'rolify/role'
7
4
  require 'rolify/configure'
8
5
  require 'rolify/dynamic'
9
6
  require 'rolify/resource'
7
+ require 'rolify/adapters/base'
10
8
 
11
9
  module Rolify
12
10
  extend Configure
11
+
13
12
  attr_accessor :role_cname, :adapter
14
13
 
15
14
  def rolify(options = { :role_cname => 'Role' })
16
15
  include Role
17
16
  extend Dynamic if Rolify.dynamic_shortcuts
17
+
18
18
  rolify_options = { :class_name => options[:role_cname].camelize }
19
19
  rolify_options.merge!({ :join_table => "#{self.to_s.tableize}_#{options[:role_cname].tableize}" }) if Rolify.orm == "active_record"
20
20
  has_and_belongs_to_many :roles, rolify_options
21
21
 
22
- load_dynamic_methods if Rolify.dynamic_shortcuts
22
+ self.adapter = Rolify::Adapter::Base.create("role_adapter", options[:role_cname], self.name)
23
23
  self.role_cname = options[:role_cname]
24
- self.adapter = Rolify::Adapter.const_get(Rolify.orm.camelize).new(options[:role_cname])
24
+
25
+ load_dynamic_methods if Rolify.dynamic_shortcuts
25
26
  end
26
27
 
27
28
  def resourcify(options = { :role_cname => 'Role' })
28
29
  include Resource
30
+
29
31
  resourcify_options = { :class_name => options[:role_cname].camelize }
30
32
  resourcify_options.merge!({ :as => :resource })
31
33
  has_many :roles, resourcify_options
34
+
35
+ self.adapter = Rolify::Adapter::Base.create("resource_adapter", options[:role_cname], self.name)
32
36
  self.role_cname = options[:role_cname]
33
- self.adapter = Rolify::Adapter.const_get(Rolify.orm.camelize).new(options[:role_cname])
34
37
  end
35
38
 
36
39
  def role_class
@@ -0,0 +1,17 @@
1
+ require 'rolify/adapters/base'
2
+
3
+ module Rolify
4
+ module Adapter
5
+ class ResourceAdapter < ResourceAdapterBase
6
+ def resources_find(roles_table, relation, role_name)
7
+ resources = relation.joins("INNER JOIN \"#{roles_table}\" ON \"#{roles_table}\".\"resource_type\" = '#{relation.to_s}'")
8
+ resources = resources.where("#{roles_table}.name = ? AND #{roles_table}.resource_type = ?", role_name, relation.to_s)
9
+ resources
10
+ end
11
+
12
+ def in(relation, roles)
13
+ relation.where("#{role_class.to_s.tableize}.id IN (?) AND ((resource_id = #{relation.table_name}.id) OR (resource_id IS NULL))", roles)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -2,13 +2,8 @@ require 'rolify/adapters/base'
2
2
 
3
3
  module Rolify
4
4
  module Adapter
5
- class ActiveRecord < Adapter::Base
6
- def find(relation, role_name, resource)
7
- query, values = build_query(role_name, resource)
8
- relation.where(query, *values)
9
- end
10
-
11
- def where(relation, args)
5
+ class RoleAdapter < RoleAdapterBase
6
+ def where(relation, *args)
12
7
  conditions, values = build_conditions(relation, args)
13
8
  relation.where(conditions, *values)
14
9
  end
@@ -22,20 +17,16 @@ module Rolify
22
17
  end
23
18
 
24
19
  def remove(relation, role_name, resource = nil)
25
- role = relation.where(:name => role_name)
26
- role = role.where(:resource_type => (resource.is_a?(Class) ? resource.to_s : resource.class.name)) if resource
27
- role = role.where(:resource_id => resource.id) if resource && !resource.is_a?(Class)
28
- relation.delete(role) if role
29
- end
30
-
31
- def resources_find(roles_table, relation, role_name)
32
- resources = relation.joins("INNER JOIN \"#{roles_table}\" ON \"#{roles_table}\".\"resource_type\" = '#{relation.to_s}'")
33
- resources = resources.where("#{roles_table}.name = ? AND #{roles_table}.resource_type = ?", role_name, relation.to_s)
34
- resources
35
- end
36
-
37
- def in(relation, roles)
38
- relation.where("#{role_class.to_s.tableize}.id IN (?) AND ((resource_id = #{relation.table_name}.id) OR (resource_id IS NULL))", roles)
20
+ roles = relation.roles.where(:name => role_name)
21
+ roles = roles.where(:resource_type => (resource.is_a?(Class) ? resource.to_s : resource.class.name)) if resource
22
+ roles = roles.where(:resource_id => resource.id) if resource && !resource.is_a?(Class)
23
+ if roles
24
+ relation.roles.delete(roles)
25
+ roles.each do |role|
26
+ role.destroy if role.send(user_class.table_name.to_sym).empty?
27
+ end
28
+ end
29
+ roles
39
30
  end
40
31
 
41
32
  def exists?(relation, column)
@@ -1,52 +1,54 @@
1
1
  module Rolify
2
2
  module Adapter
3
3
  class Base
4
- def initialize(role_cname)
4
+ def initialize(role_cname, user_cname)
5
5
  @role_cname = role_cname
6
+ @user_cname = user_cname
6
7
  end
7
-
8
+
8
9
  def role_class
9
10
  @role_cname.constantize
10
11
  end
11
12
 
12
- def find(relation, role_name, resource)
13
- raise NotImplementedError.new("You must implement find")
13
+ def user_class
14
+ @user_cname.constantize
14
15
  end
15
16
 
17
+ def self.create(adapter, role_cname, user_cname)
18
+ load "rolify/adapters/#{Rolify.orm}/#{adapter}.rb"
19
+ Rolify::Adapter.const_get(adapter.camelize.to_sym).new(role_cname, user_cname)
20
+ end
21
+ end
22
+
23
+ class RoleAdapterBase < Adapter::Base
16
24
  def where(relation, args)
17
25
  raise NotImplementedError.new("You must implement where")
18
26
  end
19
-
27
+
20
28
  def find_or_create_by(role_name, resource_type = nil, resource_id = nil)
21
29
  raise NotImplementedError.new("You must implement find_or_create_by")
22
30
  end
23
-
31
+
24
32
  def add(relation, role_name, resource = nil)
25
33
  raise NotImplementedError.new("You must implement add")
26
34
  end
27
-
35
+
28
36
  def remove(relation, role_name, resource = nil)
29
37
  raise NotImplementedError.new("You must implement delete")
30
38
  end
31
-
32
- def resources_find(roles_table, relation, role_name)
33
- raise NotImplementedError.new("You must implement resources_find")
34
- end
35
-
36
- def in(resources, roles)
37
- raise NotImplementedError.new("You must implement in")
38
- end
39
-
39
+
40
40
  def exists?(relation, column)
41
41
  raise NotImplementedError.new("You must implement exists?")
42
42
  end
43
-
44
- def build_conditions(relation, args)
45
- raise NotImplementedError.new("You must implement build_conditions")
43
+ end
44
+
45
+ class ResourceAdapterBase < Adapter::Base
46
+ def resources_find(roles_table, relation, role_name)
47
+ raise NotImplementedError.new("You must implement resources_find")
46
48
  end
47
-
48
- def build_query(role, resource = nil)
49
- raise NotImplementedError.new("You must implement build_query")
49
+
50
+ def in(resources, roles)
51
+ raise NotImplementedError.new("You must implement in")
50
52
  end
51
53
  end
52
54
  end