domodoro 0.0.1
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.
- data/.gitignore +6 -0
- data/Gemfile +8 -0
- data/Guardfile +5 -0
- data/Rakefile +11 -0
- data/Readme.md +70 -0
- data/assets/start.wav +0 -0
- data/assets/stop.wav +0 -0
- data/bin/domodoro +7 -0
- data/domodoro.gemspec +26 -0
- data/lib/domodoro/channel.rb +44 -0
- data/lib/domodoro/client.rb +77 -0
- data/lib/domodoro/config.rb +20 -0
- data/lib/domodoro/server.rb +49 -0
- data/lib/domodoro/version.rb +3 -0
- data/lib/domodoro.rb +20 -0
- data/test/domodoro/channel_test.rb +133 -0
- data/test/domodoro/client_test.rb +160 -0
- data/test/domodoro/config_test.rb +30 -0
- data/test/domodoro/server_test.rb +65 -0
- data/test/domodoro_test.rb +25 -0
- data/test/support/em-minitest.rb +103 -0
- data/test/test_helper.rb +23 -0
- metadata +164 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/Rakefile
ADDED
data/Readme.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# domodoro
|
2
|
+
|
3
|
+
Distributed pomodoro architecture running on EventMachine.
|
4
|
+
|
5
|
+
Domodoro uses a pub/sub approach to send scheduled pomodoro events (such as
|
6
|
+
'start working' or 'pomodoro break') to a number of subscribers simultaneously.
|
7
|
+
|
8
|
+
The subscriber (i.e. the domodoro client) receives those events and triggers
|
9
|
+
an appropriate alarm/notification (via Growl for example).
|
10
|
+
|
11
|
+
The idea behind this is that synchronized pomodoros between coworkers are a
|
12
|
+
win, but difficult to implement with traditional software, because of the
|
13
|
+
following reasons:
|
14
|
+
|
15
|
+
* Using local pomodoro timers/software makes pomodoro cycles unsynced between
|
16
|
+
different coworkers, so that one may have a pomodoro break while others are
|
17
|
+
still working and that may put at risk their ability to focus.
|
18
|
+
|
19
|
+
* Using a global pomodoro (for example, alarms for the entire office) **does**
|
20
|
+
annoy the hell out of whoever does'nt want/can't benefit from pomodoro
|
21
|
+
cycles at a particular moment.
|
22
|
+
|
23
|
+
* Plus, the "alarms for the entire office" does not work with coworkers that
|
24
|
+
may not be in the office (telecommuting workers for example).
|
25
|
+
|
26
|
+
Solution: a pub/sub architecture where a centralized publisher (the domodoro
|
27
|
+
server) broadcasts pomodoro events to whoever wants to subscribe to them, so
|
28
|
+
whoever wants to stay synced, can, and who doesn't, can stay out of it.
|
29
|
+
|
30
|
+
## Install
|
31
|
+
|
32
|
+
$ gem install domodoro
|
33
|
+
|
34
|
+
In the server machine:
|
35
|
+
|
36
|
+
$ domodoro serve [port]
|
37
|
+
|
38
|
+
Each of the clients that want to connect must do this:
|
39
|
+
|
40
|
+
$ domodoro join [ip of the server machine] [port]
|
41
|
+
|
42
|
+
The clients will receive notifications via sound/growl (configurable in a
|
43
|
+
`~/.domodororc` file).
|
44
|
+
|
45
|
+
## Caveats
|
46
|
+
|
47
|
+
* Sound notifications use `afplay`, which ships by default with OSX.
|
48
|
+
If you're not using OSX, try to install the `afplay` program manually or...
|
49
|
+
send a patch to make it work with your OS :)
|
50
|
+
|
51
|
+
* The pomodoro schedule is (as of v0.0.1) hard-coded. It starts at 8:00 AM,
|
52
|
+
stops at 13:00 for lunch, and starts again at 13:20. In the following
|
53
|
+
versions this will be freely configurable.
|
54
|
+
|
55
|
+
## Configuration
|
56
|
+
|
57
|
+
By default, both sound and visual notifications are displayed on each event.
|
58
|
+
If you want to configure this, create a file in your home directory named
|
59
|
+
`.domodororc` with some YAML configuration:
|
60
|
+
|
61
|
+
$ touch ~/.domodororc
|
62
|
+
$ echo "visual: true" >> ~/.domodororc
|
63
|
+
$ echo "sound: false" >> ~/.domodororc
|
64
|
+
|
65
|
+
## Copyright
|
66
|
+
|
67
|
+
Copyright (c) 2011 Josep M. Bach. Released under the MIT license.
|
68
|
+
|
69
|
+
|
70
|
+
|
data/assets/start.wav
ADDED
Binary file
|
data/assets/stop.wav
ADDED
Binary file
|
data/bin/domodoro
ADDED
data/domodoro.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "domodoro/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "domodoro"
|
7
|
+
s.version = Domodoro::VERSION
|
8
|
+
s.authors = ["Josep M. Bach"]
|
9
|
+
s.email = ["josep.m.bach@gmail.com"]
|
10
|
+
s.homepage = "http://github.com/txus/domodoro"
|
11
|
+
s.summary = %q{Distributed Pomodoro for the masses}
|
12
|
+
s.description = %q{Distributed Pomodoro for the masses}
|
13
|
+
|
14
|
+
s.rubyforge_project = "domodoro"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency 'eventmachine'
|
22
|
+
s.add_runtime_dependency 'notify'
|
23
|
+
s.add_development_dependency 'minitest'
|
24
|
+
s.add_development_dependency 'mocha'
|
25
|
+
s.add_development_dependency 'purdytest'
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Domodoro
|
2
|
+
class Channel < EM::Channel
|
3
|
+
def broadcast(hour, min)
|
4
|
+
if ENV['DEBUG']
|
5
|
+
puts 'DEBUG MODE: Start on even minutes, stop on odd minutes'
|
6
|
+
if min % 2 == 0
|
7
|
+
puts "#{Time.now} - Starting pomodoro!"
|
8
|
+
self << :start
|
9
|
+
else
|
10
|
+
puts "#{Time.now} - Pomodoro break!"
|
11
|
+
self << :stop
|
12
|
+
end
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
if (hour >= 8 && hour < 13)
|
17
|
+
morning(min)
|
18
|
+
elsif (hour >= 13 && min >= 20) &&
|
19
|
+
afternoon(min)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def morning(min)
|
23
|
+
case min
|
24
|
+
when 0, 30
|
25
|
+
puts "#{Time.now} - Starting pomodoro!"
|
26
|
+
self << :start
|
27
|
+
when 25, 55
|
28
|
+
puts "#{Time.now} - Pomodoro break!"
|
29
|
+
self << :stop
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def afternoon(min)
|
34
|
+
case min
|
35
|
+
when 20, 50
|
36
|
+
puts "#{Time.now} - Starting pomodoro!"
|
37
|
+
self << :start
|
38
|
+
when 45, 15
|
39
|
+
puts "#{Time.now} - Pomodoro break!"
|
40
|
+
self << :stop
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'notify'
|
2
|
+
|
3
|
+
module Domodoro
|
4
|
+
class Client
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def start(host, port='9111')
|
8
|
+
Config.load
|
9
|
+
puts "#{Time.now} - Domodoro listening on #{host}:#{port}"
|
10
|
+
puts "Visual notifications: #{Config.visual}"
|
11
|
+
puts "Sound notifications: #{Config.sound}\n"
|
12
|
+
|
13
|
+
EM.run do
|
14
|
+
EM.connect(host, port) do |c|
|
15
|
+
c.extend EM::P::LineText2
|
16
|
+
def c.receive_line(line)
|
17
|
+
case line
|
18
|
+
when /start/
|
19
|
+
puts " - Starting pomodoro!"
|
20
|
+
Client.work
|
21
|
+
when /stop/
|
22
|
+
puts " - Pomodoro break!"
|
23
|
+
Client.break
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
EM.add_periodic_timer(1) do
|
28
|
+
EM.next_tick do
|
29
|
+
print_time
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def work
|
37
|
+
EM.defer do
|
38
|
+
if Config.visual
|
39
|
+
Notify.notify "Domodoro", "Time to work!"
|
40
|
+
end
|
41
|
+
if Config.sound
|
42
|
+
system("afplay #{path_to('start.wav')}")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def break
|
48
|
+
EM.defer do
|
49
|
+
if Config.visual
|
50
|
+
Notify.notify "Domodoro", "Take a 5 min. break."
|
51
|
+
end
|
52
|
+
if Config.sound
|
53
|
+
system("afplay #{path_to('stop.wav')}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def path_to(asset)
|
61
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'assets', asset))
|
62
|
+
end
|
63
|
+
|
64
|
+
def print_time
|
65
|
+
hour = Time.now.hour.to_s.rjust(2, '0')
|
66
|
+
min = Time.now.min.to_s.rjust(2, '0')
|
67
|
+
secs = Time.now.sec.to_s.rjust(2, '0')
|
68
|
+
$stdout.print "\r"
|
69
|
+
$stdout.print " " * 20
|
70
|
+
$stdout.print "\r"
|
71
|
+
$stdout.print "#{hour}:#{min}:#{secs}"
|
72
|
+
$stdout.flush
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Domodoro
|
4
|
+
module Config
|
5
|
+
attr_accessor :visual, :sound
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def load
|
9
|
+
if File.exist?(File.expand_path('~/.domodororc'))
|
10
|
+
file = YAML.load(File.read(File.expand_path('~/.domodororc')))
|
11
|
+
|
12
|
+
self.visual = file['visual']
|
13
|
+
self.sound = file['sound']
|
14
|
+
else
|
15
|
+
self.visual = true
|
16
|
+
self.sound = true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Domodoro
|
2
|
+
class Server < EM::Connection
|
3
|
+
attr_reader :channel, :sid
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def start(host='127.0.0.1', port='9111')
|
7
|
+
puts "#{Time.now} - Domodoro serving at #{host}:#{port}"
|
8
|
+
EM.run do
|
9
|
+
channel = Channel.new
|
10
|
+
|
11
|
+
EM.start_server(host, port, self, channel)
|
12
|
+
|
13
|
+
EM.add_periodic_timer(1) do
|
14
|
+
if Time.now.sec == 0
|
15
|
+
channel.broadcast(Time.now.hour, Time.now.min)
|
16
|
+
else
|
17
|
+
EM.next_tick do
|
18
|
+
print_time
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def print_time
|
26
|
+
hour = Time.now.hour.to_s.rjust(2, '0')
|
27
|
+
min = Time.now.min.to_s.rjust(2, '0')
|
28
|
+
secs = Time.now.sec.to_s.rjust(2, '0')
|
29
|
+
$stdout.print "\r"
|
30
|
+
$stdout.print " " * 20
|
31
|
+
$stdout.print "\r"
|
32
|
+
$stdout.print "#{hour}:#{min}:#{secs}"
|
33
|
+
$stdout.flush
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(channel)
|
38
|
+
@channel = channel
|
39
|
+
end
|
40
|
+
|
41
|
+
def post_init
|
42
|
+
@sid = channel.subscribe { |m| send_data "#{m.inspect}\n" }
|
43
|
+
end
|
44
|
+
|
45
|
+
def unbind
|
46
|
+
channel.unsubscribe @sid
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/domodoro.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
require "domodoro/version"
|
4
|
+
require "domodoro/config"
|
5
|
+
require "domodoro/channel"
|
6
|
+
require "domodoro/server"
|
7
|
+
require "domodoro/client"
|
8
|
+
|
9
|
+
module Domodoro
|
10
|
+
extend self
|
11
|
+
|
12
|
+
def start(action, *args)
|
13
|
+
case action
|
14
|
+
when 'serve', 'server'
|
15
|
+
Server.start(*args)
|
16
|
+
when 'join', 'connect'
|
17
|
+
Client.start(*args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Domodoro
|
4
|
+
describe Channel do
|
5
|
+
before do
|
6
|
+
@channel = Channel.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'is an EventMachine channel' do
|
10
|
+
assert @channel.is_a?(EM::Channel)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'broadcast' do
|
14
|
+
describe 'in the morning' do
|
15
|
+
it 'broadcasts from 8:00 until 12:59' do
|
16
|
+
@channel.expects(:morning).with(5).never
|
17
|
+
@channel.expects(:morning).with(10).once
|
18
|
+
@channel.expects(:morning).with(15).once
|
19
|
+
@channel.expects(:morning).with(20).once
|
20
|
+
@channel.expects(:morning).with(25).once
|
21
|
+
@channel.expects(:morning).with(30).once
|
22
|
+
@channel.expects(:morning).with(35).never
|
23
|
+
|
24
|
+
min = 0
|
25
|
+
(7..13).each do |hour|
|
26
|
+
min += 5
|
27
|
+
@channel.broadcast(hour, min)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'in the afternoon' do
|
33
|
+
it 'broadcasts from 13:20 on' do
|
34
|
+
@channel.expects(:afternoon).with(5).never
|
35
|
+
@channel.expects(:afternoon).with(10).never
|
36
|
+
@channel.expects(:afternoon).with(15).never
|
37
|
+
@channel.expects(:afternoon).with(20).once
|
38
|
+
@channel.expects(:afternoon).with(25).once
|
39
|
+
@channel.expects(:afternoon).with(30).once
|
40
|
+
@channel.expects(:afternoon).with(35).once
|
41
|
+
|
42
|
+
min = 0
|
43
|
+
(13..19).each do |hour|
|
44
|
+
min += 5
|
45
|
+
@channel.broadcast(hour, min)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does not broadcast during lunch time' do
|
51
|
+
%w(morning afternoon).each do |timespan|
|
52
|
+
@channel.expects(timespan).with(5).never
|
53
|
+
end
|
54
|
+
|
55
|
+
(0..19).each do |min|
|
56
|
+
@channel.broadcast(13, min)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#morning' do
|
62
|
+
describe 'pomodoro starts' do
|
63
|
+
it 'broadcasts a :start message when the time is XX:00' do
|
64
|
+
@channel.expects(:<<).with(:start)
|
65
|
+
@channel.morning 0
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'broadcasts a :start message when the time is XX:30' do
|
69
|
+
@channel.expects(:<<).with(:start)
|
70
|
+
@channel.morning 30
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'pomodoro stops' do
|
75
|
+
it 'broadcasts a :stop message when the time is XX:25' do
|
76
|
+
@channel.expects(:<<).with(:stop)
|
77
|
+
@channel.morning 25
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'broadcasts a :stop message when the time is XX:55' do
|
81
|
+
@channel.expects(:<<).with(:stop)
|
82
|
+
@channel.morning 55
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'does not broadcast anything at other times' do
|
87
|
+
@channel.expects(:<<).never
|
88
|
+
|
89
|
+
@channel.morning 5
|
90
|
+
@channel.morning 10
|
91
|
+
@channel.morning 20
|
92
|
+
@channel.morning 40
|
93
|
+
@channel.morning 50
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#afternoon' do
|
98
|
+
describe 'pomodoro starts' do
|
99
|
+
it 'broadcasts a :start message when the time is XX:20' do
|
100
|
+
@channel.expects(:<<).with(:start)
|
101
|
+
@channel.afternoon 20
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'broadcasts a :start message when the time is XX:50' do
|
105
|
+
@channel.expects(:<<).with(:start)
|
106
|
+
@channel.afternoon 50
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe 'pomodoro stops' do
|
111
|
+
it 'broadcasts a :stop message when the time is XX:45' do
|
112
|
+
@channel.expects(:<<).with(:stop)
|
113
|
+
@channel.afternoon 45
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'broadcasts a :stop message when the time is XX:15' do
|
117
|
+
@channel.expects(:<<).with(:stop)
|
118
|
+
@channel.afternoon 15
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'does not broadcast anything at other times' do
|
123
|
+
@channel.expects(:<<).never
|
124
|
+
|
125
|
+
@channel.afternoon 5
|
126
|
+
@channel.afternoon 10
|
127
|
+
@channel.afternoon 25
|
128
|
+
@channel.afternoon 40
|
129
|
+
@channel.afternoon 55
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module ExampleServer; end
|
4
|
+
|
5
|
+
module Domodoro
|
6
|
+
describe Client do
|
7
|
+
include EM::MiniTest::Spec
|
8
|
+
|
9
|
+
it 'calls .work when receives :start', :timeout => 0.3 do
|
10
|
+
EM.start_server '127.0.0.1', 12345, ExampleServer do |conn|
|
11
|
+
conn.send_data ":start\n"
|
12
|
+
end
|
13
|
+
|
14
|
+
Client.expects(:work)
|
15
|
+
|
16
|
+
Client.start '127.0.0.1', 12345
|
17
|
+
|
18
|
+
EM.add_timer(0.2) do
|
19
|
+
mocha_verify
|
20
|
+
done!
|
21
|
+
end
|
22
|
+
|
23
|
+
wait!
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'calls .break when receives :stop', :timeout => 0.3 do
|
27
|
+
EM.start_server '127.0.0.1', 12345, ExampleServer do |conn|
|
28
|
+
conn.send_data ":stop\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
Client.expects(:break)
|
32
|
+
|
33
|
+
Client.start '127.0.0.1', 12345
|
34
|
+
|
35
|
+
EM.add_timer(0.2) do
|
36
|
+
mocha_verify
|
37
|
+
done!
|
38
|
+
end
|
39
|
+
|
40
|
+
wait!
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.work' do
|
44
|
+
describe 'if sound is activated' do
|
45
|
+
it 'plays a sound', :timeout => 0.2 do
|
46
|
+
Config.stubs(:sound).returns true
|
47
|
+
Client.expects(:system)
|
48
|
+
Client.work
|
49
|
+
|
50
|
+
EM.add_timer(0.1) do
|
51
|
+
mocha_verify
|
52
|
+
done!
|
53
|
+
end
|
54
|
+
wait!
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'otherwise' do
|
59
|
+
it 'does not play a sound', :timeout => 0.2 do
|
60
|
+
Config.stubs(:sound).returns false
|
61
|
+
Client.expects(:system).never
|
62
|
+
Client.work
|
63
|
+
|
64
|
+
EM.add_timer(0.1) do
|
65
|
+
mocha_verify
|
66
|
+
done!
|
67
|
+
end
|
68
|
+
wait!
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'if visual is activated' do
|
73
|
+
it 'displays a visual notification', :timeout => 0.2 do
|
74
|
+
Config.stubs(:visual).returns true
|
75
|
+
Notify.expects(:notify)
|
76
|
+
Client.work
|
77
|
+
|
78
|
+
EM.add_timer(0.1) do
|
79
|
+
mocha_verify
|
80
|
+
done!
|
81
|
+
end
|
82
|
+
wait!
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'otherwise' do
|
87
|
+
it 'does not display any visual notification', :timeout => 0.2 do
|
88
|
+
Config.stubs(:visual).returns false
|
89
|
+
Notify.expects(:notify).never
|
90
|
+
Client.work
|
91
|
+
|
92
|
+
EM.add_timer(0.1) do
|
93
|
+
mocha_verify
|
94
|
+
done!
|
95
|
+
end
|
96
|
+
wait!
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '.break' do
|
102
|
+
describe 'if sound is activated' do
|
103
|
+
it 'plays a sound', :timeout => 0.2 do
|
104
|
+
Config.stubs(:sound).returns true
|
105
|
+
Client.expects(:system)
|
106
|
+
Client.break
|
107
|
+
|
108
|
+
EM.add_timer(0.1) do
|
109
|
+
mocha_verify
|
110
|
+
done!
|
111
|
+
end
|
112
|
+
wait!
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
describe 'otherwise' do
|
118
|
+
it 'does not play a sound', :timeout => 0.2 do
|
119
|
+
Config.stubs(:sound).returns false
|
120
|
+
Client.expects(:system).never
|
121
|
+
Client.break
|
122
|
+
|
123
|
+
EM.add_timer(0.1) do
|
124
|
+
mocha_verify
|
125
|
+
done!
|
126
|
+
end
|
127
|
+
wait!
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe 'if visual is activated' do
|
132
|
+
it 'displays a visual notification', :timeout => 0.2 do
|
133
|
+
Config.stubs(:visual).returns true
|
134
|
+
Notify.expects(:notify)
|
135
|
+
Client.break
|
136
|
+
|
137
|
+
EM.add_timer(0.1) do
|
138
|
+
mocha_verify
|
139
|
+
done!
|
140
|
+
end
|
141
|
+
wait!
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe 'otherwise' do
|
146
|
+
it 'does not display any visual notification', :timeout => 0.2 do
|
147
|
+
Config.stubs(:visual).returns false
|
148
|
+
Notify.expects(:notify).never
|
149
|
+
Client.break
|
150
|
+
|
151
|
+
EM.add_timer(0.1) do
|
152
|
+
mocha_verify
|
153
|
+
done!
|
154
|
+
end
|
155
|
+
wait!
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Domodoro
|
4
|
+
describe Config do
|
5
|
+
describe 'if ~/.domodororc exists' do
|
6
|
+
it 'loads the config from a ~/.domodororc file' do
|
7
|
+
File.stubs(:exist?).returns true
|
8
|
+
File.stubs(:read).returns """
|
9
|
+
visual: true
|
10
|
+
sound: false
|
11
|
+
"""
|
12
|
+
Domodoro::Config.load
|
13
|
+
|
14
|
+
assert_equal true, Domodoro::Config.visual
|
15
|
+
assert_equal false, Domodoro::Config.sound
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'otherwise' do
|
20
|
+
it 'sets both options to true' do
|
21
|
+
File.stubs(:exist?).returns false
|
22
|
+
|
23
|
+
Domodoro::Config.load
|
24
|
+
|
25
|
+
assert_equal true, Domodoro::Config.visual
|
26
|
+
assert_equal true, Domodoro::Config.sound
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Domodoro
|
4
|
+
describe Server do
|
5
|
+
include EM::MiniTest::Spec
|
6
|
+
|
7
|
+
before do
|
8
|
+
@channel = Channel.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'initializes with a channel' do
|
12
|
+
server = Domodoro::Server.new(nil, @channel)
|
13
|
+
assert_equal @channel, server.channel
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'repeats every message broadcasted to the channel' do
|
17
|
+
server = Server.new(nil, @channel)
|
18
|
+
EM.start_server('127.0.0.1', 8888, Server, :app => server)
|
19
|
+
|
20
|
+
server.expects(:send_data).with(":start\n")
|
21
|
+
|
22
|
+
@channel << :start
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '.start' do
|
26
|
+
it 'opens a server with a new channel' do
|
27
|
+
Channel.expects(:new).returns @channel
|
28
|
+
EM.expects(:start_server).with('0.0.0.0', '8888', Server, @channel)
|
29
|
+
|
30
|
+
Server.start('0.0.0.0', '8888')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'adds a timer by second that calls broadcast every minute', :timeout => 4 do
|
34
|
+
Channel.expects(:new).returns(@channel).at_least(1)
|
35
|
+
Server.start('0.0.0.0', '8888')
|
36
|
+
|
37
|
+
module Tick
|
38
|
+
attr_accessor :num
|
39
|
+
extend self
|
40
|
+
end
|
41
|
+
|
42
|
+
Tick.num = 0
|
43
|
+
|
44
|
+
now = stub
|
45
|
+
Time.expects(:now).at_least(3).returns now
|
46
|
+
now.stubs(:hour).returns 0
|
47
|
+
now.stubs(:min).returns 0
|
48
|
+
now.stubs(:sec).returns 0
|
49
|
+
|
50
|
+
def @channel.broadcast(*args)
|
51
|
+
puts 'tick'
|
52
|
+
Tick.num += 1
|
53
|
+
end
|
54
|
+
|
55
|
+
EM.add_timer(3.5) do
|
56
|
+
assert_equal 3, Tick.num
|
57
|
+
done!
|
58
|
+
end
|
59
|
+
|
60
|
+
wait!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Domodoro do
|
4
|
+
describe '.start' do
|
5
|
+
|
6
|
+
describe 'as a server' do
|
7
|
+
it 'spawns a Domodoro server' do
|
8
|
+
port = stub
|
9
|
+
Domodoro::Server.expects(:start).with(port)
|
10
|
+
|
11
|
+
Domodoro.start 'serve', port
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'as a client' do
|
16
|
+
it 'starts a Domodoro client' do
|
17
|
+
host, port = stub, stub
|
18
|
+
Domodoro::Client.expects(:start).with(host, port)
|
19
|
+
|
20
|
+
Domodoro.start 'join', host, port
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'eventmachine'
|
3
|
+
|
4
|
+
module EM # :nodoc:
|
5
|
+
module MiniTest # :nodoc:
|
6
|
+
module Spec # :nodoc
|
7
|
+
VERSION = '1.1.0' # :nodoc:
|
8
|
+
|
9
|
+
##
|
10
|
+
# +wait+ indicates that the spec is not expected to be completed when
|
11
|
+
# the block is finished running. A call to +done+ is required when using
|
12
|
+
# +wait+.
|
13
|
+
#
|
14
|
+
# # setup my spec to use EM::MiniTest::Spec
|
15
|
+
# describe MyClass do
|
16
|
+
# include EM::MiniTest::Spec
|
17
|
+
#
|
18
|
+
# # The call to defer will return immediately, so the spec code
|
19
|
+
# # needs to keep running until callback is called.
|
20
|
+
# it "does some async things" do
|
21
|
+
# defer_me = lambda do
|
22
|
+
# # some async stuff
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# callback = lambda do
|
26
|
+
# done!
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# EM.defer defer_me, callback
|
30
|
+
#
|
31
|
+
# wait!
|
32
|
+
# end
|
33
|
+
def wait
|
34
|
+
@wait = true
|
35
|
+
end
|
36
|
+
alias wait! wait
|
37
|
+
|
38
|
+
##
|
39
|
+
# Indicates that an async spec is finished. See +wait+ for example usage.
|
40
|
+
def done
|
41
|
+
EM.cancel_timer(@timeout)
|
42
|
+
EM.stop
|
43
|
+
end
|
44
|
+
alias done! done
|
45
|
+
|
46
|
+
##
|
47
|
+
# A helper method for the use case of waiting for some operation to
|
48
|
+
# complete that is not necessarily under the control of the spec code.
|
49
|
+
#
|
50
|
+
# # These are exactly equivalent
|
51
|
+
# it "waits with the helper" do
|
52
|
+
# wait_for do
|
53
|
+
# assert true
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# it "waits manually" do
|
58
|
+
# EM.next_tick do
|
59
|
+
# assert true
|
60
|
+
# done!
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# wait!
|
64
|
+
# end
|
65
|
+
def wait_for
|
66
|
+
EM.next_tick do
|
67
|
+
yield
|
68
|
+
done!
|
69
|
+
end
|
70
|
+
|
71
|
+
wait!
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.included base # :nodoc:
|
75
|
+
base.extend(ClassMethods)
|
76
|
+
end
|
77
|
+
|
78
|
+
module ClassMethods # :nodoc:
|
79
|
+
def it *args, &block # :nodoc:
|
80
|
+
return super unless block_given?
|
81
|
+
|
82
|
+
timeout = 0.1
|
83
|
+
if args.last.is_a?(Hash) && args.last[:timeout]
|
84
|
+
timeout = args.pop[:timeout]
|
85
|
+
end
|
86
|
+
|
87
|
+
super do
|
88
|
+
@wait = false
|
89
|
+
|
90
|
+
EM.run do
|
91
|
+
@timeout = EM.add_timer(timeout) do
|
92
|
+
flunk "test timed out!"
|
93
|
+
end
|
94
|
+
|
95
|
+
instance_eval(&block)
|
96
|
+
done! unless @wait
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
gem 'minitest'
|
4
|
+
require 'mocha'
|
5
|
+
require 'minitest/spec'
|
6
|
+
require 'purdytest'
|
7
|
+
require 'minitest/autorun'
|
8
|
+
|
9
|
+
Dir['test/support/*.rb'].each do |file|
|
10
|
+
require file
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'domodoro'
|
14
|
+
|
15
|
+
class MiniTest::Unit::TestCase
|
16
|
+
include Mocha::API
|
17
|
+
def setup
|
18
|
+
mocha_setup
|
19
|
+
end
|
20
|
+
def teardown
|
21
|
+
mocha_teardown
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: domodoro
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 856480538658449761
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Josep M. Bach
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-08-28 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: eventmachine
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 2002549777813010636
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: notify
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 2002549777813010636
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: minitest
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 2002549777813010636
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: mocha
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 2002549777813010636
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :development
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: purdytest
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 2002549777813010636
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
type: :development
|
90
|
+
version_requirements: *id005
|
91
|
+
description: Distributed Pomodoro for the masses
|
92
|
+
email:
|
93
|
+
- josep.m.bach@gmail.com
|
94
|
+
executables:
|
95
|
+
- domodoro
|
96
|
+
extensions: []
|
97
|
+
|
98
|
+
extra_rdoc_files: []
|
99
|
+
|
100
|
+
files:
|
101
|
+
- .gitignore
|
102
|
+
- Gemfile
|
103
|
+
- Guardfile
|
104
|
+
- Rakefile
|
105
|
+
- Readme.md
|
106
|
+
- assets/start.wav
|
107
|
+
- assets/stop.wav
|
108
|
+
- bin/domodoro
|
109
|
+
- domodoro.gemspec
|
110
|
+
- lib/domodoro.rb
|
111
|
+
- lib/domodoro/channel.rb
|
112
|
+
- lib/domodoro/client.rb
|
113
|
+
- lib/domodoro/config.rb
|
114
|
+
- lib/domodoro/server.rb
|
115
|
+
- lib/domodoro/version.rb
|
116
|
+
- test/domodoro/channel_test.rb
|
117
|
+
- test/domodoro/client_test.rb
|
118
|
+
- test/domodoro/config_test.rb
|
119
|
+
- test/domodoro/server_test.rb
|
120
|
+
- test/domodoro_test.rb
|
121
|
+
- test/support/em-minitest.rb
|
122
|
+
- test/test_helper.rb
|
123
|
+
has_rdoc: true
|
124
|
+
homepage: http://github.com/txus/domodoro
|
125
|
+
licenses: []
|
126
|
+
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
none: false
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
hash: 2002549777813010636
|
138
|
+
segments:
|
139
|
+
- 0
|
140
|
+
version: "0"
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
hash: 2002549777813010636
|
147
|
+
segments:
|
148
|
+
- 0
|
149
|
+
version: "0"
|
150
|
+
requirements: []
|
151
|
+
|
152
|
+
rubyforge_project: domodoro
|
153
|
+
rubygems_version: 1.5.2
|
154
|
+
signing_key:
|
155
|
+
specification_version: 3
|
156
|
+
summary: Distributed Pomodoro for the masses
|
157
|
+
test_files:
|
158
|
+
- test/domodoro/channel_test.rb
|
159
|
+
- test/domodoro/client_test.rb
|
160
|
+
- test/domodoro/config_test.rb
|
161
|
+
- test/domodoro/server_test.rb
|
162
|
+
- test/domodoro_test.rb
|
163
|
+
- test/support/em-minitest.rb
|
164
|
+
- test/test_helper.rb
|