subroutine 4.4.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/version.rb +1 -1
- data/subroutine.gemspec +5 -2
- metadata +6 -53
- 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 -26
- data/CHANGELOG.MD +0 -189
- data/Gemfile +0 -8
- data/LICENSE.txt +0 -22
- data/README.md +0 -88
- data/Rakefile +0 -10
- data/gemfiles/rails_6.1.gemfile +0 -10
- data/gemfiles/rails_6.1.gemfile.lock +0 -177
- data/gemfiles/rails_7.0.gemfile +0 -10
- data/gemfiles/rails_7.0.gemfile.lock +0 -174
- data/gemfiles/rails_7.1.gemfile +0 -9
- data/gemfiles/rails_7.1.gemfile.lock +0 -189
- data/gemfiles/rails_7.2.gemfile +0 -9
- data/gemfiles/rails_7.2.gemfile.lock +0 -191
- data/gemfiles/rails_8.0.gemfile +0 -9
- data/gemfiles/rails_8.0.gemfile.lock +0 -204
- 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 -162
- 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
|
data/lib/subroutine/version.rb
CHANGED
data/subroutine.gemspec
CHANGED
@@ -14,11 +14,14 @@ Gem::Specification.new do |spec|
|
|
14
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"
|
24
27
|
spec.add_dependency "base64"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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
|
@@ -198,29 +198,6 @@ executables: []
|
|
198
198
|
extensions: []
|
199
199
|
extra_rdoc_files: []
|
200
200
|
files:
|
201
|
-
- ".bundler-version"
|
202
|
-
- ".github/CODEOWNERS"
|
203
|
-
- ".github/dependabot.yml"
|
204
|
-
- ".github/workflows/build.yml"
|
205
|
-
- ".gitignore"
|
206
|
-
- ".ruby-gemset"
|
207
|
-
- ".ruby-version"
|
208
|
-
- Appraisals
|
209
|
-
- CHANGELOG.MD
|
210
|
-
- Gemfile
|
211
|
-
- LICENSE.txt
|
212
|
-
- README.md
|
213
|
-
- Rakefile
|
214
|
-
- gemfiles/rails_6.1.gemfile
|
215
|
-
- gemfiles/rails_6.1.gemfile.lock
|
216
|
-
- gemfiles/rails_7.0.gemfile
|
217
|
-
- gemfiles/rails_7.0.gemfile.lock
|
218
|
-
- gemfiles/rails_7.1.gemfile
|
219
|
-
- gemfiles/rails_7.1.gemfile.lock
|
220
|
-
- gemfiles/rails_7.2.gemfile
|
221
|
-
- gemfiles/rails_7.2.gemfile.lock
|
222
|
-
- gemfiles/rails_8.0.gemfile
|
223
|
-
- gemfiles/rails_8.0.gemfile.lock
|
224
201
|
- lib/subroutine.rb
|
225
202
|
- lib/subroutine/association_fields.rb
|
226
203
|
- lib/subroutine/association_fields/association_type_mismatch_error.rb
|
@@ -242,18 +219,12 @@ files:
|
|
242
219
|
- lib/subroutine/type_caster.rb
|
243
220
|
- lib/subroutine/version.rb
|
244
221
|
- subroutine.gemspec
|
245
|
-
- test/subroutine/association_test.rb
|
246
|
-
- test/subroutine/auth_test.rb
|
247
|
-
- test/subroutine/base_test.rb
|
248
|
-
- test/subroutine/fields_test.rb
|
249
|
-
- test/subroutine/outputs_test.rb
|
250
|
-
- test/subroutine/type_caster_test.rb
|
251
|
-
- test/support/ops.rb
|
252
|
-
- test/test_helper.rb
|
253
222
|
homepage: https://github.com/guideline-tech/subroutine
|
254
223
|
licenses:
|
255
224
|
- MIT
|
256
|
-
metadata:
|
225
|
+
metadata:
|
226
|
+
allowed_push_host: https://rubygems.org
|
227
|
+
rubygems_mfa_required: 'true'
|
257
228
|
rdoc_options: []
|
258
229
|
require_paths:
|
259
230
|
- lib
|
@@ -268,25 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
239
|
- !ruby/object:Gem::Version
|
269
240
|
version: '0'
|
270
241
|
requirements: []
|
271
|
-
rubygems_version: 3.6.
|
242
|
+
rubygems_version: 3.6.9
|
272
243
|
specification_version: 4
|
273
244
|
summary: Feature-driven operation objects.
|
274
|
-
test_files:
|
275
|
-
- gemfiles/rails_6.1.gemfile
|
276
|
-
- gemfiles/rails_6.1.gemfile.lock
|
277
|
-
- gemfiles/rails_7.0.gemfile
|
278
|
-
- gemfiles/rails_7.0.gemfile.lock
|
279
|
-
- gemfiles/rails_7.1.gemfile
|
280
|
-
- gemfiles/rails_7.1.gemfile.lock
|
281
|
-
- gemfiles/rails_7.2.gemfile
|
282
|
-
- gemfiles/rails_7.2.gemfile.lock
|
283
|
-
- gemfiles/rails_8.0.gemfile
|
284
|
-
- gemfiles/rails_8.0.gemfile.lock
|
285
|
-
- test/subroutine/association_test.rb
|
286
|
-
- test/subroutine/auth_test.rb
|
287
|
-
- test/subroutine/base_test.rb
|
288
|
-
- test/subroutine/fields_test.rb
|
289
|
-
- test/subroutine/outputs_test.rb
|
290
|
-
- test/subroutine/type_caster_test.rb
|
291
|
-
- test/support/ops.rb
|
292
|
-
- 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.2, 3.3, 3.4]
|
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.3.7
|
data/Appraisals
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
appraise "rails-6.1" do
|
2
|
-
gem 'activemodel', '~> 6.1.0'
|
3
|
-
gem 'actionpack', '~> 6.1.0'
|
4
|
-
gem 'concurrent-ruby', '1.3.4'
|
5
|
-
end
|
6
|
-
|
7
|
-
appraise "rails-7.0" do
|
8
|
-
gem 'activemodel', '~> 7.0.0'
|
9
|
-
gem 'actionpack', '~> 7.0.0'
|
10
|
-
gem 'concurrent-ruby', '1.3.4'
|
11
|
-
end
|
12
|
-
|
13
|
-
appraise "rails-7.1" do
|
14
|
-
gem 'activemodel', '~> 7.1.0'
|
15
|
-
gem 'actionpack', '~> 7.1.0'
|
16
|
-
end
|
17
|
-
|
18
|
-
appraise "rails-7.2" do
|
19
|
-
gem 'activemodel', '~> 7.2.0'
|
20
|
-
gem 'actionpack', '~> 7.2.0'
|
21
|
-
end
|
22
|
-
|
23
|
-
appraise "rails-8.0" do
|
24
|
-
gem 'activemodel', '~> 8.0.0'
|
25
|
-
gem 'actionpack', '~> 8.0.0'
|
26
|
-
end
|
data/CHANGELOG.MD
DELETED
@@ -1,189 +0,0 @@
|
|
1
|
-
# Changelog
|
2
|
-
|
3
|
-
## Subroutine 4.3.0
|
4
|
-
|
5
|
-
Default repository to Ruby 3.3.7, and gem to a minimum version of 3.2.0.
|
6
|
-
Tests continue to run against both Ruby 3.2 and Ruby 3.3.
|
7
|
-
|
8
|
-
## Subroutine 4.2.0
|
9
|
-
|
10
|
-
If you are using polymorphic association fields, you can now customize how Subroutine
|
11
|
-
resolves those class names to a ruby class by setting a global callable/lambda/proc:
|
12
|
-
|
13
|
-
```ruby
|
14
|
-
::Subroutine.constantize_polymorphic_class_name = ->(class_name) do
|
15
|
-
class_name.classify.constantize
|
16
|
-
end
|
17
|
-
```
|
18
|
-
|
19
|
-
## Subroutine 4.1.4
|
20
|
-
|
21
|
-
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`.
|
22
|
-
|
23
|
-
`precision: :seconds` will retain the old behavior of always parsing to a new Time object
|
24
|
-
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.)
|
25
|
-
|
26
|
-
`precision: :high` will now use the new functionality of re-using Time objects when they
|
27
|
-
are passed in, or if not parsing exactly the provided string as to a Time object.
|
28
|
-
|
29
|
-
## Subroutine 4.1.1
|
30
|
-
|
31
|
-
Fields using the time/timestamp/datetime caster will now return exactly the passed in value
|
32
|
-
if it acts like a time object (`acts_like?(:time)`/`acts_like_time?`), instead of serializing
|
33
|
-
to string and re-parsing to a Time object. This fixes issues with losing usec precision.
|
34
|
-
|
35
|
-
## Subroutine 4.1.0
|
36
|
-
|
37
|
-
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.
|
38
|
-
|
39
|
-
```ruby
|
40
|
-
class MyOp < Subroutine::Op
|
41
|
-
|
42
|
-
object :some_hash
|
43
|
-
object :some_other_hash, bypass_indifferent_assignment: true
|
44
|
-
|
45
|
-
end
|
46
|
-
```
|
47
|
-
|
48
|
-
## Subroutine 4.0.1
|
49
|
-
|
50
|
-
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.
|
51
|
-
|
52
|
-
## Subroutine 4.0
|
53
|
-
|
54
|
-
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.
|
55
|
-
|
56
|
-
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`.
|
57
|
-
|
58
|
-
Polymorphic association fields now resolve class names via `klass.camelize.constantize`,
|
59
|
-
previously was `klass.classify.constantize`.
|
60
|
-
|
61
|
-
## Subroutine 3.0
|
62
|
-
|
63
|
-
Add support for Rails 6.1. Drop support for Rails 6.0 and lower.
|
64
|
-
|
65
|
-
## Subroutine 2.3
|
66
|
-
|
67
|
-
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.
|
68
|
-
|
69
|
-
## Subroutine 2.2
|
70
|
-
|
71
|
-
Add `type` validation for Output.
|
72
|
-
|
73
|
-
## Subroutine 2.0
|
74
|
-
|
75
|
-
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.
|
76
|
-
|
77
|
-
**Note:** 2.0.0 was released with a bug and subsequently yanked. 2.0.1 is the first available 2.x version.
|
78
|
-
|
79
|
-
## Subroutine 1.0
|
80
|
-
|
81
|
-
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.
|
82
|
-
|
83
|
-
### Subroutine::Fields
|
84
|
-
|
85
|
-
`Subroutine::Fields` was completely refactored to manage field declaration, configuration, and access in a more systematic and safer way.
|
86
|
-
|
87
|
-
`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.
|
88
|
-
|
89
|
-
Fields can be accessed via helpers and accessors can be managed by the field declration. Helpers include `get_field`, `set_field`, and `clear_field`.
|
90
|
-
|
91
|
-
```ruby
|
92
|
-
class SomeOp < ::Subroutine::Op
|
93
|
-
string :foo, read_accessor: field_reader: false, field_writer: true
|
94
|
-
|
95
|
-
def perform
|
96
|
-
self.foo = "bar"
|
97
|
-
self.foo # NoMethodError
|
98
|
-
self.get_field(:foo) # => "bar"
|
99
|
-
end
|
100
|
-
end
|
101
|
-
```
|
102
|
-
|
103
|
-
Fields can be omitted from mass assignment, meaning they would not be respected via constructor signatures.
|
104
|
-
|
105
|
-
```ruby
|
106
|
-
class SomeOp < ::Subroutine::Op
|
107
|
-
string :foo, mass_assignable: false
|
108
|
-
def perform
|
109
|
-
puts foo
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
SomeOp.submit!(foo: "Hello World!") # raises ::Subroutine::Fields::MassAssignmentError
|
114
|
-
SomeOp.new{|op| op.foo = "Hello World!" }.submit! # prints "Hello World!"
|
115
|
-
```
|
116
|
-
|
117
|
-
This is especially useful when dealing with user input and potentially unsafe attributes.
|
118
|
-
|
119
|
-
```ruby
|
120
|
-
class UserUpdateOp < ::Op
|
121
|
-
association :user
|
122
|
-
string :first_name
|
123
|
-
string :last_name
|
124
|
-
integer :credit_balance_cents, mass_assignable: false
|
125
|
-
|
126
|
-
def perform
|
127
|
-
user.update(params)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# some_controller.rb
|
132
|
-
def update
|
133
|
-
UserUpdateOp.submit!(params.merge(user: current_user))
|
134
|
-
end
|
135
|
-
```
|
136
|
-
|
137
|
-
Field groups were added as well, allowing you to access subsets of the fields easily.
|
138
|
-
|
139
|
-
```ruby
|
140
|
-
class AccountUpdateOp < ::Op
|
141
|
-
association :account
|
142
|
-
|
143
|
-
with_options group: :user do
|
144
|
-
string :first_name
|
145
|
-
string :last_name
|
146
|
-
date :dob
|
147
|
-
end
|
148
|
-
|
149
|
-
with_options group: :business do
|
150
|
-
string :company_name
|
151
|
-
string :ein
|
152
|
-
end
|
153
|
-
|
154
|
-
def perform
|
155
|
-
account.user.update(user_params)
|
156
|
-
account.business.update(business_params)
|
157
|
-
end
|
158
|
-
|
159
|
-
end
|
160
|
-
```
|
161
|
-
|
162
|
-
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`.
|
163
|
-
|
164
|
-
Read more about field management and access in https://github.com/guideline-tech/subroutine/wiki/Param-Usage
|
165
|
-
|
166
|
-
### Subroutine::Association
|
167
|
-
|
168
|
-
The `Subroutine::Association` module has been moved to `Subroutine::AssociationFields`.
|
169
|
-
|
170
|
-
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.
|
171
|
-
|
172
|
-
```ruby
|
173
|
-
class SomeOp < ::Subroutine::Op
|
174
|
-
association :user
|
175
|
-
association :resource, polymorphic: true
|
176
|
-
end
|
177
|
-
|
178
|
-
user = User.find(4)
|
179
|
-
|
180
|
-
op = SomeOp.new(user: user, resource: user)
|
181
|
-
op.params #=> { user_id: 4, resource_type: "User", resource_id: 4 }
|
182
|
-
op.params_with_association #=> { user: <User:103204 @id=4>, resource: <User:103204 @id=4> }
|
183
|
-
|
184
|
-
op = SomeOp.new(user_id: user.id, resource_type: "User", resource_id: user.id)
|
185
|
-
op.params #=> { user_id: 4, resource_type: "User", resource_id: 4 }
|
186
|
-
op.params_with_association #=> { user: <User:290053 @id=4>, resource: <User:29042 @id=4> }
|
187
|
-
```
|
188
|
-
|
189
|
-
Assignment of associations now validates the type. If an association is not polymorphic, the type will be validated against the expected type.
|
data/Gemfile
DELETED
data/LICENSE.txt
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
Copyright (c) 2015 Mike Nelson
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
# Subroutine
|
2
|
-
|
3
|
-
A gem that provides an interface for creating feature-driven operations. You've probably heard at least one of these terms: "service objects", "form objects", "intentions", or "commands". Subroutine calls these "ops" and really it's just about enabling clear, concise, testable, and meaningful code.
|
4
|
-
|
5
|
-
## Example
|
6
|
-
|
7
|
-
So you need to sign up a user? or maybe update one's account? or change a password? or maybe you need to sign up a business along with a user, associate them, send an email, and queue a worker in a single request? Not a problem, create an op for any of these use cases. Here's the signup example.
|
8
|
-
|
9
|
-
```ruby
|
10
|
-
class SignupOp < ::Subroutine::Op
|
11
|
-
|
12
|
-
string :name
|
13
|
-
string :email
|
14
|
-
string :password
|
15
|
-
|
16
|
-
string :company_name
|
17
|
-
|
18
|
-
validates :name, presence: true
|
19
|
-
validates :email, presence: true
|
20
|
-
validates :password, presence: true
|
21
|
-
validates :company_name, presence: true
|
22
|
-
|
23
|
-
outputs :user
|
24
|
-
outputs :business, type: Business # validate that output type is an instance of Business
|
25
|
-
outputs :heavy_operation, lazy: true # delay the execution of the output until accessed
|
26
|
-
|
27
|
-
protected
|
28
|
-
|
29
|
-
def perform
|
30
|
-
u = create_user!
|
31
|
-
b = create_business!(u)
|
32
|
-
|
33
|
-
deliver_welcome_email(u)
|
34
|
-
|
35
|
-
output :user, u
|
36
|
-
output :business, b
|
37
|
-
output :heavy_operation, -> { some_heavy_operation }
|
38
|
-
end
|
39
|
-
|
40
|
-
def create_user!
|
41
|
-
User.create!(name: name, email: email, password: password)
|
42
|
-
end
|
43
|
-
|
44
|
-
def create_business!(owner)
|
45
|
-
Business.create!(company_name: company_name, owner: owner)
|
46
|
-
end
|
47
|
-
|
48
|
-
def some_heavy_operation
|
49
|
-
# ...
|
50
|
-
end
|
51
|
-
|
52
|
-
def deliver_welcome_email(u)
|
53
|
-
UserMailer.welcome(u.id).deliver_later
|
54
|
-
end
|
55
|
-
end
|
56
|
-
```
|
57
|
-
|
58
|
-
## So why use this?
|
59
|
-
|
60
|
-
- Avoid cluttering models or controllers with logic only applicable to one intention. You also don't need strong parameters because the inputs to the Op are well-defined.
|
61
|
-
- Test the Op in isolation
|
62
|
-
- Clear and concise intention in a single file
|
63
|
-
- Multi-model operations become simple
|
64
|
-
|
65
|
-
## Continue Reading
|
66
|
-
|
67
|
-
- [Implementing an Op](https://github.com/guideline-tech/subroutine/wiki/Implementing-an-Op)
|
68
|
-
- [Using an Op](https://github.com/guideline-tech/subroutine/wiki/Using-an-Op)
|
69
|
-
- [Errors](https://github.com/guideline-tech/subroutine/wiki/Errors)
|
70
|
-
- [Basic Usage in Rails](https://github.com/guideline-tech/subroutine/wiki/Rails-Usage)
|
71
|
-
|
72
|
-
## Development
|
73
|
-
|
74
|
-
Run the test suite against current Rails version:
|
75
|
-
|
76
|
-
```
|
77
|
-
bundle exec rake test
|
78
|
-
```
|
79
|
-
|
80
|
-
Run the test suite against all supported Rails versions using `appraisal`:
|
81
|
-
|
82
|
-
```
|
83
|
-
bundle exec appraisal rake test
|
84
|
-
```
|
85
|
-
|
86
|
-
For help updating the `Gemfile` or changing supported Rails versions, see the `appraisal` gem [README](https://github.com/thoughtbot/appraisal#usage).
|
87
|
-
|
88
|
-
Note that the gemfiles in `gemfiles/*` are auto-generated by `appraisal` and should not be modified directly.
|
data/Rakefile
DELETED