typed_operation 1.0.0.pre2 → 1.0.0.pre3

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: 1337560f2bffbfd45574c1df5f007c951dba43293324c309c3409aab18e57f0c
4
- data.tar.gz: 63deedbca3cde1fadb88668af46ec21652e17f9600d23c0e13c8b22429c05e0c
3
+ metadata.gz: c16e335815316d0cc264b143755c63556fa1a016403198e83aeeeeb067307d4a
4
+ data.tar.gz: ddd2475fd3bb2898245b9430136dd881b3ef52fe92abbd7ffe4230e243398aad
5
5
  SHA512:
6
- metadata.gz: 0a8fdc89c772282de6c654365e18d7397532b84aebd738285499998c04c32906dc1e63961f5b47d139dfff532a458461dbe07a29bdfcda9f29d8132d645599df
7
- data.tar.gz: cee03eb2c1e2d9100e657d98a1b70f6889b0e276251dd839f343586ff89f17287853224a487357343fc421ff6723686a2bf672c7ad53a058246668bb1d80a926
6
+ metadata.gz: 1134e7f162febf03703dc2b36a60f799779a9170f6bf75888df7ffe58245b73b3f66b3b434509d0444537ed818c7eb5d3946df61cbe0cf485e4cd02b4bdbc2de
7
+ data.tar.gz: 1ceee6f8f37b538cf25b9011e188c79d49be4618f23b12b314fbb4295affffbf25173d61dcbfebf20589c2aad2a85a8531636adf3e4870286ab8b790351322d7
data/README.md CHANGED
@@ -49,16 +49,28 @@ class ShelveBookOperation < ::TypedOperation::Base
49
49
 
50
50
  named_param :category, String, default: "unknown".freeze
51
51
 
52
- # to setup (optional)
52
+ # optional hook called when the operation is initialized, and after the parameters have been set
53
53
  def prepare
54
54
  raise ArgumentError, "ISBN is invalid" unless valid_isbn?
55
55
  end
56
56
 
57
- # The 'work' of the operation
58
- def call
57
+ # optionally hook in before execution ... and call super to allow subclasses to hook in too
58
+ def before_execute_operation
59
+ # ...
60
+ super
61
+ end
62
+
63
+ # The 'work' of the operation, this is the main body of the operation and must be implemented
64
+ def perform
59
65
  "Put away '#{title}' by author ID #{author_id}#{shelf_code ? " on shelf #{shelf_code}" : "" }"
60
66
  end
61
67
 
68
+ # optionally hook in after execution ... and call super to allow subclasses to hook in too
69
+ def after_execute_operation(result)
70
+ # ...
71
+ super
72
+ end
73
+
62
74
  private
63
75
 
64
76
  def valid_isbn?
@@ -100,7 +112,7 @@ class TestOperation < ::TypedOperation::Base
100
112
  param :bar, String
101
113
  param :baz, String, &:to_s
102
114
 
103
- def call = "It worked! (#{foo}, #{bar}, #{baz})"
115
+ def perform = "It worked! (#{foo}, #{bar}, #{baz})"
104
116
  end
105
117
 
106
118
  # Invoking the operation directly
@@ -162,12 +174,32 @@ Create an operation by subclassing `TypedOperation::Base` or `TypedOperation::Im
162
174
  - `TypedOperation::ImmutableBase` (uses `Literal::Data`) is the parent class for an operation where the arguments are immutable (frozen on initialization),
163
175
  thus giving a somewhat stronger immutability guarantee (ie that the operation does not mutate its arguments).
164
176
 
165
- The subclass must implement the `#call` method which is where the operations main work is done.
177
+ The subclass must implement the `#perform` method which is where the operations main work is done.
166
178
 
167
179
  The operation can also implement:
168
180
 
169
181
  - `#prepare` - called when the operation is initialized, and after the parameters have been set
182
+ - `#before_execute_operation` - optionally hook in before execution ... and call super to allow subclasses to hook in too
183
+ - `#after_execute_operation` - optionally hook in after execution ... and call super to allow subclasses to hook in too
184
+
185
+ ```ruby
186
+ # optionally hook in before execution...
187
+ def before_execute_operation
188
+ # Remember to call super
189
+ super
190
+ end
191
+
192
+ def perform
193
+ # ... implement me!
194
+ end
170
195
 
196
+ # optionally hook in after execution...
197
+ def after_execute_operation(result)
198
+ # Remember to call super, note the result is passed in and the return value of this method is the result of the operation
199
+ # thus allowing you to modify the result if you wish
200
+ super
201
+ end
202
+ ```
171
203
 
172
204
  ### Specifying parameters (using `.param`)
173
205
 
@@ -260,7 +292,7 @@ class MyOperation < ::TypedOperation::Base
260
292
  # Or alternatively => `param :name, String, positional: true`
261
293
  positional_param :age, Integer, default: -> { 0 }
262
294
 
263
- def call
295
+ def perform
264
296
  puts "Hello #{name} (#{age})"
265
297
  end
266
298
  end
@@ -291,7 +323,7 @@ class MyOperation < ::TypedOperation::Base
291
323
  # Or alternatively => `param :name, String`
292
324
  named_param :age, Integer, default: -> { 0 }
293
325
 
294
- def call
326
+ def perform
295
327
  puts "Hello #{name} (#{age})"
296
328
  end
297
329
  end
@@ -312,7 +344,7 @@ class MyOperation < ::TypedOperation::Base
312
344
  positional_param :name, String
313
345
  named_param :age, Integer, default: -> { 0 }
314
346
 
315
- def call
347
+ def perform
316
348
  puts "Hello #{name} (#{age})"
317
349
  end
318
350
  end
@@ -403,9 +435,10 @@ An operation can be invoked by:
403
435
 
404
436
  - instantiating it with at least required params and then calling the `#call` method on the instance
405
437
  - once a partially applied operation has been prepared (all required parameters have been set), the call
406
- method on TypedOperation::Prepared can be used to instantiate and call the operation.
438
+ method on `TypedOperation::Prepared` can be used to instantiate and call the operation.
407
439
  - once an operation is curried, the `#call` method on last TypedOperation::Curried in the chain will invoke the operation
408
440
  - calling `#call` on a partially applied operation and passing in any remaining required parameters
441
+ - calling `#execute_operation` on an operation instance (this is the method that is called by `#call`)
409
442
 
410
443
  See the many examples in this document.
411
444
 
@@ -524,6 +557,26 @@ class ApplicationOperation < ::TypedOperation::Base
524
557
  end
525
558
  ```
526
559
 
560
+ ### Using with Action Policy (`action_policy` gem)
561
+
562
+ Base you `ApplicationOperation` on the following:
563
+
564
+ ```ruby
565
+ class ApplicationOperation < ::TypedOperation::Base
566
+ # ...
567
+ include ActionPolicy::Behaviour
568
+
569
+ # ...
570
+
571
+ param :initiator, ::User
572
+ authorize :initiator
573
+ # Or
574
+ # param :initiator, optional(::User)
575
+ # authorize :initiator, through: {optional: true}
576
+
577
+ # ...
578
+ end
579
+ ```
527
580
  ### Using with `literal` monads
528
581
 
529
582
  You can use the `literal` gem to provide a `Result` type for your operations.
@@ -533,7 +586,7 @@ class MyOperation < ::TypedOperation::Base
533
586
  param :account_name, String
534
587
  param :owner, String
535
588
 
536
- def call
589
+ def perform
537
590
  create_account.bind do |account|
538
591
  associate_owner(account).map { account }
539
592
  end
@@ -570,7 +623,7 @@ class MyOperation < ::TypedOperation::Base
570
623
  param :account_name, String
571
624
  param :owner, ::Owner
572
625
 
573
- def call
626
+ def perform
574
627
  account = yield create_account(account_name)
575
628
  yield associate_owner(account, owner)
576
629
 
@@ -14,7 +14,7 @@ module <%= namespace_name %>
14
14
  # Prepare...
15
15
  end
16
16
 
17
- def call
17
+ def perform
18
18
  # Perform...
19
19
  "Hello World!"
20
20
  end
@@ -33,7 +33,7 @@ class <%= name %> < ::ApplicationOperation
33
33
  # Prepare...
34
34
  end
35
35
 
36
- def call
36
+ def perform
37
37
  # Perform...
38
38
  "Hello World!"
39
39
  end
@@ -6,9 +6,10 @@ module TypedOperation
6
6
  extend Operations::Parameters
7
7
  extend Operations::PartialApplication
8
8
 
9
- include Operations::Callable
10
9
  include Operations::Lifecycle
10
+ include Operations::Callable
11
11
  include Operations::Deconstruct
12
+ include Operations::Executable
12
13
 
13
14
  class << self
14
15
  def attribute(name, type, special = nil, reader: :public, writer: :public, positional: false, default: nil)
@@ -6,8 +6,9 @@ module TypedOperation
6
6
  extend Operations::Parameters
7
7
  extend Operations::PartialApplication
8
8
 
9
- include Operations::Callable
10
9
  include Operations::Lifecycle
10
+ include Operations::Callable
11
11
  include Operations::Deconstruct
12
+ include Operations::Executable
12
13
  end
13
14
  end
@@ -17,13 +17,19 @@ module TypedOperation
17
17
  end
18
18
 
19
19
  def define(&converter)
20
+ # If nilable, then converter should not attempt to call the type converter block if the value is nil
21
+ coerce_by = if type_nilable? && converter
22
+ ->(v) { (v == Literal::Null || v.nil?) ? v : converter.call(v) }
23
+ else
24
+ converter
25
+ end
20
26
  @typed_operation.attribute(
21
27
  @name,
22
28
  @signature,
23
29
  default: default_value_for_literal,
24
30
  positional: @positional,
25
31
  reader: @reader,
26
- &converter
32
+ &coerce_by
27
33
  )
28
34
  end
29
35
 
@@ -18,10 +18,6 @@ module TypedOperation
18
18
  end
19
19
 
20
20
  include CallableMethods
21
-
22
- def call
23
- raise InvalidOperationError, "You must implement #call"
24
- end
25
21
  end
26
22
  end
27
23
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypedOperation
4
+ module Operations
5
+ module Executable
6
+ def call
7
+ execute_operation
8
+ end
9
+
10
+ def execute_operation
11
+ before_execute_operation
12
+ retval = perform
13
+ after_execute_operation(retval)
14
+ end
15
+
16
+ def before_execute_operation
17
+ # noop
18
+ end
19
+
20
+ def after_execute_operation(retval)
21
+ retval
22
+ end
23
+
24
+ def perform
25
+ raise InvalidOperationError, "Operation #{self.class} does not implement #perform"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -3,6 +3,7 @@
3
3
  module TypedOperation
4
4
  module Operations
5
5
  module Lifecycle
6
+ # This is called by Literal on initialization of underlying Struct/Data
6
7
  def after_initialization
7
8
  prepare if respond_to?(:prepare)
8
9
  end
@@ -1,3 +1,3 @@
1
1
  module TypedOperation
2
- VERSION = "1.0.0.pre2"
2
+ VERSION = "1.0.0.pre3"
3
3
  end
@@ -13,6 +13,7 @@ require "typed_operation/operations/callable"
13
13
  require "typed_operation/operations/lifecycle"
14
14
  require "typed_operation/operations/deconstruct"
15
15
  require "typed_operation/operations/attribute_builder"
16
+ require "typed_operation/operations/executable"
16
17
  require "typed_operation/curried"
17
18
  require "typed_operation/immutable_base"
18
19
  require "typed_operation/base"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typed_operation
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre2
4
+ version: 1.0.0.pre3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Ierodiaconou
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-22 00:00:00.000000000 Z
11
+ date: 2023-08-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: TypedOperation is a command pattern implementation where inputs can be
14
14
  defined with runtime type checks. Operations can be partially applied.
@@ -36,6 +36,7 @@ files:
36
36
  - lib/typed_operation/operations/attribute_builder.rb
37
37
  - lib/typed_operation/operations/callable.rb
38
38
  - lib/typed_operation/operations/deconstruct.rb
39
+ - lib/typed_operation/operations/executable.rb
39
40
  - lib/typed_operation/operations/introspection.rb
40
41
  - lib/typed_operation/operations/lifecycle.rb
41
42
  - lib/typed_operation/operations/parameters.rb