Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support combining scopes when loading records, use Arel instead of SQL #717

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
52 changes: 0 additions & 52 deletions Appraisals
Original file line number Diff line number Diff line change
@@ -1,55 +1,3 @@
appraise 'activerecord_4.2.0' do
gem 'activerecord', '~> 4.2.0', require: 'active_record'
gem 'activesupport', '~> 4.2.0', require: 'active_support/all'
gem 'actionpack', '~> 4.2.0', require: 'action_pack'
gem 'nokogiri', '~> 1.6.8', require: 'nokogiri' # TODO: fix for ruby 2.0.0

gemfile.platforms :jruby do
gem 'activerecord-jdbcsqlite3-adapter', '~> 1.3.24'
gem 'jdbc-sqlite3'
gem 'jdbc-postgres'
end

gemfile.platforms :ruby, :mswin, :mingw do
gem 'sqlite3', '~> 1.3.0'
gem 'pg', '~> 0.21'
end
end

appraise 'activerecord_5.0.2' do
gem 'activerecord', '~> 5.0.2', require: 'active_record'
gem 'activesupport', '~> 5.0.2', require: 'active_support/all'
gem 'actionpack', '~> 5.0.2', require: 'action_pack'

gemfile.platforms :jruby do
gem 'activerecord-jdbcsqlite3-adapter'
gem 'jdbc-sqlite3'
gem 'jdbc-postgres'
end

gemfile.platforms :ruby, :mswin, :mingw do
gem 'sqlite3', '~> 1.3.0'
gem 'pg', '~> 0.21'
end
end

appraise 'activerecord_5.1.0' do
gem 'activerecord', '~> 5.1.0', require: 'active_record'
gem 'activesupport', '~> 5.1.0', require: 'active_support/all'
gem 'actionpack', '~> 5.1.0', require: 'action_pack'

gemfile.platforms :jruby do
gem 'activerecord-jdbcsqlite3-adapter'
gem 'jdbc-sqlite3'
gem 'jdbc-postgres'
end

gemfile.platforms :ruby, :mswin, :mingw do
gem 'sqlite3', '~> 1.3.0'
gem 'pg', '~> 0.21'
end
end

appraise 'activerecord_5.2.2' do
gem 'activerecord', '~> 5.2.2', require: 'active_record'
gem 'activesupport', '~> 5.2.2', require: 'active_support/all'
Expand Down
10 changes: 5 additions & 5 deletions docs/Fetching-Records.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Sometimes you need to restrict which records are returned from the database base
You can change the action by passing it as the second argument. Here we find only the records the user has permission to update.

```ruby
@articles = Article.accessible_by(current_ability, :update)
@articles = Article.accessible_by(current_ability, :update)
```

If you want to use the current controller's action, make sure to call `to_sym` on it:
Expand All @@ -17,7 +17,7 @@ If you want to use the current controller's action, make sure to call `to_sym` o
@articles = Article.accessible_by(current_ability, params[:action].to_sym)
```

This is an Active Record scope so other scopes and pagination can be chained onto it.
This is an Active Record scope so other scopes and pagination can be chained onto it. It can also be chained onto existing scopes.

This works with multiple `can` definitions, which allows you to define complex permission logic and have it translated properly to SQL.

Expand All @@ -29,19 +29,19 @@ class Ability
can :manage, User, id: user.id
end
```
a call to User.accessible_by(current_ability) generates the following SQL
a call to User.accessible_by(current_ability) generates the following or equivalent SQL, depending on your version of ActiveRecord.

```sql
SELECT *
FROM users
WHERE (id = 1) OR (not (self_managed = 't') AND (manager_id = 1))
```

It will raise an exception if any requested model's ability definition is defined using just block.
It will raise an exception if any requested model's ability definition is defined using just block.
You can define SQL fragment in addition to block (look for more examples in [[Defining Abilities with Blocks]]).

If you are using something other than Active Record you can fetch the conditions hash directly from the current ability.

```ruby
current_ability.model_adapter(TargetClass, :read).conditions
```
```
2 changes: 0 additions & 2 deletions lib/cancan.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,4 @@
require 'cancan/model_adapters/conditions_normalizer'
require 'cancan/model_adapters/sti_normalizer'
require 'cancan/model_adapters/active_record_adapter'
require 'cancan/model_adapters/active_record_4_adapter'
require 'cancan/model_adapters/active_record_5_adapter'
end
5 changes: 3 additions & 2 deletions lib/cancan/ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,10 @@ def validate_target(target)
raise Error, error_message if aliased_actions.values.flatten.include? target
end

def model_adapter(model_class, action)
def model_adapter(relation_or_class, action)
model_class = relation_or_class.respond_to?(:klass) ? relation_or_class.klass : relation_or_class
adapter_class = ModelAdapters::AbstractAdapter.adapter_class(model_class)
adapter_class.new(model_class, relevant_rules_for_query(action, model_class))
adapter_class.new(relation_or_class, relevant_rules_for_query(action, model_class))
end

# See ControllerAdditions#authorize! for documentation.
Expand Down
34 changes: 4 additions & 30 deletions lib/cancan/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

module CanCan
def self.valid_accessible_by_strategies
strategies = [:left_join]
strategies << :subquery unless does_not_support_subquery_strategy?
strategies
%i[left_join subquery]
end

# Determines how CanCan should build queries when calling accessible_by,
Expand All @@ -27,31 +25,18 @@ def self.accessible_by_strategy
end

def self.default_accessible_by_strategy
if does_not_support_subquery_strategy?
# see https://github.com/CanCanCommunity/cancancan/pull/655 for where this was added
# the `subquery` strategy (from https://github.com/CanCanCommunity/cancancan/pull/619
# only works in Rails 5 and higher
:left_join
else
:subquery
end
# see https://github.com/CanCanCommunity/cancancan/pull/655 for where this was added
# the `subquery` strategy (from https://github.com/CanCanCommunity/cancancan/pull/619
:left_join
end

def self.accessible_by_strategy=(value)
validate_accessible_by_strategy!(value)

if value == :subquery && does_not_support_subquery_strategy?
raise ArgumentError, 'accessible_by_strategy = :subquery requires ActiveRecord 5 or newer'
end

@accessible_by_strategy = value
end

def self.with_accessible_by_strategy(value)
return yield if value == accessible_by_strategy

validate_accessible_by_strategy!(value)

begin
strategy_was = accessible_by_strategy
@accessible_by_strategy = value
Expand All @@ -60,15 +45,4 @@ def self.with_accessible_by_strategy(value)
@accessible_by_strategy = strategy_was
end
end

def self.validate_accessible_by_strategy!(value)
return if valid_accessible_by_strategies.include?(value)

raise ArgumentError, "accessible_by_strategy must be one of #{valid_accessible_by_strategies.join(', ')}"
end

def self.does_not_support_subquery_strategy?
!defined?(CanCan::ModelAdapters::ActiveRecordAdapter) ||
CanCan::ModelAdapters::ActiveRecordAdapter.version_lower?('5.0.0')
end
end
62 changes: 0 additions & 62 deletions lib/cancan/model_adapters/active_record_4_adapter.rb

This file was deleted.

65 changes: 0 additions & 65 deletions lib/cancan/model_adapters/active_record_5_adapter.rb

This file was deleted.

Loading