eyecare 0.0.2 → 0.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.
- checksums.yaml +4 -4
- data/README.md +18 -5
- data/bin/eyecare +9 -1
- data/eyecare.gemspec +2 -0
- data/lib/eyecare/alert.rb +37 -31
- data/lib/eyecare/audio.rb +13 -0
- data/lib/eyecare/config.rb +53 -27
- data/lib/eyecare/daemon.rb +101 -0
- data/lib/eyecare/version.rb +1 -1
- data/lib/eyecare.rb +45 -20
- data/test/alert_test.rb +7 -3
- data/test/config_test.rb +108 -28
- data/test/eyecare_test.rb +45 -0
- metadata +35 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28fe112827d32de770483d3b5eaf9636b18c8ee6
|
4
|
+
data.tar.gz: 744a0290be6f3433f9df56f3fb25fac29af2812d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5306596df896dc1155036bc56a26e9e6ce36f5e5e17fcca1940614dd7b58ff747a7f71fcdd85e71aad34c8dd5a57c3c109d8b8a86866e102d8a936ff4f6281d8
|
7
|
+
data.tar.gz: 1aa7b04e93905f680e4ffff061a11d709a193a40a837a0b50ba4fc95371f6ddf46e68e5f3ad4b60aee672b76d1e157523291f1912a152bc9f764ac54a068cd1f
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
Reminds you to look away to avoid computer eye strain.
|
4
4
|
It is recommended to look away from your screen for 20 seconds every 20 minutes.
|
5
|
+
This might only work on Ubuntu!
|
5
6
|
|
6
7
|
## Installation
|
7
8
|
|
@@ -9,12 +10,24 @@ It is recommended to look away from your screen for 20 seconds every 20 minutes.
|
|
9
10
|
|
10
11
|
## Usage
|
11
12
|
|
12
|
-
$ eyecare
|
13
|
+
$ eyecare start
|
14
|
+
$ eyecare stop
|
13
15
|
|
14
|
-
|
16
|
+
The app will run as a daemon and pid file will be saved into ~/.eyecare/eyecare.pid
|
17
|
+
To see how much time left till next notification:
|
18
|
+
|
19
|
+
$ ps aux | grep Eyecare
|
20
|
+
|
21
|
+
|
22
|
+
Create a config file ~/.eyecare/config.yml
|
15
23
|
|
16
24
|
Config options are:
|
17
25
|
|
18
|
-
alert
|
19
|
-
|
20
|
-
|
26
|
+
alert:
|
27
|
+
message: 'Yor message here'
|
28
|
+
timeout: 20 seconds
|
29
|
+
interval: 20 minutes
|
30
|
+
icon: /path/to/icon.png
|
31
|
+
beep:
|
32
|
+
start: /path/to/audio.wav
|
33
|
+
end: /path/to/audio.wav
|
data/bin/eyecare
CHANGED
data/eyecare.gemspec
CHANGED
@@ -18,7 +18,9 @@ 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.add_dependency 'ffi'
|
21
22
|
spec.add_dependency 'libnotify'
|
23
|
+
spec.add_dependency 'chronic_duration'
|
22
24
|
spec.add_development_dependency 'bundler', '~> 1.5'
|
23
25
|
spec.add_development_dependency 'rake'
|
24
26
|
spec.add_development_dependency 'minitest'
|
data/lib/eyecare/alert.rb
CHANGED
@@ -1,25 +1,55 @@
|
|
1
1
|
require 'libnotify'
|
2
2
|
require 'singleton'
|
3
|
+
require 'eyecare/config'
|
3
4
|
|
4
5
|
module Eyecare
|
5
6
|
class Alert
|
6
7
|
include Singleton
|
7
8
|
attr_accessor :message
|
8
9
|
attr_accessor :timeout
|
10
|
+
attr_accessor :beep
|
11
|
+
attr_accessor :icon_path
|
9
12
|
|
10
|
-
DEFAULT_MESSAGE =
|
11
|
-
DEFAULT_TIMEOUT =
|
13
|
+
DEFAULT_MESSAGE = Config::DEFAULTS[:alert][:message]
|
14
|
+
DEFAULT_TIMEOUT = Config::DEFAULTS[:alert][:timeout]
|
15
|
+
DEFAULT_BEEP_START = File.join(Config::AUDIOS_PATH, 'beep_start.wav')
|
16
|
+
DEFAULT_BEEP_END = File.join(Config::AUDIOS_PATH, 'beep_end.wav')
|
17
|
+
DEFAULT_ICON_PATH = Config::DEFAULTS[:alert][:icon]
|
18
|
+
|
19
|
+
class Beep
|
20
|
+
attr_accessor :start
|
21
|
+
attr_accessor :end
|
22
|
+
|
23
|
+
def initialize(options = {})
|
24
|
+
@start = Eyecare::Audio.new(options[:start])
|
25
|
+
@end = Eyecare::Audio.new(options[:end])
|
26
|
+
end
|
27
|
+
|
28
|
+
def play(name)
|
29
|
+
self.send(name).play if [:start, :end].include?(name)
|
30
|
+
end
|
31
|
+
end
|
12
32
|
|
13
33
|
def init(options = {})
|
14
34
|
@message = options.fetch(:message, DEFAULT_MESSAGE)
|
15
35
|
@timeout = options.fetch(:timeout, DEFAULT_TIMEOUT)
|
36
|
+
@icon_path = options.fetch(:icon, DEFAULT_ICON_PATH)
|
37
|
+
|
38
|
+
beep_start = DEFAULT_BEEP_START
|
39
|
+
beep_end = DEFAULT_BEEP_END
|
40
|
+
if options[:beep]
|
41
|
+
beep_start = options[:beep][:start] if options[:beep][:start]
|
42
|
+
beep_end = options[:beep][:end] if options[:beep][:end]
|
43
|
+
end
|
44
|
+
|
45
|
+
@beep = Beep.new(start: beep_start, end: beep_end)
|
16
46
|
self
|
17
47
|
end
|
18
48
|
|
19
49
|
def show
|
20
|
-
|
50
|
+
beep.play(:start)
|
21
51
|
notification.show!
|
22
|
-
run_after(self.timeout) {
|
52
|
+
run_after(self.timeout) { beep.play(:end) }
|
23
53
|
end
|
24
54
|
|
25
55
|
private
|
@@ -27,35 +57,14 @@ module Eyecare
|
|
27
57
|
self.init
|
28
58
|
end
|
29
59
|
|
30
|
-
def icon_path
|
31
|
-
File.join(Eyecare::IMAGES_PATH, 'eyecare.png')
|
32
|
-
end
|
33
|
-
|
34
|
-
def beep_start
|
35
|
-
play_audio('beep_start.wav')
|
36
|
-
end
|
37
|
-
|
38
|
-
def beep_end
|
39
|
-
play_audio('beep_end.wav')
|
40
|
-
end
|
41
|
-
|
42
|
-
def play_audio(filename)
|
43
|
-
audio_path = File.join(Eyecare::AUDIOS_PATH, filename)
|
44
|
-
pid = spawn("aplay #{audio_path} > /dev/null 2>&1")
|
45
|
-
Process.detach(pid)
|
46
|
-
end
|
47
|
-
|
48
60
|
def run_after(timeout, &block)
|
49
|
-
|
50
|
-
|
51
|
-
yield
|
52
|
-
end
|
53
|
-
Process.detach(pid)
|
61
|
+
sleep(timeout)
|
62
|
+
yield
|
54
63
|
end
|
55
64
|
|
56
65
|
def notification
|
57
66
|
Libnotify.new do |s|
|
58
|
-
s.summary = '
|
67
|
+
s.summary = 'Eyecare'
|
59
68
|
s.body = self.message
|
60
69
|
s.timeout = self.timeout
|
61
70
|
s.urgency = :normal
|
@@ -64,6 +73,3 @@ module Eyecare
|
|
64
73
|
end
|
65
74
|
end
|
66
75
|
end
|
67
|
-
|
68
|
-
#puts Eyecare::ASSETS_PATH
|
69
|
-
#Eyecare::Alert.new.show
|
data/lib/eyecare/config.rb
CHANGED
@@ -1,43 +1,69 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'chronic_duration'
|
3
|
+
require 'eyecare'
|
4
|
+
|
1
5
|
module Eyecare
|
6
|
+
|
7
|
+
class ::Hash
|
8
|
+
def symbolize_keys
|
9
|
+
self.inject({}) do |h, (k, v)|
|
10
|
+
v = v.symbolize_keys if v.respond_to?(:symbolize_keys)
|
11
|
+
h.merge({ k.to_sym => v })
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
2
16
|
class Config
|
3
|
-
|
17
|
+
ASSETS_PATH = File.expand_path(File.join(File.dirname(__FILE__), 'assets'))
|
18
|
+
IMAGES_PATH = File.join(ASSETS_PATH, 'images')
|
19
|
+
AUDIOS_PATH = File.join(ASSETS_PATH, 'audios')
|
20
|
+
|
21
|
+
DEFAULTS = {
|
22
|
+
alert: {
|
23
|
+
message: 'Look away',
|
24
|
+
timeout: 20,
|
25
|
+
interval: 20 * 60,
|
26
|
+
icon: File.join(IMAGES_PATH, 'eyecare.png'),
|
27
|
+
beep: {
|
28
|
+
start: File.join(AUDIOS_PATH, 'beep_start.wav'),
|
29
|
+
end: File.join(AUDIOS_PATH, 'beep_end.wav')
|
30
|
+
}
|
31
|
+
},
|
4
32
|
|
5
|
-
|
6
|
-
|
7
|
-
|
33
|
+
pid_file: File.expand_path(File.join('.eyecare', 'eyecare.pid'), '~')
|
34
|
+
}
|
35
|
+
|
36
|
+
def initialize(options = {})
|
37
|
+
@options = DEFAULTS
|
38
|
+
if options.respond_to?(:symbolize_keys)
|
39
|
+
options = options.symbolize_keys
|
40
|
+
options = parse_duration_values(options)
|
41
|
+
@options.merge!(options)
|
42
|
+
end
|
8
43
|
end
|
9
44
|
|
10
45
|
def self.load_from_file(filename)
|
11
|
-
|
46
|
+
self.new(YAML.load_file(File.open(filename)))
|
12
47
|
end
|
13
48
|
|
14
49
|
def self.load_from_text(text)
|
15
|
-
|
16
|
-
config.instance_eval do eval(text) end
|
17
|
-
config
|
18
|
-
end
|
19
|
-
|
20
|
-
def to_h
|
21
|
-
{ alert: alert.to_hash }
|
50
|
+
self.new(YAML.load(text))
|
22
51
|
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Eyecare::Config::Alert
|
26
|
-
attr_accessor :message
|
27
|
-
attr_accessor :timeout
|
28
|
-
attr_accessor :interval
|
29
52
|
|
30
|
-
def
|
31
|
-
@
|
32
|
-
@interval = 20 * 60
|
53
|
+
def [](key)
|
54
|
+
@options[key]
|
33
55
|
end
|
34
56
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
57
|
+
private
|
58
|
+
def parse_duration_values(options)
|
59
|
+
return options unless options.is_a?(Hash)
|
60
|
+
if options[:alert].is_a?(Hash)
|
61
|
+
[:interval, :timeout].each do |k|
|
62
|
+
options[:alert][k] = ChronicDuration.parse(options[:alert][k]) if options[:alert][k].is_a?(String)
|
63
|
+
options[:alert][k] = options[:alert][k].to_i
|
64
|
+
end
|
65
|
+
end
|
66
|
+
options
|
41
67
|
end
|
42
68
|
end
|
43
69
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Eyecare
|
2
|
+
class Daemon
|
3
|
+
attr_accessor :pid_file
|
4
|
+
attr_accessor :out
|
5
|
+
attr_accessor :err
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
options = Hash[pid_file: options] if options.is_a?(String)
|
9
|
+
@pid_file = options[:pid_file]
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.start(options = {}, &block)
|
13
|
+
return false unless block_given?
|
14
|
+
daemon = self.new(options)
|
15
|
+
|
16
|
+
begin
|
17
|
+
daemon.send(:cleanup) if daemon.stale_pid?
|
18
|
+
if daemon.started?
|
19
|
+
puts 'Already started'
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
|
23
|
+
print 'Starting...'
|
24
|
+
Process.daemon(nil, true)
|
25
|
+
daemon.send(:write_pid)
|
26
|
+
|
27
|
+
trap('INT') { exit(0) }
|
28
|
+
at_exit { daemon.send(:cleanup) }
|
29
|
+
rescue StandardError => e
|
30
|
+
puts 'Error: Start failed'
|
31
|
+
puts e.message
|
32
|
+
end
|
33
|
+
|
34
|
+
puts "OK\n"
|
35
|
+
yield
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.stop(daemon)
|
39
|
+
daemon = Daemon.new(pid_file: daemon) if daemon.is_a?(String)
|
40
|
+
|
41
|
+
begin
|
42
|
+
unless daemon.started?
|
43
|
+
puts 'Not started or pid file is missing'
|
44
|
+
return false
|
45
|
+
end
|
46
|
+
|
47
|
+
print 'Stopping...'
|
48
|
+
pid = daemon.pid
|
49
|
+
return false unless daemon.running?
|
50
|
+
Process.kill('INT', pid)
|
51
|
+
puts 'OK'
|
52
|
+
rescue StandardError => e
|
53
|
+
puts 'Error: Stop failed'
|
54
|
+
puts e.inspect
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def started?
|
59
|
+
File.file?(pid_file)
|
60
|
+
end
|
61
|
+
|
62
|
+
def stale_pid?
|
63
|
+
!process_running?(read_pid) rescue false
|
64
|
+
end
|
65
|
+
|
66
|
+
def pid
|
67
|
+
read_pid rescue 0
|
68
|
+
end
|
69
|
+
|
70
|
+
def running?
|
71
|
+
process_running?(pid)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def process_running?(pid)
|
76
|
+
return false unless pid && pid != 0
|
77
|
+
system("kill -s 0 #{pid}")
|
78
|
+
end
|
79
|
+
|
80
|
+
def cleanup
|
81
|
+
File.unlink(pid_file) rescue false
|
82
|
+
end
|
83
|
+
|
84
|
+
def read_pid
|
85
|
+
pid = nil
|
86
|
+
File.open(pid_file, 'r') do |f|
|
87
|
+
pid = f.read
|
88
|
+
end
|
89
|
+
pid.to_i
|
90
|
+
end
|
91
|
+
|
92
|
+
def write_pid
|
93
|
+
FileUtils.mkdir_p(File.dirname(pid_file))
|
94
|
+
|
95
|
+
pid = Process.pid
|
96
|
+
File.open(pid_file, 'w') do |f|
|
97
|
+
f.write(pid)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/eyecare/version.rb
CHANGED
data/lib/eyecare.rb
CHANGED
@@ -1,40 +1,65 @@
|
|
1
1
|
require "eyecare/version"
|
2
2
|
require 'eyecare/alert'
|
3
|
+
require 'eyecare/audio'
|
3
4
|
require 'eyecare/config'
|
5
|
+
require 'eyecare/daemon'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'ffi'
|
4
8
|
|
5
9
|
module Eyecare
|
6
|
-
|
7
|
-
|
8
|
-
|
10
|
+
extend FFI::Library
|
11
|
+
ffi_lib FFI::Library::LIBC
|
12
|
+
|
13
|
+
begin
|
14
|
+
attach_function :prctl, [ :ulong, :ulong, :ulong, :ulong ], :int
|
15
|
+
rescue FFI::NotFoundError
|
16
|
+
# We couldn't find the method
|
17
|
+
end
|
18
|
+
|
19
|
+
@config_path = File.expand_path('~/.eyecare/config.yml')
|
9
20
|
|
10
21
|
class << self
|
11
|
-
|
12
|
-
Process.daemon
|
13
|
-
while true
|
14
|
-
sleep(config.alert.interval)
|
15
|
-
alert.show
|
16
|
-
end
|
17
|
-
end
|
22
|
+
attr_reader :config_path
|
18
23
|
|
19
24
|
def run
|
20
|
-
|
21
|
-
|
22
|
-
|
25
|
+
Daemon.start(config[:pid_file]) do
|
26
|
+
while true
|
27
|
+
seconds = config[:alert][:interval]
|
28
|
+
while seconds > 0
|
29
|
+
proc_name('Eyecare - %s' % ChronicDuration.output(seconds, :format => :short))
|
30
|
+
seconds -= 1
|
31
|
+
sleep(1)
|
32
|
+
end
|
33
|
+
alert.show
|
34
|
+
end
|
23
35
|
end
|
24
36
|
end
|
25
37
|
|
26
|
-
|
38
|
+
def stop
|
39
|
+
Daemon.stop(config[:pid_file])
|
40
|
+
end
|
41
|
+
|
27
42
|
def alert
|
28
|
-
Alert.instance.init(config
|
43
|
+
Alert.instance.init(config[:alert])
|
29
44
|
end
|
30
45
|
|
31
46
|
def config
|
32
47
|
return @config if @config
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
48
|
+
|
49
|
+
config_file = File.expand_path(config_path)
|
50
|
+
@config = Config.load_from_file(config_file) rescue Config.new
|
37
51
|
end
|
38
|
-
end
|
39
52
|
|
53
|
+
private
|
54
|
+
def proc_name(name)
|
55
|
+
$0 = name
|
56
|
+
return false unless self.respond_to?(:prctl)
|
57
|
+
|
58
|
+
name = name.slice(0, 16)
|
59
|
+
ptr = FFI::MemoryPointer.from_string(name)
|
60
|
+
self.prctl(15, ptr.address, 0, 0)
|
61
|
+
ensure
|
62
|
+
ptr.free if ptr
|
63
|
+
end
|
64
|
+
end
|
40
65
|
end
|
data/test/alert_test.rb
CHANGED
@@ -3,17 +3,21 @@ require 'eyecare/alert'
|
|
3
3
|
|
4
4
|
describe Eyecare::Alert do
|
5
5
|
let(:config) do
|
6
|
-
{
|
6
|
+
{
|
7
|
+
message: 'Blink blink',
|
8
|
+
timeout: 40,
|
9
|
+
interval: 1800
|
10
|
+
}
|
7
11
|
end
|
8
12
|
|
9
|
-
it '
|
13
|
+
it 'defaults when no config' do
|
10
14
|
alert = Eyecare::Alert.send(:new)
|
11
15
|
alert.message.wont_be_empty
|
12
16
|
alert.message.must_equal Eyecare::Alert::DEFAULT_MESSAGE
|
13
17
|
alert.timeout.must_equal Eyecare::Alert::DEFAULT_TIMEOUT
|
14
18
|
end
|
15
19
|
|
16
|
-
it '
|
20
|
+
it 'correctly initialized from config hash' do
|
17
21
|
alert = Eyecare::Alert.send(:new).init(config)
|
18
22
|
|
19
23
|
alert.message.wont_be_empty
|
data/test/config_test.rb
CHANGED
@@ -2,46 +2,126 @@ require 'test_helper'
|
|
2
2
|
require 'eyecare/config'
|
3
3
|
|
4
4
|
describe Eyecare::Config do
|
5
|
-
|
6
|
-
config
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
def assert_default(config)
|
6
|
+
config[:alert][:message].wont_be_empty
|
7
|
+
config[:alert][:message].must_equal Eyecare::Config::DEFAULTS[:alert][:message]
|
8
|
+
config[:alert][:timeout].must_equal Eyecare::Config::DEFAULTS[:alert][:timeout]
|
9
|
+
config[:alert][:interval].must_equal Eyecare::Config::DEFAULTS[:alert][:interval]
|
10
|
+
config[:alert][:beep][:start].must_equal Eyecare::Config::DEFAULTS[:alert][:beep][:start]
|
11
|
+
config[:alert][:beep][:end].must_equal Eyecare::Config::DEFAULTS[:alert][:beep][:end]
|
12
|
+
config[:pid_file].must_equal Eyecare::Config::DEFAULTS[:pid_file]
|
13
|
+
end
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
+
let(:config_text) do
|
16
|
+
%"
|
17
|
+
alert:
|
18
|
+
message: 'This is a test message'
|
19
|
+
timeout: 40
|
20
|
+
interval: 1800
|
21
|
+
beep:
|
22
|
+
start: /path/to/beep/start.wav
|
23
|
+
end: /path/to/beep/end.wav
|
24
|
+
pid_file: /path/to/my/pid/file.pid
|
25
|
+
"
|
15
26
|
end
|
16
27
|
|
17
|
-
|
18
|
-
|
19
|
-
alert
|
20
|
-
|
21
|
-
|
28
|
+
let(:friendly_config_text) do
|
29
|
+
%"
|
30
|
+
alert:
|
31
|
+
message: 'This is a test message'
|
32
|
+
timeout: 30 seconds
|
33
|
+
interval: 50 minutes
|
34
|
+
beep:
|
35
|
+
start: /path/to/beep/start.wav
|
36
|
+
end: /path/to/beep/end.wav
|
37
|
+
pid_file: /path/to/my/pid/file.pid
|
22
38
|
"
|
23
|
-
config = Eyecare::Config.load_from_text(text)
|
24
|
-
config.alert.message.wont_be_empty
|
25
|
-
config.alert.timeout.must_equal 40
|
26
|
-
config.alert.interval.must_equal 30 * 60
|
27
39
|
end
|
28
40
|
|
29
|
-
|
30
|
-
|
31
|
-
alert
|
32
|
-
|
33
|
-
|
41
|
+
let(:friendly_config_text_wrong) do
|
42
|
+
%"
|
43
|
+
alert:
|
44
|
+
message: 'This is a test message'
|
45
|
+
timeout: test
|
46
|
+
interval: blah
|
47
|
+
beep:
|
48
|
+
start: /path/to/beep/start.wav
|
49
|
+
end: /path/to/beep/end.wav
|
50
|
+
pid_file: /path/to/my/pid/file.pid
|
51
|
+
"
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:config_text_missing_values) do
|
55
|
+
%"
|
56
|
+
alert:
|
57
|
+
message: 'This is a test message'
|
58
|
+
timeout:
|
59
|
+
interval:
|
60
|
+
beep:
|
61
|
+
start: /path/to/beep/start.wav
|
62
|
+
end: /path/to/beep/end.wav
|
63
|
+
pid_file: /path/to/my/pid/file.pid
|
34
64
|
"
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'is loaded from text' do
|
68
|
+
config = Eyecare::Config.load_from_text(config_text)
|
69
|
+
config[:alert][:message].wont_be_empty
|
70
|
+
config[:alert][:message].must_equal 'This is a test message'
|
71
|
+
config[:alert][:timeout].must_equal 40
|
72
|
+
config[:alert][:interval].must_equal 30 * 60
|
73
|
+
config[:alert][:beep][:start].must_equal '/path/to/beep/start.wav'
|
74
|
+
config[:alert][:beep][:end].must_equal '/path/to/beep/end.wav'
|
75
|
+
config[:pid_file].must_equal '/path/to/my/pid/file.pid'
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'is empty' do
|
79
|
+
config = Eyecare::Config.load_from_text('')
|
80
|
+
assert_default(config)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'is malformed' do
|
84
|
+
config = Eyecare::Config.load_from_text('fdaf-fff:fsdafd')
|
85
|
+
assert_default(config)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'is loaded from file' do
|
35
89
|
require 'tempfile'
|
36
90
|
f = Tempfile.new('config')
|
37
|
-
f.write(
|
91
|
+
f.write(config_text)
|
38
92
|
f.close
|
39
93
|
|
40
94
|
config = Eyecare::Config.load_from_file(f.path)
|
41
95
|
f.unlink
|
42
|
-
config
|
43
|
-
config
|
44
|
-
config
|
96
|
+
config[:alert][:message].wont_be_empty
|
97
|
+
config[:alert][:message].must_equal 'This is a test message'
|
98
|
+
config[:alert][:timeout].must_equal 40
|
99
|
+
config[:alert][:interval].must_equal 30 * 60
|
100
|
+
config[:alert][:beep][:start].must_equal '/path/to/beep/start.wav'
|
101
|
+
config[:alert][:beep][:end].must_equal '/path/to/beep/end.wav'
|
102
|
+
config[:pid_file].must_equal '/path/to/my/pid/file.pid'
|
45
103
|
end
|
46
|
-
end
|
47
104
|
|
105
|
+
it 'friendly config is parsed correctly' do
|
106
|
+
config = Eyecare::Config.load_from_text(friendly_config_text)
|
107
|
+
config[:alert][:message].must_equal 'This is a test message'
|
108
|
+
config[:alert][:interval].must_equal 60 * 50
|
109
|
+
config[:alert][:timeout].must_equal 30
|
110
|
+
config[:alert][:beep][:start].must_equal '/path/to/beep/start.wav'
|
111
|
+
config[:alert][:beep][:end].must_equal '/path/to/beep/end.wav'
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'friendly config is has wrong values' do
|
115
|
+
config = Eyecare::Config.load_from_text(friendly_config_text_wrong)
|
116
|
+
config[:alert][:message].must_equal 'This is a test message'
|
117
|
+
config[:alert][:interval].must_equal Eyecare::Config::DEFAULTS[:alert][:interval]
|
118
|
+
config[:alert][:timeout].must_equal Eyecare::Config::DEFAULTS[:alert][:timeout]
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'has missing values' do
|
122
|
+
config = Eyecare::Config.load_from_text(config_text_missing_values)
|
123
|
+
config[:alert][:message].must_equal 'This is a test message'
|
124
|
+
config[:alert][:interval].must_equal Eyecare::Config::DEFAULTS[:alert][:interval]
|
125
|
+
config[:alert][:timeout].must_equal Eyecare::Config::DEFAULTS[:alert][:timeout]
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
describe Eyecare do
|
5
|
+
describe 'config' do
|
6
|
+
it 'is default if config file is missing' do
|
7
|
+
config = Eyecare.config
|
8
|
+
config[:alert][:message].wont_be_empty
|
9
|
+
config[:alert][:message].must_equal Eyecare::Config::DEFAULTS[:alert][:message]
|
10
|
+
config[:alert][:timeout].must_equal Eyecare::Config::DEFAULTS[:alert][:timeout]
|
11
|
+
config[:alert][:interval].must_equal Eyecare::Config::DEFAULTS[:alert][:interval]
|
12
|
+
config[:pid_file].must_equal Eyecare::Config::DEFAULTS[:pid_file]
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'is loaded from file if file exists' do
|
16
|
+
config_yml = %"
|
17
|
+
alert:
|
18
|
+
message: 'Yadayada'
|
19
|
+
timeout: 10
|
20
|
+
interval: 1000
|
21
|
+
pid_file: '/path/to/pid/file.pid'
|
22
|
+
"
|
23
|
+
config_file = Tempfile.new('config')
|
24
|
+
config_file.write(config_yml)
|
25
|
+
config_file.close
|
26
|
+
|
27
|
+
EyecareStub = Eyecare.dup
|
28
|
+
EyecareStub.instance_eval do
|
29
|
+
@config = nil
|
30
|
+
@config_path = config_file.path
|
31
|
+
end
|
32
|
+
|
33
|
+
config = EyecareStub.config
|
34
|
+
|
35
|
+
config[:alert][:message].wont_be_empty
|
36
|
+
config[:alert][:message].must_equal 'Yadayada'
|
37
|
+
config[:alert][:timeout].must_equal 10
|
38
|
+
config[:alert][:interval].must_equal 1000
|
39
|
+
config[:pid_file].must_equal '/path/to/pid/file.pid'
|
40
|
+
|
41
|
+
config_file.unlink
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eyecare
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Viorel Craescu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: libnotify
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -24,6 +38,20 @@ dependencies:
|
|
24
38
|
- - ">="
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: chronic_duration
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: bundler
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,10 +115,13 @@ files:
|
|
87
115
|
- lib/eyecare/assets/audios/beep_start.wav
|
88
116
|
- lib/eyecare/assets/images/eye.png
|
89
117
|
- lib/eyecare/assets/images/eyecare.png
|
118
|
+
- lib/eyecare/audio.rb
|
90
119
|
- lib/eyecare/config.rb
|
120
|
+
- lib/eyecare/daemon.rb
|
91
121
|
- lib/eyecare/version.rb
|
92
122
|
- test/alert_test.rb
|
93
123
|
- test/config_test.rb
|
124
|
+
- test/eyecare_test.rb
|
94
125
|
- test/test_helper.rb
|
95
126
|
homepage: ''
|
96
127
|
licenses:
|
@@ -112,11 +143,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
143
|
version: '0'
|
113
144
|
requirements: []
|
114
145
|
rubyforge_project:
|
115
|
-
rubygems_version: 2.
|
146
|
+
rubygems_version: 2.4.1
|
116
147
|
signing_key:
|
117
148
|
specification_version: 4
|
118
149
|
summary: Protect your eyes
|
119
150
|
test_files:
|
120
151
|
- test/alert_test.rb
|
121
152
|
- test/config_test.rb
|
153
|
+
- test/eyecare_test.rb
|
122
154
|
- test/test_helper.rb
|