task_progress 0.0.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.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +29 -0
  3. data/LICENSE +21 -0
  4. data/MANUAL.md +851 -0
  5. data/README.md +158 -0
  6. data/RECIPES.md +413 -0
  7. data/config/importmap.rb +17 -0
  8. data/exe/task_progress +51 -0
  9. data/js/local_tasks.d.ts +44 -0
  10. data/js/local_tasks.js +229 -0
  11. data/js/package.json +31 -0
  12. data/js/paths.d.ts +5 -0
  13. data/js/paths.js +7 -0
  14. data/js/react_task_tree.d.ts +49 -0
  15. data/js/react_task_tree.js +123 -0
  16. data/js/render_queue.d.ts +24 -0
  17. data/js/render_queue.js +79 -0
  18. data/js/row_view.d.ts +25 -0
  19. data/js/row_view.js +69 -0
  20. data/js/stimulus_controller.d.ts +10 -0
  21. data/js/stimulus_controller.js +137 -0
  22. data/js/task_progress_clock.d.ts +2 -0
  23. data/js/task_progress_clock.js +33 -0
  24. data/js/task_tree.css +51 -0
  25. data/js/task_tree.d.ts +111 -0
  26. data/js/task_tree.js +444 -0
  27. data/lib/task_progress/assertions.rb +49 -0
  28. data/lib/task_progress/batch.rb +23 -0
  29. data/lib/task_progress/branch.rb +74 -0
  30. data/lib/task_progress/buffered_output.rb +63 -0
  31. data/lib/task_progress/cancellations_app.rb +76 -0
  32. data/lib/task_progress/event.rb +46 -0
  33. data/lib/task_progress/execution_log_output.rb +183 -0
  34. data/lib/task_progress/fold.rb +18 -0
  35. data/lib/task_progress/html_renderer.rb +153 -0
  36. data/lib/task_progress/job_serializer.rb +92 -0
  37. data/lib/task_progress/journal.rb +35 -0
  38. data/lib/task_progress/log.rb +263 -0
  39. data/lib/task_progress/memory_cache.rb +31 -0
  40. data/lib/task_progress/memory_stream_store.rb +142 -0
  41. data/lib/task_progress/mount.rb +448 -0
  42. data/lib/task_progress/ndjson_output.rb +24 -0
  43. data/lib/task_progress/node.rb +52 -0
  44. data/lib/task_progress/poll.rb +87 -0
  45. data/lib/task_progress/publishing_output.rb +30 -0
  46. data/lib/task_progress/ractor_output.rb +23 -0
  47. data/lib/task_progress/rails_defaults.rb +46 -0
  48. data/lib/task_progress/railtie.rb +91 -0
  49. data/lib/task_progress/recording_output.rb +51 -0
  50. data/lib/task_progress/redis_stream_store.rb +131 -0
  51. data/lib/task_progress/squasher.rb +59 -0
  52. data/lib/task_progress/sse.rb +114 -0
  53. data/lib/task_progress/state.rb +319 -0
  54. data/lib/task_progress/stream_output.rb +222 -0
  55. data/lib/task_progress/stream_reader.rb +76 -0
  56. data/lib/task_progress/summary_output.rb +37 -0
  57. data/lib/task_progress/terminal_output.rb +241 -0
  58. data/lib/task_progress/token.rb +58 -0
  59. data/lib/task_progress/tracked_io.rb +68 -0
  60. data/lib/task_progress/transaction.rb +41 -0
  61. data/lib/task_progress/trees_app.rb +66 -0
  62. data/lib/task_progress/turbo_helper.rb +18 -0
  63. data/lib/task_progress/turbo_sse.rb +103 -0
  64. data/lib/task_progress/turbo_stream_output.rb +61 -0
  65. data/lib/task_progress/version.rb +7 -0
  66. data/lib/task_progress/wire.rb +211 -0
  67. data/lib/task_progress.rb +1036 -0
  68. data/sig/generated/task_progress/assertions.rbs +36 -0
  69. data/sig/generated/task_progress/batch.rbs +33 -0
  70. data/sig/generated/task_progress/branch.rbs +47 -0
  71. data/sig/generated/task_progress/buffered_output.rbs +38 -0
  72. data/sig/generated/task_progress/cancellations_app.rbs +41 -0
  73. data/sig/generated/task_progress/event.rbs +20 -0
  74. data/sig/generated/task_progress/execution_log_output.rbs +93 -0
  75. data/sig/generated/task_progress/fold.rbs +25 -0
  76. data/sig/generated/task_progress/html_renderer.rbs +71 -0
  77. data/sig/generated/task_progress/journal.rbs +25 -0
  78. data/sig/generated/task_progress/log.rbs +140 -0
  79. data/sig/generated/task_progress/memory_cache.rbs +28 -0
  80. data/sig/generated/task_progress/memory_stream_store.rbs +83 -0
  81. data/sig/generated/task_progress/mount.rbs +200 -0
  82. data/sig/generated/task_progress/ndjson_output.rbs +18 -0
  83. data/sig/generated/task_progress/node.rbs +66 -0
  84. data/sig/generated/task_progress/poll.rbs +50 -0
  85. data/sig/generated/task_progress/publishing_output.rbs +24 -0
  86. data/sig/generated/task_progress/ractor_output.rbs +17 -0
  87. data/sig/generated/task_progress/rails_defaults.rbs +33 -0
  88. data/sig/generated/task_progress/recording_output.rbs +41 -0
  89. data/sig/generated/task_progress/redis_stream_store.rbs +75 -0
  90. data/sig/generated/task_progress/squasher.rbs +20 -0
  91. data/sig/generated/task_progress/sse.rbs +73 -0
  92. data/sig/generated/task_progress/state.rbs +165 -0
  93. data/sig/generated/task_progress/stream_output.rbs +142 -0
  94. data/sig/generated/task_progress/stream_reader.rbs +46 -0
  95. data/sig/generated/task_progress/summary_output.rbs +19 -0
  96. data/sig/generated/task_progress/terminal_output.rbs +117 -0
  97. data/sig/generated/task_progress/token.rbs +41 -0
  98. data/sig/generated/task_progress/tracked_io.rbs +48 -0
  99. data/sig/generated/task_progress/transaction.rbs +33 -0
  100. data/sig/generated/task_progress/trees_app.rbs +40 -0
  101. data/sig/generated/task_progress/turbo_helper.rbs +14 -0
  102. data/sig/generated/task_progress/turbo_sse.rbs +69 -0
  103. data/sig/generated/task_progress/turbo_stream_output.rbs +51 -0
  104. data/sig/generated/task_progress/version.rbs +6 -0
  105. data/sig/generated/task_progress/wire.rbs +102 -0
  106. data/sig/generated/task_progress.rbs +553 -0
  107. metadata +178 -0
@@ -0,0 +1,73 @@
1
+ # Generated from lib/task_progress/sse.rb with RBS::Inline
2
+
3
+ class TaskProgress
4
+ # A Rack response body streaming a tree as Server-Sent Events: first the tree so far, squashed
5
+ # into one batch with `prev` 0, then every batch as it commits - each in a `batch` event whose
6
+ # `id:` is its seq. A browser that reconnects starts over from a fresh squash, which is always
7
+ # right and costs a few puts. The stream ends once the root is finished. Given a subtask, it
8
+ # streams only that task's subtree, with the subtask as the root.
9
+ #
10
+ # # Rack
11
+ # TaskProgress::SSE.response(task)
12
+ # # Rails
13
+ # headers.merge!(TaskProgress::SSE::HEADERS)
14
+ # self.response_body = TaskProgress::SSE.new(task)
15
+ #
16
+ # Given a key instead of a task - a tree handed off with TaskProgress.handoff - it follows that
17
+ # tree from the stream for as long as it streams, and stops following when the stream ends or
18
+ # the server closes it. `response` answers 404 for a key with nothing at it
19
+ class SSE
20
+ # What an SSE response is sent with
21
+ HEADERS: Hash[String, String]
22
+
23
+ @source: TaskProgress | Log
24
+
25
+ @heartbeat: Integer | Float
26
+
27
+ @reconnect: Integer | Float
28
+
29
+ @close: bool
30
+
31
+ # A whole Rack response: status, headers and this body - or a 404, for a key with nothing at it
32
+ # : (TaskProgress | Log | String, **untyped) -> [Integer, Hash[String, String], SSE | Array[String]]
33
+ def self.response: (TaskProgress | Log | String, **untyped) -> [ Integer, Hash[String, String], SSE | Array[String] ]
34
+
35
+ # One batch as an SSE event
36
+ # : (Batch) -> String
37
+ def self.frame: (Batch) -> String
38
+
39
+ # Streams the task - its subtree only, for a subtask - or the tree handed off at a key, from
40
+ # `stream`. It pings every `heartbeat` seconds of quiet, and tells the browser to reconnect
41
+ # after `reconnect` seconds
42
+ # : (TaskProgress | Log | String, ?stream_store: untyped, ?heartbeat: Integer | Float, ?reconnect: Integer | Float) -> void
43
+ def initialize: (TaskProgress | Log | String, ?stream_store: untyped, ?heartbeat: Integer | Float, ?reconnect: Integer | Float) -> void
44
+
45
+ # Stops following a tree it followed by its key - Rack calls it when the response is over
46
+ # : () -> void
47
+ def close: () -> void
48
+
49
+ # Yields the stream a chunk at a time, until the task finishes - as a Rack body does
50
+ # : () { (String) -> void } -> void
51
+ def each: () { (String) -> void } -> void
52
+
53
+ # How many batches wait for a slow client before it gets a squash instead
54
+ BACKLOG: Integer
55
+
56
+ private
57
+
58
+ # : () -> Log
59
+ def log: () -> Log
60
+
61
+ # : () -> path
62
+ def path: () -> path
63
+
64
+ # : () -> bool
65
+ def present?: () -> bool
66
+
67
+ # Judged by what was sent, never by the snapshot: that runs ahead of delivery, and ending on
68
+ # it could cut the stream before the batch that finished the root went out. An empty reset
69
+ # is a branch whose task was pruned, which will not come back either
70
+ # : (Batch) -> bool
71
+ def finishes_root?: (Batch) -> bool
72
+ end
73
+ end
@@ -0,0 +1,165 @@
1
+ # Generated from lib/task_progress/state.rb with RBS::Inline
2
+
3
+ class TaskProgress
4
+ # The tree as it stands after a run of events: what `apply` folds them into. It checks every
5
+ # event before it changes anything, so a bad one raises and leaves the state as it was. Not
6
+ # thread-safe on its own - the Log holds its lock around every apply
7
+ class State
8
+ # What an `open` or a `meta` may set
9
+ ATTRIBUTES: Array[Symbol]
10
+
11
+ CHILD_IDS: Integer
12
+
13
+ # Siblings past this many are folded by `rows`...
14
+ FOLD_OVER: Integer
15
+
16
+ # ...keeping this many of the done ones, the latest to finish, and of the ones waiting, the first
17
+ FOLD_KEEP: Integer
18
+
19
+ ENDINGS: Hash[Symbol, Symbol]
20
+
21
+ # What a `put` carries - a Node, less what never goes over the wire
22
+ FIELDS: Array[Symbol]
23
+
24
+ @nodes: Hash[path, Node]
25
+
26
+ # A new node, opened at `at`, before its attributes are set
27
+ # : (Float) -> Node
28
+ def self.blank_node: (Float) -> Node
29
+
30
+ # Folding the squash of a state gives that state back, which is what makes a squashed log
31
+ # interchangeable with the one it came from
32
+ # : (Array[Event], ?at: Float) -> State
33
+ def self.fold: (Array[Event], ?at: Float) -> State
34
+
35
+ # An empty tree - or one made of these nodes
36
+ # : (?Hash[path, Node]) -> void
37
+ def initialize: (?Hash[path, Node]) -> void
38
+
39
+ # The node at `path`, or nil
40
+ # : (path) -> Node?
41
+ def []: (path) -> Node?
42
+
43
+ # Whether there is a node at `path`
44
+ # : (path) -> bool
45
+ def key?: (path) -> bool
46
+
47
+ # Whether it has no root yet
48
+ # : () -> bool
49
+ def empty?: () -> bool
50
+
51
+ # How many nodes it has
52
+ # : () -> Integer
53
+ def size: () -> Integer
54
+
55
+ # The paths of the nodes directly under `path`
56
+ # : (path) -> Array[path]
57
+ def children: (path) -> Array[path]
58
+
59
+ # A path for a new child of `path`. A child is named by a random id rather than its position,
60
+ # so a pruned task's path never turns up again on another one - not after a prune here, nor
61
+ # when a restarted remote replaces its tree. Ids only have to differ between siblings, since
62
+ # the path tells cousins apart, and a draw that hits a sibling is drawn again
63
+ # : (path, Random) -> path
64
+ def new_child: (path, Random) -> path
65
+
66
+ # Pre-order, children in the order they opened - the order a renderer wants and the order
67
+ # puts replay in, so a reader rebuilding from a squash ends up with the same order
68
+ # : (?path) { (path, Node) -> void } -> void
69
+ # : (?path) -> Enumerator[[path, Node], void]
70
+ def each: (?path) { (path, Node) -> void } -> void
71
+ | (?path) -> Enumerator[[ path, Node ], void]
72
+
73
+ # What to draw, in the order of `each` - but where a task has more than `fold_over` children,
74
+ # the ones that are done (save the last `keep` to finish) are one Fold row ahead of the rest,
75
+ # and the ones still waiting (save the first `keep`) one Fold row after them. What is
76
+ # running, failed or canceled is always drawn. A Fold comes with its parent's path and
77
+ # `~done` or `~pending` after it, which no task id can be
78
+ # : (?path, ?fold_over: Integer, ?keep: Integer) { (path, Node | Fold) -> void } -> void
79
+ # : (?path, ?fold_over: Integer, ?keep: Integer) -> Enumerator[[path, Node | Fold], void]
80
+ def rows: (?path, ?fold_over: Integer, ?keep: Integer) { (path, Node | Fold) -> void } -> void
81
+ | (?path, ?fold_over: Integer, ?keep: Integer) -> Enumerator[[ path, Node | Fold ], void]
82
+
83
+ # Folds one event in, or raises ArgumentError and leaves the state as it was
84
+ # : (Event, at: Float, ?journal: Journal?) -> self
85
+ def apply: (Event, at: Float, ?journal: Journal?) -> self
86
+
87
+ # The tasks under `path` that have not ended, in pre-order - so reversed, every task comes
88
+ # before the one it is under. A mounted subtree is left out unless `mounts` - a remote that
89
+ # has not finished may yet, so a `done` from here would be a guess, but calling it off is not
90
+ # : (path, ?mounts: bool) -> Array[path]
91
+ def open_under: (path, ?mounts: bool) -> Array[path]
92
+
93
+ # The state as a list of puts, one per node, in pre-order - or just the subtree at `path`
94
+ # : (?path) -> Array[Event]
95
+ def squash: (?path) -> Array[Event]
96
+
97
+ # Done, and everything under it ended - each task by a `done`, `fail` or `cancel` of its own.
98
+ # Children that are all through do not finish their parent, which may have more to do after
99
+ # them; a failed or canceled one does not hold back a parent that went on to finish regardless
100
+ # : (?path, ?Node?) -> bool
101
+ def complete?: (?path, ?Node?) -> bool
102
+
103
+ # 0.0..1.0, unrounded. Children decide a parent's progress, by weight, and children it was
104
+ # told to expect but has not got yet count as zero. A leaf goes by its count when it has a
105
+ # total, and by its percent otherwise
106
+ # : (?path, ?Node?) -> Float
107
+ def fraction: (?path, ?Node?) -> Float
108
+
109
+ # A whole percent, and 100 only once everything under it is done: 99.5 does not round up
110
+ # into a bar that reads as finished while work is still running
111
+ # : (?path) -> Integer
112
+ def percent: (?path) -> Integer
113
+
114
+ # Every node, as a `put` carries it
115
+ # : () -> Hash[path, Hash[Symbol, untyped]]
116
+ def to_h: () -> Hash[path, Hash[Symbol, untyped]]
117
+
118
+ # A frozen copy, safe to read from any thread. Given an earlier snapshot of this state and the
119
+ # paths changed since, it copies only those, and takes the rest from the earlier one
120
+ # : (?State?, ?Enumerable[path]?) -> State
121
+ def snapshot: (?State?, ?Enumerable[path]?) -> State
122
+
123
+ # A copy that can change without changing this one
124
+ # : () -> State
125
+ def dup: () -> State
126
+
127
+ # Undoes what one transaction did
128
+ # : (Journal) -> void
129
+ def rollback: (Journal) -> void
130
+
131
+ # The nodes themselves, for an earlier snapshot to lend a new one what has not changed
132
+ # : () -> Hash[path, Node]
133
+ attr_reader nodes: Hash[path, Node]
134
+
135
+ private
136
+
137
+ # : (Node) -> Node
138
+ def frozen: (Node) -> Node
139
+
140
+ # : (Node) -> Integer
141
+ def missing: (Node) -> Integer
142
+
143
+ # : (path) -> Node?
144
+ def parent_of!: (path) -> Node?
145
+
146
+ # : (path, Node?) -> Node
147
+ def existing!: (path, Node?) -> Node
148
+
149
+ # : (path, Node?) -> Node
150
+ def updatable!: (path, Node?) -> Node
151
+
152
+ # Work started anywhere starts everything above it too
153
+ # : (path, Float, Journal?) -> void
154
+ def start: (path, Float, Journal?) -> void
155
+
156
+ # : (Node, Hash[Symbol, untyped]) -> void
157
+ def assign: (Node, Hash[Symbol, untyped]) -> void
158
+
159
+ # : (path) -> path
160
+ def up: (path) -> path
161
+
162
+ # : (Node, path) -> void
163
+ def adopt: (Node, path) -> void
164
+ end
165
+ end
@@ -0,0 +1,142 @@
1
+ # Generated from lib/task_progress/stream_output.rb with RBS::Inline
2
+
3
+ class TaskProgress
4
+ # Writes a tree to a stream - a MemoryStreamStore, a RedisStreamStore - for readers anywhere to follow
5
+ # with StreamReader. What TaskProgress.handoff and TaskProgress.adopt write through. Under one
6
+ # key it keeps:
7
+ #
8
+ # key every batch, one entry each
9
+ # key:run the log id of the run that writes the tree now
10
+ # key:snapshot the tree squashed into one batch, and the id of the entry it is as of -
11
+ # where a reader starts, so the stream before it can be trimmed away
12
+ # key:beat there while the run is alive, and gone a few seconds after it is not
13
+ # key:control cancel requests, the other way - see Mount#follow_stream
14
+ #
15
+ # **One run at a time.** Each writer is a run, named by its log id, and claims `key:run` with its
16
+ # first batch. A run that a later one has taken over from - a job retried while the first
17
+ # attempt still limps on, two workers that adopted at once - may still append, and readers
18
+ # ignore what it appends; but every snapshot, trim, beat and expiry is written only while
19
+ # `key:run` still names the run that writes it, checked and written in one step. So a run that
20
+ # was replaced can never take the tree back.
21
+ #
22
+ # A snapshot is taken at the first batch, every `snapshot_every` seconds after, and when the
23
+ # root finishes, and each one trims the stream back to the one before it. Every key expires
24
+ # `retain` seconds after the last snapshot - a run that dies leaves nothing behind for good.
25
+ #
26
+ # With `heartbeat`, every so many seconds it marks itself alive and takes the cancel requests that
27
+ # came for it, on a thread of its own - from the first batch until the root finishes or it is
28
+ # closed. A request names the run it is for; a run also takes those for the run it took over
29
+ # from (`took_over`), so a cancel sent while a job still waited in the queue reaches whoever
30
+ # takes it up.
31
+ #
32
+ # A write that fails - Redis went away for a moment - is not lost: the next batch goes out as
33
+ # the whole tree, squashed, and readers start over from it
34
+ class StreamOutput
35
+ # The key it writes under
36
+ attr_reader key: String
37
+
38
+ @log: Log?
39
+
40
+ @replaced: bool
41
+
42
+ @covered: Integer
43
+
44
+ @broken: bool
45
+
46
+ @stop: Thread::Queue
47
+
48
+ @beating: Thread?
49
+
50
+ @snapped_at: Float
51
+
52
+ @snapshot_id: String?
53
+
54
+ @state: State
55
+
56
+ @run: String?
57
+
58
+ @took_over: String?
59
+
60
+ @retain: Integer | Float
61
+
62
+ @snapshot_every: Integer | Float
63
+
64
+ @heartbeat: (Integer | Float)?
65
+
66
+ @stream: untyped
67
+
68
+ # The key of the squashed tree a reader starts from
69
+ # : (String) -> String
70
+ def self.snapshot_key: (String) -> String
71
+
72
+ # The key that is there while the writer is alive
73
+ # : (String) -> String
74
+ def self.beat_key: (String) -> String
75
+
76
+ # The key cancel requests are appended to
77
+ # : (String) -> String
78
+ def self.control_key: (String) -> String
79
+
80
+ # Whether a cancel request is for one of these runs. A request names the run it was sent to -
81
+ # the one its sender saw writing the tree; one from a parent's control pipe names none
82
+ # : (String, *String?) -> bool
83
+ def self.for_run?: (String, *String?) -> bool
84
+
85
+ # The key that names the run writing the tree now
86
+ # : (String) -> String
87
+ def self.run_key: (String) -> String
88
+
89
+ # A key for a new tree. The braces make it a Redis Cluster hash tag, so all five keys of a
90
+ # tree land on the same node
91
+ # : (String) -> String
92
+ def self.key_for: (String) -> String
93
+
94
+ # Writes to `stream` under `key`. `took_over` is the run it takes the tree over from
95
+ # : (untyped, String, ?heartbeat: (Integer | Float)?, ?snapshot_every: Integer | Float, ?retain: Integer | Float, ?took_over: String?) -> void
96
+ def initialize: (untyped, String, ?heartbeat: (Integer | Float)?, ?snapshot_every: Integer | Float, ?retain: Integer | Float, ?took_over: String?) -> void
97
+
98
+ # Appends the batch, and takes a snapshot when one is due
99
+ # : (Batch, Log) -> void
100
+ def call: (Batch, Log) -> void
101
+
102
+ # Stops beating, and taking cancel requests - the work is over, one way or another. If the
103
+ # last write failed, the tree goes out whole once more, so readers do not stop short of the end
104
+ # : () -> void
105
+ def close: () -> void
106
+
107
+ # Whether a later run has taken the tree over, so this one writes no more
108
+ # : () -> bool
109
+ def replaced?: () -> bool
110
+
111
+ private
112
+
113
+ # : () -> Float
114
+ def now: () -> Float
115
+
116
+ # : () -> void
117
+ def stop: () -> void
118
+
119
+ # : (String) -> [String, String]
120
+ def guard: (String) -> [ String, String ]
121
+
122
+ # The run starts: from now on the tree is this run's to snapshot, trim and beat for
123
+ # : (String) -> String
124
+ def claim: (String) -> String
125
+
126
+ # The whole tree as one batch, in place of what failed to go out
127
+ # : (Log) -> Batch
128
+ def resync: (Log) -> Batch
129
+
130
+ # : (String, Batch, String) -> void
131
+ def snapshot: (String, Batch, String) -> void
132
+
133
+ # : (String) -> void
134
+ def finish: (String) -> void
135
+
136
+ # : () -> void
137
+ def replaced: () -> void
138
+
139
+ # : (Log) -> void
140
+ def beat: (Log) -> void
141
+ end
142
+ end
@@ -0,0 +1,46 @@
1
+ # Generated from lib/task_progress/stream_reader.rb with RBS::Inline
2
+
3
+ class TaskProgress
4
+ # Reads a tree a StreamOutput writes: the snapshot first, as one batch with `prev` 0, then
5
+ # every batch after it as it comes. Each read picks up where the last one left off.
6
+ #
7
+ # It hands on what it reads, and judges none of it - Mount does that, see Mount#follow_stream.
8
+ # After a gap, `resync!` makes the next read start over from the latest snapshot
9
+ class StreamReader
10
+ # The key it reads
11
+ attr_reader key: String
12
+
13
+ @stream: untyped
14
+
15
+ @after: String?
16
+
17
+ @started: bool
18
+
19
+ # Reads `stream` at `key`
20
+ # : (untyped, String) -> void
21
+ def initialize: (untyped, String) -> void
22
+
23
+ # The batches since the last read - starting from the snapshot. With a `timeout`, waits up
24
+ # to that many seconds for one when there are none yet
25
+ # : (?timeout: (Integer | Float)?) -> Array[Batch]
26
+ def read: (?timeout: (Integer | Float)?) -> Array[Batch]
27
+
28
+ # The next read starts over, from the latest snapshot
29
+ # : () -> void
30
+ def resync!: () -> void
31
+
32
+ # The tree as of the latest snapshot, as one batch - or nil when nothing was ever written
33
+ # here, or it has expired
34
+ # : () -> Batch?
35
+ def snapshot: () -> Batch?
36
+
37
+ # Whether the writer said lately that it is alive
38
+ # : () -> bool
39
+ def alive?: () -> bool
40
+
41
+ private
42
+
43
+ # : () -> [String, Batch]?
44
+ def snapshot_entry: () -> [ String, Batch ]?
45
+ end
46
+ end
@@ -0,0 +1,19 @@
1
+ # Generated from lib/task_progress/summary_output.rb with RBS::Inline
2
+
3
+ class TaskProgress
4
+ # The tree boiled down to the root's percent and the latest thing said, for a progress
5
+ # column in a database or a single status line. A batch that changes neither is not passed on
6
+ class SummaryOutput
7
+ @block: ^(percent: Integer, message: String?) -> void
8
+
9
+ @percent: Integer
10
+
11
+ # Calls the block with `percent:` and `message:` whenever either changes
12
+ # : () { (percent: Integer, message: String?) -> void } -> void
13
+ def initialize: () { (percent: Integer, message: String?) -> void } -> void
14
+
15
+ # Takes one batch, see Log#add_output
16
+ # : (Batch, Log) -> void
17
+ def call: (Batch, Log) -> void
18
+ end
19
+ end
@@ -0,0 +1,117 @@
1
+ # Generated from lib/task_progress/terminal_output.rb with RBS::Inline
2
+
3
+ class TaskProgress
4
+ # Redraws the tree in place on a terminal, from a ticker thread of its own so spinners turn
5
+ # while nothing reports. A finished subtree folds into its one line, and when the tree is
6
+ # taller than the screen the finished lines go first, then the ones not started. When the
7
+ # root finishes the tree is drawn one last time and left on screen - or erased, with `erase: true`.
8
+ # A line a task `puts` goes above the tree and stays there
9
+ #
10
+ # With `interval: nil` there is no thread and every batch redraws, which is what tests want
11
+ class TerminalOutput
12
+ SPINNER: Array[String]
13
+
14
+ BAR: Integer
15
+
16
+ DEFAULT_SIZE: [ Integer, Integer ]
17
+
18
+ @dirty: bool
19
+
20
+ @ticker: Thread?
21
+
22
+ @closed: bool
23
+
24
+ @log: Log?
25
+
26
+ @frame: Integer
27
+
28
+ @drawn: Integer
29
+
30
+ @lock: Thread::Mutex
31
+
32
+ @clock: ^() -> Float
33
+
34
+ @erase: bool
35
+
36
+ @interval: (Integer | Float)?
37
+
38
+ @io: IO | StringIO
39
+
40
+ # Draws on `io` every `interval` seconds - or on every batch, with `interval: nil`. With `erase: true` the tree is cleared once the root finishes
41
+ # : (?IO | StringIO, ?interval: (Integer | Float)?, ?erase: bool, ?clock: ^() -> Float) -> void
42
+ def initialize: (?IO | StringIO, ?interval: (Integer | Float)?, ?erase: bool, ?clock: ^() -> Float) -> void
43
+
44
+ # Takes one batch, see Log#add_output
45
+ # : (Batch, Log) -> void
46
+ def call: (Batch, Log) -> void
47
+
48
+ # Draws the tree one last time and stops the ticker
49
+ # : () -> void
50
+ def close: () -> void
51
+
52
+ # The lines for a snapshot, without the terminal control around them
53
+ # : (State, ?width: Integer, ?height: Integer) -> Array[String]
54
+ def lines: (State, ?width: Integer, ?height: Integer) -> Array[String]
55
+
56
+ private
57
+
58
+ # : () -> void
59
+ def tick: () -> void
60
+
61
+ # : () -> void
62
+ def finish: () -> void
63
+
64
+ # : () -> void
65
+ def draw: () -> void
66
+
67
+ # : (Array[String]) -> void
68
+ def print_above: (Array[String]) -> void
69
+
70
+ # : () -> void
71
+ def clear: () -> void
72
+
73
+ # : () -> String
74
+ def up: () -> String
75
+
76
+ # Under a finished node only what failed shows, so a failure leaves the way down to it
77
+ # : (State, path) -> bool
78
+ def hidden?: (State, path) -> bool
79
+
80
+ # What fits in `room` lines - the last of them saying how many more there are
81
+ # : (Array[[path?, Node | Fold | Integer]], Integer) -> Array[[path?, Node | Fold | Integer]]
82
+ def trim: (Array[[ path?, Node | Fold | Integer ]], Integer) -> Array[[ path?, Node | Fold | Integer ]]
83
+
84
+ # : (State, path?, Node | Fold | Integer, Integer) -> String
85
+ def line: (State, path?, Node | Fold | Integer, Integer) -> String
86
+
87
+ # : (Node) -> String
88
+ def icon: (Node) -> String
89
+
90
+ # : (State, path, Node) -> String?
91
+ def measure: (State, path, Node) -> String?
92
+
93
+ # Diagonal stripes that crawl one cell along every frame - for work with no measure
94
+ # : () -> String
95
+ def stripes: () -> String
96
+
97
+ # : (Integer) -> String
98
+ def bar: (Integer) -> String
99
+
100
+ # The rate over the whole run so far, and the time left at that rate. Steadier than a
101
+ # rate over the last few ticks, which jumps about with every pause in the work
102
+ # : (Node) -> String?
103
+ def pace: (Node) -> String?
104
+
105
+ # : (Node) -> String?
106
+ def elapsed: (Node) -> String?
107
+
108
+ # : () -> Integer
109
+ def columns: () -> Integer
110
+
111
+ # : () -> Integer
112
+ def rows: () -> Integer
113
+
114
+ # : () -> [Integer, Integer]
115
+ def size: () -> [ Integer, Integer ]
116
+ end
117
+ end
@@ -0,0 +1,41 @@
1
+ # Generated from lib/task_progress/token.rb with RBS::Inline
2
+
3
+ class TaskProgress
4
+ # The right to see a tree and to ask for its tasks to be canceled, signed, and good for a while.
5
+ # The server issues one when it hands out a page - after it has decided the page may show that
6
+ # tree - and checks it when the page comes back for the tree or with a cancellation. So neither
7
+ # can be forged without the secret, or used after the token expires, and the tree's own key never
8
+ # has to reach a page. Which tasks may be canceled is for `cancelable` to say.
9
+ #
10
+ # tokens = TaskProgress::Token.new(ENV.fetch("TASK_PROGRESS_SECRET"))
11
+ # token = tokens.issue(import.key, expires_in: 3600) # into the page
12
+ # tokens.verify(token) # => import.key, when the page comes back
13
+ class Token
14
+ @secret: String
15
+
16
+ @clock: ^() -> Integer
17
+
18
+ # Signs with `secret`, which must be at least 32 bytes
19
+ # : (String, ?clock: ^() -> Integer) -> void
20
+ def initialize: (String, ?clock: ^() -> Integer) -> void
21
+
22
+ # A token for `subject` - a log id, a run, whatever the tokens are checked against - good for `expires_in` seconds
23
+ # : (String, ?expires_in: Integer) -> String
24
+ def issue: (String, ?expires_in: Integer) -> String
25
+
26
+ # What the token was issued for, or nil if it is forged, mangled or expired
27
+ # : (String?) -> String?
28
+ def verify: (String?) -> String?
29
+
30
+ private
31
+
32
+ # : (String) -> String
33
+ def sign: (String) -> String
34
+
35
+ # : (String) -> String
36
+ def encode: (String) -> String
37
+
38
+ # : (String) -> String
39
+ def decode: (String) -> String
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ # Generated from lib/task_progress/tracked_io.rb with RBS::Inline
2
+
3
+ class TaskProgress
4
+ # A ProgressiveIO that advances a task by how far it has been read. Readers seek (ZipKit reads
5
+ # the end of an archive first), so only the furthest position counts. The task moves in steps,
6
+ # a thousandth of the total by default - a loop of small reads over a big file would otherwise
7
+ # commit a batch per read, and every output and browser would get each of them
8
+ class TrackedIO < ::ProgressiveIO
9
+ STEP_WITHOUT_TOTAL: Integer
10
+
11
+ @task: TaskProgress
12
+
13
+ @total: Integer?
14
+
15
+ @step: Integer
16
+
17
+ @furthest: Integer
18
+
19
+ @reported: Integer
20
+
21
+ # Advances `task` as `io` is read, in steps of `step` bytes - see TaskProgress#track_io, which makes one
22
+ # : (IO, TaskProgress, ?total: Integer?, ?step: Integer?) -> void
23
+ def initialize: (IO, TaskProgress, ?total: Integer?, ?step: Integer?) -> void
24
+
25
+ # ProgressiveIO lets these through unreported, and IO.copy_stream reads anything that is not
26
+ # an IO with readpartial
27
+ # Reads the way IO#readpartial does, and reports how far it got
28
+ # : (*untyped, **untyped) -> untyped
29
+ def readpartial: (*untyped, **untyped) -> untyped
30
+
31
+ # Reads the way IO#read_nonblock does, and reports how far it got
32
+ # : (*untyped, **untyped) -> untyped
33
+ def read_nonblock: (*untyped, **untyped) -> untyped
34
+
35
+ # Reads the way IO#sysread does, and reports how far it got
36
+ # : (*untyped, **untyped) -> untyped
37
+ def sysread: (*untyped, **untyped) -> untyped
38
+
39
+ # Reports what was read since the last step
40
+ # : () -> void
41
+ def flush_progress: () -> void
42
+
43
+ private
44
+
45
+ # : (Integer) -> void
46
+ def moved_to: (Integer) -> void
47
+ end
48
+ end
@@ -0,0 +1,33 @@
1
+ # Generated from lib/task_progress/transaction.rb with RBS::Inline
2
+
3
+ class TaskProgress
4
+ # What a block given to Log#transaction adds its events through. Each one is applied to the
5
+ # tree as it comes, and journaled, so the whole transaction can be undone if the block raises
6
+ class Transaction
7
+ # What it has added so far, in order
8
+ attr_reader events: Array[Event]
9
+
10
+ # What it changed, to undo it by
11
+ attr_reader journal: Journal
12
+
13
+ # The time it opened - every event in it happens then
14
+ attr_reader at: Float
15
+
16
+ # The tree as the transaction has it so far
17
+ attr_reader state: State
18
+
19
+ @random: Random
20
+
21
+ # For changing `state` at `at`, drawing task ids from `random` - see Log#transaction, which opens one
22
+ # : (State, Float, Random) -> void
23
+ def initialize: (State, Float, Random) -> void
24
+
25
+ # Applies the event, and adds it to the batch
26
+ # : (Event) -> self
27
+ def <<: (Event) -> self
28
+
29
+ # A path for a new child of `path` - see State#new_child
30
+ # : (path) -> path
31
+ def new_child: (path) -> path
32
+ end
33
+ end