typed_operation 1.0.0.pre2 → 1.0.0.pre3
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/README.md +64 -11
- data/lib/generators/templates/operation.rb +2 -2
- data/lib/typed_operation/base.rb +2 -1
- data/lib/typed_operation/immutable_base.rb +2 -1
- data/lib/typed_operation/operations/attribute_builder.rb +7 -1
- data/lib/typed_operation/operations/callable.rb +0 -4
- data/lib/typed_operation/operations/executable.rb +29 -0
- data/lib/typed_operation/operations/lifecycle.rb +1 -0
- data/lib/typed_operation/version.rb +1 -1
- data/lib/typed_operation.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c16e335815316d0cc264b143755c63556fa1a016403198e83aeeeeb067307d4a
|
4
|
+
data.tar.gz: ddd2475fd3bb2898245b9430136dd881b3ef52fe92abbd7ffe4230e243398aad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
#
|
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
|
-
#
|
58
|
-
def
|
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
|
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 `#
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
36
|
+
def perform
|
37
37
|
# Perform...
|
38
38
|
"Hello World!"
|
39
39
|
end
|
data/lib/typed_operation/base.rb
CHANGED
@@ -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
|
-
&
|
32
|
+
&coerce_by
|
27
33
|
)
|
28
34
|
end
|
29
35
|
|
@@ -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
|
data/lib/typed_operation.rb
CHANGED
@@ -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.
|
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-
|
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
|