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
         
     |