diff --git a/docs/scoping.md b/docs/scoping.md index 30677c7..8f195b5 100644 --- a/docs/scoping.md +++ b/docs/scoping.md @@ -37,6 +37,29 @@ class PostPolicy < ApplicationPolicy end ``` +Or just pass class name where included logic of scope to `relation_scope` and write `.call` method: + +```ruby +class PostsController < ApplicationController + def index + @posts = authorized_scope(Post.all) + end +end + +class PostPolicy < ApplicationPolicy + relation_scope AuthorizedPosts +end + +class AuthorizedPosts + class << self + def call(policy, relation) + user = policy.user + user.admin? ? relation : relation.where(user: user) + end + end +end +``` + ## Define scopes To define scope you should use either `scope_for` or `smth_scope` methods in your policy: diff --git a/lib/action_policy/policy/scoping.rb b/lib/action_policy/policy/scoping.rb index e1b5e9d..df7aff6 100644 --- a/lib/action_policy/policy/scoping.rb +++ b/lib/action_policy/policy/scoping.rb @@ -117,7 +117,7 @@ def scope_for(type, name = :default, callable = nil, &block) name, callable = prepare_args(name, callable) mid = :"__scoping__#{type}__#{name}" - block = ->(target) { callable.call(target) } if callable + block = ->(target) { callable.call(self, target) } if callable define_method(mid, &block) diff --git a/test/action_policy/rails/scope_matchers_test.rb b/test/action_policy/rails/scope_matchers_test.rb index 4130b4a..5a5f20a 100644 --- a/test/action_policy/rails/scope_matchers_test.rb +++ b/test/action_policy/rails/scope_matchers_test.rb @@ -154,6 +154,96 @@ def test_named_filtered_params_admin end end +class TestRailsScopeMatchersViaClass < ActionController::TestCase + class AuthorizedPosts + class << self + def call(policy, relation) + user = policy.user + scope = user.admin? ? relation : relation.where(user: user) + scope.order(:title) + end + end + end + + class PostPolicy < ActionPolicy::Base + relation_scope AuthorizedPosts + end + + class PostsController < ActionController::Base + authorize :user, through: :current_user + + def index + render json: authorized_scope(AR::Post.all) + end + + private + + def current_user + @current_user ||= AR::User.find(params[:user_id]) + end + end + + tests PostsController + + attr_reader :admin, :guest + + def setup + ActiveRecord::Base.connection.begin_transaction(joinable: false) + @guest = AR::User.create!(name: "Jack") + @admin = AR::User.create!(name: "John", role: "admin") + end + + def teardown + ActiveRecord::Base.connection.rollback_transaction + end + + def json_body + @json_body ||= JSON.parse(response.body) + end + + def test_authorized_relation_guest + get :index, params: {user_id: guest.id} + + assert_equal 0, json_body.size + end + + def test_authorized_relation_admin + get :index, params: {user_id: admin.id} + + assert_equal 0, json_body.size + end + + def test_authorized_association + AR::Post.create!(title: "[wip]", user: admin) + AR::Post.create!(title: "Good news!", user: guest) + + get :index, params: {user_id: guest.id} + + assert_equal 1, json_body.size + assert_equal "Good news!", json_body.first["title"] + end + + def test_authorized_association_2 + AR::Post.create!(title: "[wip]", user: admin) + AR::Post.create!(title: "Good news!", user: guest) + + get :index, params: {user_id: admin.id} + + assert_equal 2, json_body.size + assert_equal ["Good news!", "[wip]"], json_body.map { _1["title"] } + end + + def test_authorized_association_3 + AR::Post.create!(title: "[wip]", user: guest) + AR::Post.create!(title: "Admin news", user: admin) + AR::Post.create!(title: "Good news!", user: guest) + + get :index, params: {user_id: guest.id} + + assert_equal 2, json_body.size + end +end + # See https://github.com/palkan/action_policy/issues/101 class TestRelationMutability < ActionController::TestCase class UserPolicy < ActionPolicy::Base