Skip to content

Commit

Permalink
parent c426bb2
Browse files Browse the repository at this point in the history
author Remo Fritzsche <remo.fritzsche@sitrox.com> 1612868567 +0100
committer Alessandro Rodi <coorasse@gmail.com> 1657114188 +0200

# This is a combination of 4 commits.
# This is the 1st commit message:

Preserve nil values as extra arguments

# This is the commit message #2:

Fix link (#744)

# This is the commit message #3:

Documentation fixes and improvements

- fix typo, punctuations, and grammar
- fix broken and misformatted links
- fix code sample

# This is the commit message #4:

Format documentation/guides and fix linting issues

The motivation of the changes is to make these documents neater when
reading offline using a text or code editor.

Reading the documentation offline is faster, and the source code is
readily available for further learning.

The changes do not affect the meaning of each documentation or
instruction.

The following changes were made:
- remove extra and trailing spaces
- add new lines to separate the title, explanation, code samples, etc
- fix headings, links
- specify code block language (bash, ruby)

Updated Devise.md as exception is changed

CanCan::Unathorized exception not exists anymore, CanCan::AccessDenied is used. Also added responses for different content types as a recommendation.

Add support for non-hash conditions

Co-authored-by: Juleffel <juleffel@protonmail.com>

Improve ability checks with Hashes

Add Ruby 3.0 and Ruby 3.1 to CI

Drop ActiveRecord4 from CI (#778)
  • Loading branch information
sudoremo authored and coorasse committed Jul 6, 2022
1 parent c426bb2 commit e307e9d
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 15 deletions.
2 changes: 1 addition & 1 deletion docs/accessible_attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ CanCanCan gives you the possibility to define actions on single instances' attri
Given you want users to only read a user first name and last name you can define:

```ruby
can :read, User, [:first_name, :last_name]
can :read, User, :first_name, :last_name
```

and check it with:
Expand Down
4 changes: 2 additions & 2 deletions docs/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ end

## Issue Tracker

If you are still unable to resolve the issue, [open a question on Stackoverflow](https://stackoverflow.com/questions/ask?tags=cancancan) with tag
[cancancan](https://stackoverflow.com/questions/tagged/cancancan).
If you are still unable to resolve the issue, [open a question on Stackoverflow](http://stackoverflow.com/questions/ask?tags=cancancan) with tag
[cancancan](http://stackoverflow.com/questions/tagged/cancancan).
16 changes: 13 additions & 3 deletions lib/cancan/conditions_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ def subject_class?(subject)
[Class, Module].include? klass
end

def matches_block_conditions(subject, *extra_args)
def matches_block_conditions(subject, attribute, *extra_args)
return @base_behavior if subject_class?(subject)

@block.call(subject, *extra_args.compact)
if attribute
@block.call(subject, attribute, *extra_args)
else
@block.call(subject, *extra_args)
end
end

def matches_non_block_conditions(subject)
Expand Down Expand Up @@ -72,7 +76,13 @@ def matches_all_conditions?(adapter, subject, conditions)
end
end

def matches_hash_conditions(adapter, subject, conditions)
# conditions can be a collection, object, or hash
def matches_all_conditions?(adapter, conditions, subject)
unless conditions.is_a?(Hash)
return conditions.include?(subject) if conditions.respond_to?(:include?)
return subject == conditions
end

conditions.all? do |name, value|
if adapter.override_condition_matching?(subject, name, value)
adapter.matches_condition?(subject, name, value)
Expand Down
29 changes: 20 additions & 9 deletions spec/cancan/ability_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,17 @@
expect(@block_called).to be(true)
end

it 'allows passing nil as extra arguments' do
@ability.can :to_s, Integer do |integer, arg_1, arg_2|
expect(integer).to eq(42)
expect(arg_1).to eq(nil)
expect(arg_2).to eq(:foo)
@block_called = true
end
@ability.can?(:to_s, 42, nil, nil, :foo)
expect(@block_called).to be(true)
end

it 'passes nil to object when comparing class with can check' do
@ability.can do |action, object_class, object|
expect(action).to eq(:foo)
Expand Down Expand Up @@ -832,35 +843,35 @@ class Account

describe 'when #can? is used with a Hash (nested resources)' do
it 'is unauthorized with no rules' do
expect(@ability.can?(:read, 1 => Symbol)).to be(false)
expect(@ability.can?(:read, {1 => Symbol})).to be(false)
end

it 'is authorized when the child is authorized' do
@ability.can :read, Symbol
expect(@ability.can?(:read, 1 => Symbol)).to be(true)
expect(@ability.can?(:read, {1 => Symbol})).to be(true)
end

it 'is authorized when the condition doesn\'t concern the parent' do
@ability.can :read, Symbol, whatever: true
expect(@ability.can?(:read, 1 => Symbol)).to be(true)
expect(@ability.can?(:read, {1 => Symbol})).to be(true)
end

it 'verifies the parent against an equality condition' do
@ability.can :read, Symbol, integer: 1
expect(@ability.can?(:read, 1 => Symbol)).to be(true)
expect(@ability.can?(:read, 2 => Symbol)).to be(false)
expect(@ability.can?(:read, {1 => Symbol})).to be(true)
expect(@ability.can?(:read, {2 => Symbol})).to be(false)
end

it 'verifies the parent against an array condition' do
@ability.can :read, Symbol, integer: [0, 1]
expect(@ability.can?(:read, 1 => Symbol)).to be(true)
expect(@ability.can?(:read, 2 => Symbol)).to be(false)
expect(@ability.can?(:read, {1 => Symbol})).to be(true)
expect(@ability.can?(:read, {2 => Symbol})).to be(false)
end

it 'verifies the parent against a hash condition' do
@ability.can :read, Symbol, integer: { to_i: 1 }
expect(@ability.can?(:read, 1 => Symbol)).to be(true)
expect(@ability.can?(:read, 2 => Symbol)).to be(false)
expect(@ability.can?(:read, {1 => Symbol})).to be(true)
expect(@ability.can?(:read, {2 => Symbol})).to be(false)
end
end
end
31 changes: 31 additions & 0 deletions spec/cancan/model_adapters/accessible_by_integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,35 @@ class Editor < ActiveRecord::Base
end
end
end

describe 'accessing posts through user' do
it 'works with user_id' do
ability.can :read, User
ability.can :read, Post, user_id: @user1.id
expect(ability.can?(:read, {@user1 => Post})).to be(true)
expect(ability.can?(:read, {@user2 => Post})).to be(true)
end

it 'works with user: {id: ...}' do
ability.can :read, User
ability.can :read, Post, user: { id: @user1.id }
expect(ability.can?(:read, {@user1 => Post})).to be(true)
expect(ability.can?(:read, {@user2 => Post})).to be(true)
end

it 'works with cannot user_id' do
ability.can :read, User
ability.cannot :read, Post, user_id: @user1.id
expect(ability.can?(:read, {@user1 => Post})).to be(false)
expect(ability.can?(:read, {@user2 => Post})).to be(true)
end

it 'works with cannot user: {id: ...}' do
ability.can :read, User
ability.cannot :read, Post, user: { id: @user1.id }
expect(ability.can?(:read, {@user1 => Post})).to be(false)
expect(ability.can?(:read, {@user2 => Post})).to be(true)
end
end

end

0 comments on commit e307e9d

Please sign in to comment.