subroutine 4.2.0 → 4.5.0
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 +4 -4
- data/lib/subroutine/op.rb +1 -1
- data/lib/subroutine/outputs/configuration.rb +8 -1
- data/lib/subroutine/outputs.rb +58 -14
- data/lib/subroutine/version.rb +1 -1
- data/subroutine.gemspec +12 -3
- metadata +65 -55
- data/.bundler-version +0 -1
- data/.github/CODEOWNERS +0 -1
- data/.github/dependabot.yml +0 -24
- data/.github/workflows/build.yml +0 -27
- data/.gitignore +0 -16
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/Appraisals +0 -19
- data/CHANGELOG.MD +0 -184
- data/Gemfile +0 -8
- data/LICENSE.txt +0 -22
- data/README.md +0 -82
- data/Rakefile +0 -10
- data/gemfiles/rails_6.1.gemfile +0 -9
- data/gemfiles/rails_6.1.gemfile.lock +0 -98
- data/gemfiles/rails_7.0.gemfile +0 -9
- data/gemfiles/rails_7.0.gemfile.lock +0 -96
- data/gemfiles/rails_7.1.gemfile +0 -9
- data/gemfiles/rails_7.1.gemfile.lock +0 -115
- data/gemfiles/rails_7.2.gemfile +0 -9
- data/gemfiles/rails_7.2.gemfile.lock +0 -115
- data/test/subroutine/association_test.rb +0 -364
- data/test/subroutine/auth_test.rb +0 -148
- data/test/subroutine/base_test.rb +0 -328
- data/test/subroutine/fields_test.rb +0 -218
- data/test/subroutine/outputs_test.rb +0 -103
- data/test/subroutine/type_caster_test.rb +0 -510
- data/test/support/ops.rb +0 -516
- data/test/test_helper.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 719502dade68ec34e525c97130a82e7ca6b3a5406002882a7916a93ad80d9099
|
4
|
+
data.tar.gz: 3eadd2bb114ef5d1724864b67462d0b563f241e21c3b61c92ea47366022aba9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a57ccde1e224deab1dd2854091b5a1117b026b3feabb2d47626b7227a9905c370fdd95d3aadce201d0f1a84d7409ff225bff20fb9fba07b8444c7b77b069f30c
|
7
|
+
data.tar.gz: 750f373e085dfa7a91b78c49f187f9df728a7a03794bde4806d49ae8742c52dcc2f462ba9d4ef2db203e6df190d24dede8f124ef2811af5d5ca4085f7c475411
|
data/lib/subroutine/op.rb
CHANGED
@@ -135,7 +135,7 @@ module Subroutine
|
|
135
135
|
end
|
136
136
|
|
137
137
|
if field_config
|
138
|
-
errors.add(field_config.field_name, error.message)
|
138
|
+
errors.add(field_config.field_name, error.type, message: error.message)
|
139
139
|
else
|
140
140
|
errors.add(:base, error_object.full_message(field_name, error.message))
|
141
141
|
end
|
@@ -13,7 +13,10 @@ module Subroutine
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
DEFAULT_OPTIONS = {
|
16
|
+
DEFAULT_OPTIONS = {
|
17
|
+
required: true,
|
18
|
+
lazy: false
|
19
|
+
}.freeze
|
17
20
|
|
18
21
|
attr_reader :output_name
|
19
22
|
|
@@ -28,6 +31,10 @@ module Subroutine
|
|
28
31
|
!!config[:required]
|
29
32
|
end
|
30
33
|
|
34
|
+
def lazy?
|
35
|
+
!!config[:lazy]
|
36
|
+
end
|
37
|
+
|
31
38
|
def inspect
|
32
39
|
"#<#{self.class}:#{object_id} name=#{output_name} config=#{config.inspect}>"
|
33
40
|
end
|
data/lib/subroutine/outputs.rb
CHANGED
@@ -10,11 +10,27 @@ module Subroutine
|
|
10
10
|
|
11
11
|
extend ActiveSupport::Concern
|
12
12
|
|
13
|
+
class LazyExecutor
|
14
|
+
def initialize(value)
|
15
|
+
@value_block = value
|
16
|
+
@executed = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def value
|
20
|
+
return @value if @executed
|
21
|
+
@value = @value_block.respond_to?(:call) ? @value_block.call : @value
|
22
|
+
@executed = true
|
23
|
+
@value
|
24
|
+
end
|
25
|
+
|
26
|
+
def executed?
|
27
|
+
@executed
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
13
31
|
included do
|
14
32
|
class_attribute :output_configurations
|
15
33
|
self.output_configurations = {}
|
16
|
-
|
17
|
-
attr_reader :outputs
|
18
34
|
end
|
19
35
|
|
20
36
|
module ClassMethods
|
@@ -39,20 +55,42 @@ module Subroutine
|
|
39
55
|
@outputs = {} # don't do with_indifferent_access because it will turn provided objects into with_indifferent_access objects, which may not be the desired behavior
|
40
56
|
end
|
41
57
|
|
58
|
+
def outputs
|
59
|
+
unless @outputs_executed
|
60
|
+
@outputs.each_pair do |key, value|
|
61
|
+
@outputs[key] = value.is_a?(LazyExecutor) ? value.value : value
|
62
|
+
end
|
63
|
+
@outputs_executed = true
|
64
|
+
end
|
65
|
+
|
66
|
+
@outputs
|
67
|
+
end
|
68
|
+
|
42
69
|
def output(name, value)
|
43
70
|
name = name.to_sym
|
44
71
|
unless output_configurations.key?(name)
|
45
72
|
raise ::Subroutine::Outputs::UnknownOutputError, name
|
46
73
|
end
|
47
74
|
|
48
|
-
outputs[name] = value
|
75
|
+
@outputs[name] = output_configurations[name].lazy? ? LazyExecutor.new(value) : value
|
49
76
|
end
|
50
77
|
|
51
78
|
def get_output(name)
|
52
79
|
name = name.to_sym
|
53
80
|
raise ::Subroutine::Outputs::UnknownOutputError, name unless output_configurations.key?(name)
|
54
81
|
|
55
|
-
outputs[name]
|
82
|
+
output = @outputs[name]
|
83
|
+
unless output.is_a?(LazyExecutor)
|
84
|
+
output
|
85
|
+
else
|
86
|
+
# if its not executed, validate the type
|
87
|
+
unless output.executed?
|
88
|
+
@outputs[name] = output.value
|
89
|
+
ensure_output_type_valid!(name)
|
90
|
+
end
|
91
|
+
|
92
|
+
@outputs[name]
|
93
|
+
end
|
56
94
|
end
|
57
95
|
|
58
96
|
def validate_outputs!
|
@@ -60,21 +98,27 @@ module Subroutine
|
|
60
98
|
if config.required? && !output_provided?(name)
|
61
99
|
raise ::Subroutine::Outputs::OutputNotSetError, name
|
62
100
|
end
|
63
|
-
unless
|
64
|
-
name
|
65
|
-
raise ::Subroutine::Outputs::InvalidOutputTypeError.new(
|
66
|
-
name: name,
|
67
|
-
actual_type: outputs[name].class,
|
68
|
-
expected_type: output_configurations[name][:type]
|
69
|
-
)
|
101
|
+
unless output_configurations[name].lazy?
|
102
|
+
ensure_output_type_valid!(name)
|
70
103
|
end
|
71
104
|
end
|
72
105
|
end
|
73
106
|
|
107
|
+
def ensure_output_type_valid!(name)
|
108
|
+
return if valid_output_type?(name)
|
109
|
+
|
110
|
+
name = name.to_sym
|
111
|
+
raise ::Subroutine::Outputs::InvalidOutputTypeError.new(
|
112
|
+
name: name,
|
113
|
+
actual_type: @outputs[name].class,
|
114
|
+
expected_type: output_configurations[name][:type]
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
74
118
|
def output_provided?(name)
|
75
119
|
name = name.to_sym
|
76
120
|
|
77
|
-
outputs.key?(name)
|
121
|
+
@outputs.key?(name)
|
78
122
|
end
|
79
123
|
|
80
124
|
def valid_output_type?(name)
|
@@ -84,9 +128,9 @@ module Subroutine
|
|
84
128
|
|
85
129
|
output_configuration = output_configurations[name]
|
86
130
|
return true unless output_configuration[:type]
|
87
|
-
return true if !output_configuration.required? && outputs[name].nil?
|
131
|
+
return true if !output_configuration.required? && @outputs[name].nil?
|
88
132
|
|
89
|
-
outputs[name].is_a?(output_configuration[:type])
|
133
|
+
@outputs[name].is_a?(output_configuration[:type])
|
90
134
|
end
|
91
135
|
end
|
92
136
|
end
|
data/lib/subroutine/version.rb
CHANGED
data/subroutine.gemspec
CHANGED
@@ -11,16 +11,23 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.email = ["mike@mnelson.io"]
|
12
12
|
spec.summary = "Feature-driven operation objects."
|
13
13
|
spec.description = "An interface for creating feature-driven operations."
|
14
|
-
spec.homepage = "https://github.com/
|
14
|
+
spec.homepage = "https://github.com/guideline-tech/subroutine"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
|
-
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
18
|
+
spec.files = Dir["lib/**/*"] + Dir["*.gemspec"]
|
18
19
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
-
spec.test_files = spec.files.grep(%r{^(gemfiles|test)/})
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
23
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
24
|
+
|
22
25
|
spec.add_dependency "activemodel", ">= 6.1"
|
23
26
|
spec.add_dependency "activesupport", ">= 6.1"
|
27
|
+
spec.add_dependency "base64"
|
28
|
+
spec.add_dependency "bigdecimal"
|
29
|
+
spec.add_dependency "logger"
|
30
|
+
spec.add_dependency "mutex_m"
|
24
31
|
|
25
32
|
spec.add_development_dependency "actionpack", ">= 6.1"
|
26
33
|
spec.add_development_dependency "byebug"
|
@@ -29,4 +36,6 @@ Gem::Specification.new do |spec|
|
|
29
36
|
spec.add_development_dependency "minitest-reporters"
|
30
37
|
spec.add_development_dependency "mocha"
|
31
38
|
spec.add_development_dependency "rake"
|
39
|
+
|
40
|
+
spec.required_ruby_version = ">= 3.2.0"
|
32
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: subroutine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Nelson
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: activemodel
|
@@ -38,6 +37,62 @@ dependencies:
|
|
38
37
|
- - ">="
|
39
38
|
- !ruby/object:Gem::Version
|
40
39
|
version: '6.1'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: base64
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: bigdecimal
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: logger
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :runtime
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: mutex_m
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :runtime
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
41
96
|
- !ruby/object:Gem::Dependency
|
42
97
|
name: actionpack
|
43
98
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,27 +198,6 @@ executables: []
|
|
143
198
|
extensions: []
|
144
199
|
extra_rdoc_files: []
|
145
200
|
files:
|
146
|
-
- ".bundler-version"
|
147
|
-
- ".github/CODEOWNERS"
|
148
|
-
- ".github/dependabot.yml"
|
149
|
-
- ".github/workflows/build.yml"
|
150
|
-
- ".gitignore"
|
151
|
-
- ".ruby-gemset"
|
152
|
-
- ".ruby-version"
|
153
|
-
- Appraisals
|
154
|
-
- CHANGELOG.MD
|
155
|
-
- Gemfile
|
156
|
-
- LICENSE.txt
|
157
|
-
- README.md
|
158
|
-
- Rakefile
|
159
|
-
- gemfiles/rails_6.1.gemfile
|
160
|
-
- gemfiles/rails_6.1.gemfile.lock
|
161
|
-
- gemfiles/rails_7.0.gemfile
|
162
|
-
- gemfiles/rails_7.0.gemfile.lock
|
163
|
-
- gemfiles/rails_7.1.gemfile
|
164
|
-
- gemfiles/rails_7.1.gemfile.lock
|
165
|
-
- gemfiles/rails_7.2.gemfile
|
166
|
-
- gemfiles/rails_7.2.gemfile.lock
|
167
201
|
- lib/subroutine.rb
|
168
202
|
- lib/subroutine/association_fields.rb
|
169
203
|
- lib/subroutine/association_fields/association_type_mismatch_error.rb
|
@@ -185,19 +219,12 @@ files:
|
|
185
219
|
- lib/subroutine/type_caster.rb
|
186
220
|
- lib/subroutine/version.rb
|
187
221
|
- subroutine.gemspec
|
188
|
-
|
189
|
-
- test/subroutine/auth_test.rb
|
190
|
-
- test/subroutine/base_test.rb
|
191
|
-
- test/subroutine/fields_test.rb
|
192
|
-
- test/subroutine/outputs_test.rb
|
193
|
-
- test/subroutine/type_caster_test.rb
|
194
|
-
- test/support/ops.rb
|
195
|
-
- test/test_helper.rb
|
196
|
-
homepage: https://github.com/mnelson/subroutine
|
222
|
+
homepage: https://github.com/guideline-tech/subroutine
|
197
223
|
licenses:
|
198
224
|
- MIT
|
199
|
-
metadata:
|
200
|
-
|
225
|
+
metadata:
|
226
|
+
allowed_push_host: https://rubygems.org
|
227
|
+
rubygems_mfa_required: 'true'
|
201
228
|
rdoc_options: []
|
202
229
|
require_paths:
|
203
230
|
- lib
|
@@ -205,31 +232,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
205
232
|
requirements:
|
206
233
|
- - ">="
|
207
234
|
- !ruby/object:Gem::Version
|
208
|
-
version:
|
235
|
+
version: 3.2.0
|
209
236
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
210
237
|
requirements:
|
211
238
|
- - ">="
|
212
239
|
- !ruby/object:Gem::Version
|
213
240
|
version: '0'
|
214
241
|
requirements: []
|
215
|
-
rubygems_version: 3.
|
216
|
-
signing_key:
|
242
|
+
rubygems_version: 3.6.9
|
217
243
|
specification_version: 4
|
218
244
|
summary: Feature-driven operation objects.
|
219
|
-
test_files:
|
220
|
-
- gemfiles/rails_6.1.gemfile
|
221
|
-
- gemfiles/rails_6.1.gemfile.lock
|
222
|
-
- gemfiles/rails_7.0.gemfile
|
223
|
-
- gemfiles/rails_7.0.gemfile.lock
|
224
|
-
- gemfiles/rails_7.1.gemfile
|
225
|
-
- gemfiles/rails_7.1.gemfile.lock
|
226
|
-
- gemfiles/rails_7.2.gemfile
|
227
|
-
- gemfiles/rails_7.2.gemfile.lock
|
228
|
-
- test/subroutine/association_test.rb
|
229
|
-
- test/subroutine/auth_test.rb
|
230
|
-
- test/subroutine/base_test.rb
|
231
|
-
- test/subroutine/fields_test.rb
|
232
|
-
- test/subroutine/outputs_test.rb
|
233
|
-
- test/subroutine/type_caster_test.rb
|
234
|
-
- test/support/ops.rb
|
235
|
-
- test/test_helper.rb
|
245
|
+
test_files: []
|
data/.bundler-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
2.5.22
|
data/.github/CODEOWNERS
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
.github/workflows @guideline-tech/engineering
|
data/.github/dependabot.yml
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
version: 2
|
2
|
-
updates:
|
3
|
-
- package-ecosystem: "github-actions"
|
4
|
-
directory: "/"
|
5
|
-
open-pull-requests-limit: 20
|
6
|
-
schedule:
|
7
|
-
interval: "daily"
|
8
|
-
time: "09:00"
|
9
|
-
timezone: "America/New_York"
|
10
|
-
commit-message:
|
11
|
-
prefix: "[github-actions] "
|
12
|
-
- package-ecosystem: "bundler"
|
13
|
-
directory: "/"
|
14
|
-
schedule:
|
15
|
-
interval: "daily"
|
16
|
-
time: "08:30"
|
17
|
-
timezone: "America/New_York"
|
18
|
-
versioning-strategy: increase
|
19
|
-
open-pull-requests-limit: 20
|
20
|
-
insecure-external-code-execution: deny
|
21
|
-
allow:
|
22
|
-
- dependency-type: "all"
|
23
|
-
commit-message:
|
24
|
-
prefix: "[bundler] "
|
data/.github/workflows/build.yml
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
name: build
|
2
|
-
on:
|
3
|
-
pull_request:
|
4
|
-
push:
|
5
|
-
branches:
|
6
|
-
- main
|
7
|
-
jobs:
|
8
|
-
build:
|
9
|
-
runs-on: ubuntu-latest
|
10
|
-
strategy:
|
11
|
-
fail-fast: false
|
12
|
-
matrix:
|
13
|
-
ruby-version: [3.0, 3.1, 3.2, 3.3]
|
14
|
-
steps:
|
15
|
-
- uses: actions/checkout@v4
|
16
|
-
with:
|
17
|
-
show-progress: "false"
|
18
|
-
- name: get bundler version
|
19
|
-
run: echo "BUNDLER=$(cat .bundler-version)" >> $GITHUB_ENV
|
20
|
-
- uses: ruby/setup-ruby@v1
|
21
|
-
with:
|
22
|
-
ruby-version: ${{ matrix.ruby-version }}
|
23
|
-
bundler: ${{ env.BUNDLER }}
|
24
|
-
bundler-cache: true
|
25
|
-
- run: bundle install
|
26
|
-
- run: bundle exec appraisal install
|
27
|
-
- run: bundle exec appraisal rake test
|
data/.gitignore
DELETED
data/.ruby-gemset
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
subroutine
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
3.2.6
|
data/Appraisals
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
appraise "rails-6.1" do
|
2
|
-
gem 'activemodel', '~> 6.1.0'
|
3
|
-
gem 'actionpack', '~> 6.1.0'
|
4
|
-
end
|
5
|
-
|
6
|
-
appraise "rails-7.0" do
|
7
|
-
gem 'activemodel', '~> 7.0.0'
|
8
|
-
gem 'actionpack', '~> 7.0.0'
|
9
|
-
end
|
10
|
-
|
11
|
-
appraise "rails-7.1" do
|
12
|
-
gem 'activemodel', '~> 7.1.0'
|
13
|
-
gem 'actionpack', '~> 7.1.0'
|
14
|
-
end
|
15
|
-
|
16
|
-
appraise "rails-7.2" do
|
17
|
-
gem 'activemodel', '~> 7.2.0'
|
18
|
-
gem 'actionpack', '~> 7.2.0'
|
19
|
-
end
|
data/CHANGELOG.MD
DELETED
@@ -1,184 +0,0 @@
|
|
1
|
-
# Changelog
|
2
|
-
|
3
|
-
## Subroutine 4.2.0
|
4
|
-
|
5
|
-
If you are using polymorphic association fields, you can now customize how Subroutine
|
6
|
-
resolves those class names to a ruby class by setting a global callable/lambda/proc:
|
7
|
-
|
8
|
-
```ruby
|
9
|
-
::Subroutine.constantize_polymorphic_class_name = ->(class_name) do
|
10
|
-
class_name.classify.constantize
|
11
|
-
end
|
12
|
-
```
|
13
|
-
|
14
|
-
## Subroutine 4.1.4
|
15
|
-
|
16
|
-
Fields using the time/timestamp/datetime caster will now default back to the old behavior, and use a `precision:` option to opt-in to the new behavior introduced in `v4.1.1`.
|
17
|
-
|
18
|
-
`precision: :seconds` will retain the old behavior of always parsing to a new Time object
|
19
|
-
with floored sub-second precision, but applied more forcefully than before as it would have parsed whatever you passed to it. (This is the default, now.)
|
20
|
-
|
21
|
-
`precision: :high` will now use the new functionality of re-using Time objects when they
|
22
|
-
are passed in, or if not parsing exactly the provided string as to a Time object.
|
23
|
-
|
24
|
-
## Subroutine 4.1.1
|
25
|
-
|
26
|
-
Fields using the time/timestamp/datetime caster will now return exactly the passed in value
|
27
|
-
if it acts like a time object (`acts_like?(:time)`/`acts_like_time?`), instead of serializing
|
28
|
-
to string and re-parsing to a Time object. This fixes issues with losing usec precision.
|
29
|
-
|
30
|
-
## Subroutine 4.1.0
|
31
|
-
|
32
|
-
A field can now opt out of the natural assignment behavior of ActiveSupport::HashWithIndifferentAccess. Top level param groups are still accessible via indifferent access but if a field sets the `bypass_indifferent_assignment` option to `true` the HashWithIndifferentAccess assignment behavior will be bypassed in favor of direct Hash-like assignment.
|
33
|
-
|
34
|
-
```ruby
|
35
|
-
class MyOp < Subroutine::Op
|
36
|
-
|
37
|
-
object :some_hash
|
38
|
-
object :some_other_hash, bypass_indifferent_assignment: true
|
39
|
-
|
40
|
-
end
|
41
|
-
```
|
42
|
-
|
43
|
-
## Subroutine 4.0.1
|
44
|
-
|
45
|
-
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.
|
46
|
-
|
47
|
-
## Subroutine 4.0
|
48
|
-
|
49
|
-
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.
|
50
|
-
|
51
|
-
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`.
|
52
|
-
|
53
|
-
Polymorphic association fields now resolve class names via `klass.camelize.constantize`,
|
54
|
-
previously was `klass.classify.constantize`.
|
55
|
-
|
56
|
-
## Subroutine 3.0
|
57
|
-
|
58
|
-
Add support for Rails 6.1. Drop support for Rails 6.0 and lower.
|
59
|
-
|
60
|
-
## Subroutine 2.3
|
61
|
-
|
62
|
-
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.
|
63
|
-
|
64
|
-
## Subroutine 2.2
|
65
|
-
|
66
|
-
Add `type` validation for Output.
|
67
|
-
|
68
|
-
## Subroutine 2.0
|
69
|
-
|
70
|
-
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.
|
71
|
-
|
72
|
-
**Note:** 2.0.0 was released with a bug and subsequently yanked. 2.0.1 is the first available 2.x version.
|
73
|
-
|
74
|
-
## Subroutine 1.0
|
75
|
-
|
76
|
-
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.
|
77
|
-
|
78
|
-
### Subroutine::Fields
|
79
|
-
|
80
|
-
`Subroutine::Fields` was completely refactored to manage field declaration, configuration, and access in a more systematic and safer way.
|
81
|
-
|
82
|
-
`Op._fields` was removed in favor of `Op.field_configurations`. `field_configurations` is a hash with keys of the field name and values of `FieldConfiguration` objects. FieldConfiguration objects are SimpleDelegates to the underlying option hashes. They answer questions and provide a mechanism for validating the configuration of a field.
|
83
|
-
|
84
|
-
Fields can be accessed via helpers and accessors can be managed by the field declration. Helpers include `get_field`, `set_field`, and `clear_field`.
|
85
|
-
|
86
|
-
```ruby
|
87
|
-
class SomeOp < ::Subroutine::Op
|
88
|
-
string :foo, read_accessor: field_reader: false, field_writer: true
|
89
|
-
|
90
|
-
def perform
|
91
|
-
self.foo = "bar"
|
92
|
-
self.foo # NoMethodError
|
93
|
-
self.get_field(:foo) # => "bar"
|
94
|
-
end
|
95
|
-
end
|
96
|
-
```
|
97
|
-
|
98
|
-
Fields can be omitted from mass assignment, meaning they would not be respected via constructor signatures.
|
99
|
-
|
100
|
-
```ruby
|
101
|
-
class SomeOp < ::Subroutine::Op
|
102
|
-
string :foo, mass_assignable: false
|
103
|
-
def perform
|
104
|
-
puts foo
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
SomeOp.submit!(foo: "Hello World!") # raises ::Subroutine::Fields::MassAssignmentError
|
109
|
-
SomeOp.new{|op| op.foo = "Hello World!" }.submit! # prints "Hello World!"
|
110
|
-
```
|
111
|
-
|
112
|
-
This is especially useful when dealing with user input and potentially unsafe attributes.
|
113
|
-
|
114
|
-
```ruby
|
115
|
-
class UserUpdateOp < ::Op
|
116
|
-
association :user
|
117
|
-
string :first_name
|
118
|
-
string :last_name
|
119
|
-
integer :credit_balance_cents, mass_assignable: false
|
120
|
-
|
121
|
-
def perform
|
122
|
-
user.update(params)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
# some_controller.rb
|
127
|
-
def update
|
128
|
-
UserUpdateOp.submit!(params.merge(user: current_user))
|
129
|
-
end
|
130
|
-
```
|
131
|
-
|
132
|
-
Field groups were added as well, allowing you to access subsets of the fields easily.
|
133
|
-
|
134
|
-
```ruby
|
135
|
-
class AccountUpdateOp < ::Op
|
136
|
-
association :account
|
137
|
-
|
138
|
-
with_options group: :user do
|
139
|
-
string :first_name
|
140
|
-
string :last_name
|
141
|
-
date :dob
|
142
|
-
end
|
143
|
-
|
144
|
-
with_options group: :business do
|
145
|
-
string :company_name
|
146
|
-
string :ein
|
147
|
-
end
|
148
|
-
|
149
|
-
def perform
|
150
|
-
account.user.update(user_params)
|
151
|
-
account.business.update(business_params)
|
152
|
-
end
|
153
|
-
|
154
|
-
end
|
155
|
-
```
|
156
|
-
|
157
|
-
ActionController::Parameters from Rails 5+ are now transformed to a hash in `Subroutine::Fields` by default. This means strong parameters are essentially unused when passing `Subroutine::Fields`.
|
158
|
-
|
159
|
-
Read more about field management and access in https://github.com/guideline-tech/subroutine/wiki/Param-Usage
|
160
|
-
|
161
|
-
### Subroutine::Association
|
162
|
-
|
163
|
-
The `Subroutine::Association` module has been moved to `Subroutine::AssociationFields`.
|
164
|
-
|
165
|
-
Only native types are stored in params now. The objects loaded from associations are stored in an `association_cache`. This ensures access to fields are consistent regardless of the inputs.
|
166
|
-
|
167
|
-
```ruby
|
168
|
-
class SomeOp < ::Subroutine::Op
|
169
|
-
association :user
|
170
|
-
association :resource, polymorphic: true
|
171
|
-
end
|
172
|
-
|
173
|
-
user = User.find(4)
|
174
|
-
|
175
|
-
op = SomeOp.new(user: user, resource: user)
|
176
|
-
op.params #=> { user_id: 4, resource_type: "User", resource_id: 4 }
|
177
|
-
op.params_with_association #=> { user: <User:103204 @id=4>, resource: <User:103204 @id=4> }
|
178
|
-
|
179
|
-
op = SomeOp.new(user_id: user.id, resource_type: "User", resource_id: user.id)
|
180
|
-
op.params #=> { user_id: 4, resource_type: "User", resource_id: 4 }
|
181
|
-
op.params_with_association #=> { user: <User:290053 @id=4>, resource: <User:29042 @id=4> }
|
182
|
-
```
|
183
|
-
|
184
|
-
Assignment of associations now validates the type. If an association is not polymorphic, the type will be validated against the expected type.
|