lite-command 2.1.1 → 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +30 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +1 -1
- data/README.md +66 -47
- data/lib/lite/command/attribute.rb +10 -1
- data/lib/lite/command/version.rb +1 -1
- metadata +3 -3
- data/.travis.yml +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a80b87ce018b8d2c4097c6a3c1e22869b51089e7bb1e040f8b4be33f72a0cc47
|
4
|
+
data.tar.gz: 8db4399804fbdcd1ef0a42baa4ae902d864b8283cdb26ea0b8fdbac123f569c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a00f4af0af22973c3ec4fa43e635d7bca891b97a3cebb264b52955dd1a9e9b963045297569cae7a6144820cb3cea0907044b73200f1f9a0781befded2fd1e0c1
|
7
|
+
data.tar.gz: 7a0239e49c62c900b1df6c39e0d7a6c97ac7768bb0e6b8b1292b44726a59e5f336a9d562c20fd4979f9950c1e7c3a57528ae2d83e5e85aee34379a75fdfbe1ce
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: CI
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches:
|
5
|
+
- master
|
6
|
+
pull_request:
|
7
|
+
branches:
|
8
|
+
- master
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
ruby:
|
15
|
+
- '3.3'
|
16
|
+
- '3.2'
|
17
|
+
steps:
|
18
|
+
- name: Checkout
|
19
|
+
uses: actions/checkout@v4
|
20
|
+
- name: Install
|
21
|
+
uses: ruby/setup-ruby@v1
|
22
|
+
with:
|
23
|
+
ruby-version: ${{matrix.ruby}}
|
24
|
+
bundler-cache: true
|
25
|
+
- name: RSpec
|
26
|
+
run: bundle exec rspec .
|
27
|
+
- name: Fasterer
|
28
|
+
run: bundle exec fasterer .
|
29
|
+
- name: Rubocop
|
30
|
+
run: bundle exec rubocop .
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [2.1.2] - 2024-10-08
|
10
|
+
### Added
|
11
|
+
- Allow `filled` to pass `{ empty: false }` to check if value is empty
|
12
|
+
|
9
13
|
## [2.1.1] - 2024-10-06
|
10
14
|
### Added
|
11
15
|
- Added on_status hook to `execute!`
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -8,8 +8,8 @@ Lite::Command provides an API for building simple and complex command based serv
|
|
8
8
|
|
9
9
|
Add this line to your application's Gemfile:
|
10
10
|
|
11
|
-
> [!
|
12
|
-
> Gem versions
|
11
|
+
> [!WARNING]
|
12
|
+
> Gem versions `~> 2.0` are borked.
|
13
13
|
> Version `~> 2.1.0` is the suggested working version.
|
14
14
|
|
15
15
|
```ruby
|
@@ -48,7 +48,8 @@ Or install it yourself as:
|
|
48
48
|
|
49
49
|
## Setup
|
50
50
|
|
51
|
-
Defining a command is as simple as
|
51
|
+
Defining a command is as simple as inheriting the base class and
|
52
|
+
adding a `call` method to a command object (required).
|
52
53
|
|
53
54
|
```ruby
|
54
55
|
class CalculatePower < Lite::Command::Base
|
@@ -133,7 +134,7 @@ class CalculatePower < Lite::Command::Base
|
|
133
134
|
end
|
134
135
|
|
135
136
|
CalculatePower.call!(...)
|
136
|
-
#=> raises CalculatePower::
|
137
|
+
#=> raises CalculatePower::Failure
|
137
138
|
```
|
138
139
|
|
139
140
|
## Context
|
@@ -142,6 +143,9 @@ Accessing the call arguments can be done through its internal context.
|
|
142
143
|
It can be used as internal storage to be accessed by it self and any
|
143
144
|
of its children commands.
|
144
145
|
|
146
|
+
> [!NOTE]
|
147
|
+
> Attributes that do **NOT** exist on the context will return `nil`.
|
148
|
+
|
145
149
|
```ruby
|
146
150
|
class CalculatePower < Lite::Command::Base
|
147
151
|
|
@@ -152,8 +156,9 @@ class CalculatePower < Lite::Command::Base
|
|
152
156
|
|
153
157
|
end
|
154
158
|
|
155
|
-
|
156
|
-
|
159
|
+
cmd = CalculatePower.call(a: 2, b: 3)
|
160
|
+
cmd.context.result #=> 8
|
161
|
+
cmd.ctx.fake #=> nil
|
157
162
|
```
|
158
163
|
|
159
164
|
### Attributes
|
@@ -167,7 +172,7 @@ method which automatically delegates to `context`.
|
|
167
172
|
| `from` | Symbol, String | `:context` | The object containing the attribute. |
|
168
173
|
| `types` | Symbol, String, Array, Proc | | The allowed class types of the attribute value. |
|
169
174
|
| `required` | Symbol, String, Boolean, Proc | `false` | The attribute must be passed to the context or delegatable (no matter the value). |
|
170
|
-
| `filled` | Symbol, String, Boolean, Proc | `false` | The attribute value must be not be `nil`. |
|
175
|
+
| `filled` | Symbol, String, Boolean, Proc, Hash | `false` | The attribute value must be not be `nil`. Prevent empty values using `{ empty: false }` |
|
171
176
|
|
172
177
|
> [!NOTE]
|
173
178
|
> If optioned with some similar to `filled: true, types: [String, NilClass]`
|
@@ -180,7 +185,7 @@ class CalculatePower < Lite::Command::Base
|
|
180
185
|
|
181
186
|
attribute :a, :b
|
182
187
|
attribute :c, :d, from: :remote_storage, types: [Integer, Float]
|
183
|
-
attribute :x, :y, from: :local_storage, if: :signed_in?
|
188
|
+
attribute :x, :y, from: :local_storage, filled: { empty: false }, if: :signed_in?
|
184
189
|
|
185
190
|
def call
|
186
191
|
context.result =
|
@@ -202,20 +207,20 @@ class CalculatePower < Lite::Command::Base
|
|
202
207
|
end
|
203
208
|
|
204
209
|
# With valid options:
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
# With invalid options
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
210
|
+
rs = RemoteStorage.new(c: 2, d: 2, j: 99)
|
211
|
+
cmd = CalculatePower.call(a: 2, b: 2, remote_storage: rs)
|
212
|
+
cmd.status #=> "success"
|
213
|
+
cmd.context.result #=> 6
|
214
|
+
|
215
|
+
# With invalid options:
|
216
|
+
cmd = CalculatePower.call
|
217
|
+
cmd.status #=> "invalid"
|
218
|
+
cmd.reason #=> "Invalid context attributes"
|
219
|
+
cmd.metadata #=> {
|
220
|
+
#=> context: ["a is required", "remote_storage must be filled"],
|
221
|
+
#=> remote_storage: ["d type invalid"]
|
222
|
+
#=> local_storage: ["is not defined or an attribute"]
|
223
|
+
#=> }
|
219
224
|
```
|
220
225
|
|
221
226
|
## States
|
@@ -232,18 +237,16 @@ command.metadata #=> {
|
|
232
237
|
> States are automatically transitioned and should **NEVER** be altered manually.
|
233
238
|
|
234
239
|
```ruby
|
235
|
-
|
240
|
+
cmd = CalculatePower.call
|
241
|
+
cmd.state #=> "complete"
|
236
242
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
end
|
243
|
+
cmd.pending? #=> false
|
244
|
+
cmd.executing? #=> false
|
245
|
+
cmd.complete? #=> true
|
246
|
+
cmd.interrupted? #=> false
|
242
247
|
|
243
|
-
|
244
|
-
|
245
|
-
command.pending? #=> false
|
246
|
-
command.executed? #=> false
|
248
|
+
# `complete` or `interrupted`
|
249
|
+
cmd.executed?
|
247
250
|
```
|
248
251
|
|
249
252
|
## Statuses
|
@@ -285,14 +288,25 @@ class CalculatePower < Lite::Command::Base
|
|
285
288
|
|
286
289
|
end
|
287
290
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
291
|
+
cmd = CalculatePower.call(a: 1, b: 3)
|
292
|
+
cmd.status #=> "noop"
|
293
|
+
cmd.reason #=> "Anything to the power of 1 is 1"
|
294
|
+
cmd.metadata #=> { i18n: "some.key" }
|
295
|
+
|
296
|
+
cmd.success? #=> false
|
297
|
+
cmd.noop? #=> true
|
298
|
+
cmd.noop?("Other reason") #=> false
|
299
|
+
cmd.invalid? #=> false
|
300
|
+
cmd.failure? #=> false
|
301
|
+
cmd.error? #=> false
|
302
|
+
|
303
|
+
# `success` or `noop`
|
304
|
+
cmd.ok? #=> true
|
305
|
+
cmd.ok?("Other reason") #=> false
|
306
|
+
|
307
|
+
# NOT `success`
|
308
|
+
cmd.fault? #=> true
|
309
|
+
cmd.fault?("Other reason") #=> false
|
296
310
|
```
|
297
311
|
|
298
312
|
## Callbacks
|
@@ -423,7 +437,8 @@ that it gains automated indexing and the parents `cmd_id`.
|
|
423
437
|
class CalculatePower < Lite::Command::Base
|
424
438
|
|
425
439
|
def call
|
426
|
-
|
440
|
+
context.merge!(some_other: "required value")
|
441
|
+
CalculateSqrt.call(context)
|
427
442
|
end
|
428
443
|
|
429
444
|
end
|
@@ -434,7 +449,8 @@ end
|
|
434
449
|
Throwing faults allows you to bubble up child faults up to the parent.
|
435
450
|
Use it to create branches within your logic and create clean tracing
|
436
451
|
of your command results. You can use `throw!` as a catch-all or any
|
437
|
-
of the bang status method `failure!`.
|
452
|
+
of the bang status method `failure!`. Any `reason` and `metadata` will
|
453
|
+
be bubbled up from the original fault.
|
438
454
|
|
439
455
|
```ruby
|
440
456
|
class CalculatePower < Lite::Command::Base
|
@@ -443,10 +459,10 @@ class CalculatePower < Lite::Command::Base
|
|
443
459
|
command = CalculateSqrt.call(context.merge!(some_other: "required value"))
|
444
460
|
|
445
461
|
if command.noop?("Sqrt of 1 is 1")
|
446
|
-
# Manually throw
|
462
|
+
# Manually throw a specific fault
|
447
463
|
invalid!(command)
|
448
464
|
elsif command.fault?
|
449
|
-
# Automatically throws a matching fault
|
465
|
+
# Automatically throws a matching fault
|
450
466
|
throw!(command)
|
451
467
|
else
|
452
468
|
# Success, do nothing
|
@@ -467,6 +483,10 @@ This is useful for composing multiple steps into one call.
|
|
467
483
|
> so its no different than just passing the context forward. To change
|
468
484
|
> this behavior, just override the `ok?` method with you logic, eg: just `success`
|
469
485
|
|
486
|
+
> [!IMPORTANT]
|
487
|
+
> Do **NOT** define a call method in this class. The sequence logic is
|
488
|
+
> automatically defined by the sequence class.
|
489
|
+
|
470
490
|
```ruby
|
471
491
|
class ProcessCheckout < Lite::Command::Sequence
|
472
492
|
|
@@ -477,8 +497,7 @@ class ProcessCheckout < Lite::Command::Sequence
|
|
477
497
|
step SendConfirmationEmail, SendConfirmationText
|
478
498
|
step NotifyWarehouse, unless: proc { ctx.invoice.fullfilled_by_amazon? }
|
479
499
|
|
480
|
-
# Do NOT
|
481
|
-
# Its defined by Lite::Command::Sequence
|
500
|
+
# Do NOT define a call method.
|
482
501
|
|
483
502
|
private
|
484
503
|
|
@@ -488,7 +507,7 @@ class ProcessCheckout < Lite::Command::Sequence
|
|
488
507
|
|
489
508
|
end
|
490
509
|
|
491
|
-
|
510
|
+
seq = ProcessCheckout.call(...)
|
492
511
|
# <ProcessCheckout ...>
|
493
512
|
```
|
494
513
|
|
@@ -82,8 +82,17 @@ module Lite
|
|
82
82
|
@errors << "#{method_name} type invalid"
|
83
83
|
end
|
84
84
|
|
85
|
+
def empty?
|
86
|
+
r = Utils.try(options[:filled], :[], :empty)
|
87
|
+
return if r.nil? || r == true
|
88
|
+
return unless value.respond_to?(:empty?)
|
89
|
+
|
90
|
+
value.empty?
|
91
|
+
end
|
92
|
+
|
85
93
|
def validate_attribute_filled!
|
86
|
-
return unless filled?
|
94
|
+
return unless filled?
|
95
|
+
return unless value.nil? || empty?
|
87
96
|
|
88
97
|
@errors << "#{method_name} must be filled"
|
89
98
|
end
|
data/lib/lite/command/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lite-command
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Gomez
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ostruct
|
@@ -173,10 +173,10 @@ extra_rdoc_files: []
|
|
173
173
|
files:
|
174
174
|
- ".fasterer.yml"
|
175
175
|
- ".gitattributes"
|
176
|
+
- ".github/workflows/ci.yml"
|
176
177
|
- ".gitignore"
|
177
178
|
- ".rspec"
|
178
179
|
- ".rubocop.yml"
|
179
|
-
- ".travis.yml"
|
180
180
|
- CHANGELOG.md
|
181
181
|
- CODE_OF_CONDUCT.md
|
182
182
|
- Gemfile
|
data/.travis.yml
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
sudo: false
|
2
|
-
language: ruby
|
3
|
-
cache: bundler
|
4
|
-
rvm:
|
5
|
-
- 2.5
|
6
|
-
- 2.6
|
7
|
-
- 2.7
|
8
|
-
- ruby-head
|
9
|
-
matrix:
|
10
|
-
fast_finish: true
|
11
|
-
allow_failures:
|
12
|
-
- rvm: ruby-head
|
13
|
-
before_install:
|
14
|
-
- gem update --system
|
15
|
-
- gem install bundler
|
16
|
-
install:
|
17
|
-
- bundle install --jobs=3 --retry=3
|
18
|
-
script:
|
19
|
-
- bundle exec rspec
|
20
|
-
- bundle exec rubocop
|
21
|
-
- bundle exec fasterer
|
22
|
-
notifications:
|
23
|
-
email: false
|
24
|
-
slack:
|
25
|
-
secure: 2PptrBf+yl8evd/WA4XKg0tzJqSdEn5TaAhuHR90o5X6Rd0MA4SES+QZQn9A81CPuJk//V8Dur8NQQzrcAivVOsmC+JbIHZV1MS8v32Uvoy28Vr8omAne4Ec3so6rXCIMniREGcHtKeXHdNYO61VZPPjgxd7RTwajvTC7KS0ViuKBPct+hq+WfJmX6Xp2vxTHImNVurExBrFZkJyPtV+UG1tJ6mB+kF8MQ1+t3ZBtU4+6eVviADYi7FBEeP0j9gdKBCzU7aPoE+XLMWcP0tI9ibaP9crEMKi05JNDLzm5f6PGvGJWEQSCeh3CxNhaEF7eog3BAw6mUXkKI0hvFfrZeyG+VkDU6T9rAG8u55BrREi4mRNJl0c0rS2J45bFRmYOlvDNn9xrhMkkfFn8SMX3etYJYqHNBxdF2TX4SzpDrshd3hcm2btmkThn/Ua/mYUWs9OF3O/ZSu0zB+/g83zrAsjMyfqJ1PE5ZiiMD2Rjrzb/fyYcsNeVhncwRdse9LtJRqz1KpOh/r5LBrWfRRCQwlVNwMtBdFxQ7Ytl9dz/dfJmGKgM8zsdJdI6ZyV+lwa0jPaql6Y8wXjox1wwN3IRCyvoVsgxmiQAV8jnWD2lf6I/Ea5yXCImGluuRAyRrAizElXGBNubhAVtQwQinHWfaYO+mJElScBwrxTVXuzQUI=
|