lite-command 3.1.5 → 3.2.1

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: 25949bea3778fbe660ae361819879bc36deb461fb37e9efa4ad11012529ae682
4
- data.tar.gz: 7f9df66b9027c3c7e3cb7b8efaad1508dec68c22b4b63ab27c4ffd123707519e
3
+ metadata.gz: e36dd86e0746fcaaa37f79e73a94b0ae406e1989c02105c6378c5f1ea8694165
4
+ data.tar.gz: a10c6bda6ce19982ef9b983faa7ffa7ad3063ae8fc627229dc725238475c86c3
5
5
  SHA512:
6
- metadata.gz: 9c2ee67da223708a63852d539ab82f7a893214c1016376fc0af4496d224ccdcfef3340b1267f6fec11348ae8d2ede2c87b605494aa2090a8f449c317a3d9613a
7
- data.tar.gz: 29ec28857fc0e8f29e1ba34a41cae8b44298a44c9477e73a237f5eb708965ba555a4b82edff984b20b61b4f1967ce93586ec74075a8421f6481c353865c0e275
6
+ metadata.gz: f71e320cd7ec5b4c105900fd984c6f73fa30323ef734cf03b8b6333eebed459ac9c54234433864ce20b719ffa80eff6b3da9f4021257b965c7bc8380a3310518
7
+ data.tar.gz: 78563217138c0d55133d344e914a9edb59cf9e59aaece25af3d1486657741151737d21f33a054821b80f26e3fd6bc92934e27bc3b9b3287cce969017284fa495
data/CHANGELOG.md CHANGED
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [3.2.1] - 2024-10-29
10
+ ### Changed
11
+ - Allow if and unless evaluations to happen at the same time
12
+
13
+ ## [3.2.0] - 2024-10-29
14
+ ### Changed
15
+ - Move callbacks to hooks instead of try methods
16
+
9
17
  ## [3.1.5] - 2024-10-28
10
18
  ### Changed
11
19
  - Renamed private `delegate` method to `delegates` to prevent rails clash
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lite-command (3.1.5)
4
+ lite-command (3.2.1)
5
5
  activemodel
6
6
  ostruct
7
7
 
data/README.md CHANGED
@@ -33,10 +33,9 @@ Or install it yourself as:
33
33
  * [States](#states)
34
34
  * [Statuses](#statuses)
35
35
  * [Hooks](#hooks)
36
- * [State Hooks](#status-hooks)
37
- * [Attribute Hooks](#attribute-hooks)
38
- * [Execution Hooks](#execution-hooks)
36
+ * [Lifecycle Hooks](#lifecycle-hooks)
39
37
  * [Status Hooks](#status-hooks)
38
+ * [State Hooks](#state-hooks)
40
39
  * [Children](#children)
41
40
  * [Throwing Faults](#throwing-faults)
42
41
  * [Sequences](#sequences)
@@ -143,6 +142,10 @@ cmd.raise!(original: true) #=> raises Lite::Command::Fault
143
142
  # On exception:
144
143
  cmd.raise!(original: false) #=> raises Lite::Command::Error
145
144
  cmd.raise!(original: true) #=> raises StandardError
145
+
146
+ # Access the exception objects directly
147
+ cmd.original_exception #=> <StandardError ...>
148
+ cmd.command_exception #=> <Lite::Command::Error ...>
146
149
  ```
147
150
 
148
151
  ### Dynamic Faults
@@ -286,10 +289,10 @@ cmd.context.decrypted_message #=> "Hola Mundo"
286
289
  # With invalid options:
287
290
  cmd = DecryptSecretMessage.call(encrypted_message: "idk", version: "v23")
288
291
  cmd.status #=> "invalid"
289
- cmd.reason #=> "Encrypted message is too short (minimum is 10 character). Version is not included in list..."
292
+ cmd.reason #=> "Encrypted message is too short (minimum is 10 character). Encrypted message has invalid magic numbers. Version is not included in list."
290
293
  cmd.metadata #=> {
291
294
  #=> user: ["is not included in list"],
292
- #=> encrypted_message: ["is too short (minimum is 10 character)"]
295
+ #=> encrypted_message: ["is too short (minimum is 10 character)", "has invalid magic numbers"]
293
296
  #=> }
294
297
  ```
295
298
 
@@ -361,9 +364,6 @@ cmd.status #=> "invalid"
361
364
  cmd.reason #=> "Invalid message start value"
362
365
  cmd.metadata #=> { i18n: "gb.invalid_start_value" }
363
366
 
364
- cmd.original_exception #=> <RuntimeError ...>
365
- cmd.command_exception #=> <DecryptSecretMessage::Error ...>
366
-
367
367
  cmd.success? #=> false
368
368
  cmd.noop? #=> false
369
369
  cmd.invalid? #=> true
@@ -387,140 +387,81 @@ cmd.bad?("Other reason") #=> false
387
387
  ## Hooks
388
388
 
389
389
  Use hooks to run arbituary code at transition points and on finalized internals.
390
- The following is an example of the hooks called for a failed command with a
391
- successful child command.
390
+ All hooks are ran in the order they are defined. Hooks types can be defined
391
+ multiple times. Hooks are ran in the following order:
392
392
 
393
393
  ```ruby
394
- -> 1. FooCommand.on_pending
395
- -> 2. FooCommand.on_before_execution
396
- -> 3. FooCommand.on_executing
397
- ---> 3a. BarCommand.on_pending
398
- ---> 3b. BarCommand.on_before_execution
399
- ---> 3c. BarCommand.on_executing
400
- ---> 3d. BarCommand.on_after_execution
401
- ---> 3e. BarCommand.on_success
402
- ---> 3f. BarCommand.on_complete
403
- -> 4. FooCommand.on_after_execution
404
- -> 5. FooCommand.on_failure
405
- -> 6. FooCommand.on_interrupted
394
+ 1. after_initialize
395
+ 2. on_pending
396
+ 3. before_validation
397
+ 4. after_validation
398
+ 5. before_execution
399
+ 6. on_executing
400
+ 7. after_execution
401
+ 8. on_[success, noop, invalid, failure, error]
402
+ 9. on_[complete, interrupted]
406
403
  ```
407
404
 
408
- ### Status Hooks
405
+ ### Lifecycle Hooks
409
406
 
410
- Define one or more callbacks that are called during transitions between states.
407
+ Define before and after callbacks to call around execution.
411
408
 
412
409
  ```ruby
413
410
  class DecryptSecretMessage < Lite::Command::Base
414
411
 
415
- def call
416
- # ...
417
- end
418
-
419
- private
420
-
421
- def on_pending
422
- # eg: Append additional contextual data
423
- end
424
-
425
- def on_executing
426
- # eg: Insert inspection debugger
427
- end
428
-
429
- def on_complete
430
- # eg: Log message for posterity
431
- end
432
-
433
- def on_interrupted
434
- # eg: Report to APM with tags and metadata
435
- end
436
-
437
- end
438
- ```
439
-
440
- ### Attribute Hooks
441
-
442
- Define before attribtue validation callbacks.
443
-
444
- ```ruby
445
- class DecryptSecretMessage < Lite::Command::Base
412
+ after_initialize :some_method
413
+ before_validation :some_method
414
+ after_validation :some_method
415
+ before_execution :some_method
416
+ after_execution :some_method
446
417
 
447
418
  def call
448
419
  # ...
449
420
  end
450
421
 
451
- private
452
-
453
- def on_before_validation
454
- # eg: Normalize context data
455
- end
456
-
457
422
  end
458
423
  ```
459
424
 
460
- ### Execution Hooks
425
+ ### Status Hooks
461
426
 
462
- Define before and after callbacks to call around execution.
427
+ Define one or more callbacks that are called after execution for
428
+ specific statuses.
463
429
 
464
430
  ```ruby
465
431
  class DecryptSecretMessage < Lite::Command::Base
466
432
 
433
+ on_success :some_method
434
+ on_noop :some_method
435
+ on_invalid :some_method
436
+ on_failure :some_method
437
+ on_error :some_method
438
+
467
439
  def call
468
440
  # ...
469
441
  end
470
442
 
471
- private
472
-
473
- def on_before_execution
474
- # eg: Append additional contextual data
475
- end
476
-
477
- def on_after_execution
478
- # eg: Store results to database
479
- end
480
-
481
443
  end
482
444
  ```
483
445
 
484
- ### Status Hooks
446
+ ### State Hooks
485
447
 
486
- Define one or more callbacks that are called after execution for
487
- specific statuses.
448
+ Define one or more callbacks that are called during transitions between states.
488
449
 
489
450
  ```ruby
490
451
  class DecryptSecretMessage < Lite::Command::Base
491
452
 
453
+ on_pending :some_method
454
+ on_executing :some_method
455
+ on_complete :some_method
456
+ on_interrupted :some_method
457
+
492
458
  def call
493
459
  # ...
494
460
  end
495
461
 
496
- private
497
-
498
- def on_success
499
- # eg: Increment KPI counter
500
- end
501
-
502
- def on_noop(fault)
503
- # eg: Log message for posterity
504
- end
505
-
506
- def on_invalid(fault)
507
- # eg: Send metadata errors to frontend
508
- end
509
-
510
- def on_failure(fault)
511
- # eg: Rollback record changes
512
- end
513
-
514
- def on_error(fault_or_exception)
515
- # eg: Report to APM with tags and metadata
516
- end
517
-
518
462
  end
519
463
  ```
520
464
 
521
- > [!NOTE]
522
- > The `on_success` callback does **NOT** take any arguments.
523
-
524
465
  ## Children
525
466
 
526
467
  When building complex commands, its best that you pass the parents context to the
@@ -16,6 +16,7 @@ module Lite
16
16
  base.include Internals::Faults
17
17
  base.include Internals::Calls
18
18
  base.include Internals::Executions
19
+ base.include Internals::Hooks
19
20
  base.include Internals::Results
20
21
 
21
22
  if Lite::Command.configuration.raise_dynamic_faults # rubocop:disable Style/GuardClause
@@ -34,7 +35,7 @@ module Lite
34
35
 
35
36
  def initialize(context = {})
36
37
  @context = Context.build(context)
37
- Utils.try(self, :on_pending)
38
+ run_hooks(:after_initialize)
38
39
  end
39
40
 
40
41
  end
@@ -52,9 +52,9 @@ module Lite
52
52
  private
53
53
 
54
54
  def validate_context_attributes
55
- return if errors.empty?
56
-
57
- invalid!(errors.full_messages.join(". "), metadata: errors.messages)
55
+ run_hooks(:before_validation)
56
+ invalid!(errors.full_messages.join(". "), metadata: errors.messages) unless valid?
57
+ run_hooks(:after_validation)
58
58
  end
59
59
 
60
60
  end
@@ -24,14 +24,12 @@ module Lite
24
24
 
25
25
  def call(context = {})
26
26
  instance = send(:new, context)
27
- instance.validate
28
27
  instance.send(:execute)
29
28
  instance
30
29
  end
31
30
 
32
31
  def call!(context = {})
33
32
  instance = send(:new, context)
34
- instance.validate
35
33
  instance.send(:execute!)
36
34
  instance
37
35
  end
@@ -50,16 +50,18 @@ module Lite
50
50
  increment_execution_index
51
51
  assign_execution_cmd_id
52
52
  start_monotonic_time
53
- Utils.try(self, :on_before_validation)
53
+ run_hooks(:on_pending)
54
54
  validate_context_attributes
55
- Utils.try(self, :on_before_execution)
55
+ run_hooks(:before_execution)
56
56
  executing!
57
- Utils.try(self, :on_executing)
57
+ run_hooks(:on_executing)
58
58
  end
59
59
 
60
60
  def after_execution
61
61
  send(:"#{success? ? COMPLETE : INTERRUPTED}!")
62
- Utils.try(self, :on_after_execution)
62
+ run_hooks(:after_execution)
63
+ run_hooks(:"on_#{status}")
64
+ run_hooks(:"on_#{state}")
63
65
  stop_monotonic_time
64
66
  append_execution_result
65
67
  freeze_execution_objects
@@ -72,26 +74,17 @@ module Lite
72
74
  end
73
75
 
74
76
  def execute
75
- around_execution { call }
76
- Utils.try(self, :on_success)
77
- rescue StandardError => e
78
- fault(e, Utils.try(e, :type) || ERROR, metadata, exception: e)
79
- after_execution
80
- Utils.try(self, :"on_#{status}", e)
81
- ensure
82
- Utils.try(self, :"on_#{state}")
77
+ execute!
78
+ rescue StandardError
79
+ # Do nothing
83
80
  end
84
81
 
85
82
  def execute!
86
83
  around_execution { call }
87
- Utils.try(self, :on_success)
88
84
  rescue StandardError => e
89
85
  fault(e, Utils.try(e, :type) || ERROR, metadata, exception: e)
90
86
  after_execution
91
- Utils.try(self, :"on_#{status}", e)
92
87
  raise(e)
93
- else
94
- Utils.try(self, :"on_#{state}")
95
88
  end
96
89
 
97
90
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Command
5
+
6
+ HOOKS = [
7
+ :after_initialize,
8
+ :before_validation,
9
+ :after_validation,
10
+ :before_execution,
11
+ :after_execution,
12
+ *STATUSES.map { |s| :"on_#{s}" },
13
+ *STATES.map { |s| :"on_#{s}" }
14
+ ].freeze
15
+
16
+ module Internals
17
+ module Hooks
18
+
19
+ def self.included(base)
20
+ base.extend ClassMethods
21
+ end
22
+
23
+ module ClassMethods
24
+
25
+ def hooks
26
+ @hooks ||= Utils.try(superclass, :hooks).dup || {}
27
+ end
28
+
29
+ HOOKS.each do |h|
30
+ define_method(h) do |*method_names, &block|
31
+ method_names << block if block_given?
32
+ method_names.each { |mn| (hooks[h] ||= []) << mn }
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ private
39
+
40
+ def run_hooks(hook)
41
+ hooks = self.class.hooks[hook]
42
+ return if hooks.nil?
43
+
44
+ hooks.each { |h| Utils.call(self, h) }
45
+ end
46
+
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -40,7 +40,9 @@ module Lite
40
40
  end
41
41
 
42
42
  def evaluate(object, options = {})
43
- if options[:if]
43
+ if options[:if] && options[:unless]
44
+ call(object, options[:if]) && !call(object, options[:unless])
45
+ elsif options[:if]
44
46
  call(object, options[:if])
45
47
  elsif options[:unless]
46
48
  !call(object, options[:unless])
@@ -3,7 +3,7 @@
3
3
  module Lite
4
4
  module Command
5
5
 
6
- VERSION = "3.1.5"
6
+ VERSION = "3.2.1"
7
7
 
8
8
  end
9
9
  end
data/lib/lite/command.rb CHANGED
@@ -16,6 +16,7 @@ require "lite/command/internals/attributes"
16
16
  require "lite/command/internals/faults"
17
17
  require "lite/command/internals/calls"
18
18
  require "lite/command/internals/executions"
19
+ require "lite/command/internals/hooks"
19
20
  require "lite/command/internals/results"
20
21
  require "lite/command/base"
21
22
  require "lite/command/step"
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: 3.1.5
4
+ version: 3.2.1
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-28 00:00:00.000000000 Z
11
+ date: 2024-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -201,6 +201,7 @@ files:
201
201
  - lib/lite/command/internals/calls.rb
202
202
  - lib/lite/command/internals/executions.rb
203
203
  - lib/lite/command/internals/faults.rb
204
+ - lib/lite/command/internals/hooks.rb
204
205
  - lib/lite/command/internals/results.rb
205
206
  - lib/lite/command/internals/runtimes.rb
206
207
  - lib/lite/command/sequence.rb