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.
- checksums.yaml +4 -4
- data/Gemfile +2 -2
- data/README.md +96 -35
- data/TODO.md +14 -15
- data/VISION.md +92 -84
- data/bin/{pom → par} +32 -2
- data/doc/Paradeiser::Break_status.svg +50 -0
- data/doc/Paradeiser::Pomodoro_status.svg +40 -22
- data/lib/paradeiser.rb +3 -3
- data/lib/paradeiser/controllers/breaks_controller.rb +19 -0
- data/lib/paradeiser/controllers/controller.rb +3 -3
- data/lib/paradeiser/controllers/paradeiser_controller.rb +13 -2
- data/lib/paradeiser/controllers/pomodori_controller.rb +36 -17
- data/lib/paradeiser/errors.rb +12 -6
- data/lib/paradeiser/executor.rb +2 -0
- data/lib/paradeiser/initializers/inflections.rb +4 -0
- data/lib/paradeiser/models/break.rb +40 -0
- data/lib/paradeiser/models/hook.rb +4 -4
- data/lib/paradeiser/models/interrupt.rb +18 -0
- data/lib/paradeiser/models/job.rb +1 -1
- data/lib/paradeiser/models/pomodoro.rb +30 -22
- data/lib/paradeiser/models/repository.rb +26 -13
- data/lib/paradeiser/models/scheduled.rb +25 -0
- data/lib/paradeiser/models/scheduler.rb +1 -1
- data/lib/paradeiser/models/status.rb +21 -0
- data/lib/paradeiser/{refinements.rb → refinements/numeric.rb} +4 -0
- data/lib/paradeiser/router.rb +12 -7
- data/lib/paradeiser/version.rb +1 -1
- data/lib/paradeiser/views/paradeiser/init.erb +1 -1
- data/lib/paradeiser/views/paradeiser/report.erb +5 -0
- data/lib/paradeiser/views/paradeiser/status.erb +13 -0
- data/paradeiser.gemspec +2 -0
- data/templates/linux/hooks/after-finish-break +10 -0
- data/templates/linux/hooks/after-finish-pomodoro +10 -0
- data/templates/linux/hooks/after-start-break +7 -0
- data/templates/linux/hooks/after-start-pomodoro +7 -0
- data/templates/mac/hooks/after-finish-break +10 -0
- data/templates/mac/hooks/after-finish-pomodoro +10 -0
- data/templates/mac/hooks/after-start-break +7 -0
- data/templates/mac/hooks/after-start-pomodoro +7 -0
- data/test/helper.rb +37 -4
- data/test/integration/{test_pom.rb → test_par.rb} +4 -4
- data/test/lib/{pomodoro_mock.rb → schedulable_mock.rb} +9 -1
- data/test/unit/test_break.rb +99 -0
- data/test/unit/test_break_controller.rb +56 -0
- data/test/unit/test_interrupt.rb +36 -0
- data/test/unit/test_paradeiser_controller_init.rb +92 -0
- data/test/unit/test_paradeiser_controller_report.rb +44 -0
- data/test/unit/test_paradeiser_controller_status.rb +70 -0
- data/test/unit/test_paradeiser_view.rb +66 -0
- data/test/unit/test_pomodori_controller.rb +87 -31
- data/test/unit/test_pomodori_view.rb +0 -50
- data/test/unit/test_pomodoro.rb +131 -9
- data/test/unit/test_pomodoro_hooks.rb +165 -17
- data/test/unit/test_repository.rb +38 -15
- data/test/unit/test_router.rb +4 -4
- data/test/unit/test_status.rb +26 -0
- metadata +70 -17
- data/lib/paradeiser/views/pomodori/report.erb +0 -5
- data/lib/paradeiser/views/pomodori/status.erb +0 -9
- data/templates/linux/hooks/after-finish +0 -10
- data/templates/linux/hooks/after-start +0 -7
- data/templates/mac/hooks/after-finish +0 -10
- data/templates/mac/hooks/after-start +0 -7
- data/test/unit/test_paradeiser_controller.rb +0 -88
@@ -0,0 +1,18 @@
|
|
1
|
+
module Paradeiser
|
2
|
+
class Interrupt
|
3
|
+
KNOWN_TYPES = [:internal, :external]
|
4
|
+
attr_reader :created_at, :type
|
5
|
+
|
6
|
+
def initialize(type = nil)
|
7
|
+
@type = type || :internal
|
8
|
+
raise InvalidTypeError.new(@type, KNOWN_TYPES) unless KNOWN_TYPES.include?(@type)
|
9
|
+
@created_at = Time.now
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class ExternalInterrupt < Interrupt
|
14
|
+
def initialize
|
15
|
+
super(:external)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,32 +1,46 @@
|
|
1
1
|
module Paradeiser
|
2
|
-
class Pomodoro
|
3
|
-
|
2
|
+
class Pomodoro < Scheduled
|
3
|
+
attr_reader :interrupts, :interrupt_type
|
4
|
+
attr_accessor :canceled_at
|
4
5
|
|
5
|
-
|
6
|
+
MINUTES_25 = 25
|
6
7
|
|
7
8
|
state_machine :status, :initial => :idle do
|
8
9
|
event :start do
|
9
10
|
transition :idle => :active
|
10
11
|
end
|
11
12
|
|
13
|
+
event :interrupt do
|
14
|
+
transition :active => :active
|
15
|
+
end
|
16
|
+
|
17
|
+
event :cancel do
|
18
|
+
transition :active => :canceled
|
19
|
+
end
|
20
|
+
|
12
21
|
event :finish do
|
13
22
|
transition :active => :finished
|
14
23
|
end
|
15
24
|
|
16
|
-
state :finished
|
17
|
-
state :active
|
18
|
-
state :idle
|
19
|
-
|
20
25
|
after_transition :on => :start do |pom, transition|
|
21
26
|
pom.started_at = Time.now
|
22
27
|
Scheduler.clear # There must be no other jobs scheduled because of Rule #1
|
23
|
-
Scheduler.add(:finish,
|
28
|
+
Scheduler.add(:"#{pom.name} finish", pom.length.minutes)
|
29
|
+
end
|
30
|
+
|
31
|
+
after_transition :on => :interrupt do |pom, transition|
|
32
|
+
pom.interrupts << Interrupt.new(pom.interrupt_type)
|
24
33
|
end
|
25
34
|
|
26
35
|
around_transition do |pom, transition, block|
|
27
|
-
Hook.new(:before).execute(pom, transition)
|
36
|
+
Hook.new(:before).execute(pom, transition.event)
|
28
37
|
block.call
|
29
|
-
Hook.new(:after).execute(pom, transition)
|
38
|
+
Hook.new(:after).execute(pom, transition.event)
|
39
|
+
end
|
40
|
+
|
41
|
+
after_transition :on => :cancel do |pom, transition|
|
42
|
+
pom.canceled_at = Time.now
|
43
|
+
Scheduler.clear # There must be no other jobs scheduled because of Rule #1
|
30
44
|
end
|
31
45
|
|
32
46
|
after_transition :on => :finish do |pom, transition|
|
@@ -37,22 +51,16 @@ module Paradeiser
|
|
37
51
|
|
38
52
|
def initialize
|
39
53
|
super # required for state_machine
|
54
|
+
@interrupts = []
|
40
55
|
end
|
41
56
|
|
42
|
-
def
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
# from https://github.com/travis-ci/travis/blob/master/lib/travis/client/job.rb
|
47
|
-
def duration
|
48
|
-
start = started_at || Time.now
|
49
|
-
finish = finished_at || Time.now
|
50
|
-
(finish - start).to_i
|
57
|
+
def length
|
58
|
+
MINUTES_25 * 60
|
51
59
|
end
|
52
60
|
|
53
|
-
def
|
54
|
-
|
55
|
-
|
61
|
+
def interrupt!(type = nil)
|
62
|
+
@interrupt_type = type
|
63
|
+
super
|
56
64
|
end
|
57
65
|
end
|
58
66
|
end
|
@@ -16,18 +16,31 @@ module Paradeiser
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def active
|
19
|
-
all_active = find{|pom| pom.active?}
|
20
|
-
|
21
|
-
|
19
|
+
all_active = find{|pom| pom.active?}.sort{|a,b| a.started_at <=> b.started_at}
|
20
|
+
|
21
|
+
# Cannot recover from an internal inconsistency.
|
22
|
+
if all_active.size > 1
|
23
|
+
raise "The repository was corrupted. There are #{all_active.size} active objects, but only one is allowed to be active."
|
24
|
+
end
|
25
|
+
|
26
|
+
all_active.last
|
22
27
|
end
|
23
28
|
|
24
29
|
def active?
|
25
30
|
!!active
|
26
31
|
end
|
27
32
|
|
33
|
+
def last_finished
|
34
|
+
find{|p| p.finished?}.sort{|a,b| a.started_at <=> b.started_at}.last
|
35
|
+
end
|
36
|
+
|
37
|
+
def last_canceled
|
38
|
+
find{|p| p.canceled?}.sort{|a,b| a.started_at <=> b.started_at}.last
|
39
|
+
end
|
40
|
+
|
28
41
|
def save(pom)
|
29
42
|
raise IllegalStatusError if pom.idle?
|
30
|
-
raise SingletonError.new(self.active) if self.active? && active.id != pom.id
|
43
|
+
raise SingletonError.new(pom.class, self.active, :save) if self.active? && active.id != pom.id
|
31
44
|
|
32
45
|
pom.id = next_id if pom.new?
|
33
46
|
backend.transaction do
|
@@ -35,23 +48,23 @@ module Paradeiser
|
|
35
48
|
end
|
36
49
|
end
|
37
50
|
|
51
|
+
def next_id
|
52
|
+
if all.empty?
|
53
|
+
1
|
54
|
+
else
|
55
|
+
all.max{|a, b| a.id <=> b.id}.id + 1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
38
59
|
private
|
39
60
|
|
40
61
|
def backend
|
41
62
|
begin
|
42
|
-
@backend ||= PStore.new(File.join(Paradeiser.
|
63
|
+
@backend ||= PStore.new(File.join(Paradeiser.par_dir, 'repository.pstore'), true)
|
43
64
|
rescue PStore::Error => e
|
44
65
|
raise NotInitializedError.new(e.message)
|
45
66
|
end
|
46
67
|
end
|
47
|
-
|
48
|
-
def next_id
|
49
|
-
if all.empty?
|
50
|
-
1
|
51
|
-
else
|
52
|
-
all.max{|a, b| a.id <=> b.id}.id + 1
|
53
|
-
end
|
54
|
-
end
|
55
68
|
end
|
56
69
|
end
|
57
70
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Paradeiser
|
2
|
+
class Scheduled
|
3
|
+
attr_accessor :id, :started_at, :finished_at
|
4
|
+
|
5
|
+
def new?
|
6
|
+
@id.nil?
|
7
|
+
end
|
8
|
+
|
9
|
+
# from https://github.com/travis-ci/travis/blob/master/lib/travis/client/job.rb
|
10
|
+
def duration
|
11
|
+
start = started_at || Time.now
|
12
|
+
finish = finished_at || (respond_to?(:canceled_at) ? canceled_at : nil) || Time.now
|
13
|
+
(finish - start).to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
def remaining
|
17
|
+
raise NotActiveError unless active?
|
18
|
+
length - Time.now.to_i + started_at.to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def name
|
22
|
+
self.class.name.split("::").last.downcase
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -17,7 +17,7 @@ module Paradeiser
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def add(command, minutes)
|
20
|
-
_, err = exec("echo
|
20
|
+
_, err = exec("echo #{BIN_PAR} #{command} | #{at} -q #{queue} now + #{minutes} minutes")
|
21
21
|
id = parse_add(err.chomp)
|
22
22
|
Job.new(id)
|
23
23
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Paradeiser
|
2
|
+
module Status
|
3
|
+
|
4
|
+
MAP = {
|
5
|
+
'pomodoro:active' => 0,
|
6
|
+
'pomodoro:finished' => 1,
|
7
|
+
'break:active' => 2,
|
8
|
+
'break:finished' => 3,
|
9
|
+
}
|
10
|
+
|
11
|
+
def self.of(thing)
|
12
|
+
thing.nil? ? -1 : MAP[key(thing)]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.key(thing)
|
18
|
+
"#{thing.name}:#{thing.status}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/paradeiser/router.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
|
1
3
|
module Paradeiser
|
2
4
|
class Router
|
3
5
|
attr_reader :status
|
@@ -8,19 +10,22 @@ module Paradeiser
|
|
8
10
|
|
9
11
|
def dispatch(command)
|
10
12
|
Proc.new do |args, options|
|
11
|
-
|
13
|
+
parts = command.name.split
|
14
|
+
resource = parts.shift
|
15
|
+
controller_name = "#{resource.pluralize.capitalize}Controller".to_sym
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
controller_class =
|
17
|
+
if Paradeiser.const_defined?(controller_name)
|
18
|
+
verb = parts.join
|
19
|
+
controller_class = Paradeiser.const_get(controller_name)
|
16
20
|
else
|
17
|
-
|
21
|
+
verb = resource
|
22
|
+
controller_class = ParadeiserController
|
18
23
|
end
|
19
24
|
|
20
|
-
controller = controller_class.new(
|
25
|
+
controller = controller_class.new(verb)
|
21
26
|
controller.call(args, options)
|
22
27
|
|
23
|
-
View.new(controller.model,
|
28
|
+
View.new(controller.model, verb).render(controller.get_binding) if options.verbose || controller.has_output
|
24
29
|
|
25
30
|
@status = controller.exitstatus
|
26
31
|
end
|
data/lib/paradeiser/version.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
Suffessfully initialized <%= Paradeiser.
|
1
|
+
Suffessfully initialized <%= Paradeiser.par_dir %>.
|
@@ -0,0 +1,5 @@
|
|
1
|
+
ID | Name | Status | Started | Ended | Interrupts
|
2
|
+
<% @pom.each do |pom| %>
|
3
|
+
<% interrupts_by_type = pom.interrupts.group_by{|i| i.type } %>
|
4
|
+
<%= pom.id %> | <%= pom.name %> | <%= pom.status %> | <%= pom.started_at.strftime('%R') %> | <%= pom.finished_at.strftime('%R') if pom.finished? %><%= pom.canceled_at.strftime('%R') if pom.canceled? %> | <%= interrupts[:internal].size rescue 0 %> I, <%= interrupts_by_type[:external].size rescue 0 %> E
|
5
|
+
<% end %>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<% case @pom.status_name
|
2
|
+
when :active
|
3
|
+
%><%= @pom.name.capitalize %> #<%= @pom.id %> is active for another <%= @pom.remaining.minutes %> minutes (started at <%= @pom.started_at.strftime('%R') %>).<% unless !@pom.respond_to?(:interrupts) || @pom.interrupts.empty? %> <%= @pom.interrupts.size %> interrupts on record.<% end %><%
|
4
|
+
when :break
|
5
|
+
%><%= @pom.name.capitalize %> is active for another <%= @pom.remaining.minutes %> minutes.<%
|
6
|
+
when :finished
|
7
|
+
%>Nothing active. Last <%= @pom.name %> was finished at <%= @pom.finished_at.strftime('%R') %>.<% unless !@pom.respond_to?(:interrupts) || @pom.interrupts.empty? %> It had <%= @pom.interrupts.size %> interrupts.<% end %><%
|
8
|
+
when :canceled
|
9
|
+
%>Nothing active. Last <%= @pom.name %> was canceled at <%= @pom.canceled_at.strftime('%R') %>.<% unless !@pom.respond_to?(:interrupts) || @pom.interrupts.empty? %> It had <%= @pom.interrupts.size %> interrupts.<% end %><%
|
10
|
+
else
|
11
|
+
%>Current state is <%= @pom.status %>.<%
|
12
|
+
end
|
13
|
+
%>
|
data/paradeiser.gemspec
CHANGED
@@ -21,6 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_runtime_dependency 'commander'
|
22
22
|
spec.add_runtime_dependency 'require_all'
|
23
23
|
spec.add_runtime_dependency 'state_machine'
|
24
|
+
spec.add_runtime_dependency 'activesupport'
|
25
|
+
spec.add_runtime_dependency 'actionpack'
|
24
26
|
|
25
27
|
# Moved to the Gemfile so that Travis CI can load the test group
|
26
28
|
# spec.add_development_dependency 'rake'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
#
|
4
|
+
# Sample after-finish-break hook for Paradeiser (called after a break ended). It
|
5
|
+
# displays a simple notification.
|
6
|
+
#
|
7
|
+
# $PAR_BREAK_ID - the ID of the break that just ended
|
8
|
+
# $PAR_BREAK_STARTED_AT - the time when the break was started
|
9
|
+
#
|
10
|
+
notify-send "Pomodoro" "The break $PAR_BREAK_ID (started at $PAR_BREAK_STARTED_AT) is over." -u critical > /dev/null
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
#
|
4
|
+
# Sample after-finish-pomodoro hook for Paradeiser (called after a pomodoro ended). It
|
5
|
+
# displays a simple notification.
|
6
|
+
#
|
7
|
+
# $PAR_POMODORO_ID - the ID of the pomodoro that just ended
|
8
|
+
# $PAR_POMODORO_STARTED_AT - the time when the pomodoro was started
|
9
|
+
#
|
10
|
+
notify-send "Pomodoro" "The pomodoro $PAR_POMODORO_ID (started at $PAR_POMODORO_STARTED_AT) is over." -u critical > /dev/null
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
#
|
4
|
+
# Sample after-finish-break hook for Paradeiser (called after a break ended). It
|
5
|
+
# displays a simple notification.
|
6
|
+
#
|
7
|
+
# $PAR_BREAK_ID - the ID of the break that just ended
|
8
|
+
# $PAR_BREAK_STARTED_AT - the time when the break was started
|
9
|
+
#
|
10
|
+
terminal-notifier-success -message "The break $PAR_BREAK_ID (started at $PAR_BREAK_STARTED_AT) is over." > /dev/null
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
#
|
4
|
+
# Sample after-finish-pomodoro hook for Paradeiser (called after a pomodoro ended). It
|
5
|
+
# displays a simple notification.
|
6
|
+
#
|
7
|
+
# $PAR_POMODORO_ID - the ID of the pomodoro that just ended
|
8
|
+
# $PAR_POMODORO_STARTED_AT - the time when the pomodoro was started
|
9
|
+
#
|
10
|
+
terminal-notifier-success -message "The pomodoro $PAR_POMODORO_ID (started at $PAR_POMODORO_STARTED_AT) is over." > /dev/null
|
data/test/helper.rb
CHANGED
@@ -10,15 +10,48 @@ class MiniTest::Test
|
|
10
10
|
|
11
11
|
protected
|
12
12
|
|
13
|
-
def start!(
|
13
|
+
def start!(thing = @pom || @break)
|
14
14
|
Scheduler.stub(:add, nil) do
|
15
|
-
|
15
|
+
thing.start!
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def interrupt!(type = :internal, pom = @pom)
|
20
20
|
Scheduler.stub(:clear, nil) do
|
21
|
-
pom.
|
21
|
+
pom.interrupt!(type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def finish!(thing = @pom || @break)
|
26
|
+
Scheduler.stub(:clear, nil) do
|
27
|
+
thing.finish!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def cancel!(pom = @pom)
|
32
|
+
Scheduler.stub(:clear, nil) do
|
33
|
+
pom.cancel!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ControllerTest < MiniTest::Test
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def invoke(method, *attributes)
|
43
|
+
controller = ParadeiserController.new(method)
|
44
|
+
|
45
|
+
Repository.stub :backend, @backend do
|
46
|
+
Scheduler.stub(:add, nil) do
|
47
|
+
Scheduler.stub(:clear, nil) do
|
48
|
+
controller.call(nil, nil)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
attributes.map do |attribute|
|
54
|
+
controller.get_binding.eval(attribute)
|
22
55
|
end
|
23
56
|
end
|
24
57
|
end
|