taskchampion-rb 0.2.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.
- checksums.yaml +7 -0
- data/.claude/settings.local.json +14 -0
- data/.rubocop.yml +21 -0
- data/CHANGELOG.md +15 -0
- data/Cargo.lock +3671 -0
- data/Cargo.toml +7 -0
- data/README.md +112 -0
- data/Rakefile +28 -0
- data/docs/API_REFERENCE.md +419 -0
- data/docs/THREAD_SAFETY.md +370 -0
- data/docs/breakthrough.md +246 -0
- data/docs/description.md +3 -0
- data/docs/phase_3_plan.md +482 -0
- data/docs/plan.md +612 -0
- data/example.md +465 -0
- data/examples/basic_usage.rb +278 -0
- data/examples/sync_workflow.rb +480 -0
- data/ext/taskchampion/Cargo.toml +20 -0
- data/ext/taskchampion/extconf.rb +6 -0
- data/ext/taskchampion/src/access_mode.rs +132 -0
- data/ext/taskchampion/src/annotation.rs +77 -0
- data/ext/taskchampion/src/dependency_map.rs +65 -0
- data/ext/taskchampion/src/error.rs +78 -0
- data/ext/taskchampion/src/lib.rs +41 -0
- data/ext/taskchampion/src/operation.rs +234 -0
- data/ext/taskchampion/src/operations.rs +180 -0
- data/ext/taskchampion/src/replica.rs +289 -0
- data/ext/taskchampion/src/status.rs +186 -0
- data/ext/taskchampion/src/tag.rs +77 -0
- data/ext/taskchampion/src/task.rs +388 -0
- data/ext/taskchampion/src/thread_check.rs +61 -0
- data/ext/taskchampion/src/util.rs +131 -0
- data/ext/taskchampion/src/working_set.rs +72 -0
- data/lib/taskchampion/version.rb +5 -0
- data/lib/taskchampion.rb +41 -0
- data/sig/taskchampion.rbs +4 -0
- data/taskchampion-0.2.0.gem +0 -0
- metadata +96 -0
data/Cargo.toml
ADDED
data/README.md
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# Taskchampion
|
2
|
+
|
3
|
+
Ruby bindings for TaskChampion, the task database that powers Taskwarrior.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
### Prerequisites
|
8
|
+
|
9
|
+
1. Ruby 3.0 or later
|
10
|
+
2. Rust toolchain (install from https://rustup.rs/)
|
11
|
+
|
12
|
+
```bash
|
13
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
14
|
+
```
|
15
|
+
|
16
|
+
### Building from source
|
17
|
+
|
18
|
+
```bash
|
19
|
+
bundle install
|
20
|
+
bundle exec rake compile
|
21
|
+
```
|
22
|
+
|
23
|
+
### Testing
|
24
|
+
|
25
|
+
```bash
|
26
|
+
bundle exec rake test
|
27
|
+
```
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'taskchampion'
|
33
|
+
|
34
|
+
# Create an in-memory replica
|
35
|
+
replica = Taskchampion::Replica.new_in_memory
|
36
|
+
|
37
|
+
# Create an on-disk replica
|
38
|
+
replica = Taskchampion::Replica.new_on_disk("/path/to/taskdb", true)
|
39
|
+
|
40
|
+
# With access mode
|
41
|
+
replica = Taskchampion::Replica.new_on_disk("/path/to/taskdb", true, :read_only)
|
42
|
+
|
43
|
+
# Working with operations
|
44
|
+
ops = Taskchampion::Operations.new
|
45
|
+
create_op = Taskchampion::Operation.create(SecureRandom.uuid)
|
46
|
+
ops << create_op
|
47
|
+
|
48
|
+
# Task operations (when Operations integration is complete)
|
49
|
+
# task = replica.create_task(uuid, ops)
|
50
|
+
# task.set_description("New task description", ops)
|
51
|
+
# replica.commit_operations(ops)
|
52
|
+
|
53
|
+
# Working with tags and annotations
|
54
|
+
tag = Taskchampion::Tag.new("work")
|
55
|
+
puts tag.to_s # => "work"
|
56
|
+
puts tag.user? # => true
|
57
|
+
puts tag.synthetic? # => false
|
58
|
+
|
59
|
+
annotation = Taskchampion::Annotation.new(DateTime.now, "This is a note")
|
60
|
+
puts annotation.description # => "This is a note"
|
61
|
+
|
62
|
+
# Status constants
|
63
|
+
puts Taskchampion::PENDING # => :pending
|
64
|
+
puts Taskchampion::COMPLETED # => :completed
|
65
|
+
puts Taskchampion::ACCESS_MODES # => [:read_only, :read_write]
|
66
|
+
```
|
67
|
+
|
68
|
+
## Development Status
|
69
|
+
|
70
|
+
This is a Ruby port of the taskchampion-py Python bindings. The implementation follows the plan outlined in the `ruby_docs/` directory of the Python project.
|
71
|
+
|
72
|
+
### Completed
|
73
|
+
- ✅ Basic project structure
|
74
|
+
- ✅ Magnus and rb-sys configuration
|
75
|
+
- ✅ Error hierarchy (Error, ThreadError, StorageError, ValidationError, ConfigError)
|
76
|
+
- ✅ Thread safety utilities
|
77
|
+
- ✅ Type conversions (DateTime, Option, HashMap, Vec)
|
78
|
+
- ✅ Replica class with Ruby idiomatic API
|
79
|
+
- ✅ Task class with Ruby idiomatic API
|
80
|
+
- ✅ Tag and Annotation classes
|
81
|
+
- ✅ Status constants (:pending, :completed, :deleted, etc.)
|
82
|
+
- ✅ Operation and Operations classes
|
83
|
+
- ✅ Access mode support
|
84
|
+
- ✅ Minitest testing infrastructure
|
85
|
+
- ✅ GitHub Actions CI/CD
|
86
|
+
|
87
|
+
### TODO
|
88
|
+
- [ ] WorkingSet class implementation
|
89
|
+
- [ ] DependencyMap class implementation
|
90
|
+
- [ ] Complete Task mutation methods (requires Operations integration)
|
91
|
+
- [ ] Complete test suite porting from Python
|
92
|
+
- [ ] Cross-platform compilation
|
93
|
+
- [ ] YARD documentation
|
94
|
+
- [ ] RubyGems publication
|
95
|
+
|
96
|
+
## API Design
|
97
|
+
|
98
|
+
The Ruby API follows Ruby idioms:
|
99
|
+
- Method names use snake_case
|
100
|
+
- Boolean methods end with `?` (e.g., `active?`, `waiting?`)
|
101
|
+
- Property access doesn't use `get_` prefix (e.g., `task.uuid` not `task.get_uuid`)
|
102
|
+
- Keyword arguments for optional parameters
|
103
|
+
- `nil` instead of `None`
|
104
|
+
- Symbols for enums (e.g., `:read_only`, `:read_write`)
|
105
|
+
|
106
|
+
## Thread Safety
|
107
|
+
|
108
|
+
All TaskChampion objects are thread-local and will raise `Taskchampion::ThreadError` if accessed from a different thread than the one that created them.
|
109
|
+
|
110
|
+
## Contributing
|
111
|
+
|
112
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/GothenburgBitFactory/taskchampion.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rubocop/rake_task"
|
5
|
+
require "rake/testtask"
|
6
|
+
|
7
|
+
RuboCop::RakeTask.new
|
8
|
+
|
9
|
+
require "rb_sys/extensiontask"
|
10
|
+
|
11
|
+
task build: :compile
|
12
|
+
|
13
|
+
GEMSPEC = Gem::Specification.load("taskchampion-rb.gemspec")
|
14
|
+
|
15
|
+
RbSys::ExtensionTask.new("taskchampion", GEMSPEC) do |ext|
|
16
|
+
ext.lib_dir = "lib/taskchampion"
|
17
|
+
end
|
18
|
+
|
19
|
+
Rake::TestTask.new(:test) do |t|
|
20
|
+
t.libs << "test"
|
21
|
+
t.libs << "lib"
|
22
|
+
t.test_files = FileList["test/**/test_*.rb"]
|
23
|
+
t.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
task test: :compile
|
27
|
+
|
28
|
+
task default: %i[compile test]
|
@@ -0,0 +1,419 @@
|
|
1
|
+
# TaskChampion Ruby API Reference
|
2
|
+
|
3
|
+
This document provides comprehensive API documentation for the TaskChampion Ruby bindings.
|
4
|
+
|
5
|
+
## Core Classes
|
6
|
+
|
7
|
+
### Taskchampion::Replica
|
8
|
+
|
9
|
+
The main entry point for TaskChampion functionality. Manages task storage and synchronization.
|
10
|
+
|
11
|
+
#### Constructor Methods
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
# Create an in-memory replica (for testing)
|
15
|
+
replica = Taskchampion::Replica.new_in_memory
|
16
|
+
|
17
|
+
# Create a disk-based replica
|
18
|
+
replica = Taskchampion::Replica.new_on_disk("/path/to/tasks", create_if_missing: true)
|
19
|
+
```
|
20
|
+
|
21
|
+
#### Task Management
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# Get all task UUIDs
|
25
|
+
uuids = replica.task_uuids # => Array of String UUIDs
|
26
|
+
|
27
|
+
# Get a specific task by UUID
|
28
|
+
task = replica.task(uuid) # => Task or nil
|
29
|
+
|
30
|
+
# Create a new task
|
31
|
+
operations = Taskchampion::Operations.new
|
32
|
+
task = replica.create_task(uuid, operations) # => Task
|
33
|
+
|
34
|
+
# Commit operations to storage
|
35
|
+
replica.commit_operations(operations)
|
36
|
+
```
|
37
|
+
|
38
|
+
#### Working Set Management
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
# Get the working set
|
42
|
+
working_set = replica.working_set # => WorkingSet
|
43
|
+
|
44
|
+
# Rebuild working set indices
|
45
|
+
replica.rebuild_working_set
|
46
|
+
```
|
47
|
+
|
48
|
+
#### Dependency Management
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
# Get dependency map
|
52
|
+
dep_map = replica.dependency_map(rebuild: false) # => DependencyMap
|
53
|
+
```
|
54
|
+
|
55
|
+
#### Synchronization
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
# Sync to local directory
|
59
|
+
replica.sync_to_local(server_dir, avoid_snapshots: false)
|
60
|
+
|
61
|
+
# Sync to remote server
|
62
|
+
replica.sync_to_remote(
|
63
|
+
url: "https://taskserver.example.com",
|
64
|
+
client_id: "client-123",
|
65
|
+
encryption_secret: "secret",
|
66
|
+
avoid_snapshots: false
|
67
|
+
)
|
68
|
+
|
69
|
+
# Sync to Google Cloud Storage
|
70
|
+
replica.sync_to_gcp(
|
71
|
+
bucket: "my-tasks-bucket",
|
72
|
+
credential_path: "/path/to/credentials.json",
|
73
|
+
encryption_secret: "secret",
|
74
|
+
avoid_snapshots: false
|
75
|
+
)
|
76
|
+
```
|
77
|
+
|
78
|
+
#### Storage Information
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
# Get number of local operations
|
82
|
+
count = replica.num_local_operations # => Integer
|
83
|
+
|
84
|
+
# Get number of undo points
|
85
|
+
count = replica.num_undo_points # => Integer
|
86
|
+
```
|
87
|
+
|
88
|
+
### Taskchampion::Task
|
89
|
+
|
90
|
+
Represents a single task with all its properties.
|
91
|
+
|
92
|
+
#### Property Access
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
# Basic properties
|
96
|
+
task.uuid # => String
|
97
|
+
task.description # => String or nil
|
98
|
+
task.status # => Status
|
99
|
+
task.priority # => String or nil
|
100
|
+
|
101
|
+
# Date properties
|
102
|
+
task.entry # => Time or nil
|
103
|
+
task.modified # => Time or nil
|
104
|
+
task.start # => Time or nil
|
105
|
+
task.end # => Time or nil
|
106
|
+
task.due # => Time or nil
|
107
|
+
task.until # => Time or nil
|
108
|
+
task.wait # => Time or nil
|
109
|
+
|
110
|
+
# Collections
|
111
|
+
task.tags # => Array of Tag
|
112
|
+
task.annotations # => Array of Annotation
|
113
|
+
task.dependencies # => Array of String (UUIDs)
|
114
|
+
|
115
|
+
# User Defined Attributes (UDAs)
|
116
|
+
task.uda(namespace, key) # => String or nil
|
117
|
+
task.udas # => Hash of all UDAs
|
118
|
+
|
119
|
+
# Custom properties
|
120
|
+
task.value(property) # => String or nil
|
121
|
+
```
|
122
|
+
|
123
|
+
#### Task Modification
|
124
|
+
|
125
|
+
All modification methods require an Operations object:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
operations = Taskchampion::Operations.new
|
129
|
+
|
130
|
+
# Basic modifications
|
131
|
+
task.set_description("New description", operations)
|
132
|
+
task.set_status(Taskchampion::Status.completed, operations)
|
133
|
+
task.set_priority("H", operations) # H, M, L, or nil
|
134
|
+
|
135
|
+
# Date modifications
|
136
|
+
task.set_due(Time.now + 86400, operations) # Due tomorrow
|
137
|
+
task.set_start(Time.now, operations)
|
138
|
+
task.set_end(Time.now, operations)
|
139
|
+
|
140
|
+
# Tag management
|
141
|
+
task.add_tag(Taskchampion::Tag.new("work"), operations)
|
142
|
+
task.remove_tag(Taskchampion::Tag.new("work"), operations)
|
143
|
+
|
144
|
+
# Annotation management
|
145
|
+
annotation = Taskchampion::Annotation.new(Time.now, "Added note")
|
146
|
+
task.add_annotation(annotation, operations)
|
147
|
+
|
148
|
+
# UDA management
|
149
|
+
task.set_uda("namespace", "key", "value", operations)
|
150
|
+
task.delete_uda("namespace", "key", operations)
|
151
|
+
|
152
|
+
# Custom properties
|
153
|
+
task.set_value("custom_property", "value", operations)
|
154
|
+
|
155
|
+
# Don't forget to commit!
|
156
|
+
replica.commit_operations(operations)
|
157
|
+
```
|
158
|
+
|
159
|
+
#### Status Checking
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
# Status predicates
|
163
|
+
task.active? # => Boolean (pending or recurring)
|
164
|
+
task.pending? # => Boolean
|
165
|
+
task.completed? # => Boolean
|
166
|
+
task.deleted? # => Boolean
|
167
|
+
task.recurring? # => Boolean
|
168
|
+
|
169
|
+
# Tag checking
|
170
|
+
task.has_tag?(Taskchampion::Tag.new("work")) # => Boolean
|
171
|
+
```
|
172
|
+
|
173
|
+
### Taskchampion::Operations
|
174
|
+
|
175
|
+
Collects task modifications before committing them to storage.
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
# Create new operations collection
|
179
|
+
operations = Taskchampion::Operations.new
|
180
|
+
|
181
|
+
# Add operations (done automatically by task modification methods)
|
182
|
+
# operations.push(operation) # Usually not called directly
|
183
|
+
|
184
|
+
# Collection interface
|
185
|
+
operations.length # => Integer
|
186
|
+
operations[index] # => Operation
|
187
|
+
operations.each {|op| ... } # Block iteration
|
188
|
+
operations << operation # Append operation
|
189
|
+
operations.clear # Remove all operations
|
190
|
+
```
|
191
|
+
|
192
|
+
### Taskchampion::Operation
|
193
|
+
|
194
|
+
Represents a single task modification operation.
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
# Operation introspection
|
198
|
+
operation.operation_type # => Symbol (:create, :update, :delete)
|
199
|
+
operation.uuid # => String (task UUID)
|
200
|
+
operation.property # => String or nil
|
201
|
+
operation.value # => String or nil
|
202
|
+
operation.old_value # => String or nil
|
203
|
+
operation.timestamp # => Time
|
204
|
+
|
205
|
+
# String representation
|
206
|
+
operation.to_s # => Human readable string
|
207
|
+
operation.inspect # => Debug representation
|
208
|
+
```
|
209
|
+
|
210
|
+
### Taskchampion::Status
|
211
|
+
|
212
|
+
Enumeration of task status values.
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
# Status constants
|
216
|
+
status = Taskchampion::Status.pending # => Status
|
217
|
+
status = Taskchampion::Status.completed # => Status
|
218
|
+
status = Taskchampion::Status.deleted # => Status
|
219
|
+
status = Taskchampion::Status.recurring # => Status
|
220
|
+
|
221
|
+
# Status predicates
|
222
|
+
status.pending? # => Boolean
|
223
|
+
status.completed? # => Boolean
|
224
|
+
status.deleted? # => Boolean
|
225
|
+
status.recurring? # => Boolean
|
226
|
+
|
227
|
+
# String conversion
|
228
|
+
status.to_s # => "pending", "completed", etc.
|
229
|
+
status.inspect # => "#<Taskchampion::Status:pending>"
|
230
|
+
```
|
231
|
+
|
232
|
+
### Taskchampion::AccessMode
|
233
|
+
|
234
|
+
Enumeration of replica access modes.
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
# Access mode constants
|
238
|
+
mode = Taskchampion::AccessMode.read_only # => AccessMode
|
239
|
+
mode = Taskchampion::AccessMode.read_write # => AccessMode
|
240
|
+
|
241
|
+
# Access mode predicates
|
242
|
+
mode.read_only? # => Boolean
|
243
|
+
mode.read_write? # => Boolean
|
244
|
+
|
245
|
+
# String conversion
|
246
|
+
mode.to_s # => "read_only" or "read_write"
|
247
|
+
```
|
248
|
+
|
249
|
+
### Taskchampion::Tag
|
250
|
+
|
251
|
+
Represents a task tag.
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
# Create tags
|
255
|
+
tag = Taskchampion::Tag.new("work")
|
256
|
+
tag = Taskchampion::Tag.new("project:website")
|
257
|
+
|
258
|
+
# Access tag name
|
259
|
+
tag.name # => String
|
260
|
+
tag.to_s # => String (same as name)
|
261
|
+
|
262
|
+
# Equality
|
263
|
+
tag1 == tag2 # => Boolean
|
264
|
+
```
|
265
|
+
|
266
|
+
### Taskchampion::Annotation
|
267
|
+
|
268
|
+
Represents a task annotation with timestamp and description.
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
# Create annotations
|
272
|
+
annotation = Taskchampion::Annotation.new(Time.now, "Added note")
|
273
|
+
|
274
|
+
# Access properties
|
275
|
+
annotation.entry # => Time
|
276
|
+
annotation.description # => String
|
277
|
+
|
278
|
+
# String conversion
|
279
|
+
annotation.to_s # => "2024-01-31 12:00:00 Added note"
|
280
|
+
```
|
281
|
+
|
282
|
+
### Taskchampion::WorkingSet
|
283
|
+
|
284
|
+
Manages the current set of tasks being worked on with index-based access.
|
285
|
+
|
286
|
+
```ruby
|
287
|
+
# Get working set from replica
|
288
|
+
working_set = replica.working_set
|
289
|
+
|
290
|
+
# Index management
|
291
|
+
largest = working_set.largest_index # => Integer
|
292
|
+
|
293
|
+
# Task access by index
|
294
|
+
task = working_set.by_index(1) # => Task or nil
|
295
|
+
|
296
|
+
# UUID to index mapping
|
297
|
+
index = working_set.by_uuid(uuid) # => Integer or nil
|
298
|
+
|
299
|
+
# Renumber tasks
|
300
|
+
working_set.renumber
|
301
|
+
```
|
302
|
+
|
303
|
+
### Taskchampion::DependencyMap
|
304
|
+
|
305
|
+
Tracks task dependencies and relationships.
|
306
|
+
|
307
|
+
```ruby
|
308
|
+
# Get dependency map from replica
|
309
|
+
dep_map = replica.dependency_map(rebuild: false)
|
310
|
+
|
311
|
+
# Get task dependencies (tasks this task depends on)
|
312
|
+
deps = dep_map.dependencies(uuid) # => Array of String (UUIDs)
|
313
|
+
|
314
|
+
# Get task dependents (tasks that depend on this task)
|
315
|
+
dependents = dep_map.dependents(uuid) # => Array of String (UUIDs)
|
316
|
+
|
317
|
+
# Check if task has dependencies
|
318
|
+
has_deps = dep_map.has_dependency?(uuid) # => Boolean
|
319
|
+
```
|
320
|
+
|
321
|
+
## Error Classes
|
322
|
+
|
323
|
+
### Taskchampion::Error
|
324
|
+
|
325
|
+
Base class for all TaskChampion errors.
|
326
|
+
|
327
|
+
### Taskchampion::ThreadError
|
328
|
+
|
329
|
+
Raised when attempting to access TaskChampion objects from the wrong thread.
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
begin
|
333
|
+
# This will fail if called from wrong thread
|
334
|
+
replica.task_uuids
|
335
|
+
rescue Taskchampion::ThreadError => e
|
336
|
+
puts "Thread safety violation: #{e.message}"
|
337
|
+
end
|
338
|
+
```
|
339
|
+
|
340
|
+
### Taskchampion::StorageError
|
341
|
+
|
342
|
+
Raised for file system and storage-related errors.
|
343
|
+
|
344
|
+
### Taskchampion::ValidationError
|
345
|
+
|
346
|
+
Raised for invalid input or parameter validation failures.
|
347
|
+
|
348
|
+
### Taskchampion::ConfigError
|
349
|
+
|
350
|
+
Raised for configuration-related errors.
|
351
|
+
|
352
|
+
### Taskchampion::SyncError
|
353
|
+
|
354
|
+
Raised for synchronization failures.
|
355
|
+
|
356
|
+
## Thread Safety
|
357
|
+
|
358
|
+
**Important**: All TaskChampion objects are thread-bound and can only be used from the thread that created them. Attempting to access objects from other threads will raise `Taskchampion::ThreadError`.
|
359
|
+
|
360
|
+
See [THREAD_SAFETY.md](THREAD_SAFETY.md) for detailed thread safety guidelines.
|
361
|
+
|
362
|
+
## Common Patterns
|
363
|
+
|
364
|
+
### Creating and Modifying Tasks
|
365
|
+
|
366
|
+
```ruby
|
367
|
+
replica = Taskchampion::Replica.new_on_disk("/path/to/tasks")
|
368
|
+
operations = Taskchampion::Operations.new
|
369
|
+
|
370
|
+
# Create task
|
371
|
+
uuid = SecureRandom.uuid
|
372
|
+
task = replica.create_task(uuid, operations)
|
373
|
+
task.set_description("Buy groceries", operations)
|
374
|
+
task.set_status(Taskchampion::Status.pending, operations)
|
375
|
+
task.add_tag(Taskchampion::Tag.new("errands"), operations)
|
376
|
+
|
377
|
+
# Commit changes
|
378
|
+
replica.commit_operations(operations)
|
379
|
+
```
|
380
|
+
|
381
|
+
### Querying Tasks
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
# Get all tasks
|
385
|
+
all_uuids = replica.task_uuids
|
386
|
+
tasks = all_uuids.map { |uuid| replica.task(uuid) }.compact
|
387
|
+
|
388
|
+
# Filter tasks
|
389
|
+
pending_tasks = tasks.select(&:pending?)
|
390
|
+
work_tasks = tasks.select { |t| t.has_tag?(Taskchampion::Tag.new("work")) }
|
391
|
+
```
|
392
|
+
|
393
|
+
### Working with Operations
|
394
|
+
|
395
|
+
```ruby
|
396
|
+
# Group multiple changes
|
397
|
+
operations = Taskchampion::Operations.new
|
398
|
+
|
399
|
+
tasks.each do |task|
|
400
|
+
if task.pending? && task.priority.nil?
|
401
|
+
task.set_priority("M", operations)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
# Commit all changes at once
|
406
|
+
replica.commit_operations(operations)
|
407
|
+
```
|
408
|
+
|
409
|
+
### Error Handling
|
410
|
+
|
411
|
+
```ruby
|
412
|
+
begin
|
413
|
+
replica = Taskchampion::Replica.new_on_disk("/invalid/path")
|
414
|
+
rescue Taskchampion::StorageError => e
|
415
|
+
puts "Storage error: #{e.message}"
|
416
|
+
rescue Taskchampion::Error => e
|
417
|
+
puts "TaskChampion error: #{e.message}"
|
418
|
+
end
|
419
|
+
```
|