pomo 2.0.2 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +7 -0
- data/{HISTORY.md → CHANGELOG.md} +25 -0
- data/{LICENSE.txt → LICENSE.md} +0 -0
- data/README.md +57 -30
- data/Rakefile +12 -5
- data/bin/pomo +166 -97
- data/features/configuration.feature +35 -0
- data/features/manage_list.feature +34 -0
- data/features/manage_tasks.feature +73 -0
- data/features/step_definitions/pomo_steps.rb +8 -0
- data/features/support/env.rb +12 -0
- data/lib/pomo.rb +0 -9
- data/lib/pomo/configuration.rb +57 -15
- data/lib/pomo/github_task.rb +51 -13
- data/lib/pomo/list.rb +59 -21
- data/lib/pomo/notifier.rb +5 -0
- data/lib/pomo/notifier/growl_notifier.rb +1 -0
- data/lib/pomo/notifier/libnotify_notifier.rb +1 -0
- data/lib/pomo/notifier/notification_center_notifier.rb +1 -0
- data/lib/pomo/os.rb +2 -0
- data/lib/pomo/task.rb +16 -4
- data/lib/pomo/version.rb +1 -1
- data/pomo.gemspec +7 -3
- data/spec/pomo/configuration_spec.rb +108 -0
- data/spec/pomo/list_spec.rb +63 -0
- data/spec/pomo/task_spec.rb +15 -0
- data/spec/spec_helper.rb +40 -2
- metadata +71 -8
- data/spec/pomo_spec.rb +0 -8
@@ -0,0 +1,35 @@
|
|
1
|
+
Feature: Configure pomo
|
2
|
+
In order to use pomo
|
3
|
+
As a user
|
4
|
+
I want a sane default configuration for my system
|
5
|
+
and a data file to store my tasks
|
6
|
+
|
7
|
+
Scenario: Execute pomo without configuration
|
8
|
+
When I run `pomo`
|
9
|
+
Then the following files should exist:
|
10
|
+
| /tmp/home/.pomorc |
|
11
|
+
| /tmp/home/.pomo |
|
12
|
+
And the output should contain "Initialized default config file in /tmp/home/.pomorc. See 'pomo help initconfig' for options."
|
13
|
+
|
14
|
+
Scenario: Initialize configuration
|
15
|
+
When I run `pomo initconfig`
|
16
|
+
Then the following files should exist:
|
17
|
+
| /tmp/home/.pomorc |
|
18
|
+
| /tmp/home/.pomo |
|
19
|
+
And the output should contain "Initialized config file"
|
20
|
+
|
21
|
+
Scenario: Initialize configuration (no clobber)
|
22
|
+
Given a file named "/tmp/home/.pomorc" with:
|
23
|
+
"""
|
24
|
+
hello world
|
25
|
+
"""
|
26
|
+
When I run `pomo initconfig`
|
27
|
+
Then the output should contain "Not overwriting existing config file"
|
28
|
+
|
29
|
+
Scenario: Force initialize configuration (clobber)
|
30
|
+
Given a file named "/tmp/home/.pomorc" with:
|
31
|
+
"""
|
32
|
+
hello world
|
33
|
+
"""
|
34
|
+
When I run `pomo initconfig --force`
|
35
|
+
Then the file "/tmp/home/.pomorc" should not contain "hello world"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Feature: Manage Pomodoro list
|
2
|
+
In order to complete tasks
|
3
|
+
As a user
|
4
|
+
I want to manage the order and content of my task list
|
5
|
+
|
6
|
+
Scenario: Add tasks
|
7
|
+
When I run `pomo add 'Remember the milk'`
|
8
|
+
And I run `pomo list`
|
9
|
+
Then the output from "pomo list" should contain "Remember the milk"
|
10
|
+
|
11
|
+
Scenario: Remove tasks
|
12
|
+
Given the following tasks:
|
13
|
+
| Remember the milk |
|
14
|
+
| Walk the dog |
|
15
|
+
| Shave the yak |
|
16
|
+
When I run `pomo rm first`
|
17
|
+
And I run `pomo list`
|
18
|
+
Then the output from "pomo list" should not contain "Remember the milk"
|
19
|
+
|
20
|
+
Scenario: Move tasks
|
21
|
+
Given the following tasks:
|
22
|
+
| Remember the milk |
|
23
|
+
| Walk the dog |
|
24
|
+
| Shave the yak |
|
25
|
+
When I run `pomo mv first last`
|
26
|
+
And I run `pomo list`
|
27
|
+
Then the output from "pomo list" should contain exactly:
|
28
|
+
"""
|
29
|
+
0. Walk the dog : 25 minutes
|
30
|
+
1. Shave the yak : 25 minutes
|
31
|
+
2. Remember the milk : 25 minutes
|
32
|
+
75 minutes
|
33
|
+
|
34
|
+
"""
|
@@ -0,0 +1,73 @@
|
|
1
|
+
Feature: Manage Pomodoro task
|
2
|
+
In order to track tasks
|
3
|
+
As a user
|
4
|
+
I want to manage the metadata and status of my tasks
|
5
|
+
|
6
|
+
Scenario: Edit tasks
|
7
|
+
When I run `pomo add 'Remember the milk' -d '1 qt Almond milk' -l 60`
|
8
|
+
And I run `pomo edit first -n 'Remember the Almond milk' -d '1 qt' -l 30`
|
9
|
+
And I run `pomo show first`
|
10
|
+
Then the output from "pomo show first" should contain exactly:
|
11
|
+
"""
|
12
|
+
|
13
|
+
name : Remember the Almond milk
|
14
|
+
length : 30 minutes
|
15
|
+
description : 1 qt
|
16
|
+
complete : [ ]
|
17
|
+
|
18
|
+
|
19
|
+
"""
|
20
|
+
|
21
|
+
Scenario: Copy tasks
|
22
|
+
When I run `pomo add 'Remember the milk'`
|
23
|
+
And I run `pomo complete first`
|
24
|
+
And I run `pomo show first`
|
25
|
+
Then the output from "pomo show first" should contain exactly:
|
26
|
+
"""
|
27
|
+
|
28
|
+
name : Remember the milk
|
29
|
+
length : 25 minutes
|
30
|
+
complete : [✓]
|
31
|
+
|
32
|
+
|
33
|
+
"""
|
34
|
+
When I run `pomo copy first`
|
35
|
+
And I run `pomo show last`
|
36
|
+
Then the output from "pomo show last" should contain exactly:
|
37
|
+
"""
|
38
|
+
|
39
|
+
name : Remember the milk
|
40
|
+
length : 25 minutes
|
41
|
+
complete : [ ]
|
42
|
+
|
43
|
+
|
44
|
+
"""
|
45
|
+
|
46
|
+
Scenario: Complete tasks
|
47
|
+
When I run `pomo add 'Remember the milk'`
|
48
|
+
And I run `pomo complete first`
|
49
|
+
And I run `pomo show first`
|
50
|
+
Then the output from "pomo show first" should contain exactly:
|
51
|
+
"""
|
52
|
+
|
53
|
+
name : Remember the milk
|
54
|
+
length : 25 minutes
|
55
|
+
complete : [✓]
|
56
|
+
|
57
|
+
|
58
|
+
"""
|
59
|
+
|
60
|
+
Scenario: Incomplete tasks
|
61
|
+
When I run `pomo add 'Remember the milk'`
|
62
|
+
And I run `pomo complete first`
|
63
|
+
And I run `pomo incomplete first`
|
64
|
+
And I run `pomo show first`
|
65
|
+
Then the output from "pomo show first" should contain exactly:
|
66
|
+
"""
|
67
|
+
|
68
|
+
name : Remember the milk
|
69
|
+
length : 25 minutes
|
70
|
+
complete : [ ]
|
71
|
+
|
72
|
+
|
73
|
+
"""
|
data/lib/pomo.rb
CHANGED
@@ -1,15 +1,6 @@
|
|
1
|
-
require 'rbconfig'
|
2
1
|
require 'pomo/os'
|
3
|
-
require 'terminal-notifier' if Pomo::OS.mac?
|
4
|
-
require 'growl' if Pomo::OS.mac? || Pomo::OS.windows?
|
5
|
-
require 'libnotify' if Pomo::OS.linux?
|
6
|
-
require 'yaml'
|
7
|
-
require 'octokit'
|
8
2
|
require 'pomo/configuration'
|
9
3
|
require 'pomo/notifier'
|
10
|
-
require 'pomo/notifier/notification_center_notifier'
|
11
|
-
require 'pomo/notifier/growl_notifier'
|
12
|
-
require 'pomo/notifier/libnotify_notifier'
|
13
4
|
require 'pomo/list'
|
14
5
|
require 'pomo/task'
|
15
6
|
require 'pomo/github_task'
|
data/lib/pomo/configuration.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'commander'
|
3
|
+
include Commander::UI
|
4
|
+
|
1
5
|
module Pomo
|
2
6
|
class Configuration
|
3
7
|
##
|
@@ -7,6 +11,13 @@ module Pomo
|
|
7
11
|
|
8
12
|
attr_reader :notifier
|
9
13
|
|
14
|
+
##
|
15
|
+
# Run with progress bar.
|
16
|
+
#
|
17
|
+
# values: true | false
|
18
|
+
|
19
|
+
attr_accessor :progress
|
20
|
+
|
10
21
|
##
|
11
22
|
# Refresh tmux status bar.
|
12
23
|
#
|
@@ -16,29 +27,60 @@ module Pomo
|
|
16
27
|
|
17
28
|
##
|
18
29
|
# Initialize configuration.
|
19
|
-
def initialize
|
20
|
-
options = {
|
21
|
-
:notifier => default_notifier,
|
22
|
-
:tmux => false
|
23
|
-
}
|
24
30
|
|
25
|
-
|
31
|
+
def initialize(options = {})
|
32
|
+
@notifier = options[:notifier]
|
33
|
+
@progress = options[:progress]
|
34
|
+
@tmux = options[:tmux]
|
35
|
+
end
|
26
36
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
37
|
+
##
|
38
|
+
# Load configuration file or default_options. Passed options take precedence.
|
39
|
+
|
40
|
+
def self.load(options = {})
|
41
|
+
options.reject!{|k,v| ![:notifier, :progress, :tmux].include? k}
|
42
|
+
|
43
|
+
if !(File.exists? config_file)
|
44
|
+
File.open(config_file, 'w') { |file| YAML::dump(default_options, file) }
|
45
|
+
say "Initialized default config file in #{config_file}. See 'pomo help initconfig' for options."
|
46
|
+
end
|
47
|
+
|
48
|
+
config_file_options = YAML.load_file(config_file)
|
49
|
+
new(config_file_options.merge(options))
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Save configuration. Passed options take precendence over default_options.
|
54
|
+
|
55
|
+
def self.save(options = {})
|
56
|
+
force_save = options.delete :force
|
57
|
+
options.reject!{|k,v| ![:notifier, :progress, :tmux].include? k}
|
58
|
+
|
59
|
+
options = default_options.merge(options)
|
60
|
+
|
61
|
+
if !(File.exists? config_file) || force_save
|
31
62
|
File.open(config_file, 'w') { |file| YAML::dump(options, file) }
|
32
|
-
|
63
|
+
say "Initialized config file in #{config_file}"
|
64
|
+
else
|
65
|
+
say_error "Not overwriting existing config file #{config_file}, use --force to override. See 'pomo help initconfig'."
|
33
66
|
end
|
67
|
+
end
|
34
68
|
|
35
|
-
|
36
|
-
|
69
|
+
# Helpers
|
70
|
+
|
71
|
+
def self.config_file
|
72
|
+
File.join(ENV['HOME'],'.pomorc')
|
37
73
|
end
|
38
74
|
|
39
|
-
|
75
|
+
def self.default_options
|
76
|
+
{
|
77
|
+
:notifier => default_notifier,
|
78
|
+
:progress => false,
|
79
|
+
:tmux => false
|
80
|
+
}
|
81
|
+
end
|
40
82
|
|
41
|
-
def default_notifier
|
83
|
+
def self.default_notifier
|
42
84
|
if Pomo::OS.has_notification_center?
|
43
85
|
return 'notification_center'
|
44
86
|
elsif Pomo::OS.linux?
|
data/lib/pomo/github_task.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# This is to make sure Faraday doesn't warn the user about the `system_timer` gem missing in Ruby 1.8
|
2
|
+
old_warn, $-w = $-w, nil
|
3
|
+
begin
|
4
|
+
require 'faraday'
|
5
|
+
ensure
|
6
|
+
$-w = old_warn
|
7
|
+
end
|
8
|
+
require 'octokit'
|
1
9
|
|
2
10
|
module Pomo
|
3
|
-
class Task
|
4
|
-
def github?
|
5
|
-
false
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
11
|
class GithubTask < Task
|
10
12
|
|
11
13
|
##
|
@@ -28,27 +30,63 @@ module Pomo
|
|
28
30
|
|
29
31
|
attr_accessor :number
|
30
32
|
|
33
|
+
##
|
34
|
+
# Absolute URI.
|
35
|
+
|
36
|
+
attr_accessor :url
|
37
|
+
|
31
38
|
##
|
32
39
|
# Initialize with _name_ and _options_.
|
33
40
|
|
34
|
-
def initialize
|
41
|
+
def initialize(name = nil, options = {})
|
35
42
|
super
|
36
43
|
options.each { |k,v| send :"#{k}=", v }
|
37
44
|
end
|
38
45
|
|
39
46
|
##
|
40
|
-
#
|
47
|
+
# Quoted github issue name and number.
|
41
48
|
|
42
|
-
def
|
43
|
-
|
49
|
+
def to_s
|
50
|
+
"#%-3s %s" % [ number, name]
|
44
51
|
end
|
45
52
|
|
46
53
|
##
|
47
|
-
#
|
54
|
+
# Output verbose task information.
|
48
55
|
|
49
|
-
def
|
50
|
-
|
56
|
+
def verbose_output(format)
|
57
|
+
super(format)
|
58
|
+
say format % ['labels', labels.join(', ')] if labels and not labels.empty?
|
59
|
+
say format % ['project', [username, project].join('/')]
|
60
|
+
say format % ['url', url ]
|
51
61
|
end
|
52
62
|
|
63
|
+
##
|
64
|
+
# Import Github issue(s) with _user_, _project_, _number_ as GithubTask(s).
|
65
|
+
|
66
|
+
def self.import(user, project, number)
|
67
|
+
tasks = []
|
68
|
+
if number
|
69
|
+
issues = [Octokit.issue({:username => user, :repo => project}, number)]
|
70
|
+
else
|
71
|
+
issues = Octokit.list_issues({:username => user, :repo => project}, :state => 'open', :sort => 'created')
|
72
|
+
end
|
73
|
+
|
74
|
+
issues.each do |issue|
|
75
|
+
tasks << new(issue.title,
|
76
|
+
:username => user,
|
77
|
+
:project => project,
|
78
|
+
:description => issue.body,
|
79
|
+
:labels => issue.labels.map(&:name),
|
80
|
+
:number => issue.number,
|
81
|
+
:url => issue.html_url
|
82
|
+
)
|
83
|
+
end
|
84
|
+
return tasks
|
85
|
+
rescue Octokit::NotFound => e
|
86
|
+
say "\n"
|
87
|
+
say_error '404: This is not the repo you are looking for.'
|
88
|
+
say_error e.message
|
89
|
+
abort
|
90
|
+
end
|
53
91
|
end
|
54
92
|
end
|
data/lib/pomo/list.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'yaml'
|
1
2
|
|
2
3
|
module Pomo
|
3
4
|
class List
|
@@ -15,11 +16,11 @@ module Pomo
|
|
15
16
|
##
|
16
17
|
# Initialize with _path_.
|
17
18
|
|
18
|
-
def initialize
|
19
|
+
def initialize(opts = {})
|
19
20
|
if opts[:init]
|
20
21
|
path = '.pomo'
|
21
22
|
else
|
22
|
-
path = File.exists?('.pomo') ? '.pomo' : File.join(ENV['HOME'],'.pomo')
|
23
|
+
path = (File.exists?('.pomo') && ENV['POMO_ENV']!='test') ? '.pomo' : File.join(ENV['HOME'],'.pomo')
|
23
24
|
end
|
24
25
|
@path = File.expand_path path
|
25
26
|
@tasks = []
|
@@ -35,35 +36,51 @@ module Pomo
|
|
35
36
|
# * n n n
|
36
37
|
# * n..n
|
37
38
|
# * n..-n
|
39
|
+
# * n..n n..n n..n
|
38
40
|
# * first
|
39
41
|
# * last
|
42
|
+
# * first last
|
40
43
|
# * incomplete
|
41
44
|
# * complete[d]
|
42
45
|
# * all
|
43
46
|
#
|
44
47
|
|
45
|
-
def find
|
48
|
+
def find(*args, &block)
|
46
49
|
found = []
|
47
50
|
found << tasks.first if args.empty?
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
51
|
+
|
52
|
+
args.each do |arg|
|
53
|
+
case arg
|
54
|
+
when 'all'
|
55
|
+
found = tasks
|
56
|
+
break
|
57
|
+
when 'first'
|
58
|
+
found << tasks.first
|
59
|
+
when 'last'
|
60
|
+
found << tasks.last
|
61
|
+
when 'complete'
|
62
|
+
found.concat tasks.select { |task| task.complete? }
|
63
|
+
when 'completed'
|
64
|
+
found.concat tasks.select { |task| task.complete? }
|
65
|
+
when 'incomplete'
|
66
|
+
found.concat tasks.select { |task| not task.complete? }
|
67
|
+
when /^(\d+)$/
|
68
|
+
found << tasks[$1.to_i]
|
69
|
+
when /^(\d+)\.\.(-?\d+)$/
|
70
|
+
found.concat tasks[$1.to_i..$2.to_i]
|
63
71
|
end
|
64
72
|
end
|
65
|
-
|
66
|
-
|
73
|
+
|
74
|
+
found.compact!
|
75
|
+
|
76
|
+
if block.arity == 2
|
77
|
+
found.each_with_index do |task, i|
|
78
|
+
yield task, i
|
79
|
+
end
|
80
|
+
else
|
81
|
+
found.each do |task|
|
82
|
+
yield task
|
83
|
+
end
|
67
84
|
end
|
68
85
|
end
|
69
86
|
|
@@ -77,11 +94,18 @@ module Pomo
|
|
77
94
|
##
|
78
95
|
# Add _task_ to the list in memory (requires saving).
|
79
96
|
|
80
|
-
def add
|
97
|
+
def add(task)
|
81
98
|
@tasks << task
|
82
99
|
end
|
83
100
|
alias :<< :add
|
84
101
|
|
102
|
+
##
|
103
|
+
# Move task at _from_ index to _to_ index (requres saving).
|
104
|
+
|
105
|
+
def move(from, to)
|
106
|
+
@tasks.insert(index(to), @tasks.delete_at(index(from)))
|
107
|
+
end
|
108
|
+
|
85
109
|
##
|
86
110
|
# Save the list.
|
87
111
|
|
@@ -100,5 +124,19 @@ module Pomo
|
|
100
124
|
self
|
101
125
|
end
|
102
126
|
|
127
|
+
private
|
128
|
+
|
129
|
+
def index(arg)
|
130
|
+
case arg
|
131
|
+
when 'first'
|
132
|
+
0
|
133
|
+
when 'last'
|
134
|
+
@tasks.size - 1
|
135
|
+
when /^(\d+)$/
|
136
|
+
$1.to_i
|
137
|
+
else
|
138
|
+
abort "Invalid argument: #{arg}. See 'pomo help move'"
|
139
|
+
end
|
140
|
+
end
|
103
141
|
end
|
104
142
|
end
|