subroutine 3.0.1 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6530220e9e104f3eb840fc5ad28514e4e9bb29f6831f1c8cefa96e3d511454b
4
- data.tar.gz: 0bfaf68aae3bd5f5887c2da31b3cc1b8f8fe9c8e12b88f1365c03836bd4615cf
3
+ metadata.gz: c838c235e50f88722d2f39f19560fa5208a1e18f7cd0ee8edeeeea568e47166c
4
+ data.tar.gz: 4bb808af75863ee0785ef8989b6ffc3423838dae84bb7a481d18aa759325c5be
5
5
  SHA512:
6
- metadata.gz: 2c7e809eac6e059bed7029907f4a153b388d564c0ac93b9dde0397a6ef15c5cb57fe816b2b31fdd4d2547dafbbc8d12d52493aebe61aa550afa6c430f2db820a
7
- data.tar.gz: 74d0b0c8c647e3f97fa0899c18a33fbddf7aa7b88a878f22740cd0edacbaf873f7181a164347b88425c1a891662565433d3e6c5dfd5a35f602b785c9b4f7341c
6
+ metadata.gz: a008be5744be667696675df24ba4edf19a39716f27074ec192500ed30ba750d11974f1c3892144b13ff693631899cd0aa0931c5995b823ebe112073646fb5363
7
+ data.tar.gz: f45212abc2f417d85c73db00ee83b9359ddc80d46fd77f78a45c802efe51a7374dcc978c0d7ded73ddd67c6637f252516c86abdf0f6f0ab309ef763476c17113
data/CHANGELOG.MD CHANGED
@@ -1,7 +1,21 @@
1
+ ## Subroutine 4.0.1
2
+
3
+ Association fields can now use `find_by()` instead of `find_by!()` by passing a `raise_on_miss: false` option. This places the responsibility on the op to manage nil cases rather than handling RecordNotFound errors.
4
+
5
+ ## Subroutine 4.0
6
+
7
+ The `Subroutine::Fields` module now contains a class_attribute that allows the altering of param accessor behaviors. `include_defaults_in_params` is now available to opt into including the default values in usage of the `all_params (alias params)` method. Backwards compatibility is preserved by defaulting the value to `false`. If switched to true, when an input is omitted and the field is configured with a default value, it will be included in the `all_params` object.
8
+
9
+ Removed all usage of `ungrouped` params and refactored the storage of grouped params. Params are now stored in either the provided group or the defaults group and accessed via provided_params, params, default_params, and params_with_defaults. Grouped params are accessed the same way but with the group name prefixed eg. `my_private_default_params`.
10
+
1
11
  ## Subroutine 3.0
2
12
 
3
13
  Add support for Rails 6.1. Drop support for Rails 6.0 and lower.
4
14
 
15
+ ## Subroutine 2.3
16
+
17
+ Support dynamic types for foreign keys on association fields. The class type is used at runtime to determine the casting behavior of the foreign key field.
18
+
5
19
  ## Subroutine 2.2
6
20
 
7
21
  Add `type` validation for Output.
@@ -30,6 +30,10 @@ module Subroutine
30
30
  !!config[:polymorphic]
31
31
  end
32
32
 
33
+ def raise_on_miss?
34
+ config[:raise_on_miss] != false
35
+ end
36
+
33
37
  def as
34
38
  config[:as] || field_name
35
39
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "delegate"
4
- require "active_support/concern"
5
3
  require "subroutine/association_fields/configuration"
6
4
  require "subroutine/association_fields/association_type_mismatch_error"
7
5
 
@@ -72,8 +70,7 @@ module Subroutine
72
70
  field config.foreign_type_method, config.build_foreign_type_field
73
71
  else
74
72
  class_eval <<-EV, __FILE__, __LINE__ + 1
75
- try(:silence_redefinition_of_method, :#{config.foreign_type_method})
76
- def #{config.foreign_type_method}
73
+ silence_redefinition_of_method def #{config.foreign_type_method}
77
74
  #{config.inferred_foreign_type.inspect}
78
75
  end
79
76
  EV
@@ -113,20 +110,20 @@ module Subroutine
113
110
  out
114
111
  end
115
112
 
116
- def set_field_with_association(field_name, value, opts = {})
113
+ def set_field_with_association(field_name, value, **opts)
117
114
  config = get_field_config(field_name)
118
115
 
119
116
  if config&.behavior == :association
120
117
  maybe_raise_on_association_type_mismatch!(config, value)
121
- set_field(config.foreign_type_method, value&.class&.name, opts) if config.polymorphic?
122
- set_field(config.foreign_key_method, value&.send(config.find_by), opts)
118
+ set_field(config.foreign_type_method, value&.class&.name, **opts) if config.polymorphic?
119
+ set_field(config.foreign_key_method, value&.send(config.find_by), **opts)
123
120
  association_cache[config.field_name] = value
124
121
  else
125
122
  if config&.behavior == :association_component
126
123
  clear_field_without_association(config.association_name)
127
124
  end
128
125
 
129
- set_field_without_association(field_name, value, opts)
126
+ set_field_without_association(field_name, value, **opts)
130
127
  end
131
128
  end
132
129
 
@@ -180,7 +177,7 @@ module Subroutine
180
177
  get_field(config.foreign_type_method)
181
178
  end
182
179
 
183
- klass = klass.classify.constantize if klass.is_a?(String)
180
+ klass = klass.camelize.constantize if klass.is_a?(String)
184
181
  return nil unless klass
185
182
 
186
183
  foreign_key = config.foreign_key_method
@@ -190,7 +187,11 @@ module Subroutine
190
187
  scope = klass.all
191
188
  scope = scope.unscoped if config.unscoped?
192
189
 
193
- scope.find_by!(config.find_by => value)
190
+ if config.raise_on_miss?
191
+ scope.find_by!(config.find_by => value)
192
+ else
193
+ scope.find_by(config.find_by => value)
194
+ end
194
195
  end
195
196
 
196
197
  def maybe_raise_on_association_type_mismatch!(config, record)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "delegate"
4
-
5
3
  module Subroutine
6
4
  module Fields
7
5
  class Configuration < ::SimpleDelegator
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/concern"
4
- require "active_support/core_ext/object/duplicable"
5
- require "active_support/core_ext/hash/indifferent_access"
6
- require "active_support/core_ext/object/deep_dup"
7
-
8
3
  require "subroutine/type_caster"
9
4
  require "subroutine/fields/configuration"
10
5
  require "subroutine/fields/mass_assignment_error"
@@ -27,11 +22,9 @@ module Subroutine
27
22
  end
28
23
 
29
24
  included do
30
- class_attribute :field_configurations
31
- self.field_configurations = {}
32
-
33
- class_attribute :field_groups
34
- self.field_groups = Set.new
25
+ class_attribute :include_defaults_in_params, instance_accessor: false, instance_predicate: false
26
+ class_attribute :field_configurations, default: {}
27
+ class_attribute :fields_by_group, default: Hash.new { |h, k| h[k] = Set.new }
35
28
  end
36
29
 
37
30
  module ClassMethods
@@ -44,8 +37,15 @@ module Subroutine
44
37
 
45
38
  ensure_field_accessors(config)
46
39
 
47
- config.groups.each do |group_name|
48
- ensure_group_accessors(group_name)
40
+ if config.groups.any?
41
+ new_fields_by_group = self.fields_by_group.deep_dup
42
+
43
+ config.groups.each do |group_name|
44
+ new_fields_by_group[group_name] << config.field_name
45
+ ensure_group_accessors(group_name)
46
+ end
47
+
48
+ self.fields_by_group = new_fields_by_group
49
49
  end
50
50
 
51
51
  config
@@ -75,18 +75,16 @@ module Subroutine
75
75
  end
76
76
  alias inputs_from fields_from
77
77
 
78
- def fields_in_group(group_name)
79
- field_configurations.each_with_object({}) do |(field_name, config), h|
80
- next unless config.in_group?(group_name)
81
-
82
- h[field_name] = config
83
- end
84
- end
85
-
86
78
  def get_field_config(field_name)
87
79
  field_configurations[field_name.to_sym]
88
80
  end
89
81
 
82
+ def include_defaults_in_params?
83
+ return include_defaults_in_params unless include_defaults_in_params.nil?
84
+
85
+ Subroutine.include_defaults_in_params?
86
+ end
87
+
90
88
  def respond_to_missing?(method_name, *args, &block)
91
89
  ::Subroutine::TypeCaster.casters.key?(method_name.to_sym) || super
92
90
  end
@@ -106,29 +104,33 @@ module Subroutine
106
104
  protected
107
105
 
108
106
  def ensure_group_accessors(group_name)
109
- group_name = group_name.to_sym
110
- return if field_groups.include?(group_name)
107
+ class_eval <<-EV, __FILE__, __LINE__ + 1
108
+ silence_redefinition_of_method def #{group_name}_params
109
+ return #{group_name}_params_with_defaults if include_defaults_in_params?
111
110
 
112
- self.field_groups |= [group_name]
111
+ #{group_name}_provided_params
112
+ end
113
113
 
114
- class_eval <<-EV, __FILE__, __LINE__ + 1
115
- def #{group_name}_params
116
- param_groups[:#{group_name}]
114
+ silence_redefinition_of_method def #{group_name}_provided_params
115
+ get_param_group(:#{group_name}_provided)
117
116
  end
118
117
 
119
- def #{group_name}_default_params
120
- group_field_names = fields_in_group(:#{group_name}).keys
121
- all_default_params.slice(*group_field_names)
118
+ silence_redefinition_of_method def #{group_name}_default_params
119
+ get_param_group(:#{group_name}_default)
122
120
  end
123
121
  alias #{group_name}_defaults #{group_name}_default_params
124
122
 
125
- def #{group_name}_params_with_default_params
126
- #{group_name}_default_params.merge(param_groups[:#{group_name}])
123
+ silence_redefinition_of_method def #{group_name}_params_with_default_params
124
+ param_cache[:#{group_name}_provided_and_default] ||= begin
125
+ #{group_name}_default_params.merge(#{group_name}_provided_params)
126
+ end
127
127
  end
128
128
  alias #{group_name}_params_with_defaults #{group_name}_params_with_default_params
129
129
 
130
- def without_#{group_name}_params
131
- all_params.except(*#{group_name}_params.keys)
130
+ silence_redefinition_of_method def without_#{group_name}_params
131
+ param_cache[:without_#{group_name}] ||= begin
132
+ all_params.except(*#{group_name}_params.keys)
133
+ end
132
134
  end
133
135
  EV
134
136
  end
@@ -136,17 +138,15 @@ module Subroutine
136
138
  def ensure_field_accessors(config)
137
139
  if config.field_writer?
138
140
  class_eval <<-EV, __FILE__, __LINE__ + 1
139
- try(:silence_redefinition_of_method, :#{config.field_name}=)
140
- def #{config.field_name}=(v)
141
- set_field(:#{config.field_name}, v)
141
+ silence_redefinition_of_method def #{config.field_name}=(v)
142
+ set_field(:#{config.field_name}, v, group_type: :provided)
142
143
  end
143
144
  EV
144
145
  end
145
146
 
146
147
  if config.field_reader?
147
148
  class_eval <<-EV, __FILE__, __LINE__ + 1
148
- try(:silence_redefinition_of_method, :#{config.field_name})
149
- def #{config.field_name}
149
+ silence_redefinition_of_method def #{config.field_name}
150
150
  get_field(:#{config.field_name})
151
151
  end
152
152
  EV
@@ -159,88 +159,101 @@ module Subroutine
159
159
  if ::Subroutine::Fields.action_controller_params_loaded? && inputs.is_a?(::ActionController::Parameters)
160
160
  inputs = inputs.to_unsafe_h if inputs.respond_to?(:to_unsafe_h)
161
161
  end
162
- @provided_fields = {}.with_indifferent_access
163
- param_groups[:original] = inputs.with_indifferent_access
162
+ inputs.each_pair do |k, v|
163
+ set_param_group_value(:original, k, v)
164
+ end
164
165
  mass_assign_initial_params
165
166
  end
166
167
 
168
+ def include_defaults_in_params?
169
+ self.class.include_defaults_in_params?
170
+ end
171
+
172
+ def param_cache
173
+ @param_cache ||= {}
174
+ end
175
+
167
176
  def param_groups
168
177
  @param_groups ||= Hash.new { |h, k| h[k] = {}.with_indifferent_access }
169
178
  end
170
179
 
171
180
  def get_param_group(name)
172
- param_groups[name.to_sym]
181
+ name = name.to_sym
182
+ return param_groups[name] if param_groups.key?(name)
183
+
184
+ param_groups[name] = yield if block_given?
185
+ param_groups[name]
173
186
  end
174
187
 
175
- def original_params
176
- get_param_group(:original)
188
+ def set_param_group_value(name, key, value)
189
+ get_param_group(name)[key.to_sym] = value
177
190
  end
178
191
 
179
- def ungrouped_params
180
- get_param_group(:ungrouped)
192
+ def original_params
193
+ get_param_group(:original)
181
194
  end
182
195
 
183
- def all_params
184
- get_param_group(:all)
196
+ def all_provided_params
197
+ get_param_group(:provided)
185
198
  end
186
- alias params all_params
199
+ alias provided_params all_provided_params
187
200
 
188
201
  def all_default_params
189
202
  get_param_group(:default)
190
203
  end
191
204
  alias defaults all_default_params
205
+ alias default_params all_default_params
192
206
 
193
- def all_params_with_defaults
194
- all_default_params.merge(all_params)
195
- end
196
- alias params_with_defaults all_params_with_defaults
207
+ def all_params
208
+ return all_params_with_default_params if include_defaults_in_params?
197
209
 
198
- def ungrouped_defaults
199
- default_params.slice(*ungrouped_fields.keys)
210
+ all_provided_params
200
211
  end
212
+ alias params all_params
201
213
 
202
- def ungrouped_params_with_defaults
203
- ungrouped_defaults.merge(ungrouped_params)
214
+ def all_params_with_default_params
215
+ param_cache[:provided_and_default] ||= all_default_params.merge(all_provided_params)
204
216
  end
217
+ alias all_params_with_defaults all_params_with_default_params
218
+ alias params_with_defaults all_params_with_defaults
219
+
205
220
 
206
221
  def get_field_config(field_name)
207
222
  self.class.get_field_config(field_name)
208
223
  end
209
224
 
210
- # check if a specific field was provided
211
225
  def field_provided?(key)
212
- !!@provided_fields[key]
226
+ all_provided_params.key?(key)
213
227
  end
214
228
 
215
229
  def get_field(name)
216
- field_provided?(name) ? all_params[name] : all_default_params[name]
230
+ all_params_with_default_params[name]
217
231
  end
218
232
 
219
- def set_field(name, value, opts = {})
233
+ def set_field(name, value, group_type: :provided)
220
234
  config = get_field_config(name)
221
- @provided_fields[name] = true unless opts[:track_provided] == false
222
235
  value = attempt_cast(value, config) do |e|
223
236
  "Error during assignment of field `#{name}`: #{e}"
224
237
  end
225
- each_param_group_for_field(name) do |h|
226
- h[name] = value
238
+
239
+ set_param_group_value(group_type, config.field_name, value)
240
+
241
+ config.groups.each do |group_name|
242
+ set_param_group_value(:"#{group_name}_#{group_type}", config.field_name, value)
227
243
  end
244
+
245
+ param_cache.clear
246
+
228
247
  value
229
248
  end
230
249
 
231
250
  def clear_field(name)
232
- each_param_group_for_field(name) do |h|
233
- h.delete(name)
234
- end
235
- end
236
-
237
- def fields_in_group(group_name)
238
- self.class.fields_in_group(group_name)
239
- end
251
+ param_cache.clear
252
+ param_groups.each_pair do |key, group|
253
+ next if key == :original
254
+ next if key == :default
240
255
 
241
- def ungrouped_fields
242
- fields.select { |f| f.groups.empty? }.each_with_object({}) do |f, h|
243
- h[f.name] = f
256
+ group.delete(name)
244
257
  end
245
258
  end
246
259
 
@@ -253,16 +266,12 @@ module Subroutine
253
266
  end
254
267
 
255
268
  if original_params.key?(field_name)
256
- set_field(field_name, original_params[field_name])
269
+ set_field(field_name, original_params[field_name], group_type: :provided)
257
270
  end
258
271
 
259
272
  next unless config.has_default?
260
273
 
261
- value = attempt_cast(config.get_default, config) do |e|
262
- "Error for default `#{field}`: #{e}"
263
- end
264
-
265
- param_groups[:default][field_name] = value
274
+ set_field(field_name, config.get_default, group_type: :default)
266
275
  end
267
276
  end
268
277
 
@@ -273,18 +282,5 @@ module Subroutine
273
282
  raise ::Subroutine::TypeCaster::TypeCastError, message, e.backtrace
274
283
  end
275
284
 
276
- def each_param_group_for_field(name)
277
- config = get_field_config(name)
278
- yield all_params
279
-
280
- if config.groups.empty?
281
- yield ungrouped_params
282
- else
283
- config.groups.each do |group_name|
284
- yield param_groups[group_name]
285
- end
286
- end
287
- end
288
-
289
285
  end
290
286
  end
data/lib/subroutine/op.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_model"
4
-
5
3
  require "subroutine/failure"
6
4
  require "subroutine/fields"
7
5
  require "subroutine/outputs"
@@ -48,6 +46,17 @@ module Subroutine
48
46
  yield self if block_given?
49
47
  end
50
48
 
49
+ def inspect
50
+ values = provided_params.map do |(key, value)|
51
+ "#{key}: #{value.inspect}"
52
+ end
53
+ values.sort!
54
+ values = values.join(", ")
55
+
56
+ oid = format('%x', (object_id << 1))
57
+ "#<#{self.class}:0x#{oid} #{values}>"
58
+ end
59
+
51
60
  def submit!
52
61
  begin
53
62
  observe_submission do
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "delegate"
4
-
5
3
  module Subroutine
6
4
  module Outputs
7
5
  class Configuration < ::SimpleDelegator
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/concern"
4
3
  require "subroutine/outputs/configuration"
5
4
  require "subroutine/outputs/output_not_set_error"
6
5
  require "subroutine/outputs/unknown_output_error"
@@ -86,7 +86,7 @@ end
86
86
  end
87
87
 
88
88
  ::Subroutine::TypeCaster.register :boolean, :bool do |value, _options = {}|
89
- !!(String(value) =~ /^(yes|true|1|ok)$/)
89
+ !!(String(value) =~ /^(yes|true|1|ok)$/i)
90
90
  end
91
91
 
92
92
  ::Subroutine::TypeCaster.register :iso_date do |value, _options = {}|
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Subroutine
4
4
 
5
- MAJOR = 3
5
+ MAJOR = 4
6
6
  MINOR = 0
7
7
  PATCH = 1
8
8
  PRE = nil
data/lib/subroutine.rb CHANGED
@@ -1,5 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_model"
4
+ require "active_support/concern"
5
+ require "active_support/core_ext/hash/indifferent_access"
6
+ require "active_support/core_ext/module/redefine_method"
7
+ require "active_support/core_ext/object/deep_dup"
8
+ require "active_support/core_ext/object/duplicable"
9
+ require "active_support/core_ext/string/inflections"
10
+ require "delegate"
11
+
3
12
  require "subroutine/version"
4
13
  require "subroutine/fields"
5
14
  require "subroutine/op"
15
+
16
+ module Subroutine
17
+
18
+ def self.include_defaults_in_params=(bool)
19
+ @include_defaults_in_params = !!bool
20
+ end
21
+
22
+ def self.include_defaults_in_params?
23
+ return !!@include_defaults_in_params if defined?(@instance_defaults_in_params)
24
+
25
+ false
26
+ end
27
+
28
+ end
@@ -335,5 +335,15 @@ module Subroutine
335
335
  assert_equal({}, op.without_info_params)
336
336
  end
337
337
 
338
+ def test_find_by_is_used_if_raise_on_miss_is_false
339
+ all_mock = mock
340
+
341
+ ::User.expects(:all).returns(all_mock)
342
+ all_mock.expects(:find_by).with(id: 1).returns(nil)
343
+
344
+ op = SafeAssociationOp.new user_type: "User", user_id: 1
345
+ assert_nil op.user
346
+ end
347
+
338
348
  end
339
349
  end
@@ -318,5 +318,11 @@ module Subroutine
318
318
  assert_equal raw_params, op.params
319
319
  end
320
320
 
321
+ def test_inspect_is_pretty
322
+ op = SignupOp.new({ email: "foo@bar.com", password: "password123!" })
323
+ oid = format('%x', (op.object_id << 1))
324
+ assert_equal "#<SignupOp:0x#{oid} email: \"foo@bar.com\", password: \"password123!\">", op.inspect
325
+ end
326
+
321
327
  end
322
328
  end
@@ -25,6 +25,25 @@ module Subroutine
25
25
 
26
26
  end
27
27
 
28
+ class WhateverWithDefaultsIncluded < Whatever
29
+ self.include_defaults_in_params = true
30
+ end
31
+
32
+ class MutationBase
33
+ include Subroutine::Fields
34
+
35
+ field :foo, group: :three_letter
36
+ field :bar, group: :three_letter
37
+
38
+ end
39
+
40
+ class MutationChild < MutationBase
41
+
42
+ field :qux, group: :three_letter
43
+ field :food, group: :four_letter
44
+
45
+ end
46
+
28
47
  def test_fields_are_configured
29
48
  assert_equal 6, Whatever.field_configurations.size
30
49
  assert_equal :string, Whatever.field_configurations[:foo][:type]
@@ -75,17 +94,45 @@ module Subroutine
75
94
 
76
95
  def test_params_does_not_include_defaults
77
96
  instance = Whatever.new(foo: "abc")
97
+ assert_equal({ "foo" => "abc" }, instance.provided_params)
78
98
  assert_equal({ "foo" => "foo", "bar" => 3, "qux" => "qux" }, instance.defaults)
79
99
  assert_equal({ "foo" => "abc" }, instance.params)
80
100
  assert_equal({ "foo" => "abc", "bar" => 3, "qux" => "qux" }, instance.params_with_defaults)
81
101
  end
82
102
 
83
- def test_named_params_do_not_include_defaults_unlesss_asked_for
103
+ def test_params_include_defaults_if_globally_configured
104
+ Subroutine.stubs(:include_defaults_in_params?).returns(true)
84
105
  instance = Whatever.new(foo: "abc")
106
+ assert Whatever.include_defaults_in_params?
107
+ assert_equal({ "foo" => "abc" }, instance.provided_params)
108
+ assert_equal({ "foo" => "foo", "bar" => 3, "qux" => "qux" }, instance.defaults)
109
+ assert_equal({ "foo" => "abc", "bar" => 3, "qux" => "qux" }, instance.params)
110
+ assert_equal({ "foo" => "abc", "bar" => 3, "qux" => "qux" }, instance.params_with_defaults)
111
+ end
112
+
113
+ def test_params_includes_defaults_if_opted_into
114
+ refute Subroutine.include_defaults_in_params?
115
+ instance = WhateverWithDefaultsIncluded.new(foo: "abc")
116
+ assert_equal({ "foo" => "abc" }, instance.provided_params)
117
+ assert_equal({ "foo" => "foo", "bar" => 3, "qux" => "qux" }, instance.defaults)
118
+ assert_equal({ "foo" => "abc", "bar" => 3, "qux" => "qux" }, instance.params)
119
+ assert_equal({ "foo" => "abc", "bar" => 3, "qux" => "qux" }, instance.params_with_defaults)
120
+ end
121
+
122
+ def test_named_params_do_not_include_defaults_unless_asked_for
123
+ instance = Whatever.new(foo: "abc")
124
+ assert_equal({}, instance.sekret_provided_params)
85
125
  assert_equal({}, instance.sekret_params)
86
126
  assert_equal({ "bar" => 3 }, instance.sekret_params_with_defaults)
87
127
  end
88
128
 
129
+ def test_named_params_include_defaults_if_configured
130
+ instance = WhateverWithDefaultsIncluded.new(foo: "abc")
131
+ assert_equal({}, instance.sekret_provided_params)
132
+ assert_equal({ "bar" => 3 }, instance.sekret_params)
133
+ assert_equal({ "bar" => 3 }, instance.sekret_params_with_defaults)
134
+ end
135
+
89
136
  def test_fields_can_opt_out_of_mass_assignment
90
137
  assert_raises Subroutine::Fields::MassAssignmentError do
91
138
  Whatever.new(foo: "abc", protekted: "foo")
@@ -113,18 +160,28 @@ module Subroutine
113
160
  assert_equal "bar", instance.foo
114
161
  end
115
162
 
163
+ def test_set_field_adds_to_provided_params
164
+ instance = Whatever.new
165
+ instance.set_field(:foo, "bar")
166
+ assert_equal true, instance.provided_params.key?(:foo)
167
+ end
168
+
169
+ def test_set_field_can_add_to_the_default_params
170
+ instance = Whatever.new
171
+ instance.set_field(:foo, "bar", group_type: :default)
172
+ assert_equal false, instance.provided_params.key?(:foo)
173
+ assert_equal "bar", instance.default_params[:foo]
174
+ end
175
+
116
176
  def test_group_fields_are_accessible_at_the_class
117
- results = Whatever.fields_in_group(:sekret)
118
- assert_equal true, results.key?(:protekted_group_input)
119
- assert_equal true, results.key?(:bar)
120
- assert_equal false, results.key?(:protekted)
177
+ fields = Whatever.fields_by_group[:sekret].sort
178
+ assert_equal %i[bar protekted_group_input], fields
121
179
  end
122
180
 
123
181
  def test_groups_fields_are_accessible
124
182
  op = Whatever.new(foo: "bar", protekted_group_input: "pgi", bar: 8)
125
183
  assert_equal({ protekted_group_input: "pgi", bar: 8 }.with_indifferent_access, op.sekret_params)
126
184
  assert_equal({ protekted_group_input: "pgi", foo: "bar", bar: 8 }.with_indifferent_access, op.params)
127
- assert_equal({ foo: "bar" }.with_indifferent_access, op.ungrouped_params)
128
185
  end
129
186
 
130
187
  def test_fields_from_allows_merging_of_config
@@ -143,5 +200,19 @@ module Subroutine
143
200
  assert_equal(%i[amount_cents credit_cents], OuterInheritanceOp.field_configurations.keys.sort)
144
201
  end
145
202
 
203
+ def test_field_definitions_are_not_mutated_by_subclasses
204
+ assert_equal(%i[bar foo], MutationBase.field_configurations.keys.sort)
205
+ assert_equal(%i[bar foo food qux], MutationChild.field_configurations.keys.sort)
206
+ end
207
+
208
+ def test_group_fields_are_not_mutated_by_subclasses
209
+ assert_equal(%i[three_letter], MutationBase.fields_by_group.keys.sort)
210
+ assert_equal(%i[four_letter three_letter], MutationChild.fields_by_group.keys.sort)
211
+
212
+ assert_equal(%i[bar foo], MutationBase.fields_by_group[:three_letter].sort)
213
+ assert_equal(%i[bar foo qux], MutationChild.fields_by_group[:three_letter].sort)
214
+ assert_equal(%i[food], MutationChild.fields_by_group[:four_letter].sort)
215
+ end
216
+
146
217
  end
147
218
  end
@@ -96,15 +96,45 @@ module Subroutine
96
96
  op.boolean_input = 'no'
97
97
  assert_equal false, op.boolean_input
98
98
 
99
+ op.boolean_input = 'Yes'
100
+ assert_equal true, op.boolean_input
101
+
102
+ op.boolean_input = 'No'
103
+ assert_equal false, op.boolean_input
104
+
105
+ op.boolean_input = 'YES'
106
+ assert_equal true, op.boolean_input
107
+
108
+ op.boolean_input = 'NO'
109
+ assert_equal false, op.boolean_input
110
+
99
111
  op.boolean_input = 'true'
100
112
  assert_equal true, op.boolean_input
101
113
 
102
114
  op.boolean_input = 'false'
103
115
  assert_equal false, op.boolean_input
104
116
 
117
+ op.boolean_input = 'True'
118
+ assert_equal true, op.boolean_input
119
+
120
+ op.boolean_input = 'False'
121
+ assert_equal false, op.boolean_input
122
+
123
+ op.boolean_input = 'TRUE'
124
+ assert_equal true, op.boolean_input
125
+
126
+ op.boolean_input = 'FALSE'
127
+ assert_equal false, op.boolean_input
128
+
105
129
  op.boolean_input = 'ok'
106
130
  assert_equal true, op.boolean_input
107
131
 
132
+ op.boolean_input = 'OK'
133
+ assert_equal true, op.boolean_input
134
+
135
+ op.boolean_input = 'Ok'
136
+ assert_equal true, op.boolean_input
137
+
108
138
  op.boolean_input = ''
109
139
  assert_equal false, op.boolean_input
110
140
 
data/test/support/ops.rb CHANGED
@@ -27,10 +27,6 @@ class User
27
27
  new(params)
28
28
  end
29
29
 
30
- def self.find_by!(params)
31
- find_by(params) || raise
32
- end
33
-
34
30
  def self.type_for_attribute(attribute)
35
31
  case attribute
36
32
  when :id
@@ -342,6 +338,12 @@ class SimpleAssociationOp < ::OpWithAssociation
342
338
 
343
339
  end
344
340
 
341
+ class SafeAssociationOp < ::OpWithAssociation
342
+
343
+ association :user, raise_on_miss: false
344
+
345
+ end
346
+
345
347
  class SimpleAssociationWithStringIdOp < ::OpWithAssociation
346
348
 
347
349
  association :string_id_user
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: 3.0.1
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Nelson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-14 00:00:00.000000000 Z
11
+ date: 2024-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -221,7 +221,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
221
221
  - !ruby/object:Gem::Version
222
222
  version: '0'
223
223
  requirements: []
224
- rubygems_version: 3.4.10
224
+ rubygems_version: 3.4.19
225
225
  signing_key:
226
226
  specification_version: 4
227
227
  summary: Feature-driven operation objects.