rolify 3.1.0 → 3.2.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
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