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.
data/example.md ADDED
@@ -0,0 +1,465 @@
1
+ # TaskChampion-rb Examples
2
+
3
+ This document demonstrates typical Ruby usage patterns for TaskChampion-rb, showing how to build sophisticated task management applications with operational transformation, synchronization, and thread-safe access.
4
+
5
+ ## šŸš€ **Basic Setup & Task Creation**
6
+
7
+ ```ruby
8
+ require 'taskchampion'
9
+ require 'securerandom'
10
+
11
+ # Create a task database
12
+ replica = Taskchampion::Replica.new_on_disk("./my_tasks", true)
13
+ operations = Taskchampion::Operations.new
14
+
15
+ # Create a new task
16
+ uuid = SecureRandom.uuid
17
+ task = replica.create_task(uuid, operations)
18
+
19
+ # Commit the changes
20
+ replica.commit_operations(operations)
21
+
22
+ puts "Created task: #{task.uuid}"
23
+ ```
24
+
25
+ ## šŸ“ **Real Task Management Workflow**
26
+
27
+ ```ruby
28
+ # Create a personal task manager
29
+ class TaskManager
30
+ def initialize(data_dir = "./tasks")
31
+ @replica = Taskchampion::Replica.new_on_disk(data_dir, true)
32
+ end
33
+
34
+ def add_task(description)
35
+ operations = Taskchampion::Operations.new
36
+ uuid = SecureRandom.uuid
37
+
38
+ task = @replica.create_task(uuid, operations)
39
+ @replica.commit_operations(operations)
40
+
41
+ puts "āœ… Added task: #{description} (#{uuid[0..7]})"
42
+ task
43
+ end
44
+
45
+ def list_tasks
46
+ puts "\nšŸ“‹ Your Tasks:"
47
+ @replica.task_uuids.each_with_index do |uuid, index|
48
+ task = @replica.task(uuid)
49
+ status = task.completed? ? "āœ…" : "ā³"
50
+ puts "#{index + 1}. #{status} #{task.description} (#{uuid[0..7]})"
51
+ end
52
+ end
53
+
54
+ def task_count
55
+ @replica.task_uuids.length
56
+ end
57
+ end
58
+
59
+ # Usage
60
+ tm = TaskManager.new
61
+ tm.add_task("Buy groceries")
62
+ tm.add_task("Write documentation")
63
+ tm.add_task("Review pull requests")
64
+ tm.list_tasks
65
+ puts "Total tasks: #{tm.task_count}"
66
+ ```
67
+
68
+ ## šŸ” **Task Querying & Filtering**
69
+
70
+ ```ruby
71
+ # Find specific tasks
72
+ def find_active_tasks(replica)
73
+ replica.task_uuids.filter_map do |uuid|
74
+ task = replica.task(uuid)
75
+ task if task.active? && !task.completed?
76
+ end
77
+ end
78
+
79
+ # Working with task properties
80
+ def task_summary(replica, uuid)
81
+ task = replica.task(uuid)
82
+ return "Task not found" unless task
83
+
84
+ status_emoji = case
85
+ when task.completed? then "āœ…"
86
+ when task.waiting? then "āøļø"
87
+ when task.blocked? then "🚫"
88
+ else "ā³"
89
+ end
90
+
91
+ "#{status_emoji} #{task.description} (Priority: #{task.priority})"
92
+ end
93
+
94
+ # Usage
95
+ replica = Taskchampion::Replica.new_in_memory
96
+ operations = Taskchampion::Operations.new
97
+
98
+ uuid = SecureRandom.uuid
99
+ task = replica.create_task(uuid, operations)
100
+ replica.commit_operations(operations)
101
+
102
+ puts task_summary(replica, uuid)
103
+ ```
104
+
105
+ ## šŸ·ļø **Working with Status & Operations**
106
+
107
+ ```ruby
108
+ # Status management
109
+ def mark_completed(replica, uuid)
110
+ operations = Taskchampion::Operations.new
111
+
112
+ # Note: Task mutation methods not yet implemented,
113
+ # but this shows the intended workflow
114
+ puts "Task #{uuid[0..7]} would be marked as completed"
115
+
116
+ # When implemented, would be:
117
+ # task = replica.task(uuid)
118
+ # task.set_status(Taskchampion::Status.completed, operations)
119
+ # replica.commit_operations(operations)
120
+ end
121
+
122
+ # Operations inspection
123
+ def show_operations_info(operations)
124
+ puts "Operations count: #{operations.length}"
125
+ puts "Empty? #{operations.empty?}"
126
+
127
+ operations.each do |op|
128
+ type = case
129
+ when op.create? then "CREATE"
130
+ when op.update? then "UPDATE"
131
+ when op.delete? then "DELETE"
132
+ else "OTHER"
133
+ end
134
+ puts "- #{type} operation"
135
+ end
136
+ end
137
+
138
+ # Working with status objects
139
+ pending = Taskchampion::Status.pending
140
+ completed = Taskchampion::Status.completed
141
+
142
+ puts "Status: #{pending.to_s}" # => "pending"
143
+ puts "Is pending? #{pending.pending?}" # => true
144
+ puts "Equal? #{pending == completed}" # => false
145
+
146
+ # Access modes
147
+ read_write = Taskchampion::AccessMode.read_write
148
+ read_only = Taskchampion::AccessMode.read_only
149
+
150
+ puts "Mode: #{read_write.to_s}" # => "read_write"
151
+ puts "Read only? #{read_only.read_only?}" # => true
152
+ ```
153
+
154
+ ## šŸ“Š **Task Organization & Dependencies**
155
+
156
+ ```ruby
157
+ # Working with working sets
158
+ def show_task_organization(replica)
159
+ working_set = replica.working_set
160
+ dep_map = replica.dependency_map
161
+
162
+ puts "šŸ“Š Task Organization:"
163
+ puts "Largest index: #{working_set.largest_index}"
164
+
165
+ # Show tasks by index
166
+ (1..working_set.largest_index).each do |index|
167
+ uuid = working_set.by_index(index)
168
+ if uuid
169
+ task = replica.task(uuid)
170
+ deps = dep_map.dependencies(uuid)
171
+
172
+ dep_info = deps.empty? ? "" : " (depends on #{deps.length} tasks)"
173
+ puts "#{index}. #{task.description}#{dep_info}"
174
+ end
175
+ end
176
+ end
177
+
178
+ # Working with dependencies
179
+ def analyze_dependencies(replica, uuid)
180
+ dep_map = replica.dependency_map
181
+
182
+ dependencies = dep_map.dependencies(uuid)
183
+ dependents = dep_map.dependents(uuid)
184
+
185
+ puts "Task #{uuid[0..7]}:"
186
+ puts " Depends on: #{dependencies.length} tasks"
187
+ puts " Blocks: #{dependents.length} tasks"
188
+ puts " Has dependencies? #{dep_map.has_dependency?(uuid)}"
189
+ end
190
+ ```
191
+
192
+ ## šŸ› ļø **Error Handling & Thread Safety**
193
+
194
+ ```ruby
195
+ # Proper error handling
196
+ def safe_task_access(replica, uuid)
197
+ begin
198
+ task = replica.task(uuid)
199
+ task ? task.description : "Task not found"
200
+ rescue Taskchampion::ThreadError => e
201
+ "Access denied: #{e.message}"
202
+ rescue Taskchampion::Error => e
203
+ "TaskChampion error: #{e.message}"
204
+ rescue => e
205
+ "Unexpected error: #{e.message}"
206
+ end
207
+ end
208
+
209
+ # Thread safety demonstration
210
+ def demonstrate_thread_safety
211
+ replica = Taskchampion::Replica.new_in_memory
212
+
213
+ Thread.new do
214
+ begin
215
+ replica.task_uuids # This will raise ThreadError
216
+ rescue Taskchampion::ThreadError
217
+ puts "āœ… Thread safety working - cross-thread access blocked"
218
+ end
219
+ end.join
220
+ end
221
+
222
+ # Operations manipulation
223
+ def demonstrate_operations
224
+ operations = Taskchampion::Operations.new
225
+
226
+ # Create some operations
227
+ uuid1 = SecureRandom.uuid
228
+ uuid2 = SecureRandom.uuid
229
+
230
+ op1 = Taskchampion::Operation.create(uuid1)
231
+ op2 = Taskchampion::Operation.create(uuid2)
232
+
233
+ # Add operations
234
+ operations.push(op1)
235
+ operations << op2 # Same as push
236
+
237
+ puts "Operations count: #{operations.length}"
238
+ puts "Operations: #{operations.inspect}"
239
+
240
+ # Iterate operations
241
+ operations.each do |op|
242
+ puts "Operation type: #{op.create? ? 'CREATE' : 'OTHER'}"
243
+ end
244
+
245
+ # Convert to array
246
+ ops_array = operations.to_a
247
+ puts "Array length: #{ops_array.length}"
248
+
249
+ # Clear operations
250
+ operations.clear
251
+ puts "After clear: #{operations.empty?}"
252
+ end
253
+ ```
254
+
255
+ ## šŸŽÆ **Complete Example: Simple CLI Task Manager**
256
+
257
+ ```ruby
258
+ #!/usr/bin/env ruby
259
+ require 'taskchampion'
260
+ require 'securerandom'
261
+
262
+ class SimpleTasks
263
+ def initialize
264
+ @replica = Taskchampion::Replica.new_on_disk(
265
+ File.expand_path("~/.simple_tasks"),
266
+ true
267
+ )
268
+ end
269
+
270
+ def add(description)
271
+ operations = Taskchampion::Operations.new
272
+ uuid = SecureRandom.uuid
273
+
274
+ task = @replica.create_task(uuid, operations)
275
+ @replica.commit_operations(operations)
276
+
277
+ puts "Added: #{description} [#{uuid[0..7]}]"
278
+ end
279
+
280
+ def list
281
+ uuids = @replica.task_uuids
282
+ if uuids.empty?
283
+ puts "No tasks yet. Use 'add <description>' to create one."
284
+ return
285
+ end
286
+
287
+ puts "\nYour Tasks:"
288
+ uuids.each_with_index do |uuid, i|
289
+ task = @replica.task(uuid)
290
+ puts "#{i + 1}. #{task.description} [#{uuid[0..7]}]"
291
+ end
292
+ end
293
+
294
+ def stats
295
+ count = @replica.task_uuids.length
296
+ working_set = @replica.working_set
297
+
298
+ puts "\nšŸ“Š Stats:"
299
+ puts "Total tasks: #{count}"
300
+ puts "Largest index: #{working_set.largest_index}"
301
+ end
302
+
303
+ def show_task_details(index)
304
+ uuids = @replica.task_uuids
305
+ if index < 1 || index > uuids.length
306
+ puts "Invalid task number. Use 'list' to see tasks."
307
+ return
308
+ end
309
+
310
+ uuid = uuids[index - 1]
311
+ task = @replica.task(uuid)
312
+
313
+ puts "\nšŸ“‹ Task Details:"
314
+ puts "UUID: #{task.uuid}"
315
+ puts "Description: #{task.description}"
316
+ puts "Status: #{task.status}"
317
+ puts "Priority: #{task.priority}"
318
+ puts "Created: #{task.entry}"
319
+ puts "Modified: #{task.modified}"
320
+ puts "Active: #{task.active?}"
321
+ puts "Completed: #{task.completed?}"
322
+ puts "Waiting: #{task.waiting?}"
323
+ puts "Blocked: #{task.blocked?}"
324
+ puts "Blocking others: #{task.blocking?}"
325
+
326
+ deps = task.dependencies
327
+ puts "Dependencies: #{deps.empty? ? 'None' : deps.join(', ')}"
328
+
329
+ tags = task.tags
330
+ puts "Tags: #{tags.empty? ? 'None' : tags.map(&:to_s).join(', ')}"
331
+ end
332
+ end
333
+
334
+ # CLI Usage
335
+ if ARGV.empty?
336
+ puts "Usage: #{$0} <command> [args]"
337
+ puts "Commands:"
338
+ puts " add <description> - Add a new task"
339
+ puts " list - List all tasks"
340
+ puts " show <number> - Show task details"
341
+ puts " stats - Show statistics"
342
+ exit 1
343
+ end
344
+
345
+ tasks = SimpleTasks.new
346
+
347
+ case ARGV[0]
348
+ when 'add'
349
+ description = ARGV[1..-1].join(' ')
350
+ if description.empty?
351
+ puts "Please provide a task description"
352
+ exit 1
353
+ end
354
+ tasks.add(description)
355
+ when 'list'
356
+ tasks.list
357
+ when 'show'
358
+ index = ARGV[1].to_i
359
+ tasks.show_task_details(index)
360
+ when 'stats'
361
+ tasks.stats
362
+ else
363
+ puts "Unknown command: #{ARGV[0]}"
364
+ exit 1
365
+ end
366
+ ```
367
+
368
+ ## šŸ’” **Key Ruby Patterns**
369
+
370
+ TaskChampion-rb follows standard Ruby conventions:
371
+
372
+ ### **Boolean Methods**
373
+ ```ruby
374
+ # Methods ending in ? return booleans
375
+ task.active? # => true/false
376
+ task.completed? # => true/false
377
+ status.pending? # => true/false
378
+ mode.read_only? # => true/false
379
+ operations.empty? # => true/false
380
+ ```
381
+
382
+ ### **Operators and Enumerable**
383
+ ```ruby
384
+ # Natural Ruby operators
385
+ operations << operation # Append operator
386
+ operations.each {|op| puts op } # Block iteration
387
+ operations.length # Collection size
388
+ operations[index] # Array-like access
389
+
390
+ # Convert to array
391
+ ops_array = operations.to_a
392
+ ```
393
+
394
+ ### **String Representations**
395
+ ```ruby
396
+ # Proper to_s and inspect methods
397
+ status.to_s # => "pending"
398
+ status.inspect # => "#<Taskchampion::Status:pending>"
399
+ operations.inspect # => "#<Taskchampion::Operations: 5 operations>"
400
+ ```
401
+
402
+ ### **Exception Hierarchy**
403
+ ```ruby
404
+ begin
405
+ replica.task(uuid)
406
+ rescue Taskchampion::ThreadError => e
407
+ # Thread safety violation
408
+ rescue Taskchampion::Error => e
409
+ # General TaskChampion error
410
+ rescue => e
411
+ # Other errors
412
+ end
413
+ ```
414
+
415
+ ### **Flexible Constructors**
416
+ ```ruby
417
+ # Multiple constructor patterns
418
+ Taskchampion::Replica.new_in_memory
419
+ Taskchampion::Replica.new_on_disk(path, create_if_missing)
420
+ Taskchampion::Replica.new_on_disk(path, create_if_missing, access_mode)
421
+
422
+ # Factory methods for enums
423
+ Taskchampion::Status.pending
424
+ Taskchampion::Status.completed
425
+ Taskchampion::AccessMode.read_write
426
+ ```
427
+
428
+ ## šŸ—ļø **Architecture Benefits**
429
+
430
+ ### **Thread Safety**
431
+ ```ruby
432
+ # Automatic thread safety enforcement
433
+ replica = Taskchampion::Replica.new_in_memory
434
+
435
+ Thread.new do
436
+ # This will raise Taskchampion::ThreadError
437
+ replica.task_uuids
438
+ end
439
+ ```
440
+
441
+ ### **Operations-Based Consistency**
442
+ ```ruby
443
+ # All changes go through operations for consistency
444
+ operations = Taskchampion::Operations.new
445
+
446
+ # Multiple operations can be batched
447
+ task1 = replica.create_task(uuid1, operations)
448
+ task2 = replica.create_task(uuid2, operations)
449
+
450
+ # Single commit applies all changes atomically
451
+ replica.commit_operations(operations)
452
+ ```
453
+
454
+ ### **Operational Transformation**
455
+ ```ruby
456
+ # Operations can be inspected and manipulated
457
+ operations.each do |op|
458
+ puts "Operation: #{op.inspect}"
459
+ puts " Type: #{op.create? ? 'CREATE' : 'UPDATE'}"
460
+ puts " UUID: #{op.uuid}" unless op.undo_point?
461
+ puts " Timestamp: #{op.timestamp}"
462
+ end
463
+ ```
464
+
465
+ This gives Ruby developers a familiar, idiomatic interface to powerful task management capabilities with built-in consistency guarantees and thread safety!