subroutine 2.0.1 → 2.1.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: 9fbd9c97951c8e07f3f84fe1f9826d4fff45a7f0fe99045d375e34d530fb0934
4
- data.tar.gz: db76eef88d0afd766a7a906d0cf5a202286d90da3f6ec113e798b6709f3c96e4
3
+ metadata.gz: 63080af90148b6faaff42be6f9753d6f5ef36f3f8ebe650a1372905836f5e208
4
+ data.tar.gz: 9522cbaf937f5bdfa85fbb629ad8a1f3f014dce6ea42784ef68b39c4ecebad49
5
5
  SHA512:
6
- metadata.gz: 2310334da4ebf40292d6868c1ab59b028d790fd01db3ef55825d4f680810e157ffd35c9d6a19e6b1e58816aad8eb87f01b1ae9e9395914350d23b80a84b4874b
7
- data.tar.gz: 4b426d21e7e91f27405daf3a7438f4a049f4a5a7ee13002488259e5599989a867d2fbf9c31f56cff7c8be452708175ba786700729ab88b2f186c4ed4159fcecb
6
+ metadata.gz: a76f25b779fd8a9ccc3958febe5b68906d7260f052b5200d6b080680f9f80f79debebe2bf35ee199f6996cccd827f0844e3710d30f0ee367dd248c6900f45c74
7
+ data.tar.gz: c3e50c34de7e4f656168cec625bccb1bede1fcd4dbbdd5bef7d9d0dabf9a2695a470dd749b2ec5785448b6cd45c0c3454b469d49a157e9d19fffc2ada78b75ce
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.5.7
1
+ 2.7.5
data/CHANGELOG.MD CHANGED
@@ -1,3 +1,9 @@
1
+ ## Subroutine 2.0
2
+
3
+ The updates between 1.0 and 2.0 are relatively minor and are focused more on cleaning up the codebase and extending the use of the 0.9->1.0 refactor. There are, however, breaking changes to how associations are loaded. The association is no longer loaded via `find()` but rather `find_by!(id:)`. Given this, a major version was released.
4
+
5
+ **Note:** 2.0.0 was released with a bug and subsequently yanked. 2.0.1 is the first available 2.x version.
6
+
1
7
  ## Subroutine 1.0
2
8
 
3
9
  A massive refactor took place between 0.9 and 1.0, including breaking changes. The goal was to reduce complexity, simplify backtraces, and increase the overall safety and reliability of the library.
@@ -9,11 +9,13 @@ module Subroutine
9
9
  extend ActiveSupport::Concern
10
10
 
11
11
  included do
12
- class_attribute :authorization_declared, instance_writer: false
13
- self.authorization_declared = false
12
+ class_attribute :authorization_checks
13
+ self.authorization_checks = []
14
14
 
15
15
  class_attribute :user_class_name, instance_writer: false
16
16
  self.user_class_name = "User"
17
+
18
+ validate :validate_authorization_checks, unless: :skip_auth_checks?
17
19
  end
18
20
 
19
21
  module ClassMethods
@@ -22,28 +24,24 @@ module Subroutine
22
24
  [user_class_name, "Integer", "NilClass"].compact
23
25
  end
24
26
 
25
- def authorize(validation_name)
26
- validate validation_name, unless: :skip_auth_checks?
27
+ def authorization_declared?
28
+ authorization_checks.any?
29
+ end
30
+
31
+ def authorize(check_name)
32
+ self.authorization_checks += [check_name.to_sym]
27
33
  end
28
34
 
29
35
  def no_user_requirements!
30
- self.authorization_declared = true
36
+ authorize :authorize_user_not_required
31
37
  end
32
38
 
33
39
  def require_user!
34
- self.authorization_declared = true
35
-
36
- validate unless: :skip_auth_checks? do
37
- unauthorized! unless current_user.present?
38
- end
40
+ authorize :authorize_user_required
39
41
  end
40
42
 
41
43
  def require_no_user!
42
- self.authorization_declared = true
43
-
44
- validate unless: :skip_auth_checks? do
45
- unauthorized! :empty_unauthorized if current_user.present?
46
- end
44
+ authorize :authorize_no_user_required
47
45
  end
48
46
 
49
47
  # policy :can_update_user
@@ -57,33 +55,50 @@ module Subroutine
57
55
  if_conditionals = Array(opts[:if])
58
56
  unless_conditionals = Array(opts[:unless])
59
57
 
60
- validate unless: :skip_auth_checks? do
61
- run_it = true
62
- # http://guides.rubyonrails.org/active_record_validations.html#combining-validation-conditions
58
+ meths.each do |meth|
59
+ normalized_meth = if normalized_meth.to_s.end_with?("?")
60
+ meth.to_s[0...-1]
61
+ else
62
+ meth
63
+ end
63
64
 
64
- # The validation only runs when all the :if conditions
65
- if if_conditionals.present?
66
- run_it &&= if_conditionals.all? { |i| send(i) }
67
- end
65
+ auth_method_name = :"authorize_#{policy_name}_#{normalized_meth}"
68
66
 
69
- # and none of the :unless conditions are evaluated to true.
70
- if unless_conditionals.present?
71
- run_it &&= unless_conditionals.none? { |u| send(u) }
72
- end
67
+ define_method auth_method_name do
68
+ run_it = true
69
+ # http://guides.rubyonrails.org/active_record_validations.html#combining-validation-conditions
70
+
71
+ # The validation only runs when all the :if conditions evaluate to true
72
+ if if_conditionals.present?
73
+ run_it &&= if_conditionals.all? { |i| send(i) }
74
+ end
75
+
76
+ # and none of the :unless conditions are evaluated to true.
77
+ if unless_conditionals.present?
78
+ run_it &&= unless_conditionals.none? { |u| send(u) }
79
+ end
80
+
81
+ return unless run_it
73
82
 
74
- next unless run_it
83
+ p = send(policy_name)
84
+ unauthorized! unless p
75
85
 
76
- p = send(policy_name)
77
- if !p || meths.any? { |m| !(p.respond_to?("#{m}?") ? p.send("#{m}?") : p.send(m)) }
78
- unauthorized! opts[:error]
86
+ result = if p.respond_to?("#{normalized_meth}?")
87
+ p.send("#{normalized_meth}?")
88
+ else
89
+ p.send(normalized_meth)
90
+ end
91
+
92
+ unauthorized! opts[:error] unless result
79
93
  end
94
+
95
+ authorize auth_method_name
80
96
  end
81
97
  end
82
-
83
98
  end
84
99
 
85
100
  def initialize(*args, &block)
86
- raise Subroutine::Auth::AuthorizationNotDeclaredError unless self.class.authorization_declared
101
+ raise Subroutine::Auth::AuthorizationNotDeclaredError unless self.class.authorization_declared?
87
102
 
88
103
  @skip_auth_checks = false
89
104
 
@@ -124,5 +139,23 @@ module Subroutine
124
139
  raise ::Subroutine::Auth::NotAuthorizedError, reason
125
140
  end
126
141
 
142
+ def validate_authorization_checks
143
+ authorization_checks.each do |check|
144
+ send(check)
145
+ end
146
+ end
147
+
148
+ def authorize_user_not_required
149
+ true
150
+ end
151
+
152
+ def authorize_user_required
153
+ unauthorized! unless current_user.present?
154
+ end
155
+
156
+ def authorize_no_user_required
157
+ unauthorized! :empty_unauthorized if current_user.present?
158
+ end
159
+
127
160
  end
128
161
  end
@@ -216,9 +216,9 @@ module Subroutine
216
216
  field_provided?(name) ? all_params[name] : all_default_params[name]
217
217
  end
218
218
 
219
- def set_field(name, value, track_provided: true)
219
+ def set_field(name, value, opts = {})
220
220
  config = get_field_config(name)
221
- @provided_fields[name] = true if track_provided
221
+ @provided_fields[name] = true unless opts[:track_provided] == false
222
222
  value = attempt_cast(value, config) do |e|
223
223
  "Error during assignment of field `#{name}`: #{e}"
224
224
  end
@@ -3,8 +3,8 @@
3
3
  module Subroutine
4
4
 
5
5
  MAJOR = 2
6
- MINOR = 0
7
- PATCH = 1
6
+ MINOR = 1
7
+ PATCH = 2
8
8
  PRE = nil
9
9
 
10
10
  VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
@@ -57,6 +57,16 @@ module Subroutine
57
57
  end
58
58
  end
59
59
 
60
+ def test_authorization_checks_are_registered_on_the_class
61
+ assert_equal false, MissingAuthOp.authorization_declared?
62
+
63
+ assert_equal true, CustomAuthorizeOp.authorization_declared?
64
+ assert_equal [:authorize_user_required, :authorize_user_is_correct], CustomAuthorizeOp.authorization_checks
65
+
66
+ assert_equal true, NoUserRequirementsOp.authorization_declared?
67
+ assert_equal [:authorize_user_not_required], NoUserRequirementsOp.authorization_checks
68
+ end
69
+
60
70
  def test_the_current_user_can_be_defined_by_an_id
61
71
  user = CustomAuthorizeOp.new(1).current_user
62
72
  assert_equal 1, user.id
@@ -92,6 +102,10 @@ module Subroutine
92
102
  op.submit!
93
103
  end
94
104
 
105
+ def policy_invocations_are_registered_as_authorization_methods
106
+ assert PolicyOp.authorization_checks.include?(:authorize_policy_user_can_access)
107
+ end
108
+
95
109
  def test_it_runs_policies_with_conditionals
96
110
  # if: false
97
111
  op = IfConditionalPolicyOp.new(user, check_policy: false)
@@ -136,5 +136,12 @@ module Subroutine
136
136
  assert_equal({}.with_indifferent_access, op.without_inherited_params)
137
137
  end
138
138
 
139
+ def test_fields_are_inherited_to_subclasses
140
+ assert_equal(%i[amount_cents], ParentInheritanceOp.field_configurations.keys.sort)
141
+ assert_equal(%i[debit_cents], ParentInheritanceOp::EarlyInheritanceOp.field_configurations.keys.sort)
142
+ assert_equal(%i[amount_cents debit_cents], ParentInheritanceOp::LateInheritanceOp.field_configurations.keys.sort)
143
+ assert_equal(%i[amount_cents credit_cents], OuterInheritanceOp.field_configurations.keys.sort)
144
+ end
145
+
139
146
  end
140
147
  end
data/test/support/ops.rb CHANGED
@@ -228,13 +228,19 @@ class PolicyOp < OpWithAuth
228
228
  class FakePolicy
229
229
 
230
230
  def user_can_access?
231
+ true
232
+ end
233
+
234
+ def user_can_do_it
231
235
  false
232
236
  end
233
237
 
234
238
  end
235
239
 
236
240
  require_user!
241
+
237
242
  policy :user_can_access?
243
+ policy :user_can_do_it
238
244
 
239
245
  def policy
240
246
  @policy ||= FakePolicy.new
@@ -284,10 +290,33 @@ class UnlessConditionalPolicyOp < OpWithAuth
284
290
 
285
291
  end
286
292
 
293
+ class ParentInheritanceOp < ::Subroutine::Op
294
+ class EarlyInheritanceOp < ParentInheritanceOp
295
+ integer :debit_cents
296
+ end
297
+
298
+ integer :amount_cents
299
+
300
+ class LateInheritanceOp < ParentInheritanceOp
301
+ integer :debit_cents
302
+ end
303
+
304
+ end
305
+
306
+ class OuterInheritanceOp < ParentInheritanceOp
307
+
308
+ integer :credit_cents
309
+
310
+ end
311
+
287
312
  class OpWithAssociation < ::Subroutine::Op
288
313
 
289
314
  include ::Subroutine::AssociationFields
290
315
 
316
+ def perform
317
+ false
318
+ end
319
+
291
320
  end
292
321
 
293
322
  class SimpleAssociationOp < ::OpWithAssociation
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: subroutine
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Nelson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-18 00:00:00.000000000 Z
11
+ date: 2022-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -218,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
218
218
  - !ruby/object:Gem::Version
219
219
  version: '0'
220
220
  requirements: []
221
- rubygems_version: 3.3.7
221
+ rubygems_version: 3.1.6
222
222
  signing_key:
223
223
  specification_version: 4
224
224
  summary: Feature-driven operation objects.