|
1 | 1 | # frozen_string_literal: true |
2 | 2 |
|
3 | | -require "active_support/concern" |
4 | | -require "active_support/core_ext/class/attribute" |
| 3 | +require "active_support/callbacks" |
| 4 | +require "active_support/core_ext/array/wrap" |
5 | 5 | require "active_support/core_ext/module/redefine_method" |
6 | 6 | require "active_support/core_ext/object/blank" |
7 | 7 |
|
8 | 8 | require_relative "before/version" |
9 | 9 |
|
10 | 10 | module Pundit |
11 | 11 | module Before |
12 | | - # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength |
| 12 | + # rubocop:disable Metrics/MethodLength |
13 | 13 | def self.included(base) |
14 | 14 | base.extend ClassMethods |
| 15 | + base.include ActiveSupport::Callbacks |
15 | 16 |
|
16 | 17 | base.class_eval do |
17 | | - class_attribute :_pundit_before, default: [] |
| 18 | + define_callbacks :_pundit_before, skip_after_callbacks_if_terminated: true |
18 | 19 |
|
19 | 20 | def self.method_added(method_name) |
20 | 21 | super |
21 | 22 |
|
22 | | - return if @_pundit_before_running |
23 | 23 | return unless method_name.to_s =~ /.*\?$/ && public_method_defined?(method_name) |
| 24 | + return if @_pundit_before_running |
24 | 25 |
|
25 | 26 | @_pundit_before_running = true |
26 | 27 |
|
27 | 28 | old_method = instance_method(method_name) |
28 | 29 |
|
29 | 30 | redefine_method(method_name) do |
30 | | - result = catch :halt do |
31 | | - (_pundit_before.presence || []).each do |name, block| |
32 | | - name.present? ? send(name) : instance_eval(&block) |
33 | | - end |
34 | | - nil |
35 | | - end |
36 | | - |
37 | | - result.nil? ? old_method.bind(self).call : result |
| 31 | + @_pundit_before_result = nil |
| 32 | + @_pundit_before_method = method_name |
| 33 | + |
| 34 | + run_callbacks :_pundit_before |
| 35 | + |
| 36 | + @_pundit_before_result.nil? ? old_method.bind(self).call : @_pundit_before_result |
38 | 37 | end |
39 | 38 |
|
40 | 39 | @_pundit_before_running = false |
41 | 40 | end |
42 | 41 | end |
43 | 42 | end |
44 | | - # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength |
| 43 | + # rubocop:enable Metrics/MethodLength |
| 44 | + |
| 45 | + class CallbackFilter |
| 46 | + def initialize(methods) |
| 47 | + @methods = Array(methods).map(&:to_sym) |
| 48 | + end |
| 49 | + |
| 50 | + def match?(object) |
| 51 | + @methods.include?(object.instance_variable_get(:@_pundit_before_method).to_sym) |
| 52 | + end |
| 53 | + |
| 54 | + alias after match? |
| 55 | + alias before match? |
| 56 | + alias around match? |
| 57 | + end |
45 | 58 |
|
46 | 59 | module ClassMethods |
47 | | - def before(method_name=nil, &block) |
48 | | - self._pundit_before = _pundit_before.dup.push([method_name, block]) |
| 60 | + def before(*method_names, **options, &block) |
| 61 | + _normalize_callback_options(options) |
| 62 | + |
| 63 | + if block_given? |
| 64 | + set_callback :_pundit_before, :before, **options, &block |
| 65 | + else |
| 66 | + set_callback :_pundit_before, :before, *method_names, **options |
| 67 | + end |
| 68 | + end |
| 69 | + |
| 70 | + def skip_before(*method_names, **options) |
| 71 | + _normalize_callback_options(options) |
| 72 | + skip_callback :_pundit_before, :before, *method_names, **options |
| 73 | + end |
| 74 | + |
| 75 | + def _normalize_callback_options(options) |
| 76 | + _normalize_callback_option(options, :only, :if) |
| 77 | + _normalize_callback_option(options, :except, :unless) |
| 78 | + end |
| 79 | + |
| 80 | + def _normalize_callback_option(options, from, to) |
| 81 | + return unless (from = options.delete(from)) |
| 82 | + |
| 83 | + from = CallbackFilter.new(from) |
| 84 | + options[to] = Array(options[to]).unshift(from) |
49 | 85 | end |
50 | 86 | end |
51 | 87 |
|
52 | 88 | def allow! |
53 | | - throw :halt, true |
| 89 | + @_pundit_before_result = true |
| 90 | + throw :abort |
54 | 91 | end |
55 | 92 |
|
56 | 93 | def deny! |
57 | | - throw :halt, false |
| 94 | + @_pundit_before_result = false |
| 95 | + throw :abort |
58 | 96 | end |
59 | 97 | end |
60 | 98 | end |
0 commit comments