minder 0.2.3 → 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.
- checksums.yaml +4 -4
- data/Gemfile.lock +7 -4
- data/README.md +1 -1
- data/bin/minder +2 -2
- data/db/migrate/003_create_periods.rb +16 -0
- data/lib/minder/application.rb +24 -24
- data/lib/minder/{filter_frame.rb → cli/filter_frame.rb} +2 -2
- data/lib/minder/{frame.rb → cli/frame.rb} +2 -0
- data/lib/minder/{help_frame.rb → cli/help_frame.rb} +3 -2
- data/lib/minder/{message_frame.rb → cli/message_frame.rb} +67 -30
- data/lib/minder/{pomodoro_frame.rb → cli/pomodoro_frame.rb} +9 -3
- data/lib/minder/{quick_add_frame.rb → cli/quick_add_frame.rb} +2 -2
- data/lib/minder/{scene.rb → cli/scene.rb} +7 -6
- data/lib/minder/{search_frame.rb → cli/search_frame.rb} +2 -2
- data/lib/minder/cli/task_editor.rb +68 -0
- data/lib/minder/database/database.rb +98 -0
- data/lib/minder/{database_migrator.rb → database/database_migrator.rb} +1 -1
- data/lib/minder/database/period_mapper.rb +12 -0
- data/lib/minder/database/periods.rb +18 -0
- data/lib/minder/{tasks.rb → database/tasks.rb} +4 -0
- data/lib/minder/pomodoro/break_period.rb +15 -0
- data/lib/minder/{idle_period.rb → pomodoro/idle_period.rb} +10 -2
- data/lib/minder/pomodoro/period.rb +45 -0
- data/lib/minder/pomodoro/pomodoro_runner.rb +83 -0
- data/lib/minder/pomodoro/work_period.rb +15 -0
- data/lib/minder/{task_recorder.rb → tasks/task_manager.rb} +10 -4
- data/lib/minder/version.rb +1 -1
- data/lib/minder.rb +8 -0
- data/spec/minder/application_spec.rb +1 -1
- data/spec/minder/{pomodoro_break_spec.rb → break_period_spec.rb} +3 -3
- data/spec/minder/{pomodoro_spec.rb → pomodoro_period_spec.rb} +3 -3
- metadata +30 -27
- data/lib/minder/break_period.rb +0 -13
- data/lib/minder/database.rb +0 -47
- data/lib/minder/period.rb +0 -35
- data/lib/minder/pomodoro_period.rb +0 -13
- data/lib/minder/pomodoro_runner.rb +0 -69
- data/lib/minder/timer.rb +0 -32
- /data/lib/minder/{commands → database/commands}/delete_task.rb +0 -0
- /data/lib/minder/{task_mapper.rb → database/task_mapper.rb} +0 -0
- /data/lib/minder/{task.rb → tasks/task.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a8777b449e7047ba218141d8e227aee5e6bb2311
|
|
4
|
+
data.tar.gz: a019f497edd579dcfc62c26da1ac6d53bd49612b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 106ec92ccd594c3f53d9bc631df7ddb0d3dfcfa4d8dd34bfce525d28d15c9569bb29eb9fb867f23c1c2648d0dac7c1bad76d28d58bc1b18bec9e332214020a8f
|
|
7
|
+
data.tar.gz: 0144effc35f9fa967662fed2a9a86a9e5125cbf43a78aa45a01ec7f784a91fbf260c81d6a2caf5630dbd872aa94546af31b413168569106b863f0b1a82c2834f
|
data/Gemfile.lock
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
minder (0.
|
|
4
|
+
minder (0.3.0)
|
|
5
5
|
activesupport (~> 4.2, >= 4.2.1)
|
|
6
6
|
curses (~> 1.0, >= 1.0.1)
|
|
7
|
+
emoji
|
|
7
8
|
rom (~> 0.7, >= 0.7)
|
|
8
9
|
rom-sql (~> 0.5, >= 0.5)
|
|
9
10
|
sqlite3 (~> 1.3, >= 1.3.10)
|
|
@@ -35,10 +36,12 @@ GEM
|
|
|
35
36
|
descendants_tracker (0.0.4)
|
|
36
37
|
thread_safe (~> 0.3, >= 0.3.1)
|
|
37
38
|
diff-lcs (1.2.5)
|
|
39
|
+
emoji (1.0.4)
|
|
40
|
+
json
|
|
38
41
|
equalizer (0.0.11)
|
|
39
42
|
i18n (0.7.0)
|
|
40
43
|
ice_nine (0.11.1)
|
|
41
|
-
json (1.8.
|
|
44
|
+
json (1.8.3)
|
|
42
45
|
method_source (0.8.2)
|
|
43
46
|
minitest (5.7.0)
|
|
44
47
|
pry (0.10.1)
|
|
@@ -71,12 +74,12 @@ GEM
|
|
|
71
74
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
72
75
|
rspec-support (~> 3.2.0)
|
|
73
76
|
rspec-support (3.2.2)
|
|
74
|
-
sequel (4.
|
|
77
|
+
sequel (4.23.0)
|
|
75
78
|
slop (3.6.0)
|
|
76
79
|
sqlite3 (1.3.10)
|
|
77
80
|
thread_safe (0.3.5)
|
|
78
81
|
timecop (0.7.3)
|
|
79
|
-
transproc (0.2.
|
|
82
|
+
transproc (0.2.3)
|
|
80
83
|
tzinfo (1.2.2)
|
|
81
84
|
thread_safe (~> 0.1)
|
|
82
85
|
virtus (1.0.5)
|
data/README.md
CHANGED
|
@@ -61,7 +61,7 @@ pressing Tab. The commands for each frame only work when the frame is focused.
|
|
|
61
61
|
Vim keystrokes:
|
|
62
62
|
- `/` to open a dialog to search within the list of tasks.
|
|
63
63
|
- `d` to mark a task as done.
|
|
64
|
-
-
|
|
64
|
+
- ~~`e` to edit the whole tasks list in your `$EDITOR`~~ (this is broken atm).
|
|
65
65
|
- `f` to open a dialog to filter the list of tasks.
|
|
66
66
|
- `gg` to go to top of task list and `G` to go to bottom of list.
|
|
67
67
|
- `j` to do down the list and `k` to go up.
|
data/bin/minder
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
3
|
require 'minder'
|
|
4
|
-
require 'minder/database'
|
|
4
|
+
require 'minder/database/database'
|
|
5
5
|
database = Database.new
|
|
6
6
|
DB = database.rom.repositories[:default]
|
|
7
7
|
|
|
8
8
|
require 'minder/application'
|
|
9
|
-
require 'minder/database_migrator'
|
|
9
|
+
require 'minder/database/database_migrator'
|
|
10
10
|
|
|
11
11
|
migrator = Minder::DatabaseMigrator.new(database: database)
|
|
12
12
|
migrator.run
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Sequel.migration do
|
|
2
|
+
up do
|
|
3
|
+
create_table(:periods) do
|
|
4
|
+
primary_key :id
|
|
5
|
+
String :type, null: false
|
|
6
|
+
Datetime :started_at
|
|
7
|
+
Datetime :ended_at
|
|
8
|
+
Datetime :created_at
|
|
9
|
+
Datetime :updated_at
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
down do
|
|
14
|
+
drop_table(:periods)
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/minder/application.rb
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
require 'minder/config'
|
|
2
|
-
require 'minder/pomodoro_runner'
|
|
3
|
-
require 'minder/
|
|
4
|
-
require 'minder/scene'
|
|
5
|
-
|
|
2
|
+
require 'minder/pomodoro/pomodoro_runner'
|
|
3
|
+
require 'minder/tasks/task_manager'
|
|
4
|
+
require 'minder/cli/scene'
|
|
5
|
+
|
|
6
6
|
require 'curses'
|
|
7
|
-
require 'fileutils'
|
|
8
7
|
|
|
9
8
|
module Minder
|
|
10
9
|
class Application
|
|
@@ -39,7 +38,7 @@ module Minder
|
|
|
39
38
|
self.scene = Scene.new
|
|
40
39
|
scene.setup
|
|
41
40
|
|
|
42
|
-
options = { pomodoro_runner: pomodoro_runner, task_manager:
|
|
41
|
+
options = { pomodoro_runner: pomodoro_runner, task_manager: task_manager }
|
|
43
42
|
|
|
44
43
|
self.pomodoro_frame = PomodoroFrame.new(options)
|
|
45
44
|
self.help_frame = HelpFrame.new(options)
|
|
@@ -95,11 +94,12 @@ module Minder
|
|
|
95
94
|
@runner ||= PomodoroRunner.new(
|
|
96
95
|
work_duration: config.work_duration,
|
|
97
96
|
short_break_duration: config.short_break_duration,
|
|
98
|
-
long_break_duration: config.long_break_duration
|
|
97
|
+
long_break_duration: config.long_break_duration,
|
|
98
|
+
database: database)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
def
|
|
102
|
-
@
|
|
101
|
+
def task_manager
|
|
102
|
+
@task_manager ||= TaskManager.new(database: database)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
def handle_event(event, data = {})
|
|
@@ -114,27 +114,27 @@ module Minder
|
|
|
114
114
|
pomodoro_runner.continue
|
|
115
115
|
when :editor
|
|
116
116
|
`$EDITOR ~/.minder/doing.txt`
|
|
117
|
-
|
|
117
|
+
task_manager.reload
|
|
118
118
|
when :add_task
|
|
119
|
-
|
|
119
|
+
task_manager.add_task(data[:task])
|
|
120
120
|
when :switch_focus
|
|
121
121
|
scene.switch_focus
|
|
122
122
|
when :select_next_task
|
|
123
|
-
|
|
123
|
+
task_manager.select_next_task
|
|
124
124
|
when :select_previous_task
|
|
125
|
-
|
|
125
|
+
task_manager.select_previous_task
|
|
126
126
|
when :delete_task
|
|
127
|
-
|
|
127
|
+
task_manager.delete_task
|
|
128
128
|
when :complete_task
|
|
129
|
-
|
|
129
|
+
task_manager.complete_task
|
|
130
130
|
when :start_task
|
|
131
|
-
|
|
131
|
+
task_manager.start_task
|
|
132
132
|
when :unstart_task
|
|
133
|
-
|
|
133
|
+
task_manager.unstart_task
|
|
134
134
|
when :select_last_task
|
|
135
|
-
|
|
135
|
+
task_manager.select_last_task
|
|
136
136
|
when :select_first_task
|
|
137
|
-
|
|
137
|
+
task_manager.select_first_task
|
|
138
138
|
when :help
|
|
139
139
|
message_frame.hide
|
|
140
140
|
help_frame.unhide
|
|
@@ -150,7 +150,7 @@ module Minder
|
|
|
150
150
|
filter_frame.hide if data[:text] == ''
|
|
151
151
|
scene.focus_frame(message_frame)
|
|
152
152
|
when :update_filter
|
|
153
|
-
|
|
153
|
+
task_manager.filter(data[:text])
|
|
154
154
|
when :search
|
|
155
155
|
search_frame.unhide
|
|
156
156
|
search_frame.begin_search
|
|
@@ -158,12 +158,12 @@ module Minder
|
|
|
158
158
|
when :submit_search
|
|
159
159
|
search_frame.hide
|
|
160
160
|
scene.focus_frame(message_frame)
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
task_manager.search(data[:text])
|
|
162
|
+
task_manager.select_search_result
|
|
163
163
|
when :next_search
|
|
164
|
-
|
|
164
|
+
task_manager.next_search
|
|
165
165
|
when :previous_search
|
|
166
|
-
|
|
166
|
+
task_manager.previous_search
|
|
167
167
|
when :escape_search
|
|
168
168
|
search_frame.hide
|
|
169
169
|
scene.focus_frame(message_frame)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require 'minder/frame'
|
|
1
|
+
require 'minder/cli/frame'
|
|
2
2
|
|
|
3
3
|
module Minder
|
|
4
4
|
class FilterFrame < Frame
|
|
@@ -38,7 +38,7 @@ TEXT
|
|
|
38
38
|
notify_observers(:submit_filter, { text: filter_string })
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
@filter_string.chop! if key ==
|
|
41
|
+
@filter_string.chop! if key == Curses::Key::BACKSPACE
|
|
42
42
|
|
|
43
43
|
refresh
|
|
44
44
|
changed
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require 'minder/frame'
|
|
1
|
+
require 'minder/cli/frame'
|
|
2
2
|
|
|
3
3
|
module Minder
|
|
4
4
|
class HelpFrame < Frame
|
|
@@ -10,10 +10,11 @@ Commands: any key to dismiss
|
|
|
10
10
|
(u) un-start task
|
|
11
11
|
(d) mark task as done
|
|
12
12
|
(x) delete task
|
|
13
|
-
(e)
|
|
13
|
+
(e) edit task
|
|
14
14
|
(G) go to bottom of list
|
|
15
15
|
(gg) go to top of list
|
|
16
16
|
(/) Search among tasks
|
|
17
|
+
(m) minimize messages frame
|
|
17
18
|
(n) Next search result
|
|
18
19
|
(N) Previous search result
|
|
19
20
|
(?) to view this text
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
require 'minder/frame'
|
|
1
|
+
require 'minder/cli/frame'
|
|
2
|
+
require 'minder/cli/task_editor'
|
|
2
3
|
|
|
3
4
|
module Minder
|
|
4
5
|
class MessageFrame < Frame
|
|
5
|
-
attr_reader :current_line
|
|
6
|
+
attr_reader :current_line,
|
|
7
|
+
:task_editor
|
|
6
8
|
|
|
7
9
|
def initialize(*)
|
|
8
10
|
super
|
|
9
11
|
self.height = desired_height
|
|
10
12
|
@minimized = false
|
|
13
|
+
@editing = false
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
def minimize
|
|
@@ -23,6 +26,10 @@ module Minder
|
|
|
23
26
|
@minimized
|
|
24
27
|
end
|
|
25
28
|
|
|
29
|
+
def editing?
|
|
30
|
+
@editing
|
|
31
|
+
end
|
|
32
|
+
|
|
26
33
|
def template
|
|
27
34
|
if minimized?
|
|
28
35
|
minimized_message
|
|
@@ -101,6 +108,9 @@ TEXT
|
|
|
101
108
|
def set_cursor_position
|
|
102
109
|
if minimized?
|
|
103
110
|
window.setpos(1, 20)
|
|
111
|
+
elsif editing?
|
|
112
|
+
window.setpos(3 + task_manager.selected_task_index - scroll_offset,
|
|
113
|
+
task_editor.cursor_position + 6)
|
|
104
114
|
else
|
|
105
115
|
window.setpos(3 + task_manager.selected_task_index - scroll_offset, 3)
|
|
106
116
|
end
|
|
@@ -119,37 +129,64 @@ TEXT
|
|
|
119
129
|
end
|
|
120
130
|
end
|
|
121
131
|
|
|
132
|
+
def handle_keypress(key)
|
|
133
|
+
if editing?
|
|
134
|
+
task_editor.handle_keypress(key)
|
|
135
|
+
else
|
|
136
|
+
super
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def handle_task_editor_event(event, data = {})
|
|
141
|
+
if event == :stop_editing
|
|
142
|
+
@editing = false
|
|
143
|
+
@task_editor = nil
|
|
144
|
+
elsif event == :update_task
|
|
145
|
+
task_manager.update_task(task_manager.selected_task, data)
|
|
146
|
+
@editing = false
|
|
147
|
+
@task_editor = nil
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
changed
|
|
151
|
+
notify_observers(event)
|
|
152
|
+
end
|
|
153
|
+
|
|
122
154
|
def handle_char_keypress(key)
|
|
123
|
-
event =
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
@keypress_memory ||= []
|
|
142
|
-
@keypress_memory << 'g'
|
|
143
|
-
if @keypress_memory == ['g', 'g']
|
|
144
|
-
@keypress_memory = []
|
|
145
|
-
:select_first_task
|
|
146
|
-
end
|
|
147
|
-
when ' '
|
|
148
|
-
if minimized?
|
|
149
|
-
unminimize
|
|
155
|
+
event =
|
|
156
|
+
case key
|
|
157
|
+
when 'j' then :select_next_task
|
|
158
|
+
when 'k' then :select_previous_task
|
|
159
|
+
when 'd' then :complete_task
|
|
160
|
+
when 'x' then :delete_task
|
|
161
|
+
when 's' then :start_task
|
|
162
|
+
when 'u' then :unstart_task
|
|
163
|
+
when 'G' then :select_last_task
|
|
164
|
+
when 'e'
|
|
165
|
+
@editing = true
|
|
166
|
+
@task_editor = TaskEditor.new(task_manager.selected_task, self)
|
|
167
|
+
@task_editor.add_observer(self, :handle_task_editor_event)
|
|
168
|
+
:edit_task
|
|
169
|
+
when '?' then :help
|
|
170
|
+
when '/' then :search
|
|
171
|
+
when 'm'
|
|
172
|
+
minimize
|
|
150
173
|
:redraw
|
|
174
|
+
when 'n' then :next_search
|
|
175
|
+
when 'N' then :previous_search
|
|
176
|
+
when 'f' then :open_filter
|
|
177
|
+
when 'g'
|
|
178
|
+
@keypress_memory ||= []
|
|
179
|
+
@keypress_memory << 'g'
|
|
180
|
+
if @keypress_memory == ['g', 'g']
|
|
181
|
+
@keypress_memory = []
|
|
182
|
+
:select_first_task
|
|
183
|
+
end
|
|
184
|
+
when ' '
|
|
185
|
+
if minimized?
|
|
186
|
+
unminimize
|
|
187
|
+
:redraw
|
|
188
|
+
end
|
|
151
189
|
end
|
|
152
|
-
end
|
|
153
190
|
|
|
154
191
|
changed
|
|
155
192
|
notify_observers(event)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
require 'minder/frame'
|
|
1
|
+
require 'minder/cli/frame'
|
|
2
2
|
|
|
3
3
|
module Minder
|
|
4
4
|
class PomodoroFrame < Frame
|
|
5
5
|
def template
|
|
6
6
|
text = <<-TEXT
|
|
7
|
-
<%= period.title %>
|
|
7
|
+
<%= period.title %> #{pomodoros}
|
|
8
8
|
TEXT
|
|
9
9
|
|
|
10
10
|
if period.message
|
|
@@ -25,7 +25,7 @@ TEXT
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def period
|
|
28
|
-
pomodoro_runner.
|
|
28
|
+
pomodoro_runner.current_period
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def handle_char_keypress(key)
|
|
@@ -49,5 +49,11 @@ TEXT
|
|
|
49
49
|
def set_cursor_position
|
|
50
50
|
window.setpos(1, lines[0].strip.length + 2)
|
|
51
51
|
end
|
|
52
|
+
|
|
53
|
+
def pomodoros
|
|
54
|
+
pomodoro_runner.pomodoros_today.map do |pomodoro|
|
|
55
|
+
"\xF0\x9F\x8D\x85 "
|
|
56
|
+
end.join
|
|
57
|
+
end
|
|
52
58
|
end
|
|
53
59
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require 'minder/frame'
|
|
1
|
+
require 'minder/cli/frame'
|
|
2
2
|
|
|
3
3
|
module Minder
|
|
4
4
|
class QuickAddFrame < Frame
|
|
@@ -31,7 +31,7 @@ TEXT
|
|
|
31
31
|
|
|
32
32
|
def handle_non_char_keypress(key)
|
|
33
33
|
case key
|
|
34
|
-
when
|
|
34
|
+
when Curses::Key::BACKSPACE
|
|
35
35
|
self.input.chop!
|
|
36
36
|
refresh
|
|
37
37
|
when 10
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
require 'ostruct'
|
|
2
|
-
require 'minder/help_frame'
|
|
3
|
-
require 'minder/search_frame'
|
|
4
|
-
require 'minder/filter_frame'
|
|
5
|
-
require 'minder/pomodoro_frame'
|
|
6
|
-
require 'minder/message_frame'
|
|
7
|
-
require 'minder/quick_add_frame'
|
|
2
|
+
require 'minder/cli/help_frame'
|
|
3
|
+
require 'minder/cli/search_frame'
|
|
4
|
+
require 'minder/cli/filter_frame'
|
|
5
|
+
require 'minder/cli/pomodoro_frame'
|
|
6
|
+
require 'minder/cli/message_frame'
|
|
7
|
+
require 'minder/cli/quick_add_frame'
|
|
8
8
|
|
|
9
9
|
module Minder
|
|
10
10
|
class Scene
|
|
@@ -15,6 +15,7 @@ module Minder
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def setup
|
|
18
|
+
Curses.ESCDELAY = 0
|
|
18
19
|
Curses.noecho
|
|
19
20
|
Curses.init_screen
|
|
20
21
|
Curses.timeout = 0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require 'minder/frame'
|
|
1
|
+
require 'minder/cli/frame'
|
|
2
2
|
|
|
3
3
|
module Minder
|
|
4
4
|
class SearchFrame < Frame
|
|
@@ -35,7 +35,7 @@ TEXT
|
|
|
35
35
|
when 27
|
|
36
36
|
changed
|
|
37
37
|
notify_observers(:escape_search)
|
|
38
|
-
when
|
|
38
|
+
when Curses::Key::BACKSPACE
|
|
39
39
|
@search_string.chop!
|
|
40
40
|
refresh
|
|
41
41
|
when 10
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'observer'
|
|
2
|
+
|
|
3
|
+
module Minder
|
|
4
|
+
class TaskEditor
|
|
5
|
+
include Observable
|
|
6
|
+
|
|
7
|
+
attr_reader :cursor_position
|
|
8
|
+
|
|
9
|
+
def initialize(task, parent)
|
|
10
|
+
@original_text = task.description.dup
|
|
11
|
+
@cursor_position = 0
|
|
12
|
+
@task = task
|
|
13
|
+
@parent = parent
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def text
|
|
17
|
+
@task.description
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def handle_keypress(key)
|
|
21
|
+
return unless key
|
|
22
|
+
|
|
23
|
+
if key.is_a?(Fixnum)
|
|
24
|
+
handle_non_char_keypress(key)
|
|
25
|
+
else
|
|
26
|
+
handle_char_keypress(key)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def handle_non_char_keypress(key)
|
|
31
|
+
return unless key
|
|
32
|
+
|
|
33
|
+
data = {}
|
|
34
|
+
|
|
35
|
+
event =
|
|
36
|
+
case key
|
|
37
|
+
when Curses::Key::LEFT
|
|
38
|
+
@cursor_position -= 1 unless cursor_position == 0
|
|
39
|
+
:redraw
|
|
40
|
+
when Curses::Key::RIGHT
|
|
41
|
+
@cursor_position += 1 unless cursor_position > text.length - 1
|
|
42
|
+
:redraw
|
|
43
|
+
when Curses::Key::BACKSPACE
|
|
44
|
+
return if @cursor_position == 0
|
|
45
|
+
@task.description.slice!(@cursor_position - 1)
|
|
46
|
+
@cursor_position -= 1 unless cursor_position == 0
|
|
47
|
+
:redraw
|
|
48
|
+
when 27, 9
|
|
49
|
+
@task.description = @original_text
|
|
50
|
+
:stop_editing
|
|
51
|
+
when 10
|
|
52
|
+
data = { description: @task.description }
|
|
53
|
+
:update_task
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
changed
|
|
57
|
+
notify_observers(event, data)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def handle_char_keypress(key)
|
|
61
|
+
@task.description.insert(@cursor_position, key)
|
|
62
|
+
@cursor_position += 1
|
|
63
|
+
|
|
64
|
+
changed
|
|
65
|
+
notify_observers(:redraw)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require "sqlite3"
|
|
2
|
+
require 'rom'
|
|
3
|
+
require 'rom-sql'
|
|
4
|
+
require 'logger'
|
|
5
|
+
|
|
6
|
+
class Database
|
|
7
|
+
attr_reader :rom
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
FileUtils.mkdir_p(File.dirname(Minder::DATABASE_LOCATION))
|
|
11
|
+
ROM.setup(:sql, "sqlite://#{Minder::DATABASE_LOCATION}")
|
|
12
|
+
|
|
13
|
+
require 'minder/tasks/task'
|
|
14
|
+
require 'minder/database/task_mapper'
|
|
15
|
+
require 'minder/database/tasks'
|
|
16
|
+
require 'minder/database/period_mapper'
|
|
17
|
+
require 'minder/database/periods'
|
|
18
|
+
|
|
19
|
+
ROM.commands(:tasks) do
|
|
20
|
+
define(:delete)
|
|
21
|
+
define(:update)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
ROM.commands(:periods) do
|
|
25
|
+
define(:update)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@rom = ROM.finalize.env
|
|
29
|
+
rom.repositories[:default].use_logger(Logger.new(Minder::LOG_LOCATION))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def tasks
|
|
33
|
+
rom.relation(:tasks).active.as(:entity).to_a
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def tasks_filtered_by(text)
|
|
37
|
+
rom.relation(:tasks).active.as(:entity).filtered_by(text).to_a
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def add_task(description)
|
|
41
|
+
rom.relations.tasks.insert(description: description)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def delete_task(task)
|
|
45
|
+
rom.command(:tasks).delete.by_id(task.id).call
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def complete_task(task)
|
|
49
|
+
update_task(task, completed_at: Time.now)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def start_task(task)
|
|
53
|
+
update_task(task, started_at: Time.now)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def unstart_task(task)
|
|
57
|
+
update_task(task, started_at: nil)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def update_task(task, options = {})
|
|
61
|
+
rom.command(:tasks).update.by_id(task.id).call(options)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def complete_period(task)
|
|
65
|
+
update_period(task, ended_at: Time.now)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def add_period(period)
|
|
69
|
+
rom.relations.periods.insert(started_at: period.started_at,
|
|
70
|
+
type: period.type)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def last_period
|
|
74
|
+
require 'minder/pomodoro/work_period'
|
|
75
|
+
require 'minder/pomodoro/break_period'
|
|
76
|
+
require 'minder/pomodoro/idle_period'
|
|
77
|
+
data = rom.relations.periods.last
|
|
78
|
+
if data[:type] == 'work'
|
|
79
|
+
Minder::WorkPeriod.new(data)
|
|
80
|
+
elsif data[:type] == 'break'
|
|
81
|
+
Minder::BreakPeriod.new(data)
|
|
82
|
+
else
|
|
83
|
+
Minder::IdlePeriod.new(data)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def update_period(period, options = {})
|
|
88
|
+
rom.command(:periods).update.by_id(period.id).call(options)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def periods
|
|
92
|
+
rom.relation(:tasks).active.as(:entity).to_a
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def pomodoros_today
|
|
96
|
+
rom.relation(:periods).pomodoros_today.as(:entity).to_a
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -9,7 +9,7 @@ module Minder
|
|
|
9
9
|
def run
|
|
10
10
|
ROM::SQL::Migration::Migrator.new(
|
|
11
11
|
ROM.env.repositories[:default].connection,
|
|
12
|
-
path: File.expand_path(File.dirname(__FILE__) + '
|
|
12
|
+
path: File.expand_path(File.dirname(__FILE__) + '/../../../db/migrate')
|
|
13
13
|
).run
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class Periods < ROM::Relation[:sql]
|
|
2
|
+
def by_id(id)
|
|
3
|
+
where(id: id)
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def completed_pomodoros
|
|
7
|
+
where(type: 'work')
|
|
8
|
+
.where('started_at IS NOT NULL')
|
|
9
|
+
.where('ended_at IS NOT NULL')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def pomodoros_today
|
|
13
|
+
completed_pomodoros.
|
|
14
|
+
where('ended_at BETWEEN ? AND ?',
|
|
15
|
+
Time.now.strftime('%Y-%m-%d 00:00:00'),
|
|
16
|
+
Time.now.strftime('%Y-%m-%d 23:59:59'))
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require 'minder/period'
|
|
1
|
+
require 'minder/pomodoro/period'
|
|
2
2
|
|
|
3
3
|
module Minder
|
|
4
4
|
class IdlePeriod < Period
|
|
@@ -11,7 +11,11 @@ module Minder
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def start!
|
|
14
|
-
|
|
14
|
+
self.started_at = Time.now
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def complete!
|
|
18
|
+
self.ended_at = Time.now
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
def elapsed?
|
|
@@ -21,5 +25,9 @@ module Minder
|
|
|
21
25
|
def completed?
|
|
22
26
|
true
|
|
23
27
|
end
|
|
28
|
+
|
|
29
|
+
def type
|
|
30
|
+
'idle'
|
|
31
|
+
end
|
|
24
32
|
end
|
|
25
33
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'virtus'
|
|
2
|
+
|
|
3
|
+
module Minder
|
|
4
|
+
class Period
|
|
5
|
+
include Virtus.model
|
|
6
|
+
|
|
7
|
+
attribute :id, Integer
|
|
8
|
+
attribute :type, String
|
|
9
|
+
attribute :started_at, DateTime
|
|
10
|
+
attribute :ended_at, DateTime
|
|
11
|
+
attribute :duration_in_seconds, Integer, default: 0
|
|
12
|
+
attribute :completed, Boolean
|
|
13
|
+
|
|
14
|
+
def duration_in_minutes=(minutes)
|
|
15
|
+
self.duration_in_seconds = minutes.to_i * 60
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def start!
|
|
19
|
+
Minder.play_sound('start.wav')
|
|
20
|
+
self.started_at = Time.now
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def complete!
|
|
24
|
+
Minder.play_sound('done.wav')
|
|
25
|
+
self.ended_at = Time.now
|
|
26
|
+
self.completed = true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def elapsed?
|
|
30
|
+
elapsed_time >= duration_in_seconds
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def message
|
|
34
|
+
"#{Minder.formatted_time(elapsed_time)} " \
|
|
35
|
+
"(out of #{Minder.formatted_time(duration_in_seconds)})"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def elapsed_time
|
|
39
|
+
return 0 unless started_at
|
|
40
|
+
return ended_at.to_i - started_at.to_i if ended_at
|
|
41
|
+
|
|
42
|
+
(Time.now.to_time.to_i - started_at.to_time.to_i)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require 'observer'
|
|
2
|
+
|
|
3
|
+
require 'minder/pomodoro/work_period'
|
|
4
|
+
require 'minder/pomodoro/break_period'
|
|
5
|
+
require 'minder/pomodoro/idle_period'
|
|
6
|
+
|
|
7
|
+
module Minder
|
|
8
|
+
class PomodoroRunner
|
|
9
|
+
include Observable
|
|
10
|
+
|
|
11
|
+
attr_accessor :work_duration,
|
|
12
|
+
:short_break_duration,
|
|
13
|
+
:long_break_duration,
|
|
14
|
+
:database
|
|
15
|
+
|
|
16
|
+
attr_reader :period_count,
|
|
17
|
+
:current_period
|
|
18
|
+
|
|
19
|
+
def initialize(**options)
|
|
20
|
+
self.work_duration = options.fetch(:work_duration)
|
|
21
|
+
self.short_break_duration = options.fetch(:short_break_duration)
|
|
22
|
+
self.long_break_duration = options.fetch(:long_break_duration)
|
|
23
|
+
self.database = options.fetch(:database)
|
|
24
|
+
@period_count = 0
|
|
25
|
+
current_period = IdlePeriod.new
|
|
26
|
+
current_period.start!
|
|
27
|
+
database.add_period(current_period)
|
|
28
|
+
@current_period = database.last_period
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def tick
|
|
32
|
+
return if !current_period.elapsed? || current_period.completed?
|
|
33
|
+
|
|
34
|
+
old_period = current_period
|
|
35
|
+
@current_period = IdlePeriod.new
|
|
36
|
+
|
|
37
|
+
changed
|
|
38
|
+
if old_period.is_a?(WorkPeriod)
|
|
39
|
+
notify_observers(:completed_work)
|
|
40
|
+
elsif old_period.is_a?(BreakPeriod)
|
|
41
|
+
notify_observers(:completed_break)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def continue
|
|
46
|
+
return unless current_period.elapsed?
|
|
47
|
+
|
|
48
|
+
current_period.complete!
|
|
49
|
+
@pomodoros = nil
|
|
50
|
+
database.complete_period(current_period)
|
|
51
|
+
|
|
52
|
+
advance_period
|
|
53
|
+
current_period.start!
|
|
54
|
+
database.add_period(current_period)
|
|
55
|
+
@current_period = database.last_period
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def advance_period
|
|
59
|
+
@period_count += 1
|
|
60
|
+
changed
|
|
61
|
+
|
|
62
|
+
if period_count.odd?
|
|
63
|
+
notify_observers(:started_work)
|
|
64
|
+
@current_period = WorkPeriod.new(duration_in_minutes: work_duration)
|
|
65
|
+
else
|
|
66
|
+
notify_observers(:started_break)
|
|
67
|
+
@current_period = BreakPeriod.new(duration_in_minutes: break_duration)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def break_duration
|
|
72
|
+
if period_count % 8 == 0
|
|
73
|
+
long_break_duration
|
|
74
|
+
else
|
|
75
|
+
short_break_duration
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def pomodoros_today
|
|
80
|
+
@pomodoros_today ||= database.pomodoros_today
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
require 'minder/task'
|
|
2
|
-
require 'fileutils'
|
|
1
|
+
require 'minder/tasks/task'
|
|
3
2
|
|
|
4
3
|
module Minder
|
|
5
|
-
class
|
|
4
|
+
class TaskManager
|
|
6
5
|
attr_accessor :lines
|
|
7
6
|
|
|
8
7
|
attr_reader :search_results,
|
|
@@ -40,6 +39,10 @@ module Minder
|
|
|
40
39
|
!tasks.empty?
|
|
41
40
|
end
|
|
42
41
|
|
|
42
|
+
def update_task(task, options = {})
|
|
43
|
+
database.update_task(task, options)
|
|
44
|
+
end
|
|
45
|
+
|
|
43
46
|
def add_task(task)
|
|
44
47
|
File.open(DOING_FILE, 'a') do |file|
|
|
45
48
|
file.write("#{task}\n")
|
|
@@ -88,7 +91,10 @@ module Minder
|
|
|
88
91
|
|
|
89
92
|
def complete_task
|
|
90
93
|
task = selected_task
|
|
91
|
-
|
|
94
|
+
database.complete_task(task)
|
|
95
|
+
write_file_with_backup
|
|
96
|
+
reload
|
|
97
|
+
select_previous_task
|
|
92
98
|
add_to_done_file("Finished: #{task.description}")
|
|
93
99
|
end
|
|
94
100
|
|
data/lib/minder/version.rb
CHANGED
data/lib/minder.rb
CHANGED
|
@@ -7,6 +7,14 @@ module Minder
|
|
|
7
7
|
DONE_FILE = File.join(ENV["HOME"], '.minder', 'done.txt')
|
|
8
8
|
MIGRATIONS_PATH = File.expand_path(File.dirname(__FILE__) + '/../')
|
|
9
9
|
DATABASE_LOCATION = "#{ENV['HOME']}/.minder/database.sqlite3"
|
|
10
|
+
LOG_LOCATION = "#{ENV['HOME']}/.minder/info.log"
|
|
11
|
+
|
|
12
|
+
require 'minder/application'
|
|
13
|
+
require 'minder/config'
|
|
14
|
+
require 'minder/version'
|
|
15
|
+
|
|
16
|
+
require 'active_support'
|
|
17
|
+
require 'fileutils'
|
|
10
18
|
|
|
11
19
|
def self.formatted_time(seconds)
|
|
12
20
|
minutes = (seconds / 60).to_i
|
|
@@ -49,7 +49,7 @@ describe Minder::Application do
|
|
|
49
49
|
expect(application).to have_received(:system).with('stty -raw echo')
|
|
50
50
|
expect(STDIN).to have_received(:getc).with('stty -raw echo')
|
|
51
51
|
expect(pomodoro_runner).to have_received(:next_action)
|
|
52
|
-
expect(interval).to receive(:)
|
|
52
|
+
expect(interval).to receive(:blah)
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
end
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
require 'minder/
|
|
1
|
+
require 'minder/break_period'
|
|
2
2
|
|
|
3
|
-
describe Minder::
|
|
3
|
+
describe Minder::BreakPeriod do
|
|
4
4
|
describe '#run' do
|
|
5
5
|
let(:timer) { instance_spy(Minder::Timer) }
|
|
6
6
|
|
|
7
7
|
it 'runs the pomodoro' do
|
|
8
|
-
pomodoro = described_class.new(
|
|
8
|
+
pomodoro = described_class.new(duration_in_minutes: 5)
|
|
9
9
|
|
|
10
10
|
allow(Minder::Timer).to receive(:new)
|
|
11
11
|
.with(seconds: 300)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
require 'minder/
|
|
1
|
+
require 'minder/work_period'
|
|
2
2
|
|
|
3
|
-
describe Minder::
|
|
3
|
+
describe Minder::WorkPeriod do
|
|
4
4
|
describe '#run' do
|
|
5
5
|
let(:timer) { instance_spy(Minder::Timer) }
|
|
6
6
|
|
|
7
7
|
it 'runs the pomodoro' do
|
|
8
|
-
pomodoro = described_class.new(
|
|
8
|
+
pomodoro = described_class.new(duration_in_minutes: 5)
|
|
9
9
|
|
|
10
10
|
allow(Minder::Timer).to receive(:new)
|
|
11
11
|
.with(seconds: 300)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: minder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joseph Method
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-06-
|
|
11
|
+
date: 2015-06-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: curses
|
|
@@ -258,37 +258,40 @@ files:
|
|
|
258
258
|
- bin/minder
|
|
259
259
|
- db/migrate/001_create_tasks.rb
|
|
260
260
|
- db/migrate/002_import_tasks.rb
|
|
261
|
+
- db/migrate/003_create_periods.rb
|
|
261
262
|
- lib/minder.rb
|
|
262
263
|
- lib/minder/application.rb
|
|
263
|
-
- lib/minder/
|
|
264
|
-
- lib/minder/
|
|
264
|
+
- lib/minder/cli/filter_frame.rb
|
|
265
|
+
- lib/minder/cli/frame.rb
|
|
266
|
+
- lib/minder/cli/help_frame.rb
|
|
267
|
+
- lib/minder/cli/message_frame.rb
|
|
268
|
+
- lib/minder/cli/pomodoro_frame.rb
|
|
269
|
+
- lib/minder/cli/quick_add_frame.rb
|
|
270
|
+
- lib/minder/cli/scene.rb
|
|
271
|
+
- lib/minder/cli/search_frame.rb
|
|
272
|
+
- lib/minder/cli/task_editor.rb
|
|
265
273
|
- lib/minder/config.rb
|
|
266
|
-
- lib/minder/database.rb
|
|
267
|
-
- lib/minder/
|
|
268
|
-
- lib/minder/
|
|
269
|
-
- lib/minder/
|
|
270
|
-
- lib/minder/
|
|
271
|
-
- lib/minder/
|
|
272
|
-
- lib/minder/
|
|
273
|
-
- lib/minder/
|
|
274
|
-
- lib/minder/
|
|
275
|
-
- lib/minder/
|
|
276
|
-
- lib/minder/pomodoro_runner.rb
|
|
277
|
-
- lib/minder/
|
|
278
|
-
- lib/minder/
|
|
279
|
-
- lib/minder/
|
|
280
|
-
- lib/minder/task.rb
|
|
281
|
-
- lib/minder/task_mapper.rb
|
|
282
|
-
- lib/minder/task_recorder.rb
|
|
283
|
-
- lib/minder/tasks.rb
|
|
284
|
-
- lib/minder/timer.rb
|
|
274
|
+
- lib/minder/database/commands/delete_task.rb
|
|
275
|
+
- lib/minder/database/database.rb
|
|
276
|
+
- lib/minder/database/database_migrator.rb
|
|
277
|
+
- lib/minder/database/period_mapper.rb
|
|
278
|
+
- lib/minder/database/periods.rb
|
|
279
|
+
- lib/minder/database/task_mapper.rb
|
|
280
|
+
- lib/minder/database/tasks.rb
|
|
281
|
+
- lib/minder/pomodoro/break_period.rb
|
|
282
|
+
- lib/minder/pomodoro/idle_period.rb
|
|
283
|
+
- lib/minder/pomodoro/period.rb
|
|
284
|
+
- lib/minder/pomodoro/pomodoro_runner.rb
|
|
285
|
+
- lib/minder/pomodoro/work_period.rb
|
|
286
|
+
- lib/minder/tasks/task.rb
|
|
287
|
+
- lib/minder/tasks/task_manager.rb
|
|
285
288
|
- lib/minder/version.rb
|
|
286
289
|
- minder.gemspec
|
|
287
290
|
- spec/minder/application_spec.rb
|
|
291
|
+
- spec/minder/break_period_spec.rb
|
|
288
292
|
- spec/minder/config_spec.rb
|
|
289
|
-
- spec/minder/
|
|
293
|
+
- spec/minder/pomodoro_period_spec.rb
|
|
290
294
|
- spec/minder/pomodoro_runner_spec.rb
|
|
291
|
-
- spec/minder/pomodoro_spec.rb
|
|
292
295
|
- spec/minder/timer_spec.rb
|
|
293
296
|
- spec/spec_helper.rb
|
|
294
297
|
homepage: http://github.com/tristil/minder
|
|
@@ -318,9 +321,9 @@ summary: Combines a Pomodoro Technique runner with GTD-style task backlogs and D
|
|
|
318
321
|
One-style prompts."
|
|
319
322
|
test_files:
|
|
320
323
|
- spec/minder/application_spec.rb
|
|
324
|
+
- spec/minder/break_period_spec.rb
|
|
321
325
|
- spec/minder/config_spec.rb
|
|
322
|
-
- spec/minder/
|
|
326
|
+
- spec/minder/pomodoro_period_spec.rb
|
|
323
327
|
- spec/minder/pomodoro_runner_spec.rb
|
|
324
|
-
- spec/minder/pomodoro_spec.rb
|
|
325
328
|
- spec/minder/timer_spec.rb
|
|
326
329
|
- spec/spec_helper.rb
|
data/lib/minder/break_period.rb
DELETED
data/lib/minder/database.rb
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
require 'fileutils'
|
|
2
|
-
require "sqlite3"
|
|
3
|
-
require 'rom'
|
|
4
|
-
require 'rom-sql'
|
|
5
|
-
|
|
6
|
-
class Database
|
|
7
|
-
attr_reader :rom
|
|
8
|
-
|
|
9
|
-
def initialize
|
|
10
|
-
FileUtils.mkdir_p(File.dirname(Minder::DATABASE_LOCATION))
|
|
11
|
-
ROM.setup(:sql, "sqlite://#{Minder::DATABASE_LOCATION}")
|
|
12
|
-
require 'minder/task'
|
|
13
|
-
require 'minder/task_mapper'
|
|
14
|
-
require 'minder/tasks'
|
|
15
|
-
|
|
16
|
-
ROM.commands(:tasks) do
|
|
17
|
-
define(:delete)
|
|
18
|
-
define(:update)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
@rom = ROM.finalize.env
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def tasks
|
|
25
|
-
rom.relation(:tasks).as(:entity).to_a
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def tasks_filtered_by(text)
|
|
29
|
-
rom.relation(:tasks).as(:entity).filtered_by(text).to_a
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def add_task(description)
|
|
33
|
-
rom.relations.tasks.insert(description: description)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def delete_task(task)
|
|
37
|
-
rom.command(:tasks).delete.by_id(task.id).call
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def start_task(task)
|
|
41
|
-
rom.command(:tasks).update.by_id(task.id).call(started_at: Time.now)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def unstart_task(task)
|
|
45
|
-
rom.command(:tasks).update.by_id(task.id).call(started_at: nil)
|
|
46
|
-
end
|
|
47
|
-
end
|
data/lib/minder/period.rb
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
require 'minder/timer'
|
|
2
|
-
|
|
3
|
-
module Minder
|
|
4
|
-
class Period
|
|
5
|
-
attr_accessor :minutes,
|
|
6
|
-
:timer
|
|
7
|
-
|
|
8
|
-
def initialize(minutes: nil)
|
|
9
|
-
self.minutes = minutes
|
|
10
|
-
self.timer = Minder::Timer.new(seconds: minutes.to_i * 60)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def start!
|
|
14
|
-
Minder.play_sound('start.wav')
|
|
15
|
-
timer.start!
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def complete!
|
|
19
|
-
Minder.play_sound('done.wav')
|
|
20
|
-
@status = :completed
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def completed?
|
|
24
|
-
@status == :completed
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def elapsed?
|
|
28
|
-
timer.completed?
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def message
|
|
32
|
-
timer.to_s
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
require 'observer'
|
|
2
|
-
|
|
3
|
-
require 'minder/pomodoro_period'
|
|
4
|
-
require 'minder/break_period'
|
|
5
|
-
require 'minder/idle_period'
|
|
6
|
-
|
|
7
|
-
module Minder
|
|
8
|
-
class PomodoroRunner
|
|
9
|
-
include Observable
|
|
10
|
-
|
|
11
|
-
attr_accessor :work_duration,
|
|
12
|
-
:short_break_duration,
|
|
13
|
-
:long_break_duration
|
|
14
|
-
|
|
15
|
-
attr_reader :action_count,
|
|
16
|
-
:current_action
|
|
17
|
-
|
|
18
|
-
def initialize(**options)
|
|
19
|
-
self.work_duration = options.fetch(:work_duration)
|
|
20
|
-
self.short_break_duration = options.fetch(:short_break_duration)
|
|
21
|
-
self.long_break_duration = options.fetch(:long_break_duration)
|
|
22
|
-
@action_count = 0
|
|
23
|
-
@current_action = IdlePeriod.new
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def tick
|
|
27
|
-
return if !current_action.elapsed? || current_action.completed?
|
|
28
|
-
|
|
29
|
-
old_action = current_action
|
|
30
|
-
current_action.complete!
|
|
31
|
-
@current_action = IdlePeriod.new
|
|
32
|
-
|
|
33
|
-
changed
|
|
34
|
-
if old_action.is_a?(PomodoroPeriod)
|
|
35
|
-
notify_observers(:completed_work)
|
|
36
|
-
elsif old_action.is_a?(BreakPeriod)
|
|
37
|
-
notify_observers(:completed_break)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def continue
|
|
42
|
-
return unless current_action.elapsed?
|
|
43
|
-
|
|
44
|
-
advance_action
|
|
45
|
-
current_action.start!
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def advance_action
|
|
49
|
-
@action_count += 1
|
|
50
|
-
changed
|
|
51
|
-
|
|
52
|
-
if action_count.odd?
|
|
53
|
-
notify_observers(:started_work)
|
|
54
|
-
@current_action = PomodoroPeriod.new(minutes: work_duration)
|
|
55
|
-
else
|
|
56
|
-
notify_observers(:started_break)
|
|
57
|
-
@current_action = BreakPeriod.new(minutes: break_duration)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def break_duration
|
|
62
|
-
if action_count % 8 == 0
|
|
63
|
-
long_break_duration
|
|
64
|
-
else
|
|
65
|
-
short_break_duration
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
data/lib/minder/timer.rb
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
require 'minder'
|
|
2
|
-
|
|
3
|
-
module Minder
|
|
4
|
-
class Timer
|
|
5
|
-
attr_accessor :seconds,
|
|
6
|
-
:start_time
|
|
7
|
-
|
|
8
|
-
def initialize(seconds: DEFAULT_WORK_PERIOD)
|
|
9
|
-
self.seconds = seconds
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def start!
|
|
13
|
-
self.start_time = Time.now
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def completed?
|
|
17
|
-
elapsed_time.to_i >= seconds
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def elapsed_time
|
|
21
|
-
return 0 unless start_time
|
|
22
|
-
|
|
23
|
-
(Time.now - start_time)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def to_s
|
|
27
|
-
"#{Minder.formatted_time(elapsed_time)} " \
|
|
28
|
-
"(out of #{Minder.formatted_time(seconds)})"
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|