cancancan 1.15.0 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +38 -0
  3. data/.rubocop_todo.yml +48 -0
  4. data/.travis.yml +8 -2
  5. data/Appraisals +1 -0
  6. data/CHANGELOG.rdoc +5 -0
  7. data/Gemfile +1 -1
  8. data/README.md +58 -41
  9. data/Rakefile +7 -3
  10. data/cancancan.gemspec +13 -12
  11. data/gemfiles/activerecord_4.2.gemfile +1 -0
  12. data/lib/cancan.rb +2 -2
  13. data/lib/cancan/ability.rb +26 -24
  14. data/lib/cancan/controller_additions.rb +33 -23
  15. data/lib/cancan/controller_resource.rb +83 -56
  16. data/lib/cancan/exceptions.rb +1 -1
  17. data/lib/cancan/matchers.rb +2 -2
  18. data/lib/cancan/model_adapters/abstract_adapter.rb +8 -8
  19. data/lib/cancan/model_adapters/active_record_4_adapter.rb +48 -35
  20. data/lib/cancan/model_adapters/active_record_adapter.rb +18 -17
  21. data/lib/cancan/model_adapters/mongoid_adapter.rb +26 -21
  22. data/lib/cancan/model_adapters/sequel_adapter.rb +12 -12
  23. data/lib/cancan/model_additions.rb +0 -1
  24. data/lib/cancan/rule.rb +23 -17
  25. data/lib/cancan/version.rb +1 -1
  26. data/lib/generators/cancan/ability/ability_generator.rb +1 -1
  27. data/spec/cancan/ability_spec.rb +189 -180
  28. data/spec/cancan/controller_additions_spec.rb +77 -64
  29. data/spec/cancan/controller_resource_spec.rb +230 -228
  30. data/spec/cancan/exceptions_spec.rb +20 -20
  31. data/spec/cancan/inherited_resource_spec.rb +21 -21
  32. data/spec/cancan/matchers_spec.rb +12 -12
  33. data/spec/cancan/model_adapters/active_record_4_adapter_spec.rb +38 -32
  34. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +155 -145
  35. data/spec/cancan/model_adapters/default_adapter_spec.rb +2 -2
  36. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +87 -88
  37. data/spec/cancan/model_adapters/sequel_adapter_spec.rb +44 -47
  38. data/spec/cancan/rule_spec.rb +18 -18
  39. data/spec/spec_helper.rb +2 -2
  40. data/spec/support/ability.rb +0 -1
  41. metadata +60 -19
@@ -1,58 +1,58 @@
1
- require "spec_helper"
1
+ require 'spec_helper'
2
2
 
3
3
  describe CanCan::AccessDenied do
4
- describe "with action and subject" do
4
+ describe 'with action and subject' do
5
5
  before(:each) do
6
6
  @exception = CanCan::AccessDenied.new(nil, :some_action, :some_subject)
7
7
  end
8
8
 
9
- it "has action and subject accessors" do
9
+ it 'has action and subject accessors' do
10
10
  expect(@exception.action).to eq(:some_action)
11
11
  expect(@exception.subject).to eq(:some_subject)
12
12
  end
13
13
 
14
- it "has a changable default message" do
15
- expect(@exception.message).to eq("You are not authorized to access this page.")
16
- @exception.default_message = "Unauthorized!"
17
- expect(@exception.message).to eq("Unauthorized!")
14
+ it 'has a changable default message' do
15
+ expect(@exception.message).to eq('You are not authorized to access this page.')
16
+ @exception.default_message = 'Unauthorized!'
17
+ expect(@exception.message).to eq('Unauthorized!')
18
18
  end
19
19
  end
20
20
 
21
- describe "with only a message" do
21
+ describe 'with only a message' do
22
22
  before(:each) do
23
- @exception = CanCan::AccessDenied.new("Access denied!")
23
+ @exception = CanCan::AccessDenied.new('Access denied!')
24
24
  end
25
25
 
26
- it "has nil action and subject" do
26
+ it 'has nil action and subject' do
27
27
  expect(@exception.action).to be_nil
28
28
  expect(@exception.subject).to be_nil
29
29
  end
30
30
 
31
- it "has passed message" do
32
- expect(@exception.message).to eq("Access denied!")
31
+ it 'has passed message' do
32
+ expect(@exception.message).to eq('Access denied!')
33
33
  end
34
34
  end
35
35
 
36
- describe "i18n in the default message" do
36
+ describe 'i18n in the default message' do
37
37
  after(:each) do
38
38
  I18n.backend = nil
39
39
  end
40
40
 
41
- it "uses i18n for the default message" do
42
- I18n.backend.store_translations :en, :unauthorized => {:default => "This is a different message"}
41
+ it 'uses i18n for the default message' do
42
+ I18n.backend.store_translations :en, unauthorized: { default: 'This is a different message' }
43
43
  @exception = CanCan::AccessDenied.new
44
- expect(@exception.message).to eq("This is a different message")
44
+ expect(@exception.message).to eq('This is a different message')
45
45
  end
46
46
 
47
- it "defaults to a nice message" do
47
+ it 'defaults to a nice message' do
48
48
  @exception = CanCan::AccessDenied.new
49
- expect(@exception.message).to eq("You are not authorized to access this page.")
49
+ expect(@exception.message).to eq('You are not authorized to access this page.')
50
50
  end
51
51
 
52
- it "does not use translation if a message is given" do
52
+ it 'does not use translation if a message is given' do
53
53
  @exception = CanCan::AccessDenied.new("Hey! You're not welcome here")
54
54
  expect(@exception.message).to eq("Hey! You're not welcome here")
55
- expect(@exception.message).to_not eq("You are not authorized to access this page.")
55
+ expect(@exception.message).to_not eq('You are not authorized to access this page.')
56
56
  end
57
57
  end
58
58
  end
@@ -1,8 +1,8 @@
1
- require "spec_helper"
1
+ require 'spec_helper'
2
2
 
3
3
  describe CanCan::InheritedResource do
4
4
  let(:ability) { Ability.new(nil) }
5
- let(:params) { HashWithIndifferentAccess.new(:controller => "models") }
5
+ let(:params) { HashWithIndifferentAccess.new(controller: 'models') }
6
6
  let(:controller_class) { Class.new }
7
7
  let(:controller) { controller_class.new }
8
8
 
@@ -10,7 +10,7 @@ describe CanCan::InheritedResource do
10
10
  class Model
11
11
  attr_accessor :name
12
12
 
13
- def initialize(attributes={})
13
+ def initialize(attributes = {})
14
14
  attributes.each do |attribute, value|
15
15
  send("#{attribute}=", value)
16
16
  end
@@ -19,53 +19,53 @@ describe CanCan::InheritedResource do
19
19
 
20
20
  allow(controller).to receive(:params) { params }
21
21
  allow(controller).to receive(:current_ability) { ability }
22
- allow(controller_class).to receive(:cancan_skipper) { {:authorize => {}, :load => {}} }
22
+ allow(controller_class).to receive(:cancan_skipper) { { authorize: {}, load: {} } }
23
23
  end
24
24
 
25
- it "show loads resource through controller.resource" do
26
- params.merge!(:action => "show", :id => 123)
25
+ it 'show loads resource through controller.resource' do
26
+ params.merge!(action: 'show', id: 123)
27
27
  allow(controller).to receive(:resource) { :model_resource }
28
28
  CanCan::InheritedResource.new(controller).load_resource
29
29
  expect(controller.instance_variable_get(:@model)).to eq(:model_resource)
30
30
  end
31
31
 
32
- it "new loads through controller.build_resource" do
33
- params[:action] = "new"
32
+ it 'new loads through controller.build_resource' do
33
+ params[:action] = 'new'
34
34
  allow(controller).to receive(:build_resource) { :model_resource }
35
35
  CanCan::InheritedResource.new(controller).load_resource
36
36
  expect(controller.instance_variable_get(:@model)).to eq(:model_resource)
37
37
  end
38
38
 
39
- it "index loads through controller.association_chain when parent" do
40
- params[:action] = "index"
39
+ it 'index loads through controller.association_chain when parent' do
40
+ params[:action] = 'index'
41
41
  allow(controller).to receive(:association_chain) { controller.instance_variable_set(:@model, :model_resource) }
42
- CanCan::InheritedResource.new(controller, :parent => true).load_resource
42
+ CanCan::InheritedResource.new(controller, parent: true).load_resource
43
43
  expect(controller.instance_variable_get(:@model)).to eq(:model_resource)
44
44
  end
45
45
 
46
- it "index loads through controller.end_of_association_chain" do
47
- params[:action] = "index"
46
+ it 'index loads through controller.end_of_association_chain' do
47
+ params[:action] = 'index'
48
48
  allow(Model).to receive(:accessible_by).with(ability, :index) { :projects }
49
49
  allow(controller).to receive(:end_of_association_chain) { Model }
50
50
  CanCan::InheritedResource.new(controller).load_resource
51
51
  expect(controller.instance_variable_get(:@models)).to eq(:projects)
52
52
  end
53
53
 
54
- it "builds a new resource with attributes from current ability" do
55
- params[:action] = "new"
56
- ability.can(:create, Model, :name => "from conditions")
54
+ it 'builds a new resource with attributes from current ability' do
55
+ params[:action] = 'new'
56
+ ability.can(:create, Model, name: 'from conditions')
57
57
  allow(controller).to receive(:build_resource) { Struct.new(:name).new }
58
58
  resource = CanCan::InheritedResource.new(controller)
59
59
  resource.load_resource
60
- expect(controller.instance_variable_get(:@model).name).to eq("from conditions")
60
+ expect(controller.instance_variable_get(:@model).name).to eq('from conditions')
61
61
  end
62
62
 
63
- it "overrides initial attributes with params" do
64
- params.merge!(:action => "new", :model => {:name => "from params"})
65
- ability.can(:create, Model, :name => "from conditions")
63
+ it 'overrides initial attributes with params' do
64
+ params.merge!(action: 'new', model: { name: 'from params' })
65
+ ability.can(:create, Model, name: 'from conditions')
66
66
  allow(controller).to receive(:build_resource) { Struct.new(:name).new }
67
67
  resource = CanCan::ControllerResource.new(controller)
68
68
  resource.load_resource
69
- expect(controller.instance_variable_get(:@model).name).to eq("from params")
69
+ expect(controller.instance_variable_get(:@model).name).to eq('from params')
70
70
  end
71
71
  end
@@ -1,29 +1,29 @@
1
- require "spec_helper"
1
+ require 'spec_helper'
2
2
 
3
- describe "be_able_to" do
4
- it "delegates to can?" do
3
+ describe 'be_able_to' do
4
+ it 'delegates to can?' do
5
5
  expect(object = double).to receive(:can?).with(:read, 123) { true }
6
6
  expect(object).to be_able_to(:read, 123)
7
7
  end
8
8
 
9
- it "reports a nice failure message for should" do
9
+ it 'reports a nice failure message for should' do
10
10
  expect(object = double).to receive(:can?).with(:read, 123) { false }
11
- expect {
11
+ expect do
12
12
  expect(object).to be_able_to(:read, 123)
13
- }.to raise_error('expected to be able to :read 123')
13
+ end.to raise_error('expected to be able to :read 123')
14
14
  end
15
15
 
16
- it "reports a nice failure message for should not" do
16
+ it 'reports a nice failure message for should not' do
17
17
  expect(object = double).to receive(:can?).with(:read, 123) { true }
18
- expect {
18
+ expect do
19
19
  expect(object).to_not be_able_to(:read, 123)
20
- }.to raise_error('expected not to be able to :read 123')
20
+ end.to raise_error('expected not to be able to :read 123')
21
21
  end
22
22
 
23
- it "delegates additional arguments to can? and reports in failure message" do
23
+ it 'delegates additional arguments to can? and reports in failure message' do
24
24
  expect(object = double).to receive(:can?).with(:read, 123, 456) { false }
25
- expect {
25
+ expect do
26
26
  expect(object).to be_able_to(:read, 123, 456)
27
- }.to raise_error('expected to be able to :read 123 456')
27
+ end.to raise_error('expected to be able to :read 123 456')
28
28
  end
29
29
  end
@@ -1,24 +1,24 @@
1
- require "spec_helper"
1
+ require 'spec_helper'
2
2
 
3
3
  if defined? CanCan::ModelAdapters::ActiveRecord4Adapter
4
4
  describe CanCan::ModelAdapters::ActiveRecord4Adapter do
5
5
  context 'with sqlite3' do
6
6
  before :each do
7
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
7
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
8
8
  ActiveRecord::Migration.verbose = false
9
9
  ActiveRecord::Schema.define do
10
10
  create_table(:parents) do |t|
11
- t.timestamps :null => false
11
+ t.timestamps null: false
12
12
  end
13
13
 
14
14
  create_table(:children) do |t|
15
- t.timestamps :null => false
15
+ t.timestamps null: false
16
16
  t.integer :parent_id
17
17
  end
18
18
  end
19
19
 
20
20
  class Parent < ActiveRecord::Base
21
- has_many :children, lambda { order(:id => :desc) }
21
+ has_many :children, -> { order(id: :desc) }
22
22
  end
23
23
 
24
24
  class Child < ActiveRecord::Base
@@ -28,18 +28,19 @@ if defined? CanCan::ModelAdapters::ActiveRecord4Adapter
28
28
  (@ability = double).extend(CanCan::Ability)
29
29
  end
30
30
 
31
- it "respects scope on included associations" do
31
+ it 'respects scope on included associations' do
32
32
  @ability.can :read, [Parent, Child]
33
33
 
34
34
  parent = Parent.create!
35
- child1 = Child.create!(:parent => parent, :created_at => 1.hours.ago)
36
- child2 = Child.create!(:parent => parent, :created_at => 2.hours.ago)
35
+ child1 = Child.create!(parent: parent, created_at: 1.hours.ago)
36
+ child2 = Child.create!(parent: parent, created_at: 2.hours.ago)
37
37
 
38
- expect(Parent.accessible_by(@ability).order(:created_at => :asc).includes(:children).first.children).to eq [child2, child1]
38
+ expect(Parent.accessible_by(@ability).order(created_at: :asc).includes(:children).first.children)
39
+ .to eq [child2, child1]
39
40
  end
40
41
 
41
42
  if ActiveRecord::VERSION::MINOR >= 1
42
- it "allows filters on enums" do
43
+ it 'allows filters on enums' do
43
44
  ActiveRecord::Schema.define do
44
45
  create_table(:shapes) do |t|
45
46
  t.integer :color, default: 0, null: false
@@ -57,9 +58,9 @@ if defined? CanCan::ModelAdapters::ActiveRecord4Adapter
57
58
  # A condition with a single value.
58
59
  @ability.can :read, Shape, color: Shape.colors[:green]
59
60
 
60
- expect(@ability.cannot? :read, red).to be true
61
- expect(@ability.can? :read, green).to be true
62
- expect(@ability.cannot? :read, blue).to be true
61
+ expect(@ability.cannot?(:read, red)).to be true
62
+ expect(@ability.can?(:read, green)).to be true
63
+ expect(@ability.cannot?(:read, blue)).to be true
63
64
 
64
65
  accessible = Shape.accessible_by(@ability)
65
66
  expect(accessible).to contain_exactly(green)
@@ -68,15 +69,15 @@ if defined? CanCan::ModelAdapters::ActiveRecord4Adapter
68
69
  @ability.can :update, Shape, color: [Shape.colors[:red],
69
70
  Shape.colors[:blue]]
70
71
 
71
- expect(@ability.can? :update, red).to be true
72
- expect(@ability.cannot? :update, green).to be true
73
- expect(@ability.can? :update, blue).to be true
72
+ expect(@ability.can?(:update, red)).to be true
73
+ expect(@ability.cannot?(:update, green)).to be true
74
+ expect(@ability.can?(:update, blue)).to be true
74
75
 
75
76
  accessible = Shape.accessible_by(@ability, :update)
76
77
  expect(accessible).to contain_exactly(red, blue)
77
78
  end
78
79
 
79
- it "allows dual filter on enums" do
80
+ it 'allows dual filter on enums' do
80
81
  ActiveRecord::Schema.define do
81
82
  create_table(:discs) do |t|
82
83
  t.integer :color, default: 0, null: false
@@ -97,10 +98,10 @@ if defined? CanCan::ModelAdapters::ActiveRecord4Adapter
97
98
  # A condition with a dual filter.
98
99
  @ability.can :read, Disc, color: Disc.colors[:green], shape: Disc.shapes[:rectangle]
99
100
 
100
- expect(@ability.cannot? :read, red_triangle).to be true
101
- expect(@ability.cannot? :read, green_triangle).to be true
102
- expect(@ability.can? :read, green_rectangle).to be true
103
- expect(@ability.cannot? :read, blue_rectangle).to be true
101
+ expect(@ability.cannot?(:read, red_triangle)).to be true
102
+ expect(@ability.cannot?(:read, green_triangle)).to be true
103
+ expect(@ability.can?(:read, green_rectangle)).to be true
104
+ expect(@ability.cannot?(:read, blue_rectangle)).to be true
104
105
 
105
106
  accessible = Disc.accessible_by(@ability)
106
107
  expect(accessible).to contain_exactly(green_rectangle)
@@ -111,24 +112,29 @@ if defined? CanCan::ModelAdapters::ActiveRecord4Adapter
111
112
  if Gem::Specification.find_all_by_name('pg').any?
112
113
  context 'with postgresql' do
113
114
  before :each do
114
- ActiveRecord::Base.establish_connection(:adapter => "postgresql", :database => "postgres", :schema_search_path => 'public')
115
+ ActiveRecord::Base.establish_connection(adapter: 'postgresql',
116
+ database: 'postgres',
117
+ schema_search_path: 'public')
115
118
  ActiveRecord::Base.connection.drop_database('cancan_postgresql_spec')
116
- ActiveRecord::Base.connection.create_database 'cancan_postgresql_spec', 'encoding' => 'utf-8', 'adapter' => 'postgresql'
117
- ActiveRecord::Base.establish_connection(:adapter => "postgresql", :database => "cancan_postgresql_spec")
119
+ ActiveRecord::Base.connection.create_database('cancan_postgresql_spec',
120
+ 'encoding' => 'utf-8',
121
+ 'adapter' => 'postgresql')
122
+ ActiveRecord::Base.establish_connection(adapter: 'postgresql',
123
+ database: 'cancan_postgresql_spec')
118
124
  ActiveRecord::Migration.verbose = false
119
125
  ActiveRecord::Schema.define do
120
126
  create_table(:parents) do |t|
121
- t.timestamps :null => false
127
+ t.timestamps null: false
122
128
  end
123
129
 
124
130
  create_table(:children) do |t|
125
- t.timestamps :null => false
131
+ t.timestamps null: false
126
132
  t.integer :parent_id
127
133
  end
128
134
  end
129
135
 
130
136
  class Parent < ActiveRecord::Base
131
- has_many :children, lambda { order(:id => :desc) }
137
+ has_many :children, -> { order(id: :desc) }
132
138
  end
133
139
 
134
140
  class Child < ActiveRecord::Base
@@ -138,13 +144,13 @@ if defined? CanCan::ModelAdapters::ActiveRecord4Adapter
138
144
  (@ability = double).extend(CanCan::Ability)
139
145
  end
140
146
 
141
- it "allows overlapping conditions in SQL and merge with hash conditions" do
142
- @ability.can :read, Parent, :children => {:parent_id => 1}
143
- @ability.can :read, Parent, :children => {:parent_id => 1}
147
+ it 'allows overlapping conditions in SQL and merge with hash conditions' do
148
+ @ability.can :read, Parent, children: { parent_id: 1 }
149
+ @ability.can :read, Parent, children: { parent_id: 1 }
144
150
 
145
151
  parent = Parent.create!
146
- child1 = Child.create!(:parent => parent, :created_at => 1.hours.ago)
147
- child2 = Child.create!(:parent => parent, :created_at => 2.hours.ago)
152
+ Child.create!(parent: parent, created_at: 1.hours.ago)
153
+ Child.create!(parent: parent, created_at: 2.hours.ago)
148
154
 
149
155
  expect(Parent.accessible_by(@ability)).to eq([parent])
150
156
  end
@@ -1,27 +1,26 @@
1
- require "spec_helper"
1
+ require 'spec_helper'
2
2
 
3
3
  if defined? CanCan::ModelAdapters::ActiveRecordAdapter
4
4
 
5
5
  describe CanCan::ModelAdapters::ActiveRecordAdapter do
6
-
7
6
  before :each do
8
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
7
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
9
8
  ActiveRecord::Migration.verbose = false
10
9
  ActiveRecord::Schema.define do
11
10
  create_table(:categories) do |t|
12
11
  t.string :name
13
12
  t.boolean :visible
14
- t.timestamps :null => false
13
+ t.timestamps null: false
15
14
  end
16
15
 
17
16
  create_table(:projects) do |t|
18
17
  t.string :name
19
- t.timestamps :null => false
18
+ t.timestamps null: false
20
19
  end
21
20
 
22
21
  create_table(:articles) do |t|
23
22
  t.string :name
24
- t.timestamps :null => false
23
+ t.timestamps null: false
25
24
  t.boolean :published
26
25
  t.boolean :secret
27
26
  t.integer :priority
@@ -32,17 +31,17 @@ if defined? CanCan::ModelAdapters::ActiveRecordAdapter
32
31
  create_table(:comments) do |t|
33
32
  t.boolean :spam
34
33
  t.integer :article_id
35
- t.timestamps :null => false
34
+ t.timestamps null: false
36
35
  end
37
36
 
38
37
  create_table(:legacy_mentions) do |t|
39
38
  t.integer :user_id
40
39
  t.integer :article_id
41
- t.timestamps :null => false
40
+ t.timestamps null: false
42
41
  end
43
42
 
44
43
  create_table(:users) do |t|
45
- t.timestamps :null => false
44
+ t.timestamps null: false
46
45
  end
47
46
  end
48
47
 
@@ -57,7 +56,7 @@ if defined? CanCan::ModelAdapters::ActiveRecordAdapter
57
56
  belongs_to :category
58
57
  has_many :comments
59
58
  has_many :mentions
60
- has_many :mentioned_users, :through => :mentions, :source => :user
59
+ has_many :mentioned_users, through: :mentions, source: :user
61
60
  belongs_to :user
62
61
  end
63
62
 
@@ -80,259 +79,270 @@ if defined? CanCan::ModelAdapters::ActiveRecordAdapter
80
79
  @comment_table = Comment.table_name
81
80
  end
82
81
 
83
- it "is for only active record classes" do
82
+ it 'is for only active record classes' do
84
83
  if ActiveRecord.respond_to?(:version) &&
85
- ActiveRecord.version > Gem::Version.new("4")
84
+ ActiveRecord.version > Gem::Version.new('4')
86
85
  expect(CanCan::ModelAdapters::ActiveRecord4Adapter).to_not be_for_class(Object)
87
86
  expect(CanCan::ModelAdapters::ActiveRecord4Adapter).to be_for_class(Article)
88
- expect(CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article)).to eq(CanCan::ModelAdapters::ActiveRecord4Adapter)
87
+ expect(CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article))
88
+ .to eq(CanCan::ModelAdapters::ActiveRecord4Adapter)
89
89
  else
90
90
  expect(CanCan::ModelAdapters::ActiveRecord3Adapter).to_not be_for_class(Object)
91
91
  expect(CanCan::ModelAdapters::ActiveRecord3Adapter).to be_for_class(Article)
92
- expect(CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article)).to eq(CanCan::ModelAdapters::ActiveRecord3Adapter)
92
+ expect(CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article))
93
+ .to eq(CanCan::ModelAdapters::ActiveRecord3Adapter)
93
94
  end
94
95
  end
95
96
 
96
- it "finds record" do
97
+ it 'finds record' do
97
98
  article = Article.create!
98
99
  adapter = CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article)
99
100
  expect(adapter.find(Article, article.id)).to eq(article)
100
101
  end
101
102
 
102
- it "does not fetch any records when no abilities are defined" do
103
+ it 'does not fetch any records when no abilities are defined' do
103
104
  Article.create!
104
105
  expect(Article.accessible_by(@ability)).to be_empty
105
106
  end
106
107
 
107
- it "fetches all articles when one can read all" do
108
+ it 'fetches all articles when one can read all' do
108
109
  @ability.can :read, Article
109
110
  article = Article.create!
110
111
  expect(Article.accessible_by(@ability)).to eq([article])
111
112
  end
112
113
 
113
- it "fetches only the articles that are published" do
114
- @ability.can :read, Article, :published => true
115
- article1 = Article.create!(:published => true)
116
- article2 = Article.create!(:published => false)
114
+ it 'fetches only the articles that are published' do
115
+ @ability.can :read, Article, published: true
116
+ article1 = Article.create!(published: true)
117
+ Article.create!(published: false)
117
118
  expect(Article.accessible_by(@ability)).to eq([article1])
118
119
  end
119
120
 
120
- it "fetches any articles which are published or secret" do
121
- @ability.can :read, Article, :published => true
122
- @ability.can :read, Article, :secret => true
123
- article1 = Article.create!(:published => true, :secret => false)
124
- article2 = Article.create!(:published => true, :secret => true)
125
- article3 = Article.create!(:published => false, :secret => true)
126
- article4 = Article.create!(:published => false, :secret => false)
121
+ it 'fetches any articles which are published or secret' do
122
+ @ability.can :read, Article, published: true
123
+ @ability.can :read, Article, secret: true
124
+ article1 = Article.create!(published: true, secret: false)
125
+ article2 = Article.create!(published: true, secret: true)
126
+ article3 = Article.create!(published: false, secret: true)
127
+ Article.create!(published: false, secret: false)
127
128
  expect(Article.accessible_by(@ability)).to eq([article1, article2, article3])
128
129
  end
129
130
 
130
- it "fetches any articles which we are cited in" do
131
+ it 'fetches any articles which we are cited in' do
131
132
  user = User.create!
132
133
  cited = Article.create!
133
- not_cited = Article.create!
134
+ Article.create!
134
135
  cited.mentioned_users << user
135
- @ability.can :read, Article, { :mentioned_users => { :id => user.id } }
136
- @ability.can :read, Article, { :mentions => { :user_id => user.id } }
136
+ @ability.can :read, Article, mentioned_users: { id: user.id }
137
+ @ability.can :read, Article, mentions: { user_id: user.id }
137
138
  expect(Article.accessible_by(@ability)).to eq([cited])
138
139
  end
139
140
 
140
- it "fetches only the articles that are published and not secret" do
141
- @ability.can :read, Article, :published => true
142
- @ability.cannot :read, Article, :secret => true
143
- article1 = Article.create!(:published => true, :secret => false)
144
- article2 = Article.create!(:published => true, :secret => true)
145
- article3 = Article.create!(:published => false, :secret => true)
146
- article4 = Article.create!(:published => false, :secret => false)
141
+ it 'fetches only the articles that are published and not secret' do
142
+ @ability.can :read, Article, published: true
143
+ @ability.cannot :read, Article, secret: true
144
+ article1 = Article.create!(published: true, secret: false)
145
+ Article.create!(published: true, secret: true)
146
+ Article.create!(published: false, secret: true)
147
+ Article.create!(published: false, secret: false)
147
148
  expect(Article.accessible_by(@ability)).to eq([article1])
148
149
  end
149
150
 
150
- it "only reads comments for articles which are published" do
151
- @ability.can :read, Comment, :article => { :published => true }
152
- comment1 = Comment.create!(:article => Article.create!(:published => true))
153
- comment2 = Comment.create!(:article => Article.create!(:published => false))
151
+ it 'only reads comments for articles which are published' do
152
+ @ability.can :read, Comment, article: { published: true }
153
+ comment1 = Comment.create!(article: Article.create!(published: true))
154
+ Comment.create!(article: Article.create!(published: false))
154
155
  expect(Comment.accessible_by(@ability)).to eq([comment1])
155
156
  end
156
157
 
157
- it "should only read articles which are published or in visible categories" do
158
- @ability.can :read, Article, :category => { :visible => true }
159
- @ability.can :read, Article, :published => true
160
- article1 = Article.create!(:published => true)
161
- article2 = Article.create!(:published => false)
162
- article3 = Article.create!(:published => false, :category => Category.create!(:visible => true))
158
+ it 'should only read articles which are published or in visible categories' do
159
+ @ability.can :read, Article, category: { visible: true }
160
+ @ability.can :read, Article, published: true
161
+ article1 = Article.create!(published: true)
162
+ Article.create!(published: false)
163
+ article3 = Article.create!(published: false, category: Category.create!(visible: true))
163
164
  expect(Article.accessible_by(@ability)).to eq([article1, article3])
164
165
  end
165
166
 
166
- it "should only read categories once even if they have multiple articles" do
167
- @ability.can :read, Category, :articles => { :published => true }
168
- @ability.can :read, Article, :published => true
167
+ it 'should only read categories once even if they have multiple articles' do
168
+ @ability.can :read, Category, articles: { published: true }
169
+ @ability.can :read, Article, published: true
169
170
  category = Category.create!
170
- Article.create!(:published => true, :category => category)
171
- Article.create!(:published => true, :category => category)
171
+ Article.create!(published: true, category: category)
172
+ Article.create!(published: true, category: category)
172
173
  expect(Category.accessible_by(@ability)).to eq([category])
173
174
  end
174
175
 
175
- it "only reads comments for visible categories through articles" do
176
- @ability.can :read, Comment, :article => { :category => { :visible => true } }
177
- comment1 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => true)))
178
- comment2 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => false)))
176
+ it 'only reads comments for visible categories through articles' do
177
+ @ability.can :read, Comment, article: { category: { visible: true } }
178
+ comment1 = Comment.create!(article: Article.create!(category: Category.create!(visible: true)))
179
+ Comment.create!(article: Article.create!(category: Category.create!(visible: false)))
179
180
  expect(Comment.accessible_by(@ability)).to eq([comment1])
180
181
  end
181
182
 
182
- it "allows conditions in SQL and merge with hash conditions" do
183
- @ability.can :read, Article, :published => true
184
- @ability.can :read, Article, ["secret=?", true]
185
- article1 = Article.create!(:published => true, :secret => false)
186
- article2 = Article.create!(:published => true, :secret => true)
187
- article3 = Article.create!(:published => false, :secret => true)
188
- article4 = Article.create!(:published => false, :secret => false)
183
+ it 'allows conditions in SQL and merge with hash conditions' do
184
+ @ability.can :read, Article, published: true
185
+ @ability.can :read, Article, ['secret=?', true]
186
+ article1 = Article.create!(published: true, secret: false)
187
+ article2 = Article.create!(published: true, secret: true)
188
+ article3 = Article.create!(published: false, secret: true)
189
+ Article.create!(published: false, secret: false)
189
190
  expect(Article.accessible_by(@ability)).to eq([article1, article2, article3])
190
191
  end
191
192
 
192
- it "allows a scope for conditions" do
193
- @ability.can :read, Article, Article.where(:secret => true)
194
- article1 = Article.create!(:secret => true)
195
- article2 = Article.create!(:secret => false)
193
+ it 'allows a scope for conditions' do
194
+ @ability.can :read, Article, Article.where(secret: true)
195
+ article1 = Article.create!(secret: true)
196
+ Article.create!(secret: false)
196
197
  expect(Article.accessible_by(@ability)).to eq([article1])
197
198
  end
198
199
 
199
- it "fetches only associated records when using with a scope for conditions" do
200
- @ability.can :read, Article, Article.where(:secret => true)
201
- category1 = Category.create!(:visible => false)
202
- category2 = Category.create!(:visible => true)
203
- article1 = Article.create!(:secret => true, :category => category1)
204
- article2 = Article.create!(:secret => true, :category => category2)
200
+ it 'fetches only associated records when using with a scope for conditions' do
201
+ @ability.can :read, Article, Article.where(secret: true)
202
+ category1 = Category.create!(visible: false)
203
+ category2 = Category.create!(visible: true)
204
+ article1 = Article.create!(secret: true, category: category1)
205
+ Article.create!(secret: true, category: category2)
205
206
  expect(category1.articles.accessible_by(@ability)).to eq([article1])
206
207
  end
207
208
 
208
- it "raises an exception when trying to merge scope with other conditions" do
209
- @ability.can :read, Article, :published => true
210
- @ability.can :read, Article, Article.where(:secret => true)
211
- expect(lambda { Article.accessible_by(@ability) }).to raise_error(CanCan::Error, "Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for read Article ability.")
209
+ it 'raises an exception when trying to merge scope with other conditions' do
210
+ @ability.can :read, Article, published: true
211
+ @ability.can :read, Article, Article.where(secret: true)
212
+ expect(-> { Article.accessible_by(@ability) })
213
+ .to raise_error(CanCan::Error,
214
+ 'Unable to merge an Active Record scope with other conditions. '\
215
+ 'Instead use a hash or SQL for read Article ability.')
212
216
  end
213
217
 
214
- it "does not allow to fetch records when ability with just block present" do
218
+ it 'does not allow to fetch records when ability with just block present' do
215
219
  @ability.can :read, Article do
216
220
  false
217
221
  end
218
- expect(lambda { Article.accessible_by(@ability) }).to raise_error(CanCan::Error)
222
+ expect(-> { Article.accessible_by(@ability) }).to raise_error(CanCan::Error)
219
223
  end
220
224
 
221
- it "should support more than one deeply nested conditions" do
222
- @ability.can :read, Comment, :article => {
223
- :category => {
224
- :name => 'foo', :visible => true
225
+ it 'should support more than one deeply nested conditions' do
226
+ @ability.can :read, Comment, article: {
227
+ category: {
228
+ name: 'foo', visible: true
225
229
  }
226
230
  }
227
231
  expect { Comment.accessible_by(@ability) }.to_not raise_error
228
232
  end
229
233
 
230
- it "does not allow to check ability on object against SQL conditions without block" do
231
- @ability.can :read, Article, ["secret=?", true]
232
- expect(lambda { @ability.can? :read, Article.new }).to raise_error(CanCan::Error)
234
+ it 'does not allow to check ability on object against SQL conditions without block' do
235
+ @ability.can :read, Article, ['secret=?', true]
236
+ expect(-> { @ability.can? :read, Article.new }).to raise_error(CanCan::Error)
233
237
  end
234
238
 
235
- it "has false conditions if no abilities match" do
239
+ it 'has false conditions if no abilities match' do
236
240
  expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='f'")
237
241
  end
238
242
 
239
- it "returns false conditions for cannot clause" do
243
+ it 'returns false conditions for cannot clause' do
240
244
  @ability.cannot :read, Article
241
245
  expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='f'")
242
246
  end
243
247
 
244
- it "returns SQL for single `can` definition in front of default `cannot` condition" do
248
+ it 'returns SQL for single `can` definition in front of default `cannot` condition' do
245
249
  @ability.cannot :read, Article
246
- @ability.can :read, Article, :published => false, :secret => true
247
- expect(@ability.model_adapter(Article, :read).conditions).to orderlessly_match(%Q["#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't'])
250
+ @ability.can :read, Article, published: false, secret: true
251
+ expect(@ability.model_adapter(Article, :read).conditions)
252
+ .to orderlessly_match(%("#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't'))
248
253
  end
249
254
 
250
- it "returns true condition for single `can` definition in front of default `can` condition" do
255
+ it 'returns true condition for single `can` definition in front of default `can` condition' do
251
256
  @ability.can :read, Article
252
- @ability.can :read, Article, :published => false, :secret => true
257
+ @ability.can :read, Article, published: false, secret: true
253
258
  expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='t'")
254
259
  end
255
260
 
256
- it "returns `false condition` for single `cannot` definition in front of default `cannot` condition" do
261
+ it 'returns `false condition` for single `cannot` definition in front of default `cannot` condition' do
257
262
  @ability.cannot :read, Article
258
- @ability.cannot :read, Article, :published => false, :secret => true
263
+ @ability.cannot :read, Article, published: false, secret: true
259
264
  expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='f'")
260
265
  end
261
266
 
262
- it "returns `not (sql)` for single `cannot` definition in front of default `can` condition" do
267
+ it 'returns `not (sql)` for single `cannot` definition in front of default `can` condition' do
263
268
  @ability.can :read, Article
264
- @ability.cannot :read, Article, :published => false, :secret => true
265
- expect(@ability.model_adapter(Article, :read).conditions).to orderlessly_match(%Q["not (#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't')])
269
+ @ability.cannot :read, Article, published: false, secret: true
270
+ expect(@ability.model_adapter(Article, :read).conditions)
271
+ .to orderlessly_match(%["not (#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't')])
266
272
  end
267
273
 
268
- it "returns appropriate sql conditions in complex case" do
274
+ it 'returns appropriate sql conditions in complex case' do
269
275
  @ability.can :read, Article
270
- @ability.can :manage, Article, :id => 1
271
- @ability.can :update, Article, :published => true
272
- @ability.cannot :update, Article, :secret => true
273
- expect(@ability.model_adapter(Article, :update).conditions).to eq(%Q[not ("#{@article_table}"."secret" = 't') AND (("#{@article_table}"."published" = 't') OR ("#{@article_table}"."id" = 1))])
274
- expect(@ability.model_adapter(Article, :manage).conditions).to eq({:id => 1})
276
+ @ability.can :manage, Article, id: 1
277
+ @ability.can :update, Article, published: true
278
+ @ability.cannot :update, Article, secret: true
279
+ expect(@ability.model_adapter(Article, :update).conditions)
280
+ .to eq(%[not ("#{@article_table}"."secret" = 't') ] +
281
+ %[AND (("#{@article_table}"."published" = 't') ] +
282
+ %[OR ("#{@article_table}"."id" = 1))])
283
+ expect(@ability.model_adapter(Article, :manage).conditions).to eq(id: 1)
275
284
  expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='t'")
276
285
  end
277
286
 
278
- it "returns appropriate sql conditions in complex case with nested joins" do
279
- @ability.can :read, Comment, :article => { :category => { :visible => true } }
280
- expect(@ability.model_adapter(Comment, :read).conditions).to eq({ Category.table_name.to_sym => { :visible => true } })
287
+ it 'returns appropriate sql conditions in complex case with nested joins' do
288
+ @ability.can :read, Comment, article: { category: { visible: true } }
289
+ expect(@ability.model_adapter(Comment, :read).conditions).to eq(Category.table_name.to_sym => { visible: true })
281
290
  end
282
291
 
283
- it "returns appropriate sql conditions in complex case with nested joins of different depth" do
284
- @ability.can :read, Comment, :article => { :published => true, :category => { :visible => true } }
285
- expect(@ability.model_adapter(Comment, :read).conditions).to eq({ Article.table_name.to_sym => { :published => true }, Category.table_name.to_sym => { :visible => true } })
292
+ it 'returns appropriate sql conditions in complex case with nested joins of different depth' do
293
+ @ability.can :read, Comment, article: { published: true, category: { visible: true } }
294
+ expect(@ability.model_adapter(Comment, :read).conditions)
295
+ .to eq(Article.table_name.to_sym => { published: true }, Category.table_name.to_sym => { visible: true })
286
296
  end
287
297
 
288
- it "does not forget conditions when calling with SQL string" do
289
- @ability.can :read, Article, :published => true
298
+ it 'does not forget conditions when calling with SQL string' do
299
+ @ability.can :read, Article, published: true
290
300
  @ability.can :read, Article, ['secret=?', false]
291
301
  adapter = @ability.model_adapter(Article, :read)
292
302
  2.times do
293
- expect(adapter.conditions).to eq(%Q[(secret='f') OR ("#{@article_table}"."published" = 't')])
303
+ expect(adapter.conditions).to eq(%[(secret='f') OR ("#{@article_table}"."published" = 't')])
294
304
  end
295
305
  end
296
306
 
297
- it "has nil joins if no rules" do
307
+ it 'has nil joins if no rules' do
298
308
  expect(@ability.model_adapter(Article, :read).joins).to be_nil
299
309
  end
300
310
 
301
- it "has nil joins if no nested hashes specified in conditions" do
302
- @ability.can :read, Article, :published => false
303
- @ability.can :read, Article, :secret => true
311
+ it 'has nil joins if no nested hashes specified in conditions' do
312
+ @ability.can :read, Article, published: false
313
+ @ability.can :read, Article, secret: true
304
314
  expect(@ability.model_adapter(Article, :read).joins).to be_nil
305
315
  end
306
316
 
307
- it "merges separate joins into a single array" do
308
- @ability.can :read, Article, :project => { :blocked => false }
309
- @ability.can :read, Article, :company => { :admin => true }
317
+ it 'merges separate joins into a single array' do
318
+ @ability.can :read, Article, project: { blocked: false }
319
+ @ability.can :read, Article, company: { admin: true }
310
320
  expect(@ability.model_adapter(Article, :read).joins.inspect).to orderlessly_match([:company, :project].inspect)
311
321
  end
312
322
 
313
- it "merges same joins into a single array" do
314
- @ability.can :read, Article, :project => { :blocked => false }
315
- @ability.can :read, Article, :project => { :admin => true }
323
+ it 'merges same joins into a single array' do
324
+ @ability.can :read, Article, project: { blocked: false }
325
+ @ability.can :read, Article, project: { admin: true }
316
326
  expect(@ability.model_adapter(Article, :read).joins).to eq([:project])
317
327
  end
318
328
 
319
- it "merges nested and non-nested joins" do
320
- @ability.can :read, Article, :project => { :blocked => false }
321
- @ability.can :read, Article, :project => { :comments => { :spam => true } }
322
- expect(@ability.model_adapter(Article, :read).joins).to eq([{:project=>[:comments]}])
329
+ it 'merges nested and non-nested joins' do
330
+ @ability.can :read, Article, project: { blocked: false }
331
+ @ability.can :read, Article, project: { comments: { spam: true } }
332
+ expect(@ability.model_adapter(Article, :read).joins).to eq([{ project: [:comments] }])
323
333
  end
324
334
 
325
- it "merges :all conditions with other conditions" do
335
+ it 'merges :all conditions with other conditions' do
326
336
  user = User.create!
327
- article = Article.create!(:user => user)
337
+ article = Article.create!(user: user)
328
338
  ability = Ability.new(user)
329
339
  ability.can :manage, :all
330
- ability.can :manage, Article, :user_id => user.id
340
+ ability.can :manage, Article, user_id: user.id
331
341
  expect(Article.accessible_by(ability)).to eq([article])
332
342
  end
333
343
 
334
344
  it 'should not execute a scope when checking ability on the class' do
335
- relation = Article.where(:secret => true)
345
+ relation = Article.where(secret: true)
336
346
  @ability.can :read, Article, relation do |article|
337
347
  article.secret == true
338
348
  end
@@ -342,17 +352,17 @@ if defined? CanCan::ModelAdapters::ActiveRecordAdapter
342
352
  expect { @ability.can? :read, Article }.not_to raise_error
343
353
  end
344
354
 
345
- context "with namespaced models" do
355
+ context 'with namespaced models' do
346
356
  before :each do
347
357
  ActiveRecord::Schema.define do
348
- create_table( :table_xes ) do |t|
349
- t.timestamps :null => false
358
+ create_table(:table_xes) do |t|
359
+ t.timestamps null: false
350
360
  end
351
361
 
352
- create_table( :table_zs ) do |t|
362
+ create_table(:table_zs) do |t|
353
363
  t.integer :table_x_id
354
364
  t.integer :user_id
355
- t.timestamps :null => false
365
+ t.timestamps null: false
356
366
  end
357
367
  end
358
368
 
@@ -369,14 +379,14 @@ if defined? CanCan::ModelAdapters::ActiveRecordAdapter
369
379
  end
370
380
  end
371
381
 
372
- it "fetches all namespace::table_x when one is related by table_y" do
382
+ it 'fetches all namespace::table_x when one is related by table_y' do
373
383
  user = User.create!
374
384
 
375
385
  ability = Ability.new(user)
376
- ability.can :read, Namespace::TableX, :table_zs => { :user_id => user.id }
386
+ ability.can :read, Namespace::TableX, table_zs: { user_id: user.id }
377
387
 
378
388
  table_x = Namespace::TableX.create!
379
- table_z = table_x.table_zs.create( :user => user )
389
+ table_x.table_zs.create(user: user)
380
390
  expect(Namespace::TableX.accessible_by(ability)).to eq([table_x])
381
391
  end
382
392
  end
@@ -384,7 +394,7 @@ if defined? CanCan::ModelAdapters::ActiveRecordAdapter
384
394
  context 'when conditions are non iterable ranges' do
385
395
  before :each do
386
396
  ActiveRecord::Schema.define do
387
- create_table( :courses ) do |t|
397
+ create_table(:courses) do |t|
388
398
  t.datetime :start_at
389
399
  end
390
400
  end
@@ -394,9 +404,9 @@ if defined? CanCan::ModelAdapters::ActiveRecordAdapter
394
404
  end
395
405
 
396
406
  it 'fetches only the valid records' do
397
- @ability.can :read, Course, :start_at => 1.day.ago..1.day.from_now
398
- Course.create!(:start_at => 10.days.ago)
399
- valid_course = Course.create!(:start_at => Time.now)
407
+ @ability.can :read, Course, start_at: 1.day.ago..1.day.from_now
408
+ Course.create!(start_at: 10.days.ago)
409
+ valid_course = Course.create!(start_at: Time.now)
400
410
 
401
411
  expect(Course.accessible_by(@ability)).to eq([valid_course])
402
412
  end