yobi 0.3.1 → 1.1.0

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +17 -0
  4. data/README.md +83 -44
  5. data/lib/yobi/argv_builder.rb +1 -35
  6. data/lib/yobi/cancellable_proxy.rb +48 -0
  7. data/lib/yobi/cancellation.rb +97 -0
  8. data/lib/yobi/errors.rb +45 -30
  9. data/lib/yobi/fancy_hash.rb +1 -5
  10. data/lib/yobi/io_handle.rb +9 -17
  11. data/lib/yobi/mount_handle.rb +7 -11
  12. data/lib/yobi/repository/backup.rb +59 -90
  13. data/lib/yobi/repository/cat.rb +23 -43
  14. data/lib/yobi/repository/check.rb +21 -34
  15. data/lib/yobi/repository/copy.rb +11 -13
  16. data/lib/yobi/repository/diff.rb +18 -49
  17. data/lib/yobi/repository/dump.rb +14 -15
  18. data/lib/yobi/repository/find.rb +17 -37
  19. data/lib/yobi/repository/forget.rb +35 -39
  20. data/lib/yobi/repository/init.rb +19 -24
  21. data/lib/yobi/repository/key.rb +18 -33
  22. data/lib/yobi/repository/list.rb +5 -6
  23. data/lib/yobi/repository/ls.rb +20 -43
  24. data/lib/yobi/repository/migrate.rb +6 -6
  25. data/lib/yobi/repository/mount.rb +22 -32
  26. data/lib/yobi/repository/prune.rb +11 -9
  27. data/lib/yobi/repository/recover.rb +2 -4
  28. data/lib/yobi/repository/repair.rb +16 -23
  29. data/lib/yobi/repository/restore.rb +31 -54
  30. data/lib/yobi/repository/rewrite.rb +15 -20
  31. data/lib/yobi/repository/snapshots.rb +7 -8
  32. data/lib/yobi/repository/stats.rb +26 -21
  33. data/lib/yobi/repository/tag.rb +23 -33
  34. data/lib/yobi/repository/unlock.rb +2 -4
  35. data/lib/yobi/repository.rb +96 -25
  36. data/lib/yobi/restic.rb +93 -114
  37. data/lib/yobi/restic_output.rb +2 -74
  38. data/lib/yobi/snapshot.rb +15 -27
  39. data/lib/yobi/version.rb +1 -2
  40. data/lib/yobi.rb +2 -0
  41. data/sig/yobi.rbs +83 -6
  42. metadata +5 -2
@@ -4,21 +4,11 @@ require "json"
4
4
  require "tempfile"
5
5
 
6
6
  module Yobi
7
- # Wraps the tempfile a repository operation's stdout+stderr are both
8
- # written to, with random access so a caller asking only for the last
9
- # line (see {#last_line}) doesn't have to read the entire output.
10
- #
11
- # @private
12
- class ResticOutput
13
- # Default read window size, in bytes, for {#last_line}.
7
+ class ResticOutput # :nodoc:
14
8
  DEFAULT_TAIL_CHUNK_SIZE = 4096
15
9
 
16
- # @return [File] a fresh, already-unlinked tempfile of its own
17
10
  attr_reader :file
18
11
 
19
- # @param tail_chunk_size [Integer] read window size for {#last_line}
20
- # @param transform [Proc, nil] used by {#write_and_parse} during a streaming run to transform
21
- # each message, and as {#messages}' default transform afterward, when no explicit one is given
22
12
  def initialize(tail_chunk_size: DEFAULT_TAIL_CHUNK_SIZE, transform: nil)
23
13
  @file = Tempfile.new("yobi-restic-output")
24
14
  @file.unlink
@@ -28,25 +18,15 @@ module Yobi
28
18
  @stderr_offsets = []
29
19
  end
30
20
 
31
- # Byte offsets of every line, grouped by message_type. Built once and
32
- # memoized.
33
- #
34
- # @return [Hash{String => Array<Integer>}]
35
21
  def index
36
22
  @index ||= build_index
37
23
  end
38
24
 
39
- # @param offset [Integer] a byte offset, as recorded by {#index}
40
- # @return [String] the line starting at that offset
41
25
  def read_line_at(offset)
42
26
  @file.seek(offset)
43
27
  @file.gets
44
28
  end
45
29
 
46
- # The last non-blank line, found by seeking backward from the end of
47
- # the file.
48
- #
49
- # @return [String, nil]
50
30
  def last_line
51
31
  size = @file.size
52
32
  return nil if size.zero?
@@ -66,8 +46,6 @@ module Yobi
66
46
  end
67
47
  end
68
48
 
69
- # @yieldparam line [String]
70
- # @return [Enumerator] if no block is given
71
49
  def each_line(&block)
72
50
  return enum_for(:each_line) unless block_given?
73
51
 
@@ -75,17 +53,6 @@ module Yobi
75
53
  @file.each_line(&block)
76
54
  end
77
55
 
78
- # Every message recorded in {#index}, restricted to one +message_type+
79
- # if given, in file order. Without an explicit transform block, uses
80
- # the +transform:+ given to {#initialize} (if any); with neither, just
81
- # yields/returns the raw parsed Hash. Whenever a transform (explicit
82
- # or the stored one) applies, wraps the result in a {LazyList}
83
- # instead of yielding.
84
- #
85
- # @param message_type [String, nil] every message, if omitted
86
- # @yieldparam message [Hash] the raw parsed message, to transform
87
- # @return [Yobi::ResticOutput::LazyList] if a transform block (or a stored transform) applies
88
- # @return [Enumerator] of raw Hashes, otherwise
89
56
  def messages(message_type = nil, &transform)
90
57
  transform ||= @transform
91
58
  return raw_messages(message_type) unless transform
@@ -94,27 +61,12 @@ module Yobi
94
61
  LazyList.new(self, offsets, transform)
95
62
  end
96
63
 
97
- # Every line known to have arrived on stderr rather than stdout during
98
- # a streaming run (see {#write_and_parse}), in file order.
99
- #
100
- # @yieldparam line [String]
101
- # @return [Enumerator] if no block is given
102
64
  def stderr_lines
103
65
  return enum_for(:stderr_lines) unless block_given?
104
66
 
105
67
  @stderr_offsets.each { |offset| yield read_line_at(offset) }
106
68
  end
107
69
 
108
- # Called once per line read from a live streaming run's stdout/stderr.
109
- # Writes it to the file, parses it, and - if it's a JSON message -
110
- # indexes it by message_type and returns it transformed via the
111
- # +transform:+ given to {#initialize} (or the raw parsed Hash, if none
112
- # was given). A non-JSON line's offset is recorded in {#stderr_lines}
113
- # instead, if +origin+ is +:stderr+; either way, returns nil.
114
- #
115
- # @param line [String]
116
- # @param origin [:stdout, :stderr]
117
- # @return [Object, nil]
118
70
  def write_and_parse(line, origin)
119
71
  offset = @file.pos
120
72
  @file.write(line)
@@ -134,13 +86,11 @@ module Yobi
134
86
  end
135
87
  end
136
88
 
137
- # @return [String] the full captured output
138
89
  def to_s
139
90
  @file.rewind
140
91
  @file.read
141
92
  end
142
93
 
143
- # @return [String]
144
94
  def inspect
145
95
  "#<#{self.class} #{@file.size} bytes>"
146
96
  end
@@ -179,32 +129,17 @@ module Yobi
179
129
  index
180
130
  end
181
131
 
182
- # A lazily-transformed view over a set of {#index} offsets, printing
183
- # like a plain Array (bounded to a preview, unlike one). Returned by
184
- # {ResticOutput#messages} when given a transform block.
185
- # #inspect/#pretty_print's preview is memoized, and #each reuses it
186
- # rather than re-reading/re-transforming those same offsets again
187
- # when the caller iterates the whole thing.
188
- #
189
- # @private
190
- class LazyList
132
+ class LazyList # :nodoc:
191
133
  include Enumerable
192
134
 
193
- # Items #inspect/#pretty_print show before truncating with "...".
194
- # One louder than ActiveRecord::Relation#inspect's own preview size.
195
135
  INSPECT_PREVIEW_SIZE = 11
196
136
 
197
- # @param output [Yobi::ResticOutput]
198
- # @param offsets [Array<Integer>]
199
- # @param transform [Proc] applied to each offset's raw parsed Hash
200
137
  def initialize(output, offsets, transform)
201
138
  @output = output
202
139
  @offsets = offsets
203
140
  @transform = transform
204
141
  end
205
142
 
206
- # @yieldparam item [Object]
207
- # @return [Enumerator] if no block is given
208
143
  def each
209
144
  return enum_for(:each) unless block_given?
210
145
 
@@ -212,23 +147,17 @@ module Yobi
212
147
  @offsets[preview.size..].each { |offset| yield transform_at(offset) }
213
148
  end
214
149
 
215
- # The number of messages, known upfront from {#index} - unlike
216
- # +Enumerable#count+, doesn't read/transform a single one to answer.
217
- #
218
- # @return [Integer]
219
150
  def size
220
151
  @offsets.size
221
152
  end
222
153
  alias_method :length, :size
223
154
 
224
- # @return [String]
225
155
  def inspect
226
156
  shown = preview.first(INSPECT_PREVIEW_SIZE).map(&:inspect).join(", ")
227
157
  shown += ", ..." if preview.size > INSPECT_PREVIEW_SIZE
228
158
  "[#{shown}]"
229
159
  end
230
160
 
231
- # @return [void]
232
161
  def pretty_print(q)
233
162
  q.group(1, "[", "]") do
234
163
  q.seplist(preview.first(INSPECT_PREVIEW_SIZE)) { |item| q.pp item }
@@ -238,7 +167,6 @@ module Yobi
238
167
 
239
168
  private
240
169
 
241
- # @return [Array<Object>]
242
170
  def preview
243
171
  @preview ||= @offsets.first(INSPECT_PREVIEW_SIZE + 1).map { |offset| transform_at(offset) }
244
172
  end
data/lib/yobi/snapshot.rb CHANGED
@@ -3,120 +3,108 @@
3
3
  require "time"
4
4
 
5
5
  module Yobi
6
- # One snapshot, as Restic reports it. Shared across {Yobi::Repository#snapshots},
7
- # {Yobi::Repository#ls}, and {Yobi::Repository#forget}'s keep/remove entries.
6
+ # One snapshot, as Restic reports it. Shared across Yobi::Repository#snapshots,
7
+ # Yobi::Repository#ls, and Yobi::Repository#forget's keep/remove entries.
8
8
  class Snapshot < Yobi::FancyHash
9
- # @return [String]
9
+ # Full snapshot ID (64-char hex).
10
10
  def id
11
11
  self["id"]
12
12
  end
13
13
 
14
- # @return [String]
14
+ # First 8 chars of #id.
15
15
  def short_id
16
16
  self["short_id"]
17
17
  end
18
18
 
19
- # @return [Time]
19
+ # When the snapshot was created.
20
20
  def time
21
21
  @time ||= Time.parse(self["time"])
22
22
  end
23
23
 
24
- # @return [String]
24
+ # Hostname recorded on the snapshot.
25
25
  def host
26
26
  self["hostname"]
27
27
  end
28
28
 
29
- # @return [Array<String>]
29
+ # Tags recorded on the snapshot.
30
30
  def tags
31
31
  @tags ||= self["tags"] || []
32
32
  end
33
33
 
34
- # @return [Array<String>]
34
+ # Paths backed up in the snapshot.
35
35
  def paths
36
36
  self["paths"]
37
37
  end
38
38
 
39
- # @return [String, nil]
39
+ # Parent snapshot's #id, if any.
40
40
  def parent_id
41
41
  self["parent"]
42
42
  end
43
43
 
44
- # @return [Yobi::SnapshotSummary]
44
+ # A Yobi::SnapshotSummary of the snapshot's stats at creation time.
45
45
  def summary
46
46
  @summary ||= SnapshotSummary.new(self["summary"] || {})
47
47
  end
48
48
  end
49
49
 
50
- # The `"summary"` field of a {Yobi::Snapshot}, the stats recorded when it
51
- # was created.
52
- # https://restic.readthedocs.io/en/stable/075_scripting.html#snapshotsummary-object
50
+ # The +"summary"+ field of a Yobi::Snapshot, the stats recorded when it
51
+ # was created. See
52
+ # https://restic.readthedocs.io/en/stable/075_scripting.html#snapshotsummary-object.
53
53
  class SnapshotSummary < Yobi::FancyHash
54
- # @return [Time, nil]
54
+ # When the backup started.
55
55
  def backup_start
56
56
  @backup_start ||= Time.parse(self["backup_start"]) if self["backup_start"]
57
57
  end
58
58
 
59
- # @return [Time, nil]
59
+ # When the backup finished.
60
60
  def backup_end
61
61
  @backup_end ||= Time.parse(self["backup_end"]) if self["backup_end"]
62
62
  end
63
63
 
64
- # @return [Integer]
65
64
  def files_new
66
65
  self["files_new"] || 0
67
66
  end
68
67
 
69
- # @return [Integer]
70
68
  def files_changed
71
69
  self["files_changed"] || 0
72
70
  end
73
71
 
74
- # @return [Integer]
75
72
  def files_unmodified
76
73
  self["files_unmodified"] || 0
77
74
  end
78
75
 
79
- # @return [Integer]
80
76
  def dirs_new
81
77
  self["dirs_new"] || 0
82
78
  end
83
79
 
84
- # @return [Integer]
85
80
  def dirs_changed
86
81
  self["dirs_changed"] || 0
87
82
  end
88
83
 
89
- # @return [Integer]
90
84
  def dirs_unmodified
91
85
  self["dirs_unmodified"] || 0
92
86
  end
93
87
 
94
- # @return [Integer]
95
88
  def data_blobs
96
89
  self["data_blobs"] || 0
97
90
  end
98
91
 
99
- # @return [Integer]
100
92
  def tree_blobs
101
93
  self["tree_blobs"] || 0
102
94
  end
103
95
 
104
- # @return [Integer]
105
96
  def data_added
106
97
  self["data_added"] || 0
107
98
  end
108
99
 
109
- # @return [Integer]
110
100
  def data_added_packed
111
101
  self["data_added_packed"] || 0
112
102
  end
113
103
 
114
- # @return [Integer]
115
104
  def total_files_processed
116
105
  self["total_files_processed"] || 0
117
106
  end
118
107
 
119
- # @return [Integer]
120
108
  def total_bytes_processed
121
109
  self["total_bytes_processed"] || 0
122
110
  end
data/lib/yobi/version.rb CHANGED
@@ -3,6 +3,5 @@
3
3
  # A Ruby library for the Restic backup program, wrapping the `restic` CLI in
4
4
  # plain Ruby objects instead of shelling out to flags and raw JSON by hand.
5
5
  module Yobi
6
- # @return [String]
7
- VERSION = "0.3.1"
6
+ VERSION = "1.1.0"
8
7
  end
data/lib/yobi.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "yobi/version"
4
4
  require_relative "yobi/fancy_hash"
5
+ require_relative "yobi/cancellation"
5
6
  require_relative "yobi/errors"
6
7
  require_relative "yobi/argv_builder"
7
8
  require_relative "yobi/restic_output"
@@ -10,5 +11,6 @@ require_relative "yobi/mount_handle"
10
11
  require_relative "yobi/restic"
11
12
  require_relative "yobi/snapshot"
12
13
  require_relative "yobi/repository"
14
+ require_relative "yobi/cancellable_proxy"
13
15
 
14
16
  Dir[File.join(__dir__, "yobi", "repository", "*.rb")].sort.each { |file| require_relative file }
data/sig/yobi.rbs CHANGED
@@ -31,6 +31,32 @@ module Yobi
31
31
  def initialize: (installed_version: String, minimum_version: String) -> void
32
32
  end
33
33
 
34
+ class Cancellation
35
+ DEFAULT_SIGNAL: String
36
+
37
+ def initialize: () -> void
38
+
39
+ def cancelled?: () -> bool
40
+
41
+ def cancel!: (?signal: String) -> bool
42
+
43
+ def attach: (Integer pid) -> void
44
+
45
+ def detach: () -> void
46
+
47
+ def inspect: () -> String
48
+ end
49
+
50
+ class Cancelled < Error
51
+ attr_reader execution: cancelled_execution
52
+
53
+ def initialize: (cancelled_execution execution) -> void
54
+
55
+ def argv: () -> Array[String]
56
+
57
+ def before_start?: () -> bool
58
+ end
59
+
34
60
  class ResticNotFound < Error
35
61
  attr_reader restic_path: String
36
62
  attr_reader argv: Array[String]
@@ -39,6 +65,7 @@ module Yobi
39
65
  end
40
66
 
41
67
  type execution = { exit_code: Integer, output: ResticOutput, argv: Array[String] }
68
+ type cancelled_execution = { exit_code: Integer?, output: ResticOutput?, argv: Array[String] }
42
69
 
43
70
  class ResticExecutionError < Error
44
71
  attr_reader execution: execution
@@ -227,7 +254,7 @@ module Yobi
227
254
  MINIMUM_VERSION: Gem::Version
228
255
  ALLOWED_ENV_VARS: Set[String]
229
256
 
230
- attr_reader restic_path: String
257
+ attr_accessor restic_path: String
231
258
  attr_accessor cache_dir: String?
232
259
  attr_accessor compression: String?
233
260
  attr_accessor pack_size: Integer?
@@ -264,7 +291,7 @@ module Yobi
264
291
 
265
292
  def cache: (?cleanup: bool, ?max_age: String?, ?no_size: bool) -> true
266
293
 
267
- def run: (Array[String] argv, ?extra_env: Hash[String, String], ?skip_version_check: bool, ?output: ResticOutput?) ?{ (untyped message) -> void } -> execution
294
+ def run: (Array[String] argv, ?extra_env: Hash[String, String], ?skip_version_check: bool, ?output: ResticOutput?, ?cancellation: Cancellation?) ?{ (untyped message) -> void } -> execution
268
295
 
269
296
  def self.dispatch: (execution execution) -> execution
270
297
 
@@ -287,9 +314,9 @@ module Yobi
287
314
  end
288
315
 
289
316
  class Repository
290
- attr_reader url: String
291
- attr_reader password: password
292
- attr_reader backend_credentials: backend_credentials
317
+ attr_accessor url: String
318
+ attr_accessor password: password
319
+ attr_accessor backend_credentials: backend_credentials
293
320
 
294
321
  def initialize: (url: String, password: password, ?backend_credentials: backend_credentials, ?restic: restic_or_path) -> void
295
322
 
@@ -305,6 +332,8 @@ module Yobi
305
332
 
306
333
  alias config cat_config
307
334
 
335
+ def with_cancellation: (Cancellation token) -> CancellableProxy
336
+
308
337
  def backup: (source: String | [:stdin_from_command, String | Array[String]] | [:stdin_from_command, String | Array[String], String],
309
338
  ?excludes: Array[String] | String, ?exclude_files: Array[String] | String,
310
339
  ?exclude_if_present: Array[String] | String, ?exclude_larger_than: String?,
@@ -418,7 +447,8 @@ module Yobi
418
447
  ?include_files: Array[String] | String, ?include_xattrs: Array[String] | String,
419
448
  ?paths: Array[String] | String, ?tags: Array[String] | String, ?delete: bool, ?dry_run: bool,
420
449
  ?overwrite: String | Symbol | nil, ?ownership_by_name: bool, ?sparse: bool, ?verbose: bool,
421
- ?verify: bool) ?{ (RestoreStatus | RestoreVerboseStatus message) -> void } -> RestoreOutcome
450
+ ?verify: bool)
451
+ ?{ (RestoreStatus | RestoreVerboseStatus message) -> void } -> RestoreOutcome
422
452
 
423
453
  def rewrite: (?snapshot_ids: Array[String] | String, ?hosts: Array[String] | String,
424
454
  ?tags: Array[String] | String, ?paths: Array[String] | String, ?excludes: Array[String] | String,
@@ -441,6 +471,53 @@ module Yobi
441
471
  def unlock: (?remove_all: bool) -> true
442
472
  end
443
473
 
474
+ class CancellableProxy
475
+ def initialize: (Repository repository, Cancellation token) -> void
476
+
477
+ def backup: (source: String | [:stdin_from_command, String | Array[String]] | [:stdin_from_command, String | Array[String], String],
478
+ ?excludes: Array[String] | String, ?exclude_files: Array[String] | String,
479
+ ?exclude_if_present: Array[String] | String, ?exclude_larger_than: String?,
480
+ ?files_from: Array[String] | String, ?files_from_raw: Array[String] | String,
481
+ ?files_from_verbatim: Array[String] | String, ?iexcludes: Array[String] | String,
482
+ ?iexclude_files: Array[String] | String, ?tags: Array[String] | String, ?dry_run: bool,
483
+ ?exclude_caches: bool, ?exclude_cloud_files: bool, ?force: bool, ?group_by: String?,
484
+ ?host: String?, ?ignore_ctime: bool, ?ignore_inode: bool, ?no_scan: bool,
485
+ ?one_file_system: bool, ?parent: String?, ?read_concurrency: Integer?,
486
+ ?skip_if_unchanged: bool, ?time: String?, ?verbose: bool, ?with_atime: bool)
487
+ ?{ (BackupStatus | BackupError | BackupVerboseStatus | BackupSummary message) -> void } -> BackupOutcome
488
+
489
+ def restore: (snapshot_id: String, target: String, ?excludes: Array[String] | String,
490
+ ?exclude_files: Array[String] | String, ?exclude_xattrs: Array[String] | String,
491
+ ?hosts: Array[String] | String, ?iexcludes: Array[String] | String,
492
+ ?iexclude_files: Array[String] | String, ?iincludes: Array[String] | String,
493
+ ?iinclude_files: Array[String] | String, ?includes: Array[String] | String,
494
+ ?include_files: Array[String] | String, ?include_xattrs: Array[String] | String,
495
+ ?paths: Array[String] | String, ?tags: Array[String] | String, ?delete: bool, ?dry_run: bool,
496
+ ?overwrite: String | Symbol | nil, ?ownership_by_name: bool, ?sparse: bool, ?verbose: bool,
497
+ ?verify: bool)
498
+ ?{ (RestoreStatus | RestoreVerboseStatus message) -> void } -> RestoreOutcome
499
+
500
+ def check: (?hosts: Array[String] | String, ?paths: Array[String] | String, ?read_data: bool,
501
+ ?read_data_subset: String?, ?tags: Array[String] | String, ?with_cache: bool) -> CheckOutcome
502
+
503
+ def prune: (?dry_run: bool, ?max_repack_size: String?, ?max_unused: String?,
504
+ ?repack_cacheable_only: bool, ?repack_smaller_than: String?, ?repack_uncompressed: bool,
505
+ ?unsafe_recover_no_free_space: String?) -> true
506
+
507
+ def forget: (?keep_last: Integer?, ?keep_hourly: Integer?, ?keep_daily: Integer?, ?keep_weekly: Integer?,
508
+ ?keep_monthly: Integer?, ?keep_yearly: Integer?, ?keep_within: String?, ?keep_within_hourly: String?,
509
+ ?keep_within_daily: String?, ?keep_within_weekly: String?, ?keep_within_monthly: String?,
510
+ ?keep_within_yearly: String?, ?keep_tags: Array[String] | String, ?hosts: Array[String] | String,
511
+ ?tags: Array[String] | String, ?paths: Array[String] | String, ?compact: bool, ?group_by: String?,
512
+ ?dry_run: bool, ?prune: bool, ?unsafe_allow_remove_all: bool, ?max_unused: String?,
513
+ ?max_repack_size: String?, ?repack_cacheable_only: bool, ?repack_uncompressed: bool,
514
+ ?repack_smaller_than: String?) -> Array[ForgetGroup]
515
+
516
+ def copy: (from_repo: from_repo, ?from_password: from_password, ?snapshot_ids: Array[String] | String,
517
+ ?from_key_hint: String?, ?hosts: Array[String] | String,
518
+ ?paths: Array[String] | String, ?tags: Array[String] | String) -> true
519
+ end
520
+
444
521
  module BackupMessageWrapper
445
522
  def self.call: (Hash[String, untyped] raw) -> (BackupStatus | BackupError | BackupVerboseStatus | BackupSummary | Hash[String, untyped])
446
523
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yobi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zia Perdana
@@ -16,11 +16,14 @@ executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
+ - ".ruby-version"
19
20
  - CHANGELOG.md
20
21
  - LICENSE.txt
21
22
  - README.md
22
23
  - lib/yobi.rb
23
24
  - lib/yobi/argv_builder.rb
25
+ - lib/yobi/cancellable_proxy.rb
26
+ - lib/yobi/cancellation.rb
24
27
  - lib/yobi/errors.rb
25
28
  - lib/yobi/fancy_hash.rb
26
29
  - lib/yobi/io_handle.rb
@@ -61,7 +64,7 @@ metadata:
61
64
  homepage_uri: https://codeberg.org/ukazap/yobi
62
65
  source_code_uri: https://codeberg.org/ukazap/yobi
63
66
  changelog_uri: https://codeberg.org/ukazap/yobi/src/branch/main/CHANGELOG.md
64
- documentation_uri: https://rubydoc.info/gems/yobi/0.3.1
67
+ documentation_uri: https://rubydoc.info/gems/yobi/1.1.0
65
68
  rdoc_options: []
66
69
  require_paths:
67
70
  - lib