lite-command 3.1.5 → 3.2.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: 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