medo 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,17 +2,16 @@ require 'medo/text_task_writer'
2
2
 
3
3
  desc "Show particular note"
4
4
  command [:show, :cat] do |c|
5
- c.action do |global_options, options, args|
6
- tasks = storage.read
5
+ c.desc "Number of task to show"
6
+ c.flag [:n, :number]
7
+ c.default_value 1
7
8
 
9
+ c.action do |global_options, options, args|
8
10
  include TextTaskWriter::Decorators
9
- writer = NumbersDecorator.decorate(TextTaskWriter.new)
10
-
11
- #waiting for fix of https://github.com/davetron5000/gli/pull/90
12
- ColorsDecorator.decorate(writer) unless global_options[:"no-color"] == false
13
-
14
- task, number = choose_task(args, tasks)
15
- writer.add_tasks([task])
11
+ writer = TextTaskWriter.new
12
+ colorize { ColorsDecorator.decorate(writer) }
13
+ task, number = choose_task
14
+ writer.add_task(task)
16
15
  writer.write
17
16
  end
18
17
  end
@@ -0,0 +1,39 @@
1
+ require 'forwardable'
2
+
3
+ module Medo
4
+ class Notes
5
+ include Comparable
6
+ include Enumerable
7
+ extend Forwardable
8
+
9
+ def_delegators :@notes, :<=>, :empty?
10
+
11
+ def initialize(*args)
12
+ @notes = args.flatten.map { |n| n.to_s.strip }.reject(&:empty?).join("\n")
13
+ end
14
+
15
+ def <<(other)
16
+ @notes << "\n#{other}"
17
+ @notes.lstrip!
18
+ end
19
+
20
+ def each
21
+ @notes.each_line { |line| yield line.chomp }
22
+ end
23
+
24
+ def ==(other)
25
+ return true if other.equal? self
26
+ other.to_s == @notes
27
+ end
28
+
29
+ def initialize_copy(source)
30
+ super
31
+ @notes = @notes.dup
32
+ end
33
+
34
+ def to_s
35
+ @notes
36
+ end
37
+ end
38
+ end
39
+
@@ -1,3 +1,5 @@
1
+ require 'medo/notes'
2
+
1
3
  module Medo
2
4
  class Task
3
5
  include Comparable
@@ -20,11 +22,10 @@ module Medo
20
22
  self.clock = Time
21
23
 
22
24
  def initialize(description, options = {})
23
- @description = description.to_s.strip
24
- raise ArgumentError, "No description given!" if @description.empty?
25
+ self.description = description
25
26
  @created_at = clock.now
26
27
  @completed_at = nil
27
- @notes = parse_notes(options["notes"] || options[:notes])
28
+ self.notes = options["notes"] || options[:notes]
28
29
  end
29
30
 
30
31
  def <=>(other)
@@ -40,11 +41,44 @@ module Medo
40
41
  end
41
42
  end
42
43
 
44
+ def ==(other)
45
+ return true if other.equal? self
46
+ return false unless other.kind_of?(self.class)
47
+ self.description == other.description &&
48
+ self.created_at == other.created_at &&
49
+ self.done? == other.done? &&
50
+ self.completed_at == other.completed_at &&
51
+ self.notes == other.notes
52
+ end
53
+
54
+ def initialize_copy(source)
55
+ super
56
+ @description = @description.dup
57
+ @created_at = @created_at.dup
58
+ @completed_at = @completed_at.dup if @completed_at
59
+ @notes = @notes.dup
60
+ end
61
+
62
+ def description=(desc)
63
+ description = desc.to_s.strip
64
+ raise ArgumentError, "No description given!" if description.empty?
65
+ @description = description
66
+ end
67
+
68
+ def notes=(*args)
69
+ @notes = Notes.new(*args)
70
+ end
71
+
43
72
  def done
44
73
  @completed_at = clock.now
45
74
  @done = true
46
75
  end
47
76
 
77
+ def reset
78
+ @completed_at = nil
79
+ @done = false
80
+ end
81
+
48
82
  def done?
49
83
  !!@done
50
84
  end
@@ -54,10 +88,6 @@ module Medo
54
88
  def clock
55
89
  self.class.clock
56
90
  end
57
-
58
- def parse_notes(notes)
59
- Array(notes).map { |n| n.to_s.strip }.reject(&:empty?)
60
- end
61
91
  end
62
92
  end
63
93
 
@@ -1,3 +1,5 @@
1
+ require 'medo/task'
2
+
1
3
  module Medo
2
4
  class TaskReader
3
5
  def read
@@ -1,3 +1,5 @@
1
+ require 'medo/task'
2
+
1
3
  module Medo
2
4
  class TaskWriter
3
5
  def initialize
@@ -6,24 +6,34 @@ module Medo
6
6
  module NumbersDecorator
7
7
  extend Support::Decorator
8
8
 
9
+ after_decorate do |options|
10
+ @num_options = options
11
+ end
12
+
9
13
  def present_tasks(tasks)
10
14
  max_tasks_count = [active_tasks.count, completed_tasks.count].max
11
15
  super.each_with_index.map do |t, i|
12
- TaskNumbers.decorate(t, i + 1, max_tasks_count)
16
+ TaskNumbers.decorate(t, i + 1, max_tasks_count, @num_options)
13
17
  end
14
18
  end
15
19
 
16
20
  module TaskNumbers
17
21
  extend Support::Decorator
18
22
 
19
- after_decorate do |number, total|
23
+ after_decorate do |number, total, options|
20
24
  @number = number
21
25
  @number_length = total.to_s.size
26
+ @num_options = options || {}
22
27
  end
23
28
 
24
29
  def number
25
30
  format = "%-#{@number_length + 1}s"
26
- format % (@task.done? ? "" : "#@number.")
31
+ format % (number? ? "" : "#@number.")
32
+ end
33
+
34
+ def number?
35
+ @num_options[:done] && @task.done? ||
36
+ @num_options[:pending] && !@task.done?
27
37
  end
28
38
 
29
39
  def done
@@ -0,0 +1,6 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'medo/cli'
3
+
4
+ describe "CLI" do
5
+ end
6
+
@@ -0,0 +1,79 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'medo/cli_support'
3
+
4
+ describe "CLISupport" do
5
+ class CLISupportContext
6
+ include Medo::CLISupport
7
+
8
+ def storage
9
+ @storage ||= RSpec::Mocks::Mock.new(:Storage)
10
+ end
11
+
12
+ def global_options
13
+ @global_options ||= {}
14
+ end
15
+ end
16
+ # def load_commands
17
+ # def choose_task(select_options = {})
18
+ # def get_input
19
+
20
+ let(:tasks) { ["foo", "bar"] }
21
+
22
+ before(:each) do
23
+ @context = CLISupportContext.new
24
+ @context.storage.stub(:read => tasks)
25
+ end
26
+
27
+ describe "#tasks" do
28
+ it "should read and memoize tasks" do
29
+ @context.tasks.should == tasks
30
+ @context.tasks.object_id.should == tasks.object_id
31
+ end
32
+ end
33
+
34
+ describe "#tasks_changed" do
35
+ it "should react to changess in tasks collection" do
36
+ @context.tasks
37
+ expect do
38
+ tasks.last.upcase!
39
+ end.to change { @context.tasks_changed? }
40
+ end
41
+ end
42
+
43
+ describe "#colorize" do
44
+ let(:touch) { mock }
45
+
46
+ it "should yield if color mode enabled" do
47
+ @context.global_options[:"no-color"] = true #color
48
+ touch.should_receive(:touched)
49
+ @context.colorize { touch.touched }
50
+ end
51
+
52
+ it "should not yield if color mode disabled" do
53
+ @context.global_options[:"no-color"] = false #no color
54
+ touch.should_not_receive(:touched)
55
+ @context.colorize { touch.touched }
56
+ end
57
+ end
58
+
59
+ describe "#committing tasks" do
60
+ it "should write and commit tasks if changed" do
61
+ @context.storage.should_receive(:write).with(tasks)
62
+ @context.storage.should_receive(:commit)
63
+ @context.tasks
64
+ @context.committing_tasks do
65
+ tasks.last.upcase!
66
+ end
67
+ end
68
+
69
+ it "should not write tasks if not changed" do
70
+ @context.storage.should_not_receive(:write)
71
+ @context.storage.should_not_receive(:commit)
72
+ @context.tasks
73
+ @context.committing_tasks { }
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+
@@ -3,24 +3,24 @@ require 'support/task_stubs_spec_helper'
3
3
  require 'medo/json_task_reader'
4
4
  require 'stringio'
5
5
 
6
- include Medo
7
-
8
- describe JsonTaskReader do
6
+ module Medo
9
7
  unless defined? Task
10
8
  class Task; end
11
9
  end
12
10
 
13
- include TaskStubsSpecHelper
11
+ describe JsonTaskReader do
12
+ include TaskStubsSpecHelper
14
13
 
15
- it "should read tasks" do
16
- fake_input = StringIO.new '[{"done":false,"description":"Buy Milk","created_at":"2012-01-05 12:04:00",'\
17
- '"completed_at":null,"notes":[]},{"done":true,"description":"Buy Butter","created_at":"2012-01-05 15:30:00",'\
18
- '"completed_at":"2012-01-05 16:30:00","notes":["Note 1","Note 2"]}]'
14
+ it "should read tasks" do
15
+ fake_input = StringIO.new '[{"done":false,"description":"Buy Milk","created_at":"2012-01-05 12:04:00",'\
16
+ '"completed_at":null,"notes":""},{"done":true,"description":"Buy Butter","created_at":"2012-01-05 15:30:00",'\
17
+ '"completed_at":"2012-01-05 16:30:00","notes":"Note 1\nNote 2"}]'
19
18
 
20
- Task.should_receive(:from_attributes).with(pending_task_attributes).and_return(1)
21
- Task.should_receive(:from_attributes).with(completed_task_attributes).and_return(2)
19
+ Task.should_receive(:from_attributes).with(pending_task_attributes).and_return(1)
20
+ Task.should_receive(:from_attributes).with(completed_task_attributes).and_return(2)
22
21
 
23
- JsonTaskReader.new(fake_input).read.should == [1, 2]
24
- end
22
+ JsonTaskReader.new(fake_input).read.should == [1, 2]
23
+ end
24
+ end
25
25
  end
26
26
 
@@ -0,0 +1,48 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'medo/notes'
3
+
4
+ module Medo
5
+ describe Notes do
6
+ it "should be create from various arguments" do
7
+ Notes.new(["1", "2"]).should == "1\n2"
8
+ Notes.new(0).should == "0"
9
+ Notes.new([0, nil]).should == "0"
10
+ Notes.new("note").should == "note"
11
+ Notes.new.should == ""
12
+ end
13
+
14
+ it "should allow appending notes" do
15
+ notes = Notes.new
16
+ notes << "My Note"
17
+ notes.should == "My Note"
18
+ notes << "FooBar"
19
+ notes.should == "My Note\nFooBar"
20
+ end
21
+
22
+ it "should be coerced to string" do
23
+ Notes.new(["foo", "bar"]).to_s.should == "foo\nbar"
24
+ end
25
+
26
+ it "should respond to #empty?" do
27
+ Notes.new.should be_empty
28
+ Notes.new("foo").should_not be_empty
29
+ end
30
+
31
+ it "should be enumerable" do
32
+ expect { |b| Notes.new("foo\nbar").each(&b) }.to yield_successive_args("foo", "bar")
33
+ end
34
+
35
+ it "should test for equality" do
36
+ Notes.new("foo").should == Notes.new("foo")
37
+ Notes.new("foo").should_not == Notes.new("foo\nbar")
38
+ end
39
+
40
+ it "should be duplicable" do
41
+ n1 = Notes.new("foo")
42
+ n2 = n1.dup
43
+ n1 << "bar"
44
+ n2.should == "foo"
45
+ end
46
+ end
47
+ end
48
+
@@ -6,15 +6,11 @@ describe Medo::Task do
6
6
  Medo::Task.new("description").description.should == "description"
7
7
  end
8
8
 
9
- it "should require description to be set" do
10
- proc { Medo::Task.new("") }.should raise_error(ArgumentError)
11
- proc { Medo::Task.new(" ") }.should raise_error(ArgumentError)
12
- end
13
-
9
+ Notes = Class.new unless defined? Notes
14
10
  it "should allow notes to be set upon creation" do
15
- Medo::Task.new("description", :notes => ["1", "2"]).notes.should == ["1", "2"]
16
- Medo::Task.new("description", :notes => 0).notes.should == ["0"]
17
- Medo::Task.new("description", :notes => [0, nil]).notes.should == ["0"]
11
+ args = anything
12
+ Notes.should_receive(:new).with(args)
13
+ Medo::Task.new("description", :notes => ["1", "2"])
18
14
  end
19
15
 
20
16
  it "should assign creation time upon instantiation" do
@@ -24,12 +20,6 @@ describe Medo::Task do
24
20
  end
25
21
  end
26
22
 
27
- it "should allow assigning notes to the task" do
28
- task = Medo::Task.new("description")
29
- task.notes << "My Note"
30
- task.notes.should include("My Note")
31
- end
32
-
33
23
  it "should not allow comparison with shit" do
34
24
  task1 = Medo::Task.new("asdfsd")
35
25
  proc { task1 <=> :foo }.should raise_error(ArgumentError)
@@ -68,17 +58,24 @@ describe Medo::Task do
68
58
  task.should be_done
69
59
  end
70
60
 
61
+ it "should reset done state if #reset called" do
62
+ task = Medo::Task.new("Buy milk")
63
+ task.done
64
+ task.reset
65
+ task.should_not be_done
66
+ end
67
+
71
68
  describe ".from_attributes" do
72
69
  it "should allow all attributes to be set from hash" do
73
70
  created_at = Time.now
74
71
  completed_at = Time.now
75
72
  task = Medo::Task.from_attributes("description" => "d",
76
- "notes" => ["n"],
73
+ "notes" => "n",
77
74
  "done" => true,
78
75
  "completed_at" => completed_at,
79
76
  "created_at" => created_at)
80
77
  task.description.should == "d"
81
- task.notes.should == ["n"]
78
+ task.notes.should == "n"
82
79
  task.completed_at.should == completed_at
83
80
  task.created_at.should == created_at
84
81
  task.should be_done
@@ -92,6 +89,44 @@ describe Medo::Task do
92
89
  end
93
90
  end
94
91
 
92
+ it "should not be equal if notes differs" do
93
+ c = Time.now
94
+ t1 = Medo::Task.from_attributes("description" => "d", "created_at" => c, "notes" => "foo")
95
+ t2 = Medo::Task.from_attributes("description" => "d", "created_at" => c, "notes" => "foo")
96
+ t1.should == t2
97
+ t1.notes << "bar"
98
+ t1.should_not == t2
99
+ end
100
+
101
+ it "should duplicate with children" do
102
+ t1 = Medo::Task.new("description", "notes" => "my note")
103
+ t2 = t1.dup
104
+ t1.description.should_not equal(t2.description)
105
+ t1.notes.should_not equal(t2.notes)
106
+ end
107
+
108
+ context "#description=" do
109
+ let(:task) { Medo::Task.new("description") }
110
+
111
+ it "should allow settings description" do
112
+ task.description = "foo"
113
+ task.description.should == "foo"
114
+ end
115
+
116
+ it "should raise if description is bad" do
117
+ expect do
118
+ task.description = nil
119
+ end.to raise_error ArgumentError
120
+ end
121
+ end
122
+
123
+ Notes = Class.new unless defined? Notes
124
+ it "should create a Notes instance on #notes=" do
125
+ t = Medo::Task.new("foo")
126
+ Notes.should_receive(:new).with("bar")
127
+ t.notes = "bar"
128
+ end
129
+
95
130
  def using_fake_clock(fake_clock)
96
131
  Medo::Task.clock = fake_clock
97
132
  yield