rolify 3.1.0 → 3.2.0.rc2

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 (36) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +2 -4
  3. data/CHANGELOG.rdoc +17 -0
  4. data/README.md +42 -6
  5. data/lib/generators/rolify/role/role_generator.rb +11 -3
  6. data/lib/generators/rolify/role/templates/initializer.rb +2 -1
  7. data/lib/generators/rolify/role/templates/role-active_record.rb +2 -0
  8. data/lib/generators/rolify/role/templates/role-mongoid.rb +11 -9
  9. data/lib/rolify.rb +13 -5
  10. data/lib/rolify/adapters/active_record/resource_adapter.rb +11 -4
  11. data/lib/rolify/adapters/active_record/role_adapter.rb +11 -4
  12. data/lib/rolify/adapters/active_record/scopes.rb +27 -0
  13. data/lib/rolify/adapters/base.rb +5 -0
  14. data/lib/rolify/adapters/mongoid/resource_adapter.rb +9 -5
  15. data/lib/rolify/adapters/mongoid/role_adapter.rb +14 -2
  16. data/lib/rolify/adapters/mongoid/scopes.rb +27 -0
  17. data/lib/rolify/finders.rb +39 -0
  18. data/lib/rolify/railtie.rb +1 -1
  19. data/lib/rolify/resource.rb +2 -1
  20. data/lib/rolify/role.rb +12 -2
  21. data/lib/rolify/version.rb +1 -1
  22. data/rolify.gemspec +4 -4
  23. data/spec/generators/rolify/role/role_generator_spec.rb +17 -61
  24. data/spec/rolify/custom_spec.rb +12 -0
  25. data/spec/rolify/resource_spec.rb +31 -0
  26. data/spec/rolify/role_spec.rb +12 -0
  27. data/spec/rolify/shared_contexts.rb +12 -0
  28. data/spec/rolify/shared_examples/shared_examples_for_callbacks.rb +57 -0
  29. data/spec/rolify/shared_examples/shared_examples_for_finders.rb +77 -0
  30. data/spec/rolify/shared_examples/shared_examples_for_roles.rb +39 -27
  31. data/spec/rolify/shared_examples/shared_examples_for_scopes.rb +38 -0
  32. data/spec/spec_helper.rb +13 -0
  33. data/spec/support/adapters/active_record.rb +4 -0
  34. data/spec/support/adapters/mongoid.rb +23 -3
  35. data/spec/support/adapters/mongoid.yml +6 -0
  36. metadata +82 -30
@@ -8,7 +8,7 @@ module Rolify
8
8
  ActiveRecord::Base.send :extend, Rolify
9
9
  end
10
10
 
11
- config.after_initialize do
11
+ config.before_initialize do
12
12
  ::Mongoid::Document.module_eval do
13
13
  def self.included(base)
14
14
  base.extend Rolify
@@ -14,8 +14,9 @@ module Rolify
14
14
 
15
15
  def with_role(role_name, user = nil)
16
16
  resources = self.adapter.resources_find(self.role_class.to_s.tableize, self, role_name)
17
- user ? self.adapter.in(resources, user.roles.where(:name => role_name)) : resources
17
+ user ? self.adapter.in(resources, user, role_name) : resources
18
18
  end
19
+ alias :with_roles :with_role
19
20
  end
20
21
 
21
22
  def applied_roles
data/lib/rolify/role.rb CHANGED
@@ -1,9 +1,15 @@
1
+ require "rolify/finders"
2
+
1
3
  module Rolify
2
4
  module Role
3
5
  extend Utils
4
6
 
7
+ def self.included(base)
8
+ base.extend Finders
9
+ end
10
+
5
11
  def add_role(role_name, resource = nil)
6
- role = self.class.adapter.find_or_create_by(role_name,
12
+ role = self.class.adapter.find_or_create_by(role_name.to_s,
7
13
  (resource.is_a?(Class) ? resource.to_s : resource.class.name if resource),
8
14
  (resource.id if resource && !resource.is_a?(Class)))
9
15
 
@@ -17,7 +23,11 @@ module Rolify
17
23
  deprecate :has_role, :add_role
18
24
 
19
25
  def has_role?(role_name, resource = nil)
20
- self.class.adapter.where(self.roles, :name => role_name, :resource => resource).size > 0
26
+ if new_record?
27
+ self.roles.detect { |r| r.name == role_name.to_s && (r.resource == resource || resource.nil?) }.present?
28
+ else
29
+ self.class.adapter.where(self.roles, :name => role_name, :resource => resource).size > 0
30
+ end
21
31
  end
22
32
 
23
33
  def has_all_roles?(*args)
@@ -1,3 +1,3 @@
1
1
  module Rolify
2
- VERSION = "3.1.0"
2
+ VERSION = "3.2.0.rc2"
3
3
  end
data/rolify.gemspec CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |s|
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Florent Monbillard"]
10
10
  s.email = ["f.monbillard@gmail.com"]
11
- s.homepage = "https://github.com/EppO/rolify"
11
+ s.homepage = "http://eppo.github.com/rolify/"
12
12
  s.summary = %q{Roles library with resource scoping}
13
- s.description = %q{Very simple Roles library without any authorization enforcement supporting scope on resource objects (instance or class)}
13
+ s.description = %q{Very simple Roles library without any authorization enforcement supporting scope on resource objects (instance or class). Supports ActiveRecord and Mongoid ORMs.}
14
14
 
15
15
  s.rubyforge_project = s.name
16
16
 
@@ -23,10 +23,10 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency "activerecord-jdbcsqlite3-adapter"
24
24
  else
25
25
  s.add_development_dependency "sqlite3"
26
+ s.add_development_dependency "bson_ext" if RUBY_VERSION < "2.0.0"
26
27
  end
27
28
  s.add_development_dependency "activerecord", ">= 3.1.0"
28
- s.add_development_dependency "mongoid", ">= 2.3"
29
- s.add_development_dependency "bson_ext"
29
+ s.add_development_dependency "mongoid", ">= 3.0"
30
30
  s.add_development_dependency "ammeter"
31
31
  s.add_development_dependency "rake"
32
32
  s.add_development_dependency "rspec"
@@ -46,7 +46,7 @@ describe Rolify::Generators::RoleGenerator do
46
46
 
47
47
  describe 'app/models/user.rb' do
48
48
  subject { file('app/models/user.rb') }
49
- it { should contain "rolify" }
49
+ it { should contain /class User < ActiveRecord::Base\n rolify\n/ }
50
50
  end
51
51
 
52
52
  describe 'migration file' do
@@ -88,10 +88,10 @@ describe Rolify::Generators::RoleGenerator do
88
88
  it { should contain "belongs_to :resource, :polymorphic => true" }
89
89
  end
90
90
 
91
- describe 'app/models/client.rb' do
91
+ describe 'app/models/admin_user.rb' do
92
92
  subject { file('app/models/admin_user.rb') }
93
93
 
94
- it { should contain "rolify" }
94
+ it { should contain /class AdminUser < ActiveRecord::Base\n rolify :role_cname => 'AdminRole'\n/ }
95
95
  end
96
96
 
97
97
  describe 'migration file' do
@@ -103,61 +103,19 @@ describe Rolify::Generators::RoleGenerator do
103
103
  end
104
104
  end
105
105
 
106
- describe 'specifying dynamic shortcuts' do
107
- before(:all) { arguments [ "Role", "User", "--dynamic_shortcuts" ] }
108
-
109
- before {
110
- capture(:stdout) {
111
- generator.create_file "app/models/user.rb" do
112
- "class User < ActiveRecord::Base\nend"
113
- end
114
- }
115
- run_generator
116
- }
117
-
118
- describe 'config/initializers/rolify.rb' do
119
- subject { file('config/initializers/rolify.rb') }
120
- it { should exist }
121
- it { should contain "Rolify.configure do |config|"}
122
- it { should_not contain "# config.use_dynamic_shortcuts" }
123
- it { should contain "# config.use_mongoid" }
124
- end
125
-
126
- describe 'app/models/role.rb' do
127
- subject { file('app/models/role.rb') }
128
- it { should exist }
129
- it { should contain "class Role < ActiveRecord::Base" }
130
- it { should contain "has_and_belongs_to_many :users, :join_table => :users_roles" }
131
- it { should contain "belongs_to :resource, :polymorphic => true" }
132
- end
133
-
134
- describe 'app/models/user.rb' do
135
- subject { file('app/models/user.rb') }
136
- it { should contain "rolify" }
137
- end
138
-
139
- describe 'migration file' do
140
- subject { migration_file('db/migrate/rolify_create_roles.rb') }
141
-
142
- it { should be_a_migration }
143
- it { should contain "create_table(:roles) do" }
144
- it { should contain "create_table(:users_roles, :id => false) do" }
145
- end
146
- end
147
-
148
106
  describe 'specifying orm adapter' do
149
107
  before(:all) { arguments [ "Role", "User", "mongoid" ] }
150
108
 
151
109
  before {
152
110
  capture(:stdout) {
153
111
  generator.create_file "app/models/user.rb" do
154
- <<-CLASS
155
- class User
156
- include Mongoid::Document
112
+ <<-CLASS
113
+ class User
114
+ include Mongoid::Document
157
115
 
158
- field :login, :type => String
159
- end
160
- CLASS
116
+ field :login, :type => String
117
+ end
118
+ CLASS
161
119
  end
162
120
  }
163
121
  run_generator
@@ -178,20 +136,18 @@ describe Rolify::Generators::RoleGenerator do
178
136
  it { should contain "has_and_belongs_to_many :users\n" }
179
137
  it { should contain "belongs_to :resource, :polymorphic => true" }
180
138
  it { should contain "field :name, :type => String" }
181
- it { should contain "index :name, unique: true" }
182
- it { should contain " index(\n"
183
- " [\n"
184
- " [:name, Mongo::ASCENDING],\n"
185
- " [:resource_type, Mongo::ASCENDING],\n"
186
- " [:resource_id, Mongo::ASCENDING]\n"
187
- " ],\n"
188
- " unique: true\n"
189
- " )\n" }
139
+ it { should contain "index({ :name => 1 }, { :unique => true })" }
140
+ it { should contain " index({\n"
141
+ " { :name => 1 },\n"
142
+ " { :resource_type => 1 },\n"
143
+ " { :resource_id => 1 }\n"
144
+ " },\n"
145
+ " { unique => true })"}
190
146
  end
191
147
 
192
148
  describe 'app/models/user.rb' do
193
149
  subject { file('app/models/user.rb') }
194
- it { should contain "rolify" }
150
+ it { should contain /class User\n include Mongoid::Document\n rolify\n/ }
195
151
  end
196
152
  end
197
153
  end
@@ -1,6 +1,8 @@
1
1
  require "spec_helper"
2
2
  require "rolify/shared_examples/shared_examples_for_roles"
3
3
  require "rolify/shared_examples/shared_examples_for_dynamic"
4
+ require "rolify/shared_examples/shared_examples_for_scopes"
5
+ require "rolify/shared_examples/shared_examples_for_callbacks"
4
6
 
5
7
  describe "Using Rolify with custom User and Role class names" do
6
8
  it_behaves_like Rolify::Role do
@@ -8,8 +10,18 @@ describe "Using Rolify with custom User and Role class names" do
8
10
  let(:role_class) { Privilege }
9
11
  end
10
12
 
13
+ it_behaves_like "Role.scopes" do
14
+ let(:user_class) { Customer }
15
+ let(:role_class) { Privilege }
16
+ end
17
+
11
18
  it_behaves_like Rolify::Dynamic do
12
19
  let(:user_class) { Customer }
13
20
  let(:role_class) { Privilege }
14
21
  end
22
+
23
+ it_behaves_like "Rolify.callbacks" do
24
+ let(:user_class) { Customer }
25
+ let(:role_class) { Privilege }
26
+ end
15
27
  end
@@ -16,6 +16,7 @@ describe Rolify::Resource do
16
16
  let!(:forum_role) { admin.add_role(:forum, Forum.first) }
17
17
  let!(:godfather_role) { admin.add_role(:godfather, Forum) }
18
18
  let!(:group_role) { admin.add_role(:group, Group.last) }
19
+ let!(:grouper_role) { admin.add_role(:grouper, Group.first) }
19
20
  let!(:tourist_role) { tourist.add_role(:forum, Forum.last) }
20
21
  let!(:sneaky_role) { tourist.add_role(:group, Forum.first) }
21
22
 
@@ -47,6 +48,15 @@ describe Rolify::Resource do
47
48
 
48
49
  end
49
50
 
51
+ context "with an array of role names as argument" do
52
+ context "on the Group class" do
53
+ subject { Group }
54
+ it "should include Group instances with both group and grouper roles" do
55
+ subject.with_roles([:group, :grouper]).should include(Group.first, Group.last)
56
+ end
57
+ end
58
+ end
59
+
50
60
  context "with a role name and a user as arguments" do
51
61
  context "on the Forum class" do
52
62
  subject { Forum }
@@ -88,6 +98,27 @@ describe Rolify::Resource do
88
98
  end
89
99
  end
90
100
  end
101
+
102
+ context "with an array of role names and a user as arguments" do
103
+ context "on the Forum class" do
104
+ subject { Forum }
105
+
106
+ it "should get Forum instances binded to the forum and group roles and the tourist user" do
107
+ subject.with_roles([:forum, :group], tourist).should include(Forum.first, Forum.last)
108
+ end
109
+
110
+ end
111
+
112
+ context "on the Group class" do
113
+ subject { Group }
114
+
115
+ it "should get Group instances binded to the group and grouper roles and the admin user" do
116
+ subject.with_roles([:group, :grouper], admin).should include(Group.first, Group.last)
117
+ end
118
+
119
+ end
120
+ end
121
+
91
122
  end
92
123
 
93
124
  describe ".find_role" do
@@ -1,6 +1,8 @@
1
1
  require "spec_helper"
2
2
  require "rolify/shared_examples/shared_examples_for_roles"
3
3
  require "rolify/shared_examples/shared_examples_for_dynamic"
4
+ require "rolify/shared_examples/shared_examples_for_scopes"
5
+ require "rolify/shared_examples/shared_examples_for_callbacks"
4
6
 
5
7
  describe Rolify do
6
8
  it_behaves_like Rolify::Role do
@@ -8,8 +10,18 @@ describe Rolify do
8
10
  let(:role_class) { Role }
9
11
  end
10
12
 
13
+ it_behaves_like "Role.scopes" do
14
+ let(:user_class) { User }
15
+ let(:role_class) { Role }
16
+ end
17
+
11
18
  it_behaves_like Rolify::Dynamic do
12
19
  let(:user_class) { User }
13
20
  let(:role_class) { Role }
14
21
  end
22
+
23
+ it_behaves_like "Rolify.callbacks" do
24
+ let(:user_class) { User }
25
+ let(:role_class) { Role }
26
+ end
15
27
  end
@@ -63,6 +63,18 @@ shared_context "instance scoped role", :scope => :instance do
63
63
  end
64
64
  end
65
65
 
66
+ shared_context "mixed scoped roles", :scope => :mixed do
67
+ subject { user_class }
68
+
69
+ before(:all) do
70
+ role_class.destroy_all
71
+ end
72
+
73
+ let!(:root) { provision_user(user_class.first, [ :admin, :staff, [ :moderator, Group ], [ :visitor, Forum.last ] ]) }
74
+ let!(:modo) { provision_user(user_class.where(:login => "moderator").first, [[ :moderator, Forum ], [ :manager, Group ], [ :visitor, Group.first ]])}
75
+ let!(:visitor) { provision_user(user_class.last, [[ :visitor, Forum.last ]]) }
76
+ end
77
+
66
78
  def create_other_roles
67
79
  role_class.create :name => "superhero"
68
80
  role_class.create :name => "admin", :resource_type => "Group"
@@ -0,0 +1,57 @@
1
+ shared_examples_for "Rolify.callbacks" do
2
+ before(:all) do
3
+ reset_defaults
4
+ Rolify.dynamic_shortcuts = false
5
+ role_class.destroy_all
6
+ end
7
+
8
+ after :each do
9
+ @user.roles.destroy_all
10
+ end
11
+
12
+ describe "rolify association callbacks", :if => (Rolify.orm == "active_record") do
13
+ describe "before_add" do
14
+ it "should receive callback" do
15
+ user_class.rolify :before_add => :role_callback, :role_cname => role_class.to_s
16
+ @user = user_class.first
17
+ @user.stub(:role_callback)
18
+ @user.should_receive(:role_callback)
19
+ @user.add_role :admin
20
+ end
21
+ end
22
+
23
+ describe "after_add" do
24
+ it "should receive callback" do
25
+ user_class.rolify :after_add => :role_callback, :role_cname => role_class.to_s
26
+ @user = user_class.first
27
+ @user.stub(:role_callback)
28
+ @user.should_receive(:role_callback)
29
+ @user.add_role :admin
30
+ end
31
+ end
32
+
33
+ describe "before_remove" do
34
+ it "should receive callback" do
35
+ user_class.rolify :before_remove => :role_callback, :role_cname => role_class.to_s
36
+ @user = user_class.first
37
+ @user.add_role :admin
38
+ @user.stub(:role_callback)
39
+
40
+ @user.should_receive(:role_callback)
41
+ @user.remove_role :admin
42
+ end
43
+ end
44
+
45
+ describe "after_remove" do
46
+ it "should receive callback" do
47
+ user_class.rolify :after_remove => :role_callback, :role_cname => role_class.to_s
48
+ @user = user_class.first
49
+ @user.add_role :admin
50
+ @user.stub(:role_callback)
51
+
52
+ @user.should_receive(:role_callback)
53
+ @user.remove_role :admin
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,77 @@
1
+ shared_examples_for :finders do |param_name, param_method|
2
+ context "using #{param_name} as parameter" do
3
+ describe ".with_role" do
4
+ it { should respond_to(:with_role).with(1).argument }
5
+ it { should respond_to(:with_role).with(2).arguments }
6
+
7
+ context "with a global role" do
8
+ it { subject.with_role("admin".send(param_method)).should eq([ root ]) }
9
+ it { subject.with_role("moderator".send(param_method)).should be_empty }
10
+ it { subject.with_role("visitor".send(param_method)).should be_empty }
11
+ end
12
+
13
+ context "with a class scoped role" do
14
+ context "on Forum class" do
15
+ it { subject.with_role("admin".send(param_method), Forum).should eq([ root ]) }
16
+ it { subject.with_role("moderator".send(param_method), Forum).should eq([ modo ]) }
17
+ it { subject.with_role("visitor".send(param_method), Forum).should be_empty }
18
+ end
19
+
20
+ context "on Group class" do
21
+ it { subject.with_role("admin".send(param_method), Group).should eq([ root ]) }
22
+ it { subject.with_role("moderator".send(param_method), Group).should eq([ root ]) }
23
+ it { subject.with_role("visitor".send(param_method), Group).should be_empty }
24
+ end
25
+ end
26
+
27
+ context "with an instance scoped role" do
28
+ context "on Forum.first instance" do
29
+ it { subject.with_role("admin".send(param_method), Forum.first).should eq([ root ]) }
30
+ it { subject.with_role("moderator".send(param_method), Forum.first).should eq([ modo ]) }
31
+ it { subject.with_role("visitor".send(param_method), Forum.first).should be_empty }
32
+ end
33
+
34
+ context "on Forum.last instance" do
35
+ it { subject.with_role("admin".send(param_method), Forum.last).should eq([ root ]) }
36
+ it { subject.with_role("moderator".send(param_method), Forum.last).should eq([ modo ]) }
37
+ it { subject.with_role("visitor".send(param_method), Forum.last).should include(root, visitor) } # =~ doesn't pass using mongoid, don't know why...
38
+ end
39
+
40
+ context "on Group.first instance" do
41
+ it { subject.with_role("admin".send(param_method), Group.first).should eq([ root ]) }
42
+ it { subject.with_role("moderator".send(param_method), Group.first).should eq([ root ]) }
43
+ it { subject.with_role("visitor".send(param_method), Group.first).should eq([ modo ]) }
44
+ end
45
+ end
46
+ end
47
+
48
+ describe ".with_all_roles" do
49
+ it { should respond_to(:with_all_roles) }
50
+
51
+ it { subject.with_all_roles("admin".send(param_method), :staff).should eq([ root ]) }
52
+ it { subject.with_all_roles("admin".send(param_method), :staff, { :name => "moderator".send(param_method), :resource => Group }).should eq([ root ]) }
53
+ it { subject.with_all_roles("admin".send(param_method), "moderator".send(param_method)).should be_empty }
54
+ it { subject.with_all_roles("admin".send(param_method), :staff, { :name => "moderator".send(param_method), :resource => Forum }).should be_empty }
55
+ it { subject.with_all_roles({ :name => "moderator".send(param_method), :resource => Forum }, { :name => :manager, :resource => Group }).should eq([ modo ]) }
56
+ it { subject.with_all_roles("moderator".send(param_method), :manager).should be_empty }
57
+ it { subject.with_all_roles({ :name => "visitor".send(param_method), :resource => Forum.last }, { :name => "moderator".send(param_method), :resource => Group }).should eq([ root ]) }
58
+ it { subject.with_all_roles({ :name => "visitor".send(param_method), :resource => Group.first }, { :name => "moderator".send(param_method), :resource => Forum }).should eq([ modo ]) }
59
+ it { subject.with_all_roles({ :name => "visitor".send(param_method), :resource => :any }, { :name => "moderator".send(param_method), :resource => :any }).should =~ [ root, modo ] }
60
+ end
61
+
62
+ describe ".with_any_role" do
63
+ it { should respond_to(:with_any_role) }
64
+
65
+ it { subject.with_any_role("admin".send(param_method), :staff).should eq([ root ]) }
66
+ it { subject.with_any_role("admin".send(param_method), :staff, { :name => "moderator".send(param_method), :resource => Group }).should eq([ root ]) }
67
+ it { subject.with_any_role("admin".send(param_method), "moderator".send(param_method)).should eq([ root ]) }
68
+ it { subject.with_any_role("admin".send(param_method), :staff, { :name => "moderator".send(param_method), :resource => Forum }).should =~ [ root, modo ] }
69
+ it { subject.with_any_role({ :name => "moderator".send(param_method), :resource => Forum }, { :name => :manager, :resource => Group }).should eq([ modo ]) }
70
+ it { subject.with_any_role({ :name => "moderator".send(param_method), :resource => Group }, { :name => :manager, :resource => Group }).should =~ [ root, modo ] }
71
+ it { subject.with_any_role("moderator".send(param_method), :manager).should be_empty }
72
+ it { subject.with_any_role({ :name => "visitor".send(param_method), :resource => Forum.last }, { :name => "moderator".send(param_method), :resource => Group }).should =~ [ root, visitor ] }
73
+ it { subject.with_any_role({ :name => "visitor".send(param_method), :resource => Group.first }, { :name => "moderator".send(param_method), :resource => Forum }).should eq([ modo ]) }
74
+ it { subject.with_any_role({ :name => "visitor".send(param_method), :resource => :any }, { :name => "moderator".send(param_method), :resource => :any }).should =~ [ root, modo, visitor ] }
75
+ end
76
+ end
77
+ end