action_policy 0.6.0 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0fe2b2b40f6a3bd85c312495209382b1ee72950072257e676be008e2ef8d77c5
4
- data.tar.gz: f0c9b0cb38bc130cdb8c7f3f4cfba3adcc326f25c6435fe75723e8c0d3ba3fe9
3
+ metadata.gz: 611389adf65ee131b0b5b272885fe0ba6a7066330a1e9b97d7f0b9cb8dd47530
4
+ data.tar.gz: f2f29bf49216ef6406b408935250fc845619d14657d8956d7a20a6344b8a3032
5
5
  SHA512:
6
- metadata.gz: f49f02d335942aa0a100e681f7570c1b6d042e68d1ed9d02012ec84b9f354b5c0a1d7a42ba15fbd3c84c84536e444af95057a09ef00cdc6ae3c2f5d0b24dd3a6
7
- data.tar.gz: 2eced066c406feb11e8cdf99113fa8d27f8fe73ff015bcc1d2958c0a657540d195b25a8328dee02206b8539e55d16278f531dcde6b9567fef95ec1bead92cda0
6
+ metadata.gz: 86baaf070430319da764d5287542f418a32f9b28870518bddeb6c25ad3875ea14482993c2d0d185750e1dea36ad45263182f285f893b9a44a95f29dcd734f653
7
+ data.tar.gz: 6fbb1736b962bd2b80d24ffe2ada43ced3bf4324f5d4790d6ca39f7f1c5a654bffcde93caca846c6ce49ce5a1d68a299fd050edda595cdc114bd552de83ac1d5
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.6.3 (2022-08-16)
6
+
7
+ - Fix regression for [#179](https://github.com/palkan/action_policy/issues/179). ([@palkan][])
8
+
9
+ ## 0.6.2 (2022-08-12)
10
+
11
+ - Allow omitting authorization record if `with` is provided. ([@palkan][])
12
+
13
+ ## 0.6.1 (2022-05-23)
14
+
15
+ - Fix policy lookup when a namespaced record is passed and the strict mode is used. ([@palkan][])
16
+ - Expose `#authorized_scope` as helper. ([@palkan][])
17
+ - [Fixes [#207](https://github.com/palkan/action_policy/issues/207)] refinement#include deprecation warning on Ruby 3.1
18
+
5
19
  ## 0.6.0 (2021-09-02)
6
20
 
7
21
  - Drop Ruby 2.5 support.
@@ -23,7 +23,7 @@ module ActionPolicy
23
23
  end
24
24
 
25
25
  refine Object do
26
- include ObjectExt
26
+ ::RubyNext::Core.import_methods(ObjectExt, binding)
27
27
  end
28
28
 
29
29
  refine NilClass do
@@ -86,7 +86,7 @@ module ActionPolicy
86
86
  @result = self.class.result_class.new(self.class, rule)
87
87
 
88
88
  catch :policy_fulfilled do
89
- result.load __apply__(rule)
89
+ result.load __apply__(resolve_rule(rule))
90
90
  end
91
91
 
92
92
  result.value
@@ -139,7 +139,7 @@ module ActionPolicy
139
139
  #
140
140
  # By default, rule name is equal to activity name.
141
141
  #
142
- # Raises ActionPolicy::UknownRule when rule is not found in policy.
142
+ # Raises ActionPolicy::UnknownRule when rule is not found in policy.
143
143
  def resolve_rule(activity)
144
144
  raise UnknownRule.new(self, activity) unless
145
145
  respond_to?(activity)
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_policy/testing"
4
+
5
+ module ActionPolicy
6
+ module RSpec
7
+ # Policy rule alias matcher `be_an_alias_of`.
8
+ #
9
+ # Verifies that for given policy a policy rule has an alias.
10
+ #
11
+ # Example:
12
+ #
13
+ # # in policy specs
14
+ # subject(:policy) { described_class.new(record, user: user) }
15
+ #
16
+ # let(:user) { build_stubbed(:user) }
17
+ # let(:record) { build_stubbed(:post) }
18
+ #
19
+ # describe "#show?" do
20
+ # it "is an alias of :index? policy rule" do
21
+ # expect(:show?).to be_an_alias_of(policy, :index?)
22
+ # end
23
+ # end
24
+ #
25
+ # # negated version
26
+ # describe "#show?" do
27
+ # it "is not an alias of :index? policy rule" do
28
+ # expect(:show?).to_not be_an_alias_of(policy, :index?)
29
+ # end
30
+ # end
31
+ #
32
+ class BeAnAliasOf < ::RSpec::Matchers::BuiltIn::BaseMatcher
33
+ attr_reader :policy, :rule, :actual
34
+
35
+ def initialize(policy, rule)
36
+ @policy = policy
37
+ @rule = rule
38
+ end
39
+
40
+ def match(_expected, actual)
41
+ policy.resolve_rule(actual) == rule
42
+ end
43
+
44
+ def does_not_match?(actual)
45
+ @actual = actual
46
+ policy.resolve_rule(actual) != rule
47
+ end
48
+
49
+ def supports_block_expectations?() ; false; end
50
+
51
+ def failure_message
52
+ "expected #{policy}##{actual} " \
53
+ "to be an alias of #{policy}##{rule}"
54
+ end
55
+
56
+ def failure_message_when_negated
57
+ "expected #{policy}##{actual} " \
58
+ "to not be an alias of #{policy}##{rule}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ RSpec.configure do |config|
65
+ config.include(Module.new do
66
+ def be_an_alias_of(policy, rule)
67
+ ActionPolicy::RSpec::BeAnAliasOf.new(policy, rule)
68
+ end
69
+ end)
70
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPolicy
4
+ module Ext
5
+ # Add Module#namespace method
6
+ module ModuleNamespace # :nodoc: all
7
+ unless "".respond_to?(:safe_constantize)
8
+ require "action_policy/ext/string_constantize"
9
+ using ActionPolicy::Ext::StringConstantize
10
+ end
11
+
12
+ module Ext
13
+ def namespace
14
+ return unless name&.match?(/[^^]::/)
15
+
16
+ name.sub(/::[^:]+$/, "").safe_constantize
17
+ end
18
+ end
19
+
20
+ # See https://github.com/jruby/jruby/issues/5220
21
+ ::Module.include(Ext) if RUBY_PLATFORM.match?(/java/i)
22
+
23
+ refine Module do
24
+ if RUBY_VERSION <= "2.7.0"
25
+ include Ext
26
+ else
27
+ ::RubyNext::Core.import_methods(Ext, binding)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPolicy
4
+ module Ext
5
+ # Adds #_policy_cache_key method to Object,
6
+ # which just call #policy_cache_key or #cache_key
7
+ # or #object_id (if `use_object_id` parameter is set to true).
8
+ #
9
+ # For other core classes returns string representation.
10
+ #
11
+ # Raises ArgumentError otherwise.
12
+ module PolicyCacheKey # :nodoc: all
13
+ module ObjectExt
14
+ def _policy_cache_key(use_object_id: false)
15
+ return policy_cache_key if respond_to?(:policy_cache_key)
16
+ return cache_key_with_version if respond_to?(:cache_key_with_version)
17
+ return cache_key if respond_to?(:cache_key)
18
+
19
+ return object_id.to_s if use_object_id == true
20
+
21
+ raise ArgumentError, "object is not cacheable"
22
+ end
23
+ end
24
+
25
+ refine Object do
26
+ ::RubyNext::Core.import_methods(ObjectExt, binding)
27
+ end
28
+
29
+ refine NilClass do
30
+ def _policy_cache_key(*) = ""
31
+ end
32
+
33
+ refine TrueClass do
34
+ def _policy_cache_key(*) = "t"
35
+ end
36
+
37
+ refine FalseClass do
38
+ def _policy_cache_key(*) = "f"
39
+ end
40
+
41
+ refine String do
42
+ def _policy_cache_key(*) = self
43
+ end
44
+
45
+ refine Symbol do
46
+ def _policy_cache_key(*) = to_s
47
+ end
48
+
49
+ if RUBY_PLATFORM.match?(/java/i)
50
+ refine Integer do
51
+ def _policy_cache_key(*) = to_s
52
+ end
53
+
54
+ refine Float do
55
+ def _policy_cache_key(*) = to_s
56
+ end
57
+ else
58
+ refine Numeric do
59
+ def _policy_cache_key(*) = to_s
60
+ end
61
+ end
62
+
63
+ refine Time do
64
+ def _policy_cache_key(*) = to_s
65
+ end
66
+
67
+ refine Module do
68
+ def _policy_cache_key(*) = name
69
+ end
70
+ end
71
+ end
72
+ end
@@ -58,10 +58,11 @@ module ActionPolicy
58
58
  end
59
59
 
60
60
  def authorization_context
61
- return @__authorization_context if
62
- instance_variable_defined?(:@__authorization_context)
61
+ @_authorization_context ||= build_authorization_context
62
+ end
63
63
 
64
- @__authorization_context = self.class.authorization_targets
64
+ private def build_authorization_context
65
+ self.class.authorization_targets
65
66
  .each_with_object({}) do |(key, meth), obj|
66
67
  obj[key] = send(meth)
67
68
  end
@@ -73,11 +74,19 @@ module ActionPolicy
73
74
  policy.resolve_rule(rule)
74
75
  end
75
76
 
76
- def lookup_authorization_policy(record, **options) # :nodoc:
77
- record = implicit_authorization_target! if :__undef__ == record # rubocop:disable Style/YodaCondition See https://github.com/palkan/action_policy/pull/180
78
- Kernel.raise ArgumentError, "Record must be specified" if record.nil?
77
+ def lookup_authorization_policy(record, with: nil, **options) # :nodoc:
78
+ if :__undef__ == record # rubocop:disable Style/YodaCondition
79
+ record =
80
+ if with
81
+ implicit_authorization_target
82
+ else
83
+ implicit_authorization_target!
84
+ end
85
+ end
86
+
87
+ Kernel.raise ArgumentError, "Record or policy must be specified" if record.nil? && with.nil?
79
88
 
80
- policy_for(record: record, **options)
89
+ policy_for(record: record, with: with, **options)
81
90
  end
82
91
 
83
92
  module ClassMethods # :nodoc:
@@ -21,7 +21,11 @@ module ActionPolicy
21
21
  ::Module.include(Ext) if RUBY_PLATFORM.match?(/java/i)
22
22
 
23
23
  refine Module do
24
- include Ext
24
+ if RUBY_VERSION <= "2.7.0"
25
+ include Ext
26
+ else
27
+ import_methods Ext
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -23,7 +23,7 @@ module ActionPolicy
23
23
  end
24
24
 
25
25
  refine Object do
26
- include ObjectExt
26
+ import_methods ObjectExt
27
27
  end
28
28
 
29
29
  refine NilClass do
@@ -67,12 +67,20 @@ module ActionPolicy
67
67
  def lookup_within_namespace(policy_name, namespace, strict: false)
68
68
  NamespaceCache.fetch(namespace&.name || "Kernel", policy_name, strict: strict) do
69
69
  mod = namespace
70
+ policy_class = nil
71
+
70
72
  loop do
71
- policy = [mod&.name, policy_name].compact.join("::").safe_constantize
72
- break policy if policy || mod.nil? || strict
73
+ policy_class = [mod&.name, policy_name].compact.join("::").safe_constantize
74
+ break policy_class if policy_class || mod.nil?
73
75
 
74
76
  mod = mod.namespace
75
77
  end
78
+
79
+ next policy_class if !strict || namespace.nil? || policy_class.nil?
80
+
81
+ # If we're in the strict mode and the namespace boundary is provided,
82
+ # we must check that the found policy satisfies it
83
+ policy_class if policy_class.name.start_with?("#{namespace.name}::")
76
84
  end
77
85
  end
78
86
 
@@ -66,7 +66,7 @@ module ActionPolicy
66
66
  allow_nil ||= optional
67
67
 
68
68
  ids.each do |id|
69
- authorization_targets[id] = {allow_nil, optional}
69
+ authorization_targets[id] = {allow_nil:, optional:}
70
70
  end
71
71
 
72
72
  attr_reader(*ids)
@@ -86,7 +86,7 @@ module ActionPolicy
86
86
  @result = self.class.result_class.new(self.class, rule)
87
87
 
88
88
  catch :policy_fulfilled do
89
- result.load __apply__(rule)
89
+ result.load __apply__(resolve_rule(rule))
90
90
  end
91
91
 
92
92
  result.value
@@ -139,7 +139,7 @@ module ActionPolicy
139
139
  #
140
140
  # By default, rule name is equal to activity name.
141
141
  #
142
- # Raises ActionPolicy::UknownRule when rule is not found in policy.
142
+ # Raises ActionPolicy::UnknownRule when rule is not found in policy.
143
143
  def resolve_rule(activity)
144
144
  raise UnknownRule.new(self, activity) unless
145
145
  respond_to?(activity)
@@ -23,7 +23,10 @@ module ActionPolicy
23
23
  include ActionPolicy::Behaviours::Namespaced
24
24
 
25
25
  included do
26
- helper_method :allowed_to? if respond_to?(:helper_method)
26
+ if respond_to?(:helper_method)
27
+ helper_method :allowed_to?
28
+ helper_method :authorized_scope
29
+ end
27
30
 
28
31
  attr_writer :authorize_count
29
32
  attr_reader :verify_authorized_skipped
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_policy/testing"
4
+
5
+ module ActionPolicy
6
+ module RSpec
7
+ # Policy rule alias matcher `be_an_alias_of`.
8
+ #
9
+ # Verifies that for given policy a policy rule has an alias.
10
+ #
11
+ # Example:
12
+ #
13
+ # # in policy specs
14
+ # subject(:policy) { described_class.new(record, user: user) }
15
+ #
16
+ # let(:user) { build_stubbed(:user) }
17
+ # let(:record) { build_stubbed(:post) }
18
+ #
19
+ # describe "#show?" do
20
+ # it "is an alias of :index? policy rule" do
21
+ # expect(:show?).to be_an_alias_of(policy, :index?)
22
+ # end
23
+ # end
24
+ #
25
+ # # negated version
26
+ # describe "#show?" do
27
+ # it "is not an alias of :index? policy rule" do
28
+ # expect(:show?).to_not be_an_alias_of(policy, :index?)
29
+ # end
30
+ # end
31
+ #
32
+ class BeAnAliasOf < ::RSpec::Matchers::BuiltIn::BaseMatcher
33
+ attr_reader :policy, :rule, :actual
34
+
35
+ def initialize(policy, rule)
36
+ @policy = policy
37
+ @rule = rule
38
+ end
39
+
40
+ def match(_expected, actual)
41
+ policy.resolve_rule(actual) == rule
42
+ end
43
+
44
+ def does_not_match?(actual)
45
+ @actual = actual
46
+ policy.resolve_rule(actual) != rule
47
+ end
48
+
49
+ def supports_block_expectations?() = false
50
+
51
+ def failure_message
52
+ "expected #{policy}##{actual} " \
53
+ "to be an alias of #{policy}##{rule}"
54
+ end
55
+
56
+ def failure_message_when_negated
57
+ "expected #{policy}##{actual} " \
58
+ "to not be an alias of #{policy}##{rule}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ RSpec.configure do |config|
65
+ config.include(Module.new do
66
+ def be_an_alias_of(policy, rule)
67
+ ActionPolicy::RSpec::BeAnAliasOf.new(policy, rule)
68
+ end
69
+ end)
70
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "action_policy/rspec/be_an_alias_of"
3
4
  require "action_policy/rspec/be_authorized_to"
4
5
  require "action_policy/rspec/have_authorized_scope"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionPolicy
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-02 00:00:00.000000000 Z
11
+ date: 2022-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-next-core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.11.0
19
+ version: 0.14.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.11.0
26
+ version: 0.14.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: ammeter
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -133,9 +133,6 @@ files:
133
133
  - LICENSE.txt
134
134
  - README.md
135
135
  - config/rubocop-rspec.yml
136
- - lib/.rbnext/1995.next/action_policy/behaviours/policy_for.rb
137
- - lib/.rbnext/1995.next/action_policy/behaviours/scoping.rb
138
- - lib/.rbnext/1995.next/action_policy/policy/authorization.rb
139
136
  - lib/.rbnext/1995.next/action_policy/utils/pretty_print.rb
140
137
  - lib/.rbnext/2.7/action_policy/behaviours/policy_for.rb
141
138
  - lib/.rbnext/2.7/action_policy/i18n.rb
@@ -153,10 +150,16 @@ files:
153
150
  - lib/.rbnext/3.0/action_policy/policy/execution_result.rb
154
151
  - lib/.rbnext/3.0/action_policy/policy/pre_check.rb
155
152
  - lib/.rbnext/3.0/action_policy/policy/reasons.rb
153
+ - lib/.rbnext/3.0/action_policy/rspec/be_an_alias_of.rb
156
154
  - lib/.rbnext/3.0/action_policy/rspec/be_authorized_to.rb
157
155
  - lib/.rbnext/3.0/action_policy/rspec/have_authorized_scope.rb
158
156
  - lib/.rbnext/3.0/action_policy/utils/pretty_print.rb
159
157
  - lib/.rbnext/3.0/action_policy/utils/suggest_message.rb
158
+ - lib/.rbnext/3.1/action_policy/behaviours/policy_for.rb
159
+ - lib/.rbnext/3.1/action_policy/behaviours/scoping.rb
160
+ - lib/.rbnext/3.1/action_policy/ext/module_namespace.rb
161
+ - lib/.rbnext/3.1/action_policy/ext/policy_cache_key.rb
162
+ - lib/.rbnext/3.1/action_policy/policy/authorization.rb
160
163
  - lib/action_policy.rb
161
164
  - lib/action_policy/authorizer.rb
162
165
  - lib/action_policy/base.rb
@@ -194,6 +197,7 @@ files:
194
197
  - lib/action_policy/rails/scope_matchers/active_record.rb
195
198
  - lib/action_policy/railtie.rb
196
199
  - lib/action_policy/rspec.rb
200
+ - lib/action_policy/rspec/be_an_alias_of.rb
197
201
  - lib/action_policy/rspec/be_authorized_to.rb
198
202
  - lib/action_policy/rspec/dsl.rb
199
203
  - lib/action_policy/rspec/have_authorized_scope.rb
@@ -237,7 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
237
241
  - !ruby/object:Gem::Version
238
242
  version: '0'
239
243
  requirements: []
240
- rubygems_version: 3.2.15
244
+ rubygems_version: 3.3.11
241
245
  signing_key:
242
246
  specification_version: 4
243
247
  summary: Authorization framework for Ruby/Rails application