action_policy 0.5.7 → 0.6.2

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: 021e6da5ccd46f76732cb52c0c04ae1a45ce3a74a2b64643e179ee16c40417e3
4
- data.tar.gz: 96a978308fde0160f5b739d47abdd833005ba7109e97837ac0dd5721f1ec52a8
3
+ metadata.gz: 491af624707f597ded52f7d1cacdfc07435937250370804c0ac374f436b494ce
4
+ data.tar.gz: 33ac6abf6907738c6fce67193659e4fdb6615a2dd78a375dba4b57060c9d8f6e
5
5
  SHA512:
6
- metadata.gz: 69953fedcbaa8d007c3c922e17dd26618eda2ed840d59bac476111f62bb7aae7c20f2920606925eb4b3acab7e90f994a6e1a9a55b7de0a0740f3747bb4e85aa3
7
- data.tar.gz: 23de3c82949db85f3a41f079187493087add27f88e0cd9315ccb492e925f4490390d5ae17fb493eb3ae290f5469f216ab258e21e7c155be9853de5728c8b3311
6
+ metadata.gz: 54e1dffffae9777c07d47f5718143cfbc546d6d100e873aadc3a51a1e8821f6495a7d869a477ac7d9d65be1ef92e1fef8bb9adde6c5177186c63eebb8dc88a94
7
+ data.tar.gz: 2b866340538957aa93c5db05a9c8bc7181b9cc4b9b178a137b58a13ad0d7a5158904b911834ab5dd2cf5af4d29bca29d7003095144c7786583375ec8d273c0d5
data/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.6.2 (2022-08-12)
6
+
7
+ - Allow omitting authorization record if `with` is provided. ([@palkan][])
8
+
9
+ ## 0.6.1 (2022-05-23)
10
+
11
+ - Fix policy lookup when a namespaced record is passed and the strict mode is used. ([@palkan][])
12
+ - Expose `#authorized_scope` as helper. ([@palkan][])
13
+ - [Fixes [#207](https://github.com/palkan/action_policy/issues/207)] refinement#include deprecation warning on Ruby 3.1
14
+
15
+ ## 0.6.0 (2021-09-02)
16
+
17
+ - Drop Ruby 2.5 support.
18
+ - [Closes [#186](https://github.com/palkan/action_policy/issues/186)] Add `inline_reasons: true` option to `allowed_to?` to avoid wrapping reasons. ([@palkan][])
19
+ - [Fixes [#173](https://github.com/palkan/action_policy/issues/173)] Explicit context were not merged with implicit one within policy classes. ([@palkan][])
20
+ - Add `strict_namespace:` option to policy_for behaviour ([@kevynlebouille][])
21
+ - Prevent possible side effects in policy lookup ([@tomdalling][])
22
+
5
23
  ## 0.5.7 (2021-03-03)
6
24
 
7
25
  The previous release had incorrect dependencies (due to the missing transpiled files).
@@ -449,3 +467,4 @@ This value is now stored in a cache (if any) instead of just the call result (`t
449
467
  [@Be-ngt-oH]: https://github.com/Be-ngt-oH
450
468
  [@pirj]: https://github.com/pirj
451
469
  [@skojin]: https://github.com/skojin
470
+ [@tomdalling]: https://github.com/tomdalling
@@ -8,16 +8,18 @@ module ActionPolicy
8
8
  using ActionPolicy::Ext::PolicyCacheKey
9
9
 
10
10
  # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: authorization_context, allow_nil: false, default: default_authorization_policy_class)
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
+ context = context ? authorization_context.merge(context) : authorization_context
13
+
12
14
  policy_class = with || ::ActionPolicy.lookup(
13
15
  record,
14
- namespace: namespace, context: context, allow_nil: allow_nil, default: default
16
+ namespace: namespace, context: context, allow_nil: allow_nil, default: default, strict_namespace: strict_namespace
15
17
  )
16
18
  policy_class&.new(record, **context)
17
19
  end
18
20
 
19
21
  def authorization_context
20
- raise NotImplementedError, "Please, define `authorization_context` method!"
22
+ Kernel.raise NotImplementedError, "Please, define `authorization_context` method!"
21
23
  end
22
24
 
23
25
  def authorization_namespace
@@ -28,6 +30,10 @@ module ActionPolicy
28
30
  # override to provide a policy class use when no policy found
29
31
  end
30
32
 
33
+ def authorization_strict_namespace
34
+ # override to provide strict namespace lookup option
35
+ end
36
+
31
37
  # Override this method to provide implicit authorization target
32
38
  # that would be used in case `record` is not specified in
33
39
  # `authorize!` and `allowed_to?` call.
@@ -39,7 +45,7 @@ module ActionPolicy
39
45
 
40
46
  # Return implicit authorization target or raises an exception if it's nil
41
47
  def implicit_authorization_target!
42
- implicit_authorization_target || raise(
48
+ implicit_authorization_target || Kernel.raise(
43
49
  NotFound,
44
50
  [
45
51
  self,
@@ -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
@@ -133,13 +133,13 @@ module ActionPolicy
133
133
  end
134
134
 
135
135
  # An alias for readability purposes
136
- def check?(*args) ; allowed_to?(*args); end
136
+ def check?(*args, **hargs) ; allowed_to?(*args, **hargs); end
137
137
 
138
138
  # Returns a rule name (policy method name) for activity.
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)
@@ -31,6 +31,20 @@ module ActionPolicy
31
31
 
32
32
  def present?() ; !empty?; end
33
33
 
34
+ def merge(other)
35
+ other.reasons.each do |policy_class, rules|
36
+ reasons[policy_class] ||= []
37
+
38
+ rules.each do |rule|
39
+ if rule.is_a?(::Hash)
40
+ add_detailed_reason(reasons[policy_class], rule)
41
+ else
42
+ add_non_detailed_reason(reasons[policy_class], rule)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
34
48
  private
35
49
 
36
50
  def add_non_detailed_reason(store, rule)
@@ -182,7 +196,7 @@ module ActionPolicy
182
196
  result.details ||= {}
183
197
  end
184
198
 
185
- def allowed_to?(rule, record = :__undef__, **options)
199
+ def allowed_to?(rule, record = :__undef__, inline_reasons: false, **options)
186
200
  res =
187
201
  if (record == :__undef__ || record == self.record) && options.empty?
188
202
  rule = resolve_rule(rule)
@@ -196,7 +210,9 @@ module ActionPolicy
196
210
  policy.result
197
211
  end
198
212
 
199
- result&.reasons&.add(policy, rule, res.details) if res.fail?
213
+ if res.fail? && result&.reasons
214
+ inline_reasons ? result.reasons.merge(res.reasons) : result.reasons.add(policy, rule, res.details)
215
+ end
200
216
 
201
217
  res.clear_details
202
218
 
@@ -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
@@ -8,16 +8,18 @@ module ActionPolicy
8
8
  using ActionPolicy::Ext::PolicyCacheKey
9
9
 
10
10
  # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: authorization_context, allow_nil: false, default: default_authorization_policy_class)
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
+ context = context ? authorization_context.merge(context) : authorization_context
13
+
12
14
  policy_class = with || ::ActionPolicy.lookup(
13
15
  record,
14
- namespace: namespace, context: context, allow_nil: allow_nil, default: default
16
+ namespace: namespace, context: context, allow_nil: allow_nil, default: default, strict_namespace: strict_namespace
15
17
  )
16
18
  policy_class&.new(record, **context)
17
19
  end
18
20
 
19
21
  def authorization_context
20
- raise NotImplementedError, "Please, define `authorization_context` method!"
22
+ Kernel.raise NotImplementedError, "Please, define `authorization_context` method!"
21
23
  end
22
24
 
23
25
  def authorization_namespace
@@ -28,6 +30,10 @@ module ActionPolicy
28
30
  # override to provide a policy class use when no policy found
29
31
  end
30
32
 
33
+ def authorization_strict_namespace
34
+ # override to provide strict namespace lookup option
35
+ end
36
+
31
37
  # Override this method to provide implicit authorization target
32
38
  # that would be used in case `record` is not specified in
33
39
  # `authorize!` and `allowed_to?` call.
@@ -39,7 +45,7 @@ module ActionPolicy
39
45
 
40
46
  # Return implicit authorization target or raises an exception if it's nil
41
47
  def implicit_authorization_target!
42
- implicit_authorization_target || raise(
48
+ implicit_authorization_target || Kernel.raise(
43
49
  NotFound,
44
50
  [
45
51
  self,
@@ -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
+ 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,13 +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 record == :__undef__
78
- raise ArgumentError, "Record must be specified" if record.nil?
77
+ def lookup_authorization_policy(record, with: nil, **options) # :nodoc:
78
+ if record == :__undef__
79
+ record =
80
+ if with
81
+ implicit_authorization_target
82
+ else
83
+ implicit_authorization_target!
84
+ end
85
+ end
79
86
 
80
- options[:context] && (options[:context] = authorization_context.merge(options[:context]))
87
+ Kernel.raise ArgumentError, "Record or policy must be specified" if record.nil? && with.nil?
81
88
 
82
- policy_for(record: record, **options)
89
+ policy_for(record: record, with: with, **options)
83
90
  end
84
91
 
85
92
  module ClassMethods # :nodoc:
@@ -8,16 +8,18 @@ module ActionPolicy
8
8
  using ActionPolicy::Ext::PolicyCacheKey
9
9
 
10
10
  # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: authorization_context, allow_nil: false, default: default_authorization_policy_class)
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
+ context = context ? authorization_context.merge(context) : authorization_context
13
+
12
14
  policy_class = with || ::ActionPolicy.lookup(
13
15
  record,
14
- namespace:, context:, allow_nil:, default:
16
+ namespace:, context:, allow_nil:, default:, strict_namespace:
15
17
  )
16
18
  policy_class&.new(record, **context)
17
19
  end
18
20
 
19
21
  def authorization_context
20
- raise NotImplementedError, "Please, define `authorization_context` method!"
22
+ Kernel.raise NotImplementedError, "Please, define `authorization_context` method!"
21
23
  end
22
24
 
23
25
  def authorization_namespace
@@ -28,6 +30,10 @@ module ActionPolicy
28
30
  # override to provide a policy class use when no policy found
29
31
  end
30
32
 
33
+ def authorization_strict_namespace
34
+ # override to provide strict namespace lookup option
35
+ end
36
+
31
37
  # Override this method to provide implicit authorization target
32
38
  # that would be used in case `record` is not specified in
33
39
  # `authorize!` and `allowed_to?` call.
@@ -39,7 +45,7 @@ module ActionPolicy
39
45
 
40
46
  # Return implicit authorization target or raises an exception if it's nil
41
47
  def implicit_authorization_target!
42
- implicit_authorization_target || raise(
48
+ implicit_authorization_target || Kernel.raise(
43
49
  NotFound,
44
50
  [
45
51
  self,
@@ -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
@@ -133,13 +133,13 @@ module ActionPolicy
133
133
  end
134
134
 
135
135
  # An alias for readability purposes
136
- def check?(*args) = allowed_to?(*args)
136
+ def check?(*args, **hargs) = allowed_to?(*args, **hargs)
137
137
 
138
138
  # Returns a rule name (policy method name) for activity.
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)
@@ -31,6 +31,20 @@ module ActionPolicy
31
31
 
32
32
  def present?() = !empty?
33
33
 
34
+ def merge(other)
35
+ other.reasons.each do |policy_class, rules|
36
+ reasons[policy_class] ||= []
37
+
38
+ rules.each do |rule|
39
+ if rule.is_a?(::Hash)
40
+ add_detailed_reason(reasons[policy_class], rule)
41
+ else
42
+ add_non_detailed_reason(reasons[policy_class], rule)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
34
48
  private
35
49
 
36
50
  def add_non_detailed_reason(store, rule)
@@ -182,7 +196,7 @@ module ActionPolicy
182
196
  result.details ||= {}
183
197
  end
184
198
 
185
- def allowed_to?(rule, record = :__undef__, **options)
199
+ def allowed_to?(rule, record = :__undef__, inline_reasons: false, **options)
186
200
  res =
187
201
  if (record == :__undef__ || record == self.record) && options.empty?
188
202
  rule = resolve_rule(rule)
@@ -196,7 +210,9 @@ module ActionPolicy
196
210
  policy.result
197
211
  end
198
212
 
199
- result&.reasons&.add(policy, rule, res.details) if res.fail?
213
+ if res.fail? && result&.reasons
214
+ inline_reasons ? result.reasons.merge(res.reasons) : result.reasons.add(policy, rule, res.details)
215
+ end
200
216
 
201
217
  res.clear_details
202
218
 
@@ -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
@@ -57,7 +60,7 @@ module ActionPolicy
57
60
  end
58
61
 
59
62
  def verify_authorized
60
- raise UnauthorizedAction.new(controller_path, action_name) if
63
+ Kernel.raise UnauthorizedAction.new(controller_path, action_name) if
61
64
  authorize_count.zero? && !verify_authorized_skipped
62
65
  end
63
66
 
@@ -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.5.7"
4
+ VERSION = "0.6.2"
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.5.7
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-04 00:00:00.000000000 Z
11
+ date: 2022-08-12 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
@@ -222,7 +226,7 @@ metadata:
222
226
  documentation_uri: https://actionpolicy.evilmartians.io/
223
227
  homepage_uri: https://actionpolicy.evilmartians.io/
224
228
  source_code_uri: http://github.com/palkan/action_policy
225
- post_install_message:
229
+ post_install_message:
226
230
  rdoc_options: []
227
231
  require_paths:
228
232
  - lib
@@ -230,15 +234,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
230
234
  requirements:
231
235
  - - ">="
232
236
  - !ruby/object:Gem::Version
233
- version: 2.5.0
237
+ version: 2.6.0
234
238
  required_rubygems_version: !ruby/object:Gem::Requirement
235
239
  requirements:
236
240
  - - ">="
237
241
  - !ruby/object:Gem::Version
238
242
  version: '0'
239
243
  requirements: []
240
- rubygems_version: 3.0.6
241
- signing_key:
244
+ rubygems_version: 3.3.11
245
+ signing_key:
242
246
  specification_version: 4
243
247
  summary: Authorization framework for Ruby/Rails application
244
248
  test_files: []