dooby 0.2.0 → 0.3.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.
@@ -0,0 +1,29 @@
1
+ module Dooby
2
+ DOOBY_DIR = '.dooby'
3
+ CURRENT_TODO_LIST_FILE = "#{DOOBY_DIR}/list.yml"
4
+
5
+ ## STATUSES
6
+ AVAILABLE_STATUSES = [:hold, :doing, :done]
7
+
8
+ ## SPECIAL CHARS
9
+ SPECIAL_CHAR_COLORS = Hash['@', :blue, '#', :yellow, '%', :white, ':', :magenta]
10
+ SPECIAL_CHARS = SPECIAL_CHAR_COLORS.keys
11
+
12
+ ## TAGS
13
+ TODAY_TAG = '#today'
14
+ TOMORROW_TAG = "#tomorrow"
15
+ URGENT_TAG = '#urgent'
16
+
17
+ SPLITTABLE_TAGS = [TODAY_TAG, URGENT_TAG]
18
+
19
+ CURRENT_ITEM_TAG = ':doing'
20
+
21
+ ## DATES
22
+ DATE_FORMAT = '%b/%d/%Y'
23
+
24
+ ## TEMPLATES
25
+ TASK_ROW_TEMPLATE = lambda do |task|
26
+ " (#{task.id.red}) #{task.colorize}"
27
+ end
28
+
29
+ end
@@ -1,3 +1,13 @@
1
+ class Object
2
+ def blank?
3
+ respond_to?(:empty?) ? empty? : !self
4
+ end
5
+
6
+ def present?
7
+ !blank?
8
+ end
9
+ end
10
+
1
11
  class Array
2
12
  def only_tags!(*wanted_tags)
3
13
  replace(only_tags(wanted_tags))
@@ -5,7 +15,7 @@ class Array
5
15
 
6
16
  def only_tags(*wanted_tags)
7
17
  wanted_tags = '#' if wanted_tags.empty?
8
- tags = self.grep(/[#{wanted_tags}]/)
18
+ tags = self.grep(/[#{wanted_tags}]./)
9
19
  tags.flatten
10
20
  end
11
21
 
@@ -35,6 +45,10 @@ class String
35
45
  def only_tags(*wanted_tags)
36
46
  split(' ').only_tags(*wanted_tags)
37
47
  end
48
+
49
+ def first_char
50
+ self[0, 1]
51
+ end
38
52
  end
39
53
 
40
54
  class NilClass
@@ -0,0 +1,23 @@
1
+ module Dooby
2
+ module DatesHelper
3
+ def tomorrow
4
+ TOMORROW_TAG[1..-1]
5
+ end
6
+
7
+ def tomorrows_date
8
+ Chronic.parse(tomorrow).strftime(DATE_FORMAT)
9
+ end
10
+
11
+ def todays_date
12
+ Time.now.strftime(DATE_FORMAT)
13
+ end
14
+
15
+ def todays_date_tag
16
+ "{#{todays_date}}"
17
+ end
18
+
19
+ def today_tag
20
+ "#today"
21
+ end
22
+ end
23
+ end
@@ -1,8 +1,10 @@
1
1
  module Dooby
2
2
 
3
3
  class List
4
-
4
+
5
5
  attr_reader :location
6
+
7
+ include DatesHelper
6
8
 
7
9
  def initialize(location)
8
10
  @location = location
@@ -17,8 +19,10 @@ module Dooby
17
19
 
18
20
  def add(task = Task.new)
19
21
  yield task if block_given?
22
+ task = handle_tomorrow_tag task
20
23
  @tasks[task.id] = task
21
24
  save!
25
+ task
22
26
  end
23
27
 
24
28
  def delete!(task_id)
@@ -30,24 +34,21 @@ module Dooby
30
34
  end
31
35
 
32
36
  def bulk_delete!(terms)
33
- only_tags = terms.only_tags *SPECIAL_TAGS
37
+ only_tags = terms.only_tags *SPECIAL_CHARS
34
38
  matches = []
35
39
  @tasks.each do |id, task|
36
40
  delete! id if only_tags.all? { |tag| task.todo.include? tag }
37
- end
41
+ end unless only_tags.empty?
38
42
  end
39
43
 
40
44
  def edit!(task_id)
41
45
  old_task = @tasks[task_id]
42
- t = Task.new
43
46
  if old_task
47
+ t = Task.new
44
48
  yield t
49
+ delete! task_id
50
+ add t
45
51
  end
46
-
47
- t.status = old_task.status
48
-
49
- delete! task_id
50
- add t
51
52
  end
52
53
 
53
54
  def tasks?
@@ -58,27 +59,52 @@ module Dooby
58
59
  @tasks.dup
59
60
  end
60
61
 
61
- def list (what_to_show=[])
62
+ def find (what_to_show=[])
62
63
  list = []
63
-
64
+
64
65
  if @tasks.empty?
65
66
  list = nil
66
67
  else
67
68
  case what_to_show
68
69
  when [] then
70
+ to_delete = []
71
+ to_add = []
69
72
  @tasks.each do |id, task|
70
- list << " (#{id.red}) #{task.colorize}"
73
+
74
+ #if item has {today's date} replace it with #today
75
+ if task.todo.include? todays_date_tag
76
+ task_with_today_tag = task.dup
77
+ task_with_today_tag.todo.gsub!(todays_date_tag, TODAY_TAG)
78
+ to_add << task_with_today_tag
79
+ to_delete << id
80
+ else
81
+ list << TASK_ROW_TEMPLATE.call(task)
82
+ end
71
83
  end
72
- when *SPECIAL_TAGS then
84
+
85
+ to_delete.each { |task_id| delete!(task_id) }
86
+ to_add.each do |task|
87
+ add task
88
+ list << TASK_ROW_TEMPLATE.call(task)
89
+ end
90
+
91
+ when *SPECIAL_CHARS then
73
92
  @tasks.each do |id, task|
74
93
  task.todo.gsub(/(#{what_to_show}\w+)/).each do |tag|
75
- list << tag.blue unless list.include? tag.blue
94
+ color = SPECIAL_CHAR_COLORS[tag.first_char]
95
+ colorized = tag.send color
96
+ list << colorized unless list.include? colorized
76
97
  end
77
98
  end
78
- else
99
+ else #user has enter search terms
100
+ if what_to_show.any? { |s| s.include? tomorrow }
101
+ what_to_show << tomorrows_date
102
+ what_to_show.delete(tomorrow)
103
+ what_to_show.delete(TOMORROW_TAG)
104
+ end
79
105
  @tasks.each do |id, task|
80
106
  if what_to_show.all? { |term| task.todo.include? term }
81
- list << " (#{id.red}) #{task.colorize}"
107
+ list << TASK_ROW_TEMPLATE.call(task)
82
108
  end
83
109
  end
84
110
 
@@ -92,12 +118,28 @@ module Dooby
92
118
  def all_tags
93
119
  tags = []
94
120
  @tasks.each do |id, task|
95
- tags << task.todo.only_tags(*SPECIAL_TAGS)
121
+ tags << task.todo.only_tags(*SPECIAL_CHARS)
96
122
  end
97
123
  tags.flatten
98
124
  end
99
125
 
126
+ def current_item
127
+ current = tasks.select do |id, task|
128
+ task.todo.include? CURRENT_ITEM_TAG
129
+ end
130
+ current.flatten.last.todo.gsub(CURRENT_ITEM_TAG,'').strip unless current.blank?
131
+ end
132
+
100
133
  private
134
+ def handle_tomorrow_tag(task)
135
+ if task.todo.include? TOMORROW_TAG
136
+ task.todo.gsub!(/\{[\w\/]+\}/, '')
137
+ task.todo.gsub!(TOMORROW_TAG, "{#{tomorrows_date}}")
138
+ task.todo.gsub!(/#{TOMORROW_TAG}/, '')
139
+ end
140
+ task
141
+ end
142
+
101
143
  def save!
102
144
  File.open( @location, 'w' ) do |f|
103
145
  f << @tasks.to_yaml
@@ -117,7 +159,7 @@ module Dooby
117
159
  puts "Todo list file is invalid or do not exist."
118
160
  end
119
161
  end
120
-
162
+
121
163
  end
122
164
 
123
165
  end
@@ -0,0 +1,13 @@
1
+ module Dooby
2
+ module StatusGenerator
3
+ private
4
+ def statuses(*args)
5
+ args.each do |s|
6
+ method_name = "#{s.to_s}?".to_sym
7
+ define_method method_name do
8
+ @todo.include? ":#{s.to_s}"
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,49 +1,33 @@
1
1
  module Dooby
2
2
  class Task
3
- include Formateable
3
+ extend StatusGenerator
4
4
 
5
- attr_accessor :todo, :priority, :status
5
+ attr_accessor :todo, :priority
6
+ statuses *AVAILABLE_STATUSES
6
7
 
7
- def initialize
8
- @todo ||= nil
9
- @status ||= DEFAULT_STATUS
8
+ def initialize (todo = nil)
9
+ @todo = todo
10
10
  end
11
11
 
12
12
  def id
13
13
  @todo ? Digest::SHA1.hexdigest(@todo)[0,6] : nil
14
14
  end
15
15
 
16
- def valid?
16
+ def valid?
17
17
  @todo.nil? || @todo == '' ? false : true
18
18
  end
19
-
20
- def doing!
21
- @status = :doing
22
- end
23
-
24
- def done!
25
- @status = :done
26
- end
27
-
28
- def hold!
29
- @status = :hold
30
- end
31
-
19
+
32
20
  def colorize
33
21
  colorized_todo = @todo.dup
34
22
 
35
- colorized_todo.gsub(/(#\w+)|(@\w+)|(%\w+)/).each do |todo|
36
- if todo.include? "@"
37
- todo.blue
38
- elsif todo.include? "%"
39
- todo.white
40
- else
41
- todo.yellow
42
- end
23
+ string_pattern = SPECIAL_CHARS.collect { |c| "(#{c}\\w+)" }
24
+ pattern = Regexp.new(string_pattern.join("|"))
25
+
26
+ colorized_todo.gsub(pattern).each do |todo|
27
+ color = SPECIAL_CHAR_COLORS[todo.first_char]
28
+ todo.send color
43
29
  end
44
30
 
45
- end
46
-
31
+ end
47
32
  end
48
-
49
33
  end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ module Dooby
4
+ describe Dooby do
5
+ describe '#init' do
6
+ it "should create a .dooby dir if it doesn't exists" do
7
+ #stubs
8
+ File.stub(:exist?).and_return false
9
+ FileUtils.stub!(:mkdir).and_return true
10
+ File.stub(:new).and_return true
11
+ true.stub(:close).and_return true
12
+
13
+ #expectations
14
+ File.should_receive(:exist?).with(CURRENT_TODO_LIST_FILE)
15
+ FileUtils.should_receive(:mkdir).with(DOOBY_DIR).and_return true
16
+ File.should_receive(:new).with(CURRENT_TODO_LIST_FILE, "w+").and_return true
17
+
18
+ Dooby.init
19
+ end
20
+
21
+ it "should return false if .dooby/list.yml exists" do
22
+ File.stub(:exist?).and_return true
23
+ Dooby.init.should == false
24
+ end
25
+ end
26
+
27
+ describe '#trash!' do
28
+ it "should delete the .dooby directory" do
29
+ FileUtils.stub(:remove_dir).and_return true
30
+
31
+ FileUtils.should_receive(:remove_dir).with(DOOBY_DIR, force = true)
32
+
33
+ Dooby.trash!
34
+ end
35
+ end
36
+
37
+ describe '#current_list' do
38
+ it "should return an instance of List class" do
39
+ list = mock('list')
40
+ List.stub(:new).and_return list
41
+ List.should_receive(:new).with(CURRENT_TODO_LIST_FILE).and_return list
42
+
43
+ Dooby.current_list.should == list
44
+ end
45
+ end
46
+
47
+ describe '#cli_helper' do
48
+ it "should return the CLIHelper class" do
49
+ Dooby.cli_helper.should == CLIHelper
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ module Dooby
4
+ describe List do
5
+
6
+ before(:all) do
7
+ @location = 'location'
8
+ end
9
+
10
+ describe '#new' do
11
+
12
+ it "should load the list of items if list.yml exists" do
13
+ File.stub(:exist?).and_return true
14
+ YAML.stub(:load_file).and_return fake_tasks
15
+
16
+ File.should_receive(:exist?).with(CURRENT_TODO_LIST_FILE).and_return true
17
+ YAML.should_receive(:load_file).with(@location).and_return fake_tasks
18
+
19
+ List.new @location
20
+ end
21
+ end
22
+
23
+ describe '#flush' do
24
+ it 'should empty the list of items and clear the list.yml file' do
25
+ File.stub(:exist?).and_return true
26
+ YAML.stub(:load_file).and_return fake_tasks
27
+ File.stub(:open).and_return true
28
+
29
+ File.should_receive(:open).with(@location, 'w').and_return true
30
+
31
+ list = List.new @location
32
+ list.flush!
33
+ list.tasks.should be_empty
34
+ end
35
+ end
36
+
37
+ describe 'CRUD' do
38
+ before(:all) do
39
+ File.stub(:exist?).and_return true
40
+ end
41
+
42
+ describe '#add' do
43
+ it "should create an item" do
44
+ YAML.stub(:load_file).and_return {}
45
+
46
+ list = List.new @location
47
+ list.stub(:save!).and_return true
48
+ task = Task.new fake_todo_text
49
+
50
+ list.should_receive(:save!).and_return true
51
+ list.add task
52
+
53
+ list.tasks.should_not be_empty
54
+ list.tasks[task.id].todo.should == fake_todo_text
55
+ end
56
+ end
57
+
58
+ describe '#delete!' do
59
+ it "should delete an item" do
60
+ YAML.stub(:load_file).and_return fake_tasks
61
+
62
+ list = List.new @location
63
+ list.should_receive(:save!).and_return true
64
+
65
+ task_to_delete = list.tasks.first
66
+
67
+ list.delete! task_to_delete.first
68
+
69
+ list.tasks.should_not include(task_to_delete.first)
70
+ end
71
+ end
72
+
73
+ describe '#bulk_delete!' do
74
+ before(:each) do
75
+ YAML.stub(:load_file).and_return fake_tasks
76
+ @list = List.new @location
77
+ @list.stub(:save!).and_return true
78
+ end
79
+
80
+ it "should delete all the items containing a keyword" do
81
+ @list.should_receive(:save!).and_return true
82
+ @list.bulk_delete! ['#context']
83
+ @list.tasks.should be_empty
84
+ end
85
+
86
+ it "should delete all the items containing a set of keywords" do
87
+ @list.should_receive(:save!).and_return true
88
+ @list.bulk_delete! %w[#context @person %project]
89
+ @list.tasks.should be_empty
90
+ end
91
+
92
+ it 'should not delete anything if the argument does not contain any tags' do
93
+ old_tasks = @list.tasks
94
+ @list.bulk_delete! %w[# @ %]
95
+ @list.tasks.should == old_tasks
96
+ end
97
+
98
+ it 'should not delete anything if the argument is empty' do
99
+ old_tasks = @list.tasks
100
+ @list.bulk_delete! []
101
+ @list.tasks.should == old_tasks
102
+ end
103
+
104
+ end
105
+
106
+ describe '#edit!' do
107
+ it 'should delete the edited task and add a new one' do
108
+ YAML.stub(:load_file).and_return fake_tasks
109
+ list = List.new @location
110
+ list.stub(:save!).and_return true
111
+
112
+ list.should_receive(:save!).twice
113
+
114
+ task_to_edit = list.tasks.first
115
+ task_id = task_to_edit.first
116
+
117
+ new_todo = 'this is the new text'
118
+
119
+ new_task = list.edit! task_id do |task|
120
+ task.todo = new_todo
121
+ end
122
+
123
+ list.tasks.should_not include task_id
124
+ list.tasks.should include new_task.id
125
+ end
126
+ end
127
+
128
+ describe '#tasks?' do
129
+ it 'should return true if there are tasks' do
130
+ YAML.stub(:load_file).and_return fake_tasks
131
+ list = List.new @location
132
+
133
+ list.tasks?.should be_true
134
+ end
135
+
136
+ it "should return false if there aren't tasks" do
137
+ YAML.stub(:load_file).and_return {}
138
+ list = List.new @location
139
+
140
+ list.tasks?.should be_false
141
+ end
142
+ end
143
+
144
+ describe '#find' do
145
+ describe 'with empty array as argument' do
146
+ it 'should return a list of all the items'
147
+ end
148
+
149
+ describe 'with special tags as argument' do
150
+ it 'should return only the specified tags'
151
+ end
152
+
153
+ describe 'with an array of terms as argument' do
154
+ it 'should return only the items containing all the specified tags'
155
+ end
156
+ end
157
+
158
+ describe '#all_tags' do
159
+ it 'should return only the tags of all the items'
160
+ end
161
+
162
+ describe '#current_item' do
163
+ it 'should return the item tagged with :doing'
164
+ end
165
+ end
166
+ end
167
+ end