medo 0.1.1 → 0.1.3
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/README.md +3 -0
- data/VERSION +1 -1
- data/bin/medo +3 -52
- data/features/add_task.feature +1 -1
- data/features/edit_task.feature +14 -0
- data/features/{add_notes.feature → notes.feature} +16 -7
- data/features/step_definitions/generic_steps.rb +4 -0
- data/features/support/env.rb +1 -1
- data/lib/medo.rb +1 -2
- data/lib/medo/cli.rb +48 -0
- data/lib/medo/cli_support.rb +68 -0
- data/lib/medo/commands/clear.rb +1 -3
- data/lib/medo/commands/delete.rb +6 -7
- data/lib/medo/commands/done.rb +6 -7
- data/lib/medo/commands/edit.rb +16 -0
- data/lib/medo/commands/list.rb +7 -6
- data/lib/medo/commands/new.rb +5 -8
- data/lib/medo/commands/note.rb +21 -10
- data/lib/medo/commands/reset.rb +12 -0
- data/lib/medo/commands/show.rb +8 -9
- data/lib/medo/notes.rb +39 -0
- data/lib/medo/task.rb +37 -7
- data/lib/medo/task_reader.rb +2 -0
- data/lib/medo/task_writer.rb +2 -0
- data/lib/medo/text_task_writer/decorators/numbers_decorator.rb +13 -3
- data/spec/lib/cli_spec.rb +6 -0
- data/spec/lib/cli_support_spec.rb +79 -0
- data/spec/lib/json_task_reader_spec.rb +12 -12
- data/spec/lib/notes_spec.rb +48 -0
- data/spec/lib/task_spec.rb +51 -16
- data/spec/support/task_stubs_spec_helper.rb +14 -9
- metadata +119 -122
data/README.md
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.3
|
data/bin/medo
CHANGED
@@ -1,62 +1,13 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require '
|
5
|
-
|
6
|
-
begin
|
7
|
-
require 'gli'
|
8
|
-
require 'medo'
|
9
|
-
require 'medo/support'
|
10
|
-
require 'medo/file_task_storage'
|
11
|
-
rescue LoadError => e #development
|
12
|
-
raise if $loaded
|
13
|
-
$:.unshift File.expand_path('../../lib', __FILE__) #load what we work on, even if gem installed
|
14
|
-
require 'bundler'
|
15
|
-
Bundler.setup(:default)
|
16
|
-
$loaded = true
|
17
|
-
retry
|
18
|
-
end
|
19
|
-
|
20
|
-
include GLI::App
|
21
|
-
include Medo
|
22
|
-
|
23
|
-
program_desc 'Simple CLI To-Do manager'
|
24
|
-
version VERSION
|
25
|
-
|
26
|
-
commands_from 'medo/commands'
|
27
|
-
|
28
|
-
default_command :list
|
29
|
-
|
30
|
-
desc "A file with tasks"
|
31
|
-
flag [:f, "tasks-file"], :default_value => File.join(ENV['HOME'], '.medo-tasks')
|
32
|
-
|
33
|
-
desc "Do not use colorful output"
|
34
|
-
switch "no-color", :negatable => false
|
4
|
+
require 'medo/cli'
|
35
5
|
|
36
6
|
Signal.trap("SIGINT") do
|
37
7
|
puts "Terminating"
|
38
8
|
exit 1
|
39
9
|
end
|
40
10
|
|
41
|
-
|
42
|
-
|
43
|
-
instance_eval do
|
44
|
-
eigen = class << self; self; end
|
45
|
-
eigen.send(:define_method, :storage) { the_storage }
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.choose_task(argv, tasks)
|
49
|
-
input = argv.shift
|
50
|
-
number = Integer(input) rescue
|
51
|
-
raise(ArgumentError, "Invalid task #: #{input}")
|
52
|
-
task = tasks.reject(&:done?).sort[number - 1] or raise RuntimeError,
|
53
|
-
"No such task!"
|
54
|
-
[task, number]
|
55
|
-
end
|
56
|
-
|
57
|
-
cmd.call
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
run(ARGV)
|
11
|
+
app = Medo::CLI
|
12
|
+
app.run(ARGV)
|
62
13
|
|
data/features/add_task.feature
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
Feature: Edit todo
|
2
|
+
In order to be able to correct a mistake
|
3
|
+
As a user
|
4
|
+
I want to be able to edit task description
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given there's no file "/tmp/test-medo-tasks"
|
8
|
+
|
9
|
+
Scenario: Add todo
|
10
|
+
When I successfully run `medo --tasks-file=/tmp/test-medo-tasks new Hello World`
|
11
|
+
When I successfully run `medo --tasks-file=/tmp/test-medo-tasks edit Goodbye World`
|
12
|
+
And I successfully run `medo --tasks-file=/tmp/test-medo-tasks ls`
|
13
|
+
Then the output should contain "Goodbye World"
|
14
|
+
And the output should not contain "Hello World"
|
@@ -1,18 +1,27 @@
|
|
1
|
-
Feature:
|
1
|
+
Feature: Manage task notes
|
2
2
|
In order to be able to sketch some ideas
|
3
3
|
As a user
|
4
|
-
I want to add notes to my tasks
|
4
|
+
I want to add and edit notes to my tasks
|
5
5
|
|
6
6
|
Background:
|
7
7
|
Given there's no file "/tmp/test-medo-tasks"
|
8
8
|
|
9
|
-
Scenario: Add todo
|
9
|
+
Scenario: Add todo note
|
10
10
|
When I successfully run `medo --tasks-file=/tmp/test-medo-tasks new Hello World`
|
11
11
|
And I successfully run `medo --tasks-file=/tmp/test-medo-tasks new Goodbye Windows`
|
12
|
-
And I successfully run `medo --tasks-file=/tmp/test-medo-tasks note 1 "Trash the PC"`
|
13
|
-
And I successfully run `medo --tasks-file=/tmp/test-medo-tasks note 2 The Note`
|
14
|
-
And I successfully run `medo --tasks-file=/tmp/test-medo-tasks show
|
12
|
+
And I successfully run `medo --tasks-file=/tmp/test-medo-tasks note add -n 1 "Trash the PC"`
|
13
|
+
And I successfully run `medo --tasks-file=/tmp/test-medo-tasks note add -n 2 The Note`
|
14
|
+
And I successfully run `medo --tasks-file=/tmp/test-medo-tasks show`
|
15
15
|
Then the output should contain "Trash the PC"
|
16
16
|
And the output should not contain "The Note"
|
17
|
-
When I successfully run `medo --tasks-file=/tmp/test-medo-tasks show 2`
|
17
|
+
When I successfully run `medo --tasks-file=/tmp/test-medo-tasks show -n 2`
|
18
18
|
Then the output should contain "The Note"
|
19
|
+
|
20
|
+
Scenario: Edit todo note
|
21
|
+
When I successfully run `medo --tasks-file=/tmp/test-medo-tasks new Hello World`
|
22
|
+
And I successfully run `medo --tasks-file=/tmp/test-medo-tasks note add "Trash the PC"`
|
23
|
+
When I successfully run `medo --tasks-file=/tmp/test-medo-tasks note edit boom`
|
24
|
+
And I successfully run `medo --tasks-file=/tmp/test-medo-tasks show`
|
25
|
+
Then the output should not contain "Trash the PC"
|
26
|
+
And the output should contain "boom"
|
27
|
+
|
data/features/support/env.rb
CHANGED
@@ -5,7 +5,7 @@ require 'fileutils'
|
|
5
5
|
|
6
6
|
ENV['PATH'] = "#{File.expand_path('../../../bin', __FILE__)}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
|
7
7
|
ENV['GLI_DEBUG'] = "true"
|
8
|
-
|
8
|
+
ENV['RUBYLIB'] = File.expand_path('../../../lib', __FILE__)
|
9
9
|
After do
|
10
10
|
if defined? @tasks_file_path and File.exist?(@tasks_file_path)
|
11
11
|
FileUtils.rm(@tasks_file_path)
|
data/lib/medo.rb
CHANGED
data/lib/medo/cli.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
require 'gli'
|
6
|
+
require 'medo'
|
7
|
+
require 'medo/support'
|
8
|
+
require 'medo/file_task_storage'
|
9
|
+
require 'medo/cli_support'
|
10
|
+
|
11
|
+
module Medo
|
12
|
+
class CLI
|
13
|
+
extend GLI::App
|
14
|
+
extend Medo
|
15
|
+
extend CLISupport
|
16
|
+
|
17
|
+
program_desc 'Simple CLI To-Do manager'
|
18
|
+
version VERSION
|
19
|
+
|
20
|
+
load_commands
|
21
|
+
default_command :list
|
22
|
+
|
23
|
+
desc "A file with tasks"
|
24
|
+
flag [:f, "tasks-file"], :default_value => File.join(ENV['HOME'], '.medo-tasks')
|
25
|
+
|
26
|
+
desc "Do not use colorful output"
|
27
|
+
switch "no-color", :negatable => false
|
28
|
+
|
29
|
+
around do |global_options, command, options, arguments, cmd|
|
30
|
+
FileTaskStorage.using_storage(global_options.fetch(:"tasks-file")) do |storage|
|
31
|
+
instance_eval do
|
32
|
+
eigen = class << self; self; end
|
33
|
+
{
|
34
|
+
:storage => storage,
|
35
|
+
:global_options => global_options,
|
36
|
+
:options => options,
|
37
|
+
:arguments => arguments
|
38
|
+
}.each do |method, value|
|
39
|
+
eigen.send(:define_method, method) { value }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
cmd.call
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Medo
|
2
|
+
module CLISupport
|
3
|
+
def load_commands
|
4
|
+
Dir.glob(File.expand_path('../commands/*', __FILE__)).each do |f|
|
5
|
+
contents = File.read(f)
|
6
|
+
class_eval contents, f, 1
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def tasks
|
11
|
+
@tasks ||= storage.read
|
12
|
+
@original_tasks ||= @tasks.map(&:dup)
|
13
|
+
@tasks
|
14
|
+
end
|
15
|
+
|
16
|
+
def tasks_changed?
|
17
|
+
defined? @tasks and @tasks != @original_tasks
|
18
|
+
end
|
19
|
+
|
20
|
+
def committing_tasks
|
21
|
+
yield
|
22
|
+
|
23
|
+
if tasks_changed?
|
24
|
+
storage.write(tasks)
|
25
|
+
storage.commit
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def colorize
|
30
|
+
yield unless global_options[:"no-color"] == false
|
31
|
+
end
|
32
|
+
|
33
|
+
def choose_task(select_options = {})
|
34
|
+
task_number = Integer(options[:number] || 1) rescue
|
35
|
+
raise(ArgumentError, "Invalid task #: #{task_number}")
|
36
|
+
task = tasks.reject { |t| select_options[:done] ^ t.done? }.sort[task_number - 1] or
|
37
|
+
raise(RuntimeError, "No such task!")
|
38
|
+
[task, task_number]
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_input
|
42
|
+
process_input
|
43
|
+
end
|
44
|
+
|
45
|
+
def edit_input(value)
|
46
|
+
process_input do |path|
|
47
|
+
File.open(path, "w") { |f| f.write(value) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def process_input
|
52
|
+
result = nil
|
53
|
+
if options[:editor]
|
54
|
+
path = File.join(Dir::Tmpname.tmpdir, "taketo-input-#{Time.now.to_i}")
|
55
|
+
yield path if block_given?
|
56
|
+
status = system("$EDITOR #{path}")
|
57
|
+
if status && File.exists?(path)
|
58
|
+
result = File.read(path)
|
59
|
+
FileUtils.rm(path)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
result = arguments.join(" ").strip
|
63
|
+
end
|
64
|
+
result.to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
data/lib/medo/commands/clear.rb
CHANGED
data/lib/medo/commands/delete.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
desc "Delete a todo"
|
2
2
|
command [:delete, :rm] do |c|
|
3
|
-
c.
|
4
|
-
|
5
|
-
|
6
|
-
task, number = choose_task(args, tasks)
|
7
|
-
tasks -= [task]
|
3
|
+
c.desc "Number of the task to delete"
|
4
|
+
c.flag [:n, :number]
|
5
|
+
c.default_value 1
|
8
6
|
|
9
|
-
|
10
|
-
|
7
|
+
c.action do |global_options, options, args|
|
8
|
+
task, number = choose_task
|
9
|
+
committing_tasks { tasks.delete(task) }
|
11
10
|
puts "Task #{number} removed"
|
12
11
|
end
|
13
12
|
end
|
data/lib/medo/commands/done.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
desc "Mark todo as done"
|
2
2
|
command :done do |c|
|
3
|
-
c.
|
4
|
-
|
5
|
-
|
6
|
-
task, number = choose_task(args, tasks)
|
7
|
-
task.done
|
3
|
+
c.desc "Number of the task to mark as done"
|
4
|
+
c.flag [:n, :number]
|
5
|
+
c.default_value 1
|
8
6
|
|
9
|
-
|
10
|
-
|
7
|
+
c.action do |global_options, options, args|
|
8
|
+
task, number = choose_task
|
9
|
+
committing_tasks { task.done }
|
11
10
|
puts "Task #{number} done"
|
12
11
|
end
|
13
12
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
desc "Edit a todo"
|
2
|
+
command :edit do |c|
|
3
|
+
c.desc "Use EDITOR"
|
4
|
+
c.switch [:e, :editor]
|
5
|
+
|
6
|
+
c.desc "Number of the task to edit"
|
7
|
+
c.flag [:n, :number]
|
8
|
+
c.default_value 1
|
9
|
+
|
10
|
+
c.action do |global_options, options, args|
|
11
|
+
task, number = choose_task
|
12
|
+
task_description = get_input
|
13
|
+
committing_tasks { task.description = task_description }
|
14
|
+
puts "Task #{number} edited"
|
15
|
+
end
|
16
|
+
end
|
data/lib/medo/commands/list.rb
CHANGED
@@ -2,15 +2,16 @@ require 'medo/text_task_writer'
|
|
2
2
|
|
3
3
|
desc "List all todos"
|
4
4
|
command [:list, :ls] do |c|
|
5
|
-
c.
|
6
|
-
|
5
|
+
c.desc "Number only done tasks"
|
6
|
+
c.switch ["number-done"], :negatable => false
|
7
7
|
|
8
|
+
c.action do |global_options, options, args|
|
8
9
|
include TextTaskWriter::Decorators
|
9
|
-
writer = NumbersDecorator.decorate(TextTaskWriter.new
|
10
|
-
|
11
|
-
|
12
|
-
ColorsDecorator.decorate(writer) unless global_options[:"no-color"] == false
|
10
|
+
writer = NumbersDecorator.decorate(TextTaskWriter.new,
|
11
|
+
:done => options[:"number-done"] == false)
|
12
|
+
colorize { ColorsDecorator.decorate(writer) }
|
13
13
|
writer.add_tasks(tasks)
|
14
14
|
writer.write
|
15
15
|
end
|
16
16
|
end
|
17
|
+
|
data/lib/medo/commands/new.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
desc "Create a todo"
|
2
2
|
command :new do |c|
|
3
|
-
c.
|
4
|
-
|
3
|
+
c.desc "Use EDITOR"
|
4
|
+
c.switch [:e, :editor]
|
5
5
|
|
6
|
-
|
6
|
+
c.action do |global_options, options, args|
|
7
|
+
task_description = get_input
|
7
8
|
task, number = Task.new(task_description)
|
8
|
-
|
9
|
-
tasks << task
|
10
|
-
|
11
|
-
storage.write(tasks)
|
12
|
-
storage.commit
|
9
|
+
committing_tasks { tasks << task }
|
13
10
|
puts "Task added"
|
14
11
|
end
|
15
12
|
end
|
data/lib/medo/commands/note.rb
CHANGED
@@ -1,18 +1,29 @@
|
|
1
1
|
desc "Add note to a todo"
|
2
2
|
command :note do |c|
|
3
|
-
c.
|
4
|
-
|
3
|
+
c.desc "Number of the task to add note to"
|
4
|
+
c.flag [:n, :number]
|
5
|
+
c.default_value 1
|
5
6
|
|
6
|
-
|
7
|
+
c.desc "Use EDITOR"
|
8
|
+
c.switch [:e, :editor]
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
c.command :add do |ca|
|
11
|
+
ca.action do |global_options, options, args|
|
12
|
+
task, number = choose_task
|
13
|
+
note = get_input
|
14
|
+
raise ArgumentError, "No note given" if note.empty?
|
15
|
+
committing_tasks { task.notes << note }
|
16
|
+
puts "Note for task #{number} added"
|
17
|
+
end
|
18
|
+
end
|
12
19
|
|
13
|
-
|
14
|
-
|
15
|
-
|
20
|
+
c.command :edit do |ce|
|
21
|
+
ce.action do |global_options, options, args|
|
22
|
+
task, number = choose_task
|
23
|
+
note = edit_input(task.notes)
|
24
|
+
committing_tasks { task.notes = note }
|
25
|
+
puts "Note for task #{number} edited"
|
26
|
+
end
|
16
27
|
end
|
17
28
|
end
|
18
29
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
desc "Reset task"
|
2
|
+
command :reset do |c|
|
3
|
+
c.desc "Number of the task to mark reset"
|
4
|
+
c.flag [:n, :number]
|
5
|
+
c.default_value 1
|
6
|
+
|
7
|
+
c.action do |global_options, options, args|
|
8
|
+
task, number = choose_task(:done => true)
|
9
|
+
committing_tasks { task.reset }
|
10
|
+
puts "Task #{number} was reset"
|
11
|
+
end
|
12
|
+
end
|