action_policy 0.2.4 → 0.3.0.beta1

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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +26 -64
  3. data/.travis.yml +13 -10
  4. data/CHANGELOG.md +216 -1
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +1 -1
  7. data/Rakefile +10 -0
  8. data/action_policy.gemspec +5 -3
  9. data/benchmarks/namespaced_lookup_cache.rb +18 -22
  10. data/docs/README.md +3 -3
  11. data/docs/_sidebar.md +4 -0
  12. data/docs/aliases.md +9 -5
  13. data/docs/authorization_context.md +59 -1
  14. data/docs/behaviour.md +113 -0
  15. data/docs/caching.md +6 -4
  16. data/docs/custom_policy.md +1 -2
  17. data/docs/debugging.md +55 -0
  18. data/docs/decorators.md +27 -0
  19. data/docs/i18n.md +41 -2
  20. data/docs/instrumentation.md +70 -2
  21. data/docs/lookup_chain.md +5 -4
  22. data/docs/namespaces.md +1 -1
  23. data/docs/non_rails.md +2 -3
  24. data/docs/pundit_migration.md +77 -2
  25. data/docs/quick_start.md +5 -5
  26. data/docs/rails.md +5 -2
  27. data/docs/reasons.md +50 -3
  28. data/docs/scoping.md +262 -0
  29. data/docs/testing.md +232 -21
  30. data/docs/writing_policies.md +1 -1
  31. data/gemfiles/jruby.gemfile +3 -0
  32. data/gemfiles/rails42.gemfile +3 -0
  33. data/gemfiles/rails6.gemfile +8 -0
  34. data/gemfiles/railsmaster.gemfile +1 -1
  35. data/lib/action_policy.rb +3 -3
  36. data/lib/action_policy/authorizer.rb +12 -4
  37. data/lib/action_policy/base.rb +2 -0
  38. data/lib/action_policy/behaviour.rb +14 -3
  39. data/lib/action_policy/behaviours/memoized.rb +1 -1
  40. data/lib/action_policy/behaviours/policy_for.rb +12 -3
  41. data/lib/action_policy/behaviours/scoping.rb +32 -0
  42. data/lib/action_policy/behaviours/thread_memoized.rb +1 -1
  43. data/lib/action_policy/ext/hash_transform_keys.rb +19 -0
  44. data/lib/action_policy/ext/module_namespace.rb +1 -1
  45. data/lib/action_policy/ext/policy_cache_key.rb +2 -1
  46. data/lib/action_policy/ext/proc_case_eq.rb +14 -0
  47. data/lib/action_policy/ext/string_constantize.rb +1 -0
  48. data/lib/action_policy/ext/symbol_classify.rb +22 -0
  49. data/lib/action_policy/i18n.rb +56 -0
  50. data/lib/action_policy/lookup_chain.rb +21 -3
  51. data/lib/action_policy/policy/cache.rb +10 -6
  52. data/lib/action_policy/policy/core.rb +31 -19
  53. data/lib/action_policy/policy/execution_result.rb +12 -0
  54. data/lib/action_policy/policy/pre_check.rb +2 -6
  55. data/lib/action_policy/policy/reasons.rb +99 -12
  56. data/lib/action_policy/policy/scoping.rb +165 -0
  57. data/lib/action_policy/rails/authorizer.rb +20 -0
  58. data/lib/action_policy/rails/controller.rb +4 -14
  59. data/lib/action_policy/rails/ext/active_record.rb +10 -0
  60. data/lib/action_policy/rails/policy/instrumentation.rb +24 -0
  61. data/lib/action_policy/rails/scope_matchers/action_controller_params.rb +19 -0
  62. data/lib/action_policy/rails/scope_matchers/active_record.rb +29 -0
  63. data/lib/action_policy/railtie.rb +29 -7
  64. data/lib/action_policy/rspec.rb +1 -0
  65. data/lib/action_policy/rspec/be_authorized_to.rb +1 -1
  66. data/lib/action_policy/rspec/dsl.rb +103 -0
  67. data/lib/action_policy/rspec/have_authorized_scope.rb +126 -0
  68. data/lib/action_policy/rspec/pundit_syntax.rb +1 -1
  69. data/lib/action_policy/test_helper.rb +69 -4
  70. data/lib/action_policy/testing.rb +54 -0
  71. data/lib/action_policy/utils/pretty_print.rb +137 -0
  72. data/lib/action_policy/utils/suggest_message.rb +21 -0
  73. data/lib/action_policy/version.rb +1 -1
  74. metadata +58 -11
@@ -24,11 +24,50 @@ module ActionPolicy
24
24
  end
25
25
  end
26
26
 
27
+ class Scoping # :nodoc:
28
+ attr_reader :policy, :target, :type, :name, :scope_options
29
+
30
+ def initialize(policy, target, type, name, scope_options)
31
+ @policy = policy
32
+ @target = target
33
+ @type = type
34
+ @name = name
35
+ @scope_options = scope_options
36
+ end
37
+
38
+ def matches?(policy_class, actual_type, actual_name, actual_scope_options)
39
+ policy_class == policy.class &&
40
+ type == actual_type &&
41
+ name == actual_name &&
42
+ actual_scope_options === scope_options
43
+ end
44
+
45
+ def inspect
46
+ "#{policy.class} :#{name} for :#{type} #{scope_options_message}"
47
+ end
48
+
49
+ private
50
+
51
+ def scope_options_message
52
+ if scope_options
53
+ if defined?(::RSpec::Matchers::Composable) &&
54
+ scope_options.is_a?(::RSpec::Matchers::Composable)
55
+ "with scope options #{scope_options.description}"
56
+ else
57
+ "with scope options #{scope_options}"
58
+ end
59
+ else
60
+ "without scope options"
61
+ end
62
+ end
63
+ end
64
+
27
65
  class << self
28
66
  # Wrap code under inspection into this method
29
67
  # to track authorize! calls
30
68
  def tracking
31
69
  calls.clear
70
+ scopings.clear
32
71
  Thread.current[:__action_policy_tracking] = true
33
72
  yield
34
73
  ensure
@@ -41,10 +80,20 @@ module ActionPolicy
41
80
  calls << Call.new(policy, rule)
42
81
  end
43
82
 
83
+ # Called from Authorizer
84
+ def track_scope(target, policy, type:, name:, scope_options:)
85
+ return unless tracking?
86
+ scopings << Scoping.new(policy, target, type, name, scope_options)
87
+ end
88
+
44
89
  def calls
45
90
  Thread.current[:__action_policy_calls] ||= []
46
91
  end
47
92
 
93
+ def scopings
94
+ Thread.current[:__action_policy_scopings] ||= []
95
+ end
96
+
48
97
  def tracking?
49
98
  Thread.current[:__action_policy_tracking] == true
50
99
  end
@@ -57,6 +106,11 @@ module ActionPolicy
57
106
  AuthorizeTracker.track(*args)
58
107
  super
59
108
  end
109
+
110
+ def scopify(*args)
111
+ AuthorizeTracker.track_scope(*args)
112
+ super
113
+ end
60
114
  end
61
115
 
62
116
  ActionPolicy::Authorizer.singleton_class.prepend(AuthorizerExt)
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ old_verbose = $VERBOSE
4
+
5
+ begin
6
+ require "method_source"
7
+ # Ignore parse warnings when patch
8
+ # Ruby version mismatches
9
+ $VERBOSE = nil
10
+ require "parser/current"
11
+ require "unparser"
12
+ rescue LoadError
13
+ # do nothing
14
+ ensure
15
+ $VERBOSE = old_verbose
16
+ end
17
+
18
+ module ActionPolicy
19
+ unless "".respond_to?(:then)
20
+ require "action_policy/ext/yield_self_then"
21
+ using ActionPolicy::Ext::YieldSelfThen
22
+ end
23
+
24
+ # Takes the object and a method name,
25
+ # and returns the "annotated" source code for the method:
26
+ # code is split into parts by logical operators and each
27
+ # part is evaluated separately.
28
+ #
29
+ # Example:
30
+ #
31
+ # class MyClass
32
+ # def access?
33
+ # admin? && access_feed?
34
+ # end
35
+ # end
36
+ #
37
+ # puts PrettyPrint.format_method(MyClass.new, :access?)
38
+ #
39
+ # #=> MyClass#access?
40
+ # #=> ↳ admin? #=> false
41
+ # #=> AND
42
+ # #=> access_feed? #=> true
43
+ module PrettyPrint
44
+ class Visitor
45
+ attr_reader :lines, :object
46
+ attr_accessor :indent
47
+
48
+ def initialize(object)
49
+ @object = object
50
+ end
51
+
52
+ def collect(ast)
53
+ @lines = []
54
+ @indent = 0
55
+
56
+ visit_node(ast)
57
+
58
+ lines.join("\n")
59
+ end
60
+
61
+ def visit_node(ast)
62
+ if respond_to?("visit_#{ast.type}")
63
+ send("visit_#{ast.type}", ast)
64
+ else
65
+ visit_missing ast
66
+ end
67
+ end
68
+
69
+ def expression_with_result(sexp)
70
+ expression = Unparser.unparse(sexp)
71
+ "#{expression} #=> #{eval_exp(expression)}"
72
+ end
73
+
74
+ def eval_exp(exp)
75
+ object.instance_eval(exp)
76
+ rescue => e
77
+ "Failed: #{e.message}"
78
+ end
79
+
80
+ def visit_and(ast)
81
+ visit_node(ast.children[0])
82
+ lines << indented("AND")
83
+ visit_node(ast.children[1])
84
+ end
85
+
86
+ def visit_or(ast)
87
+ visit_node(ast.children[0])
88
+ lines << indented("OR")
89
+ visit_node(ast.children[1])
90
+ end
91
+
92
+ # Parens
93
+ def visit_begin(ast)
94
+ lines << indented("(")
95
+ self.indent += 2
96
+ visit_node(ast.children[0])
97
+ self.indent -= 2
98
+ lines << indented(")")
99
+ end
100
+
101
+ def visit_missing(ast)
102
+ lines << indented(expression_with_result(ast))
103
+ end
104
+
105
+ def indented(str)
106
+ "#{indent.zero? ? "↳ " : ""}#{" " * indent}#{str}".tap do
107
+ # increase indent after the first expression
108
+ self.indent += 2 if indent.zero?
109
+ end
110
+ end
111
+ end
112
+
113
+ class << self
114
+ if defined?(::Unparser) && defined?(::MethodSource)
115
+ def available?
116
+ true
117
+ end
118
+
119
+ def print_method(object, method_name)
120
+ ast = object.method(method_name).source.then(&Unparser.method(:parse))
121
+ # outer node is a method definition itself
122
+ body = ast.children[2]
123
+
124
+ Visitor.new(object).collect(body)
125
+ end
126
+ else
127
+ def available?
128
+ false
129
+ end
130
+
131
+ def print_method(_, _)
132
+ ""
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPolicy
4
+ # Adds `suggest` method which uses did_you_mean
5
+ # to generate a suggestion message
6
+ module SuggestMessage
7
+ if defined?(::DidYouMean::SpellChecker)
8
+ def suggest(needle, heystack)
9
+ suggestion = ::DidYouMean::SpellChecker.new(
10
+ dictionary: heystack
11
+ ).correct(needle).first
12
+
13
+ suggestion ? "\nDid you mean? #{suggestion}" : ""
14
+ end
15
+ else
16
+ def suggest(*)
17
+ ""
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionPolicy
4
- VERSION = "0.2.4"
4
+ VERSION = "0.3.0.beta1"
5
5
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-06 00:00:00.000000000 Z
11
+ date: 2019-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.15'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.15'
27
27
  - !ruby/object:Gem::Dependency
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.56.0
75
+ version: 0.65.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.56.0
82
+ version: 0.65.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rubocop-md
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0.2'
97
+ - !ruby/object:Gem::Dependency
98
+ name: standard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.0.36
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.0.36
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: benchmark-ips
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +122,20 @@ dependencies:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
124
  version: 2.7.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: i18n
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
111
139
  description: Authorization framework for Ruby/Rails application
112
140
  email:
113
141
  - dementiev.vm@gmail.com
@@ -147,10 +175,13 @@ files:
147
175
  - docs/assets/styles.css
148
176
  - docs/assets/vue.min.css
149
177
  - docs/authorization_context.md
178
+ - docs/behaviour.md
150
179
  - docs/caching.md
151
180
  - docs/controller_action_aliases.md
152
181
  - docs/custom_lookup_chain.md
153
182
  - docs/custom_policy.md
183
+ - docs/debugging.md
184
+ - docs/decorators.md
154
185
  - docs/favicon.ico
155
186
  - docs/i18n.md
156
187
  - docs/index.html
@@ -163,10 +194,12 @@ files:
163
194
  - docs/quick_start.md
164
195
  - docs/rails.md
165
196
  - docs/reasons.md
197
+ - docs/scoping.md
166
198
  - docs/testing.md
167
199
  - docs/writing_policies.md
168
200
  - gemfiles/jruby.gemfile
169
201
  - gemfiles/rails42.gemfile
202
+ - gemfiles/rails6.gemfile
170
203
  - gemfiles/railsmaster.gemfile
171
204
  - lib/action_policy.rb
172
205
  - lib/action_policy/authorizer.rb
@@ -175,14 +208,19 @@ files:
175
208
  - lib/action_policy/behaviours/memoized.rb
176
209
  - lib/action_policy/behaviours/namespaced.rb
177
210
  - lib/action_policy/behaviours/policy_for.rb
211
+ - lib/action_policy/behaviours/scoping.rb
178
212
  - lib/action_policy/behaviours/thread_memoized.rb
179
213
  - lib/action_policy/cache_middleware.rb
214
+ - lib/action_policy/ext/hash_transform_keys.rb
180
215
  - lib/action_policy/ext/module_namespace.rb
181
216
  - lib/action_policy/ext/policy_cache_key.rb
217
+ - lib/action_policy/ext/proc_case_eq.rb
182
218
  - lib/action_policy/ext/string_constantize.rb
183
219
  - lib/action_policy/ext/string_match.rb
184
220
  - lib/action_policy/ext/string_underscore.rb
221
+ - lib/action_policy/ext/symbol_classify.rb
185
222
  - lib/action_policy/ext/yield_self_then.rb
223
+ - lib/action_policy/i18n.rb
186
224
  - lib/action_policy/lookup_chain.rb
187
225
  - lib/action_policy/policy/aliases.rb
188
226
  - lib/action_policy/policy/authorization.rb
@@ -193,14 +231,24 @@ files:
193
231
  - lib/action_policy/policy/execution_result.rb
194
232
  - lib/action_policy/policy/pre_check.rb
195
233
  - lib/action_policy/policy/reasons.rb
234
+ - lib/action_policy/policy/scoping.rb
235
+ - lib/action_policy/rails/authorizer.rb
196
236
  - lib/action_policy/rails/channel.rb
197
237
  - lib/action_policy/rails/controller.rb
238
+ - lib/action_policy/rails/ext/active_record.rb
239
+ - lib/action_policy/rails/policy/instrumentation.rb
240
+ - lib/action_policy/rails/scope_matchers/action_controller_params.rb
241
+ - lib/action_policy/rails/scope_matchers/active_record.rb
198
242
  - lib/action_policy/railtie.rb
199
243
  - lib/action_policy/rspec.rb
200
244
  - lib/action_policy/rspec/be_authorized_to.rb
245
+ - lib/action_policy/rspec/dsl.rb
246
+ - lib/action_policy/rspec/have_authorized_scope.rb
201
247
  - lib/action_policy/rspec/pundit_syntax.rb
202
248
  - lib/action_policy/test_helper.rb
203
249
  - lib/action_policy/testing.rb
250
+ - lib/action_policy/utils/pretty_print.rb
251
+ - lib/action_policy/utils/suggest_message.rb
204
252
  - lib/action_policy/version.rb
205
253
  homepage: https://github.com/palkan/action_policy
206
254
  licenses:
@@ -214,15 +262,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
214
262
  requirements:
215
263
  - - ">="
216
264
  - !ruby/object:Gem::Version
217
- version: 2.3.0
265
+ version: 2.4.0
218
266
  required_rubygems_version: !ruby/object:Gem::Requirement
219
267
  requirements:
220
- - - ">="
268
+ - - ">"
221
269
  - !ruby/object:Gem::Version
222
- version: '0'
270
+ version: 1.3.1
223
271
  requirements: []
224
- rubyforge_project:
225
- rubygems_version: 2.7.6
272
+ rubygems_version: 3.0.2
226
273
  signing_key:
227
274
  specification_version: 4
228
275
  summary: Authorization framework for Ruby/Rails application