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 +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
|