paradeiser 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +8 -0
- data/Gemfile +6 -0
- data/Guardfile +17 -0
- data/README.md +76 -187
- data/Rakefile +12 -1
- data/TODO.md +62 -0
- data/VISION.md +474 -0
- data/bin/pom +60 -0
- data/doc/Paradeiser::Pomodoro_status.svg +50 -0
- data/lib/paradeiser.rb +25 -2
- data/lib/paradeiser/controllers/controller.rb +30 -0
- data/lib/paradeiser/controllers/paradeiser_controller.rb +10 -0
- data/lib/paradeiser/controllers/pomodori_controller.rb +38 -0
- data/lib/paradeiser/errors.rb +31 -0
- data/lib/paradeiser/executor.rb +15 -0
- data/lib/paradeiser/models/hook.rb +26 -0
- data/lib/paradeiser/models/job.rb +25 -0
- data/lib/paradeiser/models/pomodoro.rb +58 -0
- data/lib/paradeiser/models/repository.rb +57 -0
- data/lib/paradeiser/models/scheduler.rb +43 -0
- data/lib/paradeiser/refinements.rb +5 -0
- data/lib/paradeiser/router.rb +29 -0
- data/lib/paradeiser/version.rb +1 -1
- data/lib/paradeiser/view.rb +21 -0
- data/lib/paradeiser/views/paradeiser/init.erb +1 -0
- data/lib/paradeiser/views/pomodori/finish.erb +1 -0
- data/lib/paradeiser/views/pomodori/report.erb +5 -0
- data/lib/paradeiser/views/pomodori/start.erb +1 -0
- data/lib/paradeiser/views/pomodori/status.erb +9 -0
- data/paradeiser.gemspec +21 -4
- data/templates/linux/hooks/after-finish +10 -0
- data/templates/linux/hooks/after-start +7 -0
- data/templates/mac/hooks/after-finish +10 -0
- data/templates/mac/hooks/after-start +7 -0
- data/test/helper.rb +24 -0
- data/test/integration/test_pom.rb +17 -0
- data/test/lib/assertions.rb +10 -0
- data/test/lib/at_mock.rb +6 -0
- data/test/lib/options_mock.rb +7 -0
- data/test/lib/pomodoro_mock.rb +11 -0
- data/test/lib/process_status_mock.rb +2 -0
- data/test/lib/pstore_mock.rb +16 -0
- data/test/templates/hooks/pre-finish +1 -0
- data/test/unit/test_paradeiser_controller.rb +88 -0
- data/test/unit/test_pomodori_controller.rb +103 -0
- data/test/unit/test_pomodori_view.rb +78 -0
- data/test/unit/test_pomodoro.rb +100 -0
- data/test/unit/test_pomodoro_hooks.rb +83 -0
- data/test/unit/test_repository.rb +127 -0
- data/test/unit/test_router.rb +36 -0
- data/test/unit/test_scheduler.rb +44 -0
- metadata +244 -13
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Paradeiser
|
4
|
+
class Scheduler
|
5
|
+
class << self
|
6
|
+
include Executor
|
7
|
+
|
8
|
+
def list
|
9
|
+
out, _ = exec("#{at} -l -q #{queue}")
|
10
|
+
|
11
|
+
out.lines.map do |line|
|
12
|
+
id = parse_list(line)
|
13
|
+
Job.new(id)
|
14
|
+
end.select do |job|
|
15
|
+
job.ours?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(command, minutes)
|
20
|
+
_, err = exec("echo pom #{command} | #{at} -q #{queue} now + #{minutes} minutes")
|
21
|
+
id = parse_add(err.chomp)
|
22
|
+
Job.new(id)
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear
|
26
|
+
if list.any? # On Linux, at must not be called with an empty job list.
|
27
|
+
job_ids = list.map{|j| j.id}.join(' ')
|
28
|
+
exec("#{at} -q #{queue} -r #{job_ids}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def parse_list(line)
|
35
|
+
line[/^(\d+)/]
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_add(line)
|
39
|
+
line.match(/^job (?<job>\d+)/)[:job]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Paradeiser
|
2
|
+
class Router
|
3
|
+
attr_reader :status
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@status = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def dispatch(command)
|
10
|
+
Proc.new do |args, options|
|
11
|
+
method = command.name
|
12
|
+
|
13
|
+
# TODO Dynamically find the controller that handles the method. :pomodoro is the default.
|
14
|
+
if (:init == method.to_sym)
|
15
|
+
controller_class = ParadeiserController
|
16
|
+
else
|
17
|
+
controller_class = PomodoriController
|
18
|
+
end
|
19
|
+
|
20
|
+
controller = controller_class.new(method)
|
21
|
+
controller.call(args, options)
|
22
|
+
|
23
|
+
View.new(controller.model, method).render(controller.get_binding) if options.verbose || controller.has_output
|
24
|
+
|
25
|
+
@status = controller.exitstatus
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/paradeiser/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Paradeiser
|
2
|
+
class View
|
3
|
+
def initialize(model, method)
|
4
|
+
@model, @method = model, method
|
5
|
+
end
|
6
|
+
|
7
|
+
def render(controller_binding)
|
8
|
+
puts(template.result(controller_binding))
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def template
|
14
|
+
ERB.new(File.read(template_file), 0, '%<>')
|
15
|
+
end
|
16
|
+
|
17
|
+
def template_file
|
18
|
+
File.join(File.dirname(__FILE__), 'views', @model.downcase, "#{@method}.erb")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Suffessfully initialized <%= Paradeiser.pom_dir %>.
|
@@ -0,0 +1 @@
|
|
1
|
+
Finished pomodoro #<%= @pom.id %> after <%= @pom.duration.minutes %> minutes.
|
@@ -0,0 +1 @@
|
|
1
|
+
Starting pomodoro #<%= @pom.id %>.
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<% case @pom.status_name
|
2
|
+
when :active
|
3
|
+
%>Pomodoro #<%= @pom.id %> is active for another <%= @pom.remaining.minutes %> minutes (started at <%= @pom.started_at.strftime('%R') %>).<%
|
4
|
+
when :finished
|
5
|
+
%>No active pomodoro. Last one was finished at <%= @pom.finished_at.strftime('%R') %>.<%
|
6
|
+
else
|
7
|
+
%>Current state is <%= @pom.status %>.<%
|
8
|
+
end
|
9
|
+
%>
|
data/paradeiser.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Paradeiser::VERSION
|
9
9
|
spec.authors = ["Nicholas E. Rabenau"]
|
10
10
|
spec.email = ["nerab@gmx.net"]
|
11
|
-
spec.description = %q{Paradeiser is a tool for the
|
12
|
-
spec.summary = %q{
|
11
|
+
spec.description = %q{Paradeiser is a command-line tool for the Pomodoro Technique. It keeps track of the current pomodoro and assists the user in managing active and past pomodori as well as breaks. Status commands and reports are provided to get insights.}
|
12
|
+
spec.summary = %q{Command-line tool for the Pomodoro Technique}
|
13
13
|
spec.homepage = "https://github.com/nerab/paradeiser"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -18,6 +18,23 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.
|
22
|
-
spec.
|
21
|
+
spec.add_runtime_dependency 'commander'
|
22
|
+
spec.add_runtime_dependency 'require_all'
|
23
|
+
spec.add_runtime_dependency 'state_machine'
|
24
|
+
|
25
|
+
# Moved to the Gemfile so that Travis CI can load the test group
|
26
|
+
# spec.add_development_dependency 'rake'
|
27
|
+
# spec.add_development_dependency 'minitest'
|
28
|
+
|
29
|
+
spec.add_development_dependency 'guard-minitest'
|
30
|
+
spec.add_development_dependency 'guard-bundler'
|
31
|
+
spec.add_development_dependency 'terminal-notifier-guard'
|
32
|
+
spec.add_development_dependency 'libnotify'
|
33
|
+
spec.add_development_dependency 'rb-inotify'
|
34
|
+
spec.add_development_dependency 'rb-fsevent'
|
35
|
+
spec.add_development_dependency 'pry'
|
36
|
+
spec.add_development_dependency 'pry-nav'
|
37
|
+
spec.add_development_dependency 'pry-stack_explorer'
|
38
|
+
spec.add_development_dependency 'bundler'
|
39
|
+
spec.add_development_dependency 'ruby-graphviz'
|
23
40
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
#
|
4
|
+
# Sample post-finish hook for Paradeiser (called when a pomodoro ended). It
|
5
|
+
# displays a simple notification.
|
6
|
+
#
|
7
|
+
# $POM_ID - the ID of the pomodoro that just ended
|
8
|
+
# $POM_STARTED_AT - the time when the pomodoro was started
|
9
|
+
#
|
10
|
+
notify-send "Pomodoro" "The Pomodoro $POM_ID (started at $POM_STARTED_AT) is over." -u critical > /dev/null
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
#
|
4
|
+
# Sample post-finish hook for Paradeiser (called when a pomodoro ended). It
|
5
|
+
# displays a simple notification.
|
6
|
+
#
|
7
|
+
# $POM_ID - the ID of the pomodoro that just ended
|
8
|
+
# $POM_STARTED_AT - the time when the pomodoro was started
|
9
|
+
#
|
10
|
+
terminal-notifier-success -message "The Pomodoro $POM_ID (started at $POM_STARTED_AT) is over." > /dev/null
|
data/test/helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'paradeiser'
|
3
|
+
require_rel 'lib'
|
4
|
+
|
5
|
+
require 'lib/at_mock'
|
6
|
+
|
7
|
+
include Paradeiser
|
8
|
+
|
9
|
+
class MiniTest::Test
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def start!(pom = @pom)
|
14
|
+
Scheduler.stub(:add, nil) do
|
15
|
+
pom.start!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def finish!(pom = @pom)
|
20
|
+
Scheduler.stub(:clear, nil) do
|
21
|
+
pom.finish!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestPom < MiniTest::Test
|
4
|
+
POM_BIN = 'pom'
|
5
|
+
|
6
|
+
def test_no_args_defaults_to_help
|
7
|
+
out, err, status = Open3.capture3(POM_BIN)
|
8
|
+
assert_equal(0, status.exitstatus, "Expected exit status to be 0, but it was #{status.exitstatus}. STDERR contains: #{err}")
|
9
|
+
assert_empty(err)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_unknown
|
13
|
+
out, err, status = Open3.capture3("#{POM_BIN} unknown")
|
14
|
+
refute_equal(0, status.exitstatus)
|
15
|
+
refute_empty(err)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# from Gem::TestCase rubygems/test_case.rb
|
2
|
+
def assert_path_exists(path, msg = nil)
|
3
|
+
msg = message(msg){"Expected path '#{path}' to exist"}
|
4
|
+
assert(File.exist?(path), msg)
|
5
|
+
end
|
6
|
+
|
7
|
+
def refute_path_exists(path, msg = nil)
|
8
|
+
msg = message(msg){"Expected path '#{path}' to NOT exist"}
|
9
|
+
refute(File.exist?(path), msg)
|
10
|
+
end
|
data/test/lib/at_mock.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
echo pre-finish-hook
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'fakefs/safe'
|
3
|
+
|
4
|
+
class TestParadeiserController < MiniTest::Test
|
5
|
+
HOOKS = ['after-finish', 'before-finish']
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@orig_pom_dir = ENV['POM_DIR']
|
9
|
+
FakeFS.activate!
|
10
|
+
create_hook_templates
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
FileUtils.rm_r(Paradeiser.pom_dir, :force => true) if Dir.exists?(Paradeiser.pom_dir)
|
15
|
+
FakeFS.deactivate!
|
16
|
+
ENV['POM_DIR'] = @orig_pom_dir
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_init_virgin
|
20
|
+
ENV.delete('POM_DIR')
|
21
|
+
refute(Dir.exists?(Paradeiser.pom_dir), "Expect #{Paradeiser.pom_dir} to not exist yet")
|
22
|
+
|
23
|
+
ParadeiserController.new(:init).call(nil, nil)
|
24
|
+
assert(Dir.exists?(Paradeiser.pom_dir))
|
25
|
+
assert_hooks_exist
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_init_existing
|
29
|
+
FileUtils.mkdir_p(Paradeiser.pom_dir, 0700)
|
30
|
+
assert(Dir.exists?(Paradeiser.pom_dir))
|
31
|
+
|
32
|
+
ParadeiserController.new(:init).call(nil, nil)
|
33
|
+
assert(Dir.exists?(Paradeiser.pom_dir))
|
34
|
+
assert_hooks_exist
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_init_virgin_with_env_override
|
38
|
+
dir = tempdir_name
|
39
|
+
refute_equal(dir, Paradeiser.pom_dir)
|
40
|
+
ENV['POM_DIR'] = dir
|
41
|
+
assert_equal(dir, Paradeiser.pom_dir)
|
42
|
+
refute(Dir.exists?(Paradeiser.pom_dir), "POM_DIR override #{Paradeiser.pom_dir} must not exist")
|
43
|
+
|
44
|
+
ParadeiserController.new(:init).call(nil, nil)
|
45
|
+
|
46
|
+
assert(Dir.exists?(Paradeiser.pom_dir))
|
47
|
+
assert_hooks_exist
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_init_existing_with_env_override
|
51
|
+
ENV.delete('POM_DIR')
|
52
|
+
|
53
|
+
Dir.mktmpdir do |dir|
|
54
|
+
refute_equal(dir, Paradeiser.pom_dir)
|
55
|
+
ENV['POM_DIR'] = dir
|
56
|
+
assert_equal(dir, Paradeiser.pom_dir)
|
57
|
+
assert(Dir.exists?(Paradeiser.pom_dir), "POM_DIR override #{Paradeiser.pom_dir} must exist")
|
58
|
+
|
59
|
+
ParadeiserController.new(:init).call(nil, nil)
|
60
|
+
assert(Dir.exists?(Paradeiser.pom_dir), "POM_DIR override #{Paradeiser.pom_dir} must exist")
|
61
|
+
assert_hooks_exist
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Makes a temporary directory name, but does not create the directory
|
68
|
+
def tempdir_name
|
69
|
+
dir = File.join(Dir.tmpdir, SecureRandom.uuid)
|
70
|
+
refute(Dir.exists?(dir))
|
71
|
+
dir
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_hook_templates
|
75
|
+
hook_templates_dir = File.join(Paradeiser.templates_dir, Paradeiser.os.to_s, 'hooks')
|
76
|
+
FileUtils.mkdir_p(hook_templates_dir)
|
77
|
+
|
78
|
+
HOOKS.each do |hook|
|
79
|
+
FileUtils.touch(File.join(hook_templates_dir, hook))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def assert_hooks_exist
|
84
|
+
HOOKS.each do |hook|
|
85
|
+
assert(File.exist?(File.join(Paradeiser.hooks_dir, hook)))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestPomodoriController < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
@backend = PStoreMock.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_start
|
9
|
+
pom, has_output = invoke(:start, '@pom', 'has_output')
|
10
|
+
assert_equal(:active, pom.status_name)
|
11
|
+
assert_equal(false, has_output)
|
12
|
+
assert_equal(1, @backend.size)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_start_active
|
16
|
+
invoke(:start)
|
17
|
+
assert_equal(1, @backend.size)
|
18
|
+
|
19
|
+
assert_raises SingletonError do
|
20
|
+
invoke(:start)
|
21
|
+
end
|
22
|
+
assert_equal(1, @backend.size)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_finish
|
26
|
+
invoke(:start)
|
27
|
+
pom, has_output = invoke(:finish, '@pom', 'has_output')
|
28
|
+
assert_equal(:finished, pom.status_name)
|
29
|
+
assert_equal(false, has_output)
|
30
|
+
assert_equal(1, @backend.size)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_finish_idle
|
34
|
+
assert_raises NoActivePomodoroError do
|
35
|
+
invoke(:finish)
|
36
|
+
end
|
37
|
+
assert_equal(0, @backend.size)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_report_idle
|
41
|
+
pomodori, has_output = invoke(:report, '@pom', 'has_output')
|
42
|
+
assert_empty(pomodori)
|
43
|
+
assert_equal(true, has_output)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_report_active
|
47
|
+
invoke(:start)
|
48
|
+
pomodori, has_output = invoke(:report, '@pom', 'has_output')
|
49
|
+
assert_equal(1, pomodori.size)
|
50
|
+
assert_equal(true, has_output)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_report_finished
|
54
|
+
invoke(:start)
|
55
|
+
invoke(:finish)
|
56
|
+
invoke(:start)
|
57
|
+
pomodori, has_output = invoke(:report, '@pom', 'has_output')
|
58
|
+
assert_equal(2, pomodori.size)
|
59
|
+
assert_equal(true, has_output)
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_status_idle
|
63
|
+
pom, has_output = invoke(:status, '@pom', 'has_output')
|
64
|
+
assert_equal(:idle, pom.status_name)
|
65
|
+
assert_equal(true, has_output)
|
66
|
+
assert_equal(0, @backend.size)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_status_active
|
70
|
+
invoke(:start)
|
71
|
+
pom, has_output = invoke(:status, '@pom', 'has_output')
|
72
|
+
assert_equal(:active, pom.status_name)
|
73
|
+
assert_equal(true, has_output)
|
74
|
+
assert_equal(1, @backend.size)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_status_finished
|
78
|
+
invoke(:start)
|
79
|
+
invoke(:finish)
|
80
|
+
pom, has_output = invoke(:status, '@pom', 'has_output')
|
81
|
+
assert_equal(:finished, pom.status_name)
|
82
|
+
assert_equal(true, has_output)
|
83
|
+
assert_equal(1, @backend.size)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def invoke(method, *attributes)
|
89
|
+
controller = PomodoriController.new(method)
|
90
|
+
|
91
|
+
Repository.stub :backend, @backend do
|
92
|
+
Scheduler.stub(:add, nil) do
|
93
|
+
Scheduler.stub(:clear, nil) do
|
94
|
+
controller.call(nil, nil)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
attributes.map do |attribute|
|
100
|
+
controller.get_binding.eval(attribute)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|