pomo 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ """
@@ -0,0 +1,8 @@
1
+ Given /^the following tasks:$/ do |table|
2
+ data = table.raw
3
+ table.raw.each do |task|
4
+ steps %Q{
5
+ When I run `pomo add '#{task.first}'`
6
+ }
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ require "aruba/cucumber"
2
+
3
+ Before do
4
+ set_env('POMO_ENV', 'test')
5
+ set_env('HOME', '/tmp/home')
6
+ FileUtils.rm_rf '/tmp/home'
7
+ FileUtils.mkdir '/tmp/home'
8
+ end
9
+
10
+ After do
11
+ restore_env
12
+ end
@@ -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'
@@ -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
- config_file = File.join(ENV['HOME'],'.pomorc')
31
+ def initialize(options = {})
32
+ @notifier = options[:notifier]
33
+ @progress = options[:progress]
34
+ @tmux = options[:tmux]
35
+ end
26
36
 
27
- if File.exists? config_file
28
- config_options = YAML.load_file(config_file)
29
- options.merge!(config_options)
30
- else
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
- STDERR.puts "Initialized configuration file in #{config_file}"
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
- @notifier = options[:notifier]
36
- @tmux = options[:tmux]
69
+ # Helpers
70
+
71
+ def self.config_file
72
+ File.join(ENV['HOME'],'.pomorc')
37
73
  end
38
74
 
39
- private
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?
@@ -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 name = nil, options = {}
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
- # Check if the task is a github issue.
47
+ # Quoted github issue name and number.
41
48
 
42
- def github?
43
- true
49
+ def to_s
50
+ "#%-3s %s" % [ number, name]
44
51
  end
45
52
 
46
53
  ##
47
- # Absolute URI to github issue.
54
+ # Output verbose task information.
48
55
 
49
- def uri
50
- "https://github.com/#{username}/#{project}/issues#issue/#{number}"
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
@@ -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 opts = {}
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 *args, &block
48
+ def find(*args, &block)
46
49
  found = []
47
50
  found << tasks.first if args.empty?
48
- if args.include? 'all'
49
- found = tasks
50
- elsif args.include? 'first'
51
- found << tasks.first
52
- elsif args.include? 'last'
53
- found << tasks.last
54
- elsif args.include?('complete') || args.include?('completed')
55
- found = tasks.select { |task| task.complete? }
56
- elsif args.include? 'incomplete'
57
- found = tasks.select { |task| not task.complete? }
58
- elsif args.any? { |arg| arg =~ /(\d+)\.\.(-?\d+)/ }
59
- found = tasks[$1.to_i..$2.to_i]
60
- else
61
- tasks.each_with_index do |task, i|
62
- found << task if args.any? { |a| a.to_i == i }
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
- found.each_with_index do |task, i|
66
- yield task, i
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 task
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