standard_procedure_operations 0.5.0 → 0.5.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 +4 -4
- data/README.md +9 -23
- data/app/models/operations/task.rb +2 -2
- data/lib/operations/version.rb +1 -1
- data/lib/operations.rb +1 -1
- metadata +16 -3
- data/lib/operations/global_id_serialiser.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9a47b950a0831c0bf0a1be69de7b7f75c80237ec73a75673397a0b8112aa846
|
4
|
+
data.tar.gz: 62e9741fcd4053d0f4139aa956f5d51025d644188b2da460d6b7550873257030
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 611b50e5912231a593187ea4dc59c5e270674f65413b1c3bbd34d3e1f40849f817954282750d729bdadf23a1bacb35b202c9fb71b2edaccfa59dddd209746234
|
7
|
+
data.tar.gz: 5b4fbf04648dfe9347330385f1dfdfc32eeadfe4adbb89a015a562c79b0a7b7f6e6b0e91b8d65f3d934e7f3af19bbaf8856ea39555cdd1b649f2ae9f2937ab81
|
data/README.md
CHANGED
@@ -48,7 +48,6 @@ class PrepareDocumentForDownload < Operations::Task
|
|
48
48
|
starts_with :authorised?
|
49
49
|
|
50
50
|
decision :authorised? do
|
51
|
-
inputs :user
|
52
51
|
condition { user.can?(:read, data.document) }
|
53
52
|
|
54
53
|
if_true :within_download_limits?
|
@@ -56,7 +55,6 @@ class PrepareDocumentForDownload < Operations::Task
|
|
56
55
|
end
|
57
56
|
|
58
57
|
decision :within_download_limits? do
|
59
|
-
inputs :user
|
60
58
|
condition { user.within_download_limits? }
|
61
59
|
|
62
60
|
if_true :use_filename_scrambler?
|
@@ -64,7 +62,6 @@ class PrepareDocumentForDownload < Operations::Task
|
|
64
62
|
end
|
65
63
|
|
66
64
|
decision :use_filename_scrambler? do
|
67
|
-
inputs :use_filename_scrambler
|
68
65
|
condition { use_filename_scrambler }
|
69
66
|
|
70
67
|
if_true :scramble_filename
|
@@ -72,16 +69,11 @@ class PrepareDocumentForDownload < Operations::Task
|
|
72
69
|
end
|
73
70
|
|
74
71
|
action :scramble_filename do
|
75
|
-
inputs :document
|
76
|
-
|
77
72
|
self.filename = "#{Faker::Lorem.word}#{File.extname(document.filename.to_s)}"
|
78
|
-
end
|
73
|
+
end
|
79
74
|
go_to :return_filename
|
80
75
|
|
81
76
|
result :return_filename do |results|
|
82
|
-
inputs :document
|
83
|
-
optional :filename
|
84
|
-
|
85
77
|
results.filename = filename || document.filename.to_s
|
86
78
|
end
|
87
79
|
end
|
@@ -158,7 +150,9 @@ go_to :send_invitations
|
|
158
150
|
You can also specify the required and optional data for your action handler using parameters or within the block. `optional` is decorative and helps with documentation. When using the block form, ensure you call `inputs` at the start of the block so that the task fails before doing any meaningful work.
|
159
151
|
|
160
152
|
```ruby
|
161
|
-
action :have_a_party
|
153
|
+
action :have_a_party do
|
154
|
+
inputs :number_of_guests
|
155
|
+
optional :music
|
162
156
|
self.food = task.buy_some_food_for(number_of_guests)
|
163
157
|
self.beer = task.buy_some_beer_for(number_of_guests)
|
164
158
|
self.music ||= task.plan_a_party_playlist
|
@@ -166,8 +160,6 @@ end
|
|
166
160
|
go_to :send_invitations
|
167
161
|
```
|
168
162
|
|
169
|
-
Defining state transitions statically with `go_to` ensures that all transitions are known when the operation class is loaded, making the flow easier to understand and analyze. The `go_to` method will automatically associate the transition with the most recently defined action handler.
|
170
|
-
|
171
163
|
### Waiting
|
172
164
|
Wait handlers are very similar to decision handlers but only work within [background tasks](#background-operations-and-pauses).
|
173
165
|
|
@@ -280,15 +272,17 @@ task = CombineNames.call first_name: "Alice", last_name: "Aardvark"
|
|
280
272
|
task.results[:name] # => Alice Aardvark
|
281
273
|
```
|
282
274
|
|
283
|
-
Because handlers are run in the context of the data carrier, you do not have direct access to methods or properties on your task object. However, the data carrier holds a reference to your task; use `task.do_something` or `task.some_attribute` to access it. The exception is the `fail_with`, `call` and `start` methods which the data carrier understands (and are intercepted when you are [testing](#testing)).
|
275
|
+
Because handlers are run in the context of the data carrier, you do not have direct access to methods or properties on your task object. However, the data carrier holds a reference to your task; use `task.do_something` or `task.some_attribute` to access it. The exception is the `fail_with`, `call` and `start` methods which the data carrier understands (and are intercepted when you are [testing](#testing)).
|
284
276
|
|
285
277
|
Both your task's `data` and its final `results` are stored in the database, so they can be examined later. The `results` because that's what you're interested in, the `data` as it can be useful for debugging or auditing purposes.
|
286
278
|
|
287
279
|
They are both stored as hashes that are encoded into JSON.
|
288
280
|
|
289
|
-
Instead of using the standard [JSON coder](https://api.rubyonrails.org/v4.2/classes/ActiveModel/Serializers/JSON.html), we use a [
|
281
|
+
Instead of using the standard [JSON coder](https://api.rubyonrails.org/v4.2/classes/ActiveModel/Serializers/JSON.html), we use a [GlobalIdSerialiser](https://github.com/standard-procedure/global_id_serialiser). This serialises most data into standard JSON types, as you would expect, but it also takes any [GlobalID::Identification](https://github.com/rails/globalid) objects (which includes all ActiveRecord models) and converts them to a GlobalID string. Then when the data is deserialised from the database, the GlobalID is converted back into the appropriate model.
|
290
282
|
|
291
|
-
If the original database record was deleted between the time the hash was serialised and when it was retrieved, the `GlobalID::Locator` will fail.
|
283
|
+
If the original database record was deleted between the time the hash was serialised and when it was retrieved, the `GlobalID::Locator` will fail. In this case, the deserialised data will contain a `nil` for the value in question.
|
284
|
+
|
285
|
+
Also note that the GlobalIdSerialiser automatically converts all hash keys into symbols (unlike the standard JSON coder which uses strings).
|
292
286
|
|
293
287
|
#### Indexing data and results
|
294
288
|
|
@@ -386,15 +380,11 @@ class UserRegistration < Operations::Task
|
|
386
380
|
starts_with :create_user
|
387
381
|
|
388
382
|
action :create_user do
|
389
|
-
inputs :email
|
390
|
-
|
391
383
|
self.user = User.create! email: email
|
392
384
|
end
|
393
385
|
go_to :send_verification_email
|
394
386
|
|
395
387
|
action :send_verification_email do
|
396
|
-
inputs :user
|
397
|
-
|
398
388
|
UserMailer.with(user: user).verification_email.deliver_later
|
399
389
|
end
|
400
390
|
go_to :verified?
|
@@ -405,8 +395,6 @@ class UserRegistration < Operations::Task
|
|
405
395
|
end
|
406
396
|
|
407
397
|
action :notify_administrator do
|
408
|
-
inputs :user
|
409
|
-
|
410
398
|
AdminMailer.with(user: user).verification_completed.deliver_later
|
411
399
|
end
|
412
400
|
end
|
@@ -424,7 +412,6 @@ class ParallelTasks < Operations::Task
|
|
424
412
|
starts_with :start_sub_tasks
|
425
413
|
|
426
414
|
action :start_sub_tasks do
|
427
|
-
inputs :number_of_sub_tasks
|
428
415
|
self.sub_tasks = (1..number_of_sub_tasks).collect { |i| start LongRunningTask, number: i }
|
429
416
|
end
|
430
417
|
go_to :do_something_else
|
@@ -598,7 +585,6 @@ end
|
|
598
585
|
|
599
586
|
The visualization includes:
|
600
587
|
- Color-coded nodes by state type (decisions, actions, wait states, results)
|
601
|
-
- Required and optional inputs for each state
|
602
588
|
- Transition conditions between states with custom labels when provided
|
603
589
|
- Special handling for custom transition blocks
|
604
590
|
|
@@ -9,8 +9,8 @@ module Operations
|
|
9
9
|
|
10
10
|
enum :status, in_progress: 0, waiting: 10, completed: 100, failed: -1
|
11
11
|
|
12
|
-
serialize :data, coder:
|
13
|
-
serialize :results, coder:
|
12
|
+
serialize :data, coder: GlobalIdSerialiser, type: Hash, default: {}
|
13
|
+
serialize :results, coder: GlobalIdSerialiser, type: Hash, default: {}
|
14
14
|
|
15
15
|
has_many :task_participants, class_name: "Operations::TaskParticipant", dependent: :destroy
|
16
16
|
after_save :record_participants
|
data/lib/operations/version.rb
CHANGED
data/lib/operations.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "ostruct"
|
2
|
+
require "global_id_serialiser"
|
2
3
|
|
3
4
|
module Operations
|
4
5
|
class Error < StandardError
|
@@ -10,7 +11,6 @@ module Operations
|
|
10
11
|
end
|
11
12
|
require "operations/version"
|
12
13
|
require "operations/engine"
|
13
|
-
require "operations/global_id_serialiser"
|
14
14
|
require "operations/failure"
|
15
15
|
require "operations/cannot_wait_in_foreground"
|
16
16
|
require "operations/timeout"
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: standard_procedure_operations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rahoul Baruah
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-03-
|
10
|
+
date: 2025-03-11 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -23,6 +23,20 @@ dependencies:
|
|
23
23
|
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: 7.1.3
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: standard_procedure_global_id_serialiser
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
26
40
|
description: Pipelines and State Machines for composable, trackable business logic
|
27
41
|
email:
|
28
42
|
- rahoulb@echodek.co
|
@@ -57,7 +71,6 @@ files:
|
|
57
71
|
- lib/operations/engine.rb
|
58
72
|
- lib/operations/exporters/graphviz.rb
|
59
73
|
- lib/operations/failure.rb
|
60
|
-
- lib/operations/global_id_serialiser.rb
|
61
74
|
- lib/operations/matchers.rb
|
62
75
|
- lib/operations/no_decision.rb
|
63
76
|
- lib/operations/timeout.rb
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module Operations
|
2
|
-
# Serialise and deserialise data to and from JSON
|
3
|
-
# Unlike the standard JSON coder, this coder uses the ActiveJob::Arguments serializer.
|
4
|
-
# This means that if the data contains an ActiveRecord model, it will be serialised as a GlobalID string
|
5
|
-
#
|
6
|
-
# Usage:
|
7
|
-
# class MyModel < ApplicationRecord
|
8
|
-
# serialize :data, coder: GlobalIDSerialiser, type: Hash, default: {}
|
9
|
-
# end
|
10
|
-
# @my_model = MyModel.create! data: {hello: "world", user: User.first}
|
11
|
-
# puts @my_model[:data] # => {hello: "world", user: #<User id: 1>}
|
12
|
-
class GlobalIDSerialiser
|
13
|
-
def self.dump(data) = ActiveSupport::JSON.dump(ActiveJob::Arguments.serialize([data]))
|
14
|
-
|
15
|
-
def self.load(json)
|
16
|
-
ActiveJob::Arguments.deserialize(ActiveSupport::JSON.decode(json)).first
|
17
|
-
rescue => ex
|
18
|
-
_load_without_global_ids(json).merge exception_message: ex.message, exception_class: ex.class.name, raw_data: json.to_s
|
19
|
-
end
|
20
|
-
|
21
|
-
def self._load_without_global_ids(json)
|
22
|
-
ActiveSupport::JSON.decode(json).first.tap do |hash|
|
23
|
-
hash.delete("_aj_symbol_keys")
|
24
|
-
end.transform_values do |value|
|
25
|
-
(value.is_a?(Hash) && value.key?("_aj_globalid")) ? value["_aj_globalid"] : value
|
26
|
-
end.transform_keys(&:to_sym)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|