paradeiser 0.1.0 → 0.2.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/README.md +96 -35
  4. data/TODO.md +14 -15
  5. data/VISION.md +92 -84
  6. data/bin/{pom → par} +32 -2
  7. data/doc/Paradeiser::Break_status.svg +50 -0
  8. data/doc/Paradeiser::Pomodoro_status.svg +40 -22
  9. data/lib/paradeiser.rb +3 -3
  10. data/lib/paradeiser/controllers/breaks_controller.rb +19 -0
  11. data/lib/paradeiser/controllers/controller.rb +3 -3
  12. data/lib/paradeiser/controllers/paradeiser_controller.rb +13 -2
  13. data/lib/paradeiser/controllers/pomodori_controller.rb +36 -17
  14. data/lib/paradeiser/errors.rb +12 -6
  15. data/lib/paradeiser/executor.rb +2 -0
  16. data/lib/paradeiser/initializers/inflections.rb +4 -0
  17. data/lib/paradeiser/models/break.rb +40 -0
  18. data/lib/paradeiser/models/hook.rb +4 -4
  19. data/lib/paradeiser/models/interrupt.rb +18 -0
  20. data/lib/paradeiser/models/job.rb +1 -1
  21. data/lib/paradeiser/models/pomodoro.rb +30 -22
  22. data/lib/paradeiser/models/repository.rb +26 -13
  23. data/lib/paradeiser/models/scheduled.rb +25 -0
  24. data/lib/paradeiser/models/scheduler.rb +1 -1
  25. data/lib/paradeiser/models/status.rb +21 -0
  26. data/lib/paradeiser/{refinements.rb → refinements/numeric.rb} +4 -0
  27. data/lib/paradeiser/router.rb +12 -7
  28. data/lib/paradeiser/version.rb +1 -1
  29. data/lib/paradeiser/views/paradeiser/init.erb +1 -1
  30. data/lib/paradeiser/views/paradeiser/report.erb +5 -0
  31. data/lib/paradeiser/views/paradeiser/status.erb +13 -0
  32. data/paradeiser.gemspec +2 -0
  33. data/templates/linux/hooks/after-finish-break +10 -0
  34. data/templates/linux/hooks/after-finish-pomodoro +10 -0
  35. data/templates/linux/hooks/after-start-break +7 -0
  36. data/templates/linux/hooks/after-start-pomodoro +7 -0
  37. data/templates/mac/hooks/after-finish-break +10 -0
  38. data/templates/mac/hooks/after-finish-pomodoro +10 -0
  39. data/templates/mac/hooks/after-start-break +7 -0
  40. data/templates/mac/hooks/after-start-pomodoro +7 -0
  41. data/test/helper.rb +37 -4
  42. data/test/integration/{test_pom.rb → test_par.rb} +4 -4
  43. data/test/lib/{pomodoro_mock.rb → schedulable_mock.rb} +9 -1
  44. data/test/unit/test_break.rb +99 -0
  45. data/test/unit/test_break_controller.rb +56 -0
  46. data/test/unit/test_interrupt.rb +36 -0
  47. data/test/unit/test_paradeiser_controller_init.rb +92 -0
  48. data/test/unit/test_paradeiser_controller_report.rb +44 -0
  49. data/test/unit/test_paradeiser_controller_status.rb +70 -0
  50. data/test/unit/test_paradeiser_view.rb +66 -0
  51. data/test/unit/test_pomodori_controller.rb +87 -31
  52. data/test/unit/test_pomodori_view.rb +0 -50
  53. data/test/unit/test_pomodoro.rb +131 -9
  54. data/test/unit/test_pomodoro_hooks.rb +165 -17
  55. data/test/unit/test_repository.rb +38 -15
  56. data/test/unit/test_router.rb +4 -4
  57. data/test/unit/test_status.rb +26 -0
  58. metadata +70 -17
  59. data/lib/paradeiser/views/pomodori/report.erb +0 -5
  60. data/lib/paradeiser/views/pomodori/status.erb +0 -9
  61. data/templates/linux/hooks/after-finish +0 -10
  62. data/templates/linux/hooks/after-start +0 -7
  63. data/templates/mac/hooks/after-finish +0 -10
  64. data/templates/mac/hooks/after-start +0 -7
  65. data/test/unit/test_paradeiser_controller.rb +0 -88
@@ -1,16 +1,16 @@
1
1
  require 'helper'
2
2
 
3
- class TestPom < MiniTest::Test
4
- POM_BIN = 'pom'
3
+ class TestPar < MiniTest::Test
4
+ PAR = 'bin/par'
5
5
 
6
6
  def test_no_args_defaults_to_help
7
- out, err, status = Open3.capture3(POM_BIN)
7
+ out, err, status = Open3.capture3(PAR)
8
8
  assert_equal(0, status.exitstatus, "Expected exit status to be 0, but it was #{status.exitstatus}. STDERR contains: #{err}")
9
9
  assert_empty(err)
10
10
  end
11
11
 
12
12
  def test_unknown
13
- out, err, status = Open3.capture3("#{POM_BIN} unknown")
13
+ out, err, status = Open3.capture3("#{PAR} unknown")
14
14
  refute_equal(0, status.exitstatus)
15
15
  refute_empty(err)
16
16
  end
@@ -1,6 +1,6 @@
1
1
  require 'ostruct'
2
2
 
3
- class PomodoroMock < OpenStruct
3
+ class SchedulableMock < OpenStruct
4
4
  def initialize(attributes)
5
5
  super(attributes)
6
6
  end
@@ -8,4 +8,12 @@ class PomodoroMock < OpenStruct
8
8
  def active?
9
9
  !!active
10
10
  end
11
+
12
+ def finished?
13
+ !!finished
14
+ end
15
+
16
+ def canceled?
17
+ !!canceled
18
+ end
11
19
  end
@@ -0,0 +1,99 @@
1
+ require 'helper'
2
+
3
+ class TestBreak < MiniTest::Test
4
+ def setup
5
+ @break = Break.new
6
+ end
7
+
8
+ def test_virgin
9
+ assert_equal(:idle, @break.status_name)
10
+ end
11
+
12
+ def test_break
13
+ start!
14
+ assert_equal(:active, @break.status_name)
15
+ end
16
+
17
+ def test_finish_idle
18
+ assert_raises StateMachine::InvalidTransition do
19
+ finish!
20
+ end
21
+ end
22
+
23
+ def test_finish_break
24
+ start!
25
+ assert_equal(:active, @break.status_name)
26
+
27
+ now = srand
28
+
29
+ Time.stub :now, Time.at(now) do
30
+ finish!
31
+ end
32
+
33
+ assert_equal(:finished, @break.status_name)
34
+ assert_equal(now, @break.finished_at.to_i)
35
+ end
36
+
37
+ def test_length
38
+ assert_equal(5 * 60, @break.length)
39
+ end
40
+
41
+
42
+ def test_duration_idle
43
+ assert_equal(0, @break.duration)
44
+ end
45
+
46
+ def test_duration
47
+ now = srand
48
+
49
+ Time.stub :now, Time.at(now) do
50
+ start!
51
+ end
52
+
53
+ later = now + rand(42)
54
+
55
+ Time.stub :now, Time.at(later) do
56
+ assert_equal(later - now, @break.duration)
57
+ end
58
+ end
59
+
60
+ def test_duration_finished
61
+ now = srand
62
+
63
+ Time.stub :now, Time.at(now) do
64
+ start!
65
+ end
66
+
67
+ later = now + rand(42)
68
+
69
+ Time.stub :now, Time.at(later) do
70
+ finish!
71
+ end
72
+
73
+ assert_equal(later - now, @break.duration)
74
+ end
75
+
76
+ def test_finish_break
77
+ start!
78
+ finish!
79
+ assert_raises StateMachine::InvalidTransition do
80
+ finish!
81
+ end
82
+ end
83
+
84
+ def test_remaining
85
+ now = srand
86
+
87
+ Time.stub :now, Time.at(now) do
88
+ start!
89
+ assert_equal(@break.length, @break.remaining)
90
+ end
91
+
92
+ delta = 120
93
+ later = now + delta
94
+
95
+ Time.stub :now, Time.at(later) do
96
+ assert_equal(@break.length - delta, @break.remaining)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,56 @@
1
+ require 'helper'
2
+
3
+ class TestBreakController < MiniTest::Test
4
+ def setup
5
+ @backend = PStoreMock.new
6
+ end
7
+
8
+ def test_break
9
+ br3k, has_output = invoke(:start, '@break', 'has_output')
10
+ assert_equal(:active, br3k.status_name)
11
+ assert_equal(false, has_output)
12
+ assert_equal(1, @backend.size)
13
+ end
14
+
15
+ def test_break_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
+ br3ak, has_output = invoke(:finish, '@break', 'has_output')
28
+ assert_equal(:finished, br3ak.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 NotActiveError do
35
+ invoke(:finish)
36
+ end
37
+ assert_equal(0, @backend.size)
38
+ end
39
+ private
40
+
41
+ def invoke(method, *attributes)
42
+ controller = BreaksController.new(method)
43
+
44
+ Repository.stub :backend, @backend do
45
+ Scheduler.stub(:add, nil) do
46
+ Scheduler.stub(:clear, nil) do
47
+ controller.call(nil, nil)
48
+ end
49
+ end
50
+ end
51
+
52
+ attributes.map do |attribute|
53
+ controller.get_binding.eval(attribute)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,36 @@
1
+ require 'helper'
2
+
3
+ class TestInterrupt < MiniTest::Test
4
+ def setup
5
+ @now = srand
6
+ end
7
+
8
+ def test_created_at
9
+ int = new_interrupt
10
+ assert_equal(@now, int.created_at.to_i)
11
+ end
12
+
13
+ def test_defaults_to_internal
14
+ int = new_interrupt
15
+ assert_equal(:internal, int.type)
16
+ end
17
+
18
+ def test_external
19
+ int = new_interrupt(:external)
20
+ assert_equal(:external, int.type)
21
+ end
22
+
23
+ def test_unknown
24
+ assert_raises InvalidTypeError do
25
+ new_interrupt(:unknown)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def new_interrupt(type = nil)
32
+ Time.stub :now, Time.at(@now) do
33
+ Paradeiser::Interrupt.new(type)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,92 @@
1
+ require 'helper'
2
+ require 'fakefs/safe'
3
+
4
+ class TestParadeiserControllerInit < MiniTest::Test
5
+ HOOKS = ['after-finish', 'before-finish']
6
+
7
+ def setup
8
+ @orig_PAR_DIR = ENV['PAR_DIR']
9
+ FakeFS.activate!
10
+ create_hook_templates
11
+ end
12
+
13
+ def teardown
14
+ FileUtils.rm_r(Paradeiser.par_dir, :force => true) if Dir.exists?(Paradeiser.par_dir)
15
+ FakeFS.deactivate!
16
+ ENV['PAR_DIR'] = @orig_PAR_DIR
17
+ end
18
+
19
+ def test_init_virgin
20
+ ENV.delete('PAR_DIR')
21
+ refute(Dir.exists?(Paradeiser.par_dir), "Expect #{Paradeiser.par_dir} to not exist yet")
22
+
23
+ invoke(:init)
24
+ assert(Dir.exists?(Paradeiser.par_dir))
25
+ assert_hooks_exist
26
+ end
27
+
28
+ def test_init_existing
29
+ FileUtils.mkdir_p(Paradeiser.par_dir, 0700)
30
+ assert(Dir.exists?(Paradeiser.par_dir))
31
+
32
+ invoke(:init)
33
+ assert(Dir.exists?(Paradeiser.par_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.par_dir)
40
+ ENV['PAR_DIR'] = dir
41
+ assert_equal(dir, Paradeiser.par_dir)
42
+ refute(Dir.exists?(Paradeiser.par_dir), "PAR_DIR override #{Paradeiser.par_dir} must not exist")
43
+
44
+ invoke(:init)
45
+
46
+ assert(Dir.exists?(Paradeiser.par_dir))
47
+ assert_hooks_exist
48
+ end
49
+
50
+ def test_init_existing_with_env_override
51
+ ENV.delete('PAR_DIR')
52
+
53
+ Dir.mktmpdir do |dir|
54
+ refute_equal(dir, Paradeiser.par_dir)
55
+ ENV['PAR_DIR'] = dir
56
+ assert_equal(dir, Paradeiser.par_dir)
57
+ assert(Dir.exists?(Paradeiser.par_dir), "PAR_DIR override #{Paradeiser.par_dir} must exist")
58
+
59
+ invoke(:init)
60
+ assert(Dir.exists?(Paradeiser.par_dir), "PAR_DIR override #{Paradeiser.par_dir} must exist")
61
+ assert_hooks_exist
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def invoke(verb)
68
+ ParadeiserController.new(verb).call(nil, nil)
69
+ end
70
+
71
+ # Makes a temporary directory name, but does not create the directory
72
+ def tempdir_name
73
+ dir = File.join(Dir.tmpdir, SecureRandom.uuid)
74
+ refute(Dir.exists?(dir))
75
+ dir
76
+ end
77
+
78
+ def create_hook_templates
79
+ hook_templates_dir = File.join(Paradeiser.templates_dir, Paradeiser.os.to_s, 'hooks')
80
+ FileUtils.mkdir_p(hook_templates_dir)
81
+
82
+ HOOKS.each do |hook|
83
+ FileUtils.touch(File.join(hook_templates_dir, hook))
84
+ end
85
+ end
86
+
87
+ def assert_hooks_exist
88
+ HOOKS.each do |hook|
89
+ assert(File.exist?(File.join(Paradeiser.hooks_dir, hook)))
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,44 @@
1
+ require 'helper'
2
+
3
+ class TestParadeiserControllerReport < ControllerTest
4
+ def setup
5
+ @backend = PStoreMock.new
6
+ end
7
+
8
+ def test_idle
9
+ br3ak, has_output = invoke(:report, '@pom', 'has_output')
10
+ assert_empty(br3ak)
11
+ assert_equal(true, has_output)
12
+ end
13
+
14
+ def test_active_break
15
+ @backend[:foo] = SchedulableMock.new(:status_name => :active)
16
+
17
+ br3ak, has_output = invoke(:report, '@pom', 'has_output')
18
+ assert_equal(1, br3ak.size)
19
+ assert_equal(true, has_output)
20
+ end
21
+
22
+ def test_finished_break
23
+ @backend[:foo] = SchedulableMock.new(:status_name => :finished)
24
+ @backend[:bar] = SchedulableMock.new(:status_name => :active)
25
+ br3ak, has_output = invoke(:report, '@pom', 'has_output')
26
+ assert_equal(2, br3ak.size)
27
+ assert_equal(true, has_output)
28
+ end
29
+
30
+ def test_active_pom
31
+ @backend[:bar] = SchedulableMock.new(:status_name => :active)
32
+ pomodori, has_output = invoke(:report, '@pom', 'has_output')
33
+ assert_equal(1, pomodori.size)
34
+ assert_equal(true, has_output)
35
+ end
36
+
37
+ def test_finished_pom
38
+ @backend[:foo] = SchedulableMock.new(:status_name => :finished)
39
+ @backend[:bar] = SchedulableMock.new(:status_name => :active)
40
+ pomodori, has_output = invoke(:report, '@pom', 'has_output')
41
+ assert_equal(2, pomodori.size)
42
+ assert_equal(true, has_output)
43
+ end
44
+ end
@@ -0,0 +1,70 @@
1
+ require 'helper'
2
+
3
+ class TestParadeiserControllerStatus < ControllerTest
4
+ def setup
5
+ @backend = PStoreMock.new
6
+ end
7
+
8
+ def test_status_idle
9
+ exitstatus, has_output = invoke(:status, 'exitstatus', 'has_output')
10
+ assert_equal(-1, exitstatus)
11
+ assert_equal(true, has_output)
12
+ assert_equal(0, @backend.size)
13
+ end
14
+
15
+ def test_status_active_pom
16
+ @backend[:foo] = SchedulableMock.new(
17
+ :status => 'active',
18
+ :status_name => :active,
19
+ :active => true,
20
+ :name => 'pomodoro')
21
+
22
+ pom, has_output, exitstatus = invoke(:status, '@pom', 'has_output', 'exitstatus')
23
+ assert_equal(0, exitstatus)
24
+ assert_equal(:active, pom.status_name)
25
+ assert_equal(true, has_output)
26
+ assert_equal(1, @backend.size)
27
+ end
28
+
29
+ def test_status_finished_pom
30
+ @backend[:foo] = SchedulableMock.new(
31
+ :status => 'finished',
32
+ :status_name => :finished,
33
+ :finished => true,
34
+ :name => 'pomodoro')
35
+
36
+ pom, has_output, exitstatus = invoke(:status, '@pom', 'has_output', 'exitstatus')
37
+ assert_equal(1, exitstatus)
38
+ assert_equal(:finished, pom.status_name)
39
+ assert_equal(true, has_output)
40
+ assert_equal(1, @backend.size)
41
+ end
42
+
43
+ def test_status_active_break
44
+ @backend[:foo] = SchedulableMock.new(
45
+ :status => 'active',
46
+ :status_name => :active,
47
+ :active => true,
48
+ :name => 'break')
49
+
50
+ pom, has_output, exitstatus = invoke(:status, '@pom', 'has_output', 'exitstatus')
51
+ assert_equal(2, exitstatus)
52
+ assert_equal(:active, pom.status_name)
53
+ assert_equal(true, has_output)
54
+ assert_equal(1, @backend.size)
55
+ end
56
+
57
+ def test_status_finished_break
58
+ @backend[:foo] = SchedulableMock.new(
59
+ :status => 'finished',
60
+ :status_name => :finished,
61
+ :finished => true,
62
+ :name => 'break')
63
+
64
+ pom, has_output, exitstatus = invoke(:status, '@pom', 'has_output', 'exitstatus')
65
+ assert_equal(3, exitstatus)
66
+ assert_equal(:finished, pom.status_name)
67
+ assert_equal(true, has_output)
68
+ assert_equal(1, @backend.size)
69
+ end
70
+ end
@@ -0,0 +1,66 @@
1
+ require 'helper'
2
+
3
+ class TestParadeiserView < MiniTest::Test
4
+ def setup
5
+ @pom = Pomodoro.new
6
+ @pom.id = 1
7
+ end
8
+
9
+ def test_status_idle
10
+ out, err = render(:status)
11
+ assert_match(/^Current state is idle.$/m, out)
12
+ assert_empty(err)
13
+ end
14
+
15
+ def test_status_active
16
+ start!
17
+ out, err = render(:status)
18
+ assert_match(/^Pomodoro #1 is active for another \d{1,2} minutes \(started at .*\)\.$/m, out)
19
+ assert_empty(err)
20
+ end
21
+
22
+ def test_status_finished
23
+ start!
24
+ finish!
25
+ out, err = render(:status)
26
+ assert_match(/^Nothing active. Last pomodoro was finished at .*\.$/m, out)
27
+ assert_empty(err)
28
+ end
29
+
30
+ def test_report_empty
31
+ @pom = []
32
+ out, err = render(:report)
33
+ assert_equal(1, out.lines.size)
34
+ assert_match(/^ID \| Name \| Status \| Started \| Ended | Interrupts$/m, out.lines.first)
35
+ assert_empty(err)
36
+ end
37
+
38
+ def test_report_one_active
39
+ start!
40
+ @pom = [@pom]
41
+ out, err = render(:report)
42
+ assert_equal(2, out.lines.size)
43
+ assert_match(/^ID \| Name \| Status \| Started \| Ended | Interrupts$/m, out.lines[0])
44
+ assert_match(/^1 \| pomodoro \| active \| \d{1,2}:\d{1,2} \| \| \d+ I, \d+ E$/m, out.lines[1])
45
+ assert_empty(err)
46
+ end
47
+
48
+ def test_report_one_finished
49
+ start!
50
+ finish!
51
+ @pom = [@pom]
52
+ out, err = render(:report)
53
+ assert_equal(2, out.lines.size)
54
+ assert_match(/^ID \| Name \| Status \| Started \| Ended | Interrupts$/m, out.lines[0])
55
+ assert_match(/^1 \| pomodoro \| finished \| \d{1,2}:\d{1,2} \| \d{1,2}:\d{1,2} \| \d+ I, \d+ E$/m, out.lines[1])
56
+ assert_empty(err)
57
+ end
58
+
59
+ private
60
+
61
+ def render(method)
62
+ capture_io do
63
+ View.new('Paradeiser', method).render(binding)
64
+ end
65
+ end
66
+ end