pomo 2.0.2 → 2.1.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,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