eyecare 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 28fe112827d32de770483d3b5eaf9636b18c8ee6
4
- data.tar.gz: 744a0290be6f3433f9df56f3fb25fac29af2812d
3
+ metadata.gz: 8e004ee89fa1ccdaedc3c380870abda32967566f
4
+ data.tar.gz: 7657db390db6a9d5784df05d69147aee23bf1218
5
5
  SHA512:
6
- metadata.gz: 5306596df896dc1155036bc56a26e9e6ce36f5e5e17fcca1940614dd7b58ff747a7f71fcdd85e71aad34c8dd5a57c3c109d8b8a86866e102d8a936ff4f6281d8
7
- data.tar.gz: 1aa7b04e93905f680e4ffff061a11d709a193a40a837a0b50ba4fc95371f6ddf46e68e5f3ad4b60aee672b76d1e157523291f1912a152bc9f764ac54a068cd1f
6
+ metadata.gz: 861bec7f2f2fdabdfc16109f3c9d12df652bea2b8214b485ffe306e2112b9bf4eed60c3a80dd36c4e9109111daf0055ef51550e4fa7ec6b30770b5bd066ab4b0
7
+ data.tar.gz: 72ed5653993ecb3e8183213f7f9ff7db3280f5743fa1effbab148fd6b52d0a3824a1d0a9278fb817d11defc1675c2299042d048a27aec8e6809799a34c3a7d22
data/README.md CHANGED
@@ -2,7 +2,6 @@
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!
6
5
 
7
6
  ## Installation
8
7
 
data/eyecare.gemspec CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency 'ffi'
22
22
  spec.add_dependency 'libnotify'
23
23
  spec.add_dependency 'chronic_duration'
24
+ spec.add_dependency 'activesupport', '~> 4'
24
25
  spec.add_development_dependency 'bundler', '~> 1.5'
25
26
  spec.add_development_dependency 'rake'
26
27
  spec.add_development_dependency 'minitest'
data/lib/eyecare.rb CHANGED
@@ -6,6 +6,22 @@ require 'eyecare/daemon'
6
6
  require 'fileutils'
7
7
  require 'ffi'
8
8
 
9
+ class Hash
10
+ def deep_compact
11
+ dup.deep_compact!
12
+ end
13
+
14
+ def deep_compact!
15
+ each do |key, value|
16
+ if value.respond_to?(:deep_compact)
17
+ value = value.deep_compact
18
+ value = nil if value.length == 0
19
+ end
20
+ self[key] = value
21
+ end.compact
22
+ end
23
+ end
24
+
9
25
  module Eyecare
10
26
  extend FFI::Library
11
27
  ffi_lib FFI::Library::LIBC
@@ -22,7 +38,7 @@ module Eyecare
22
38
  attr_reader :config_path
23
39
 
24
40
  def run
25
- Daemon.start(config[:pid_file]) do
41
+ Daemon.start(config[:pid_file]) do
26
42
  while true
27
43
  seconds = config[:alert][:interval]
28
44
  while seconds > 0
@@ -47,14 +63,19 @@ module Eyecare
47
63
  return @config if @config
48
64
 
49
65
  config_file = File.expand_path(config_path)
50
- @config = Config.load_from_file(config_file) rescue Config.new
66
+
67
+ if File.exists?(config_file) && File.file?(config_file) && File.readable?(config_file)
68
+ @config = Config.load_from_file(config_file)
69
+ end
70
+
71
+ @config ||= Config.new
51
72
  end
52
73
 
53
74
  private
54
75
  def proc_name(name)
55
76
  $0 = name
56
77
  return false unless self.respond_to?(:prctl)
57
-
78
+
58
79
  name = name.slice(0, 16)
59
80
  ptr = FFI::MemoryPointer.from_string(name)
60
81
  self.prctl(15, ptr.address, 0, 0)
data/lib/eyecare/alert.rb CHANGED
@@ -10,19 +10,22 @@ module Eyecare
10
10
  attr_accessor :beep
11
11
  attr_accessor :icon_path
12
12
 
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]
13
+ DEFAULT_MESSAGE = Config::DEFAULTS[:alert][:message]
14
+ DEFAULT_TIMEOUT = Config::DEFAULTS[:alert][:timeout]
15
+ DEFAULT_BEEP_START = Config::DEFAULTS[:alert][:beep][:start]
16
+ DEFAULT_BEEP_END = Config::DEFAULTS[:alert][:beep][:end]
17
+ DEFAULT_BEEP_PLAYER = Config::DEFAULTS[:alert][:beep][:player]
18
+ DEFAULT_ICON_PATH = Config::DEFAULTS[:alert][:icon]
18
19
 
19
20
  class Beep
20
21
  attr_accessor :start
21
22
  attr_accessor :end
23
+ attr_accessor :player
22
24
 
23
25
  def initialize(options = {})
24
- @start = Eyecare::Audio.new(options[:start])
25
- @end = Eyecare::Audio.new(options[:end])
26
+ @player = options[:player]
27
+ @start = Eyecare::Audio.new(options[:start], options[:player])
28
+ @end = Eyecare::Audio.new(options[:end], options[:player])
26
29
  end
27
30
 
28
31
  def play(name)
@@ -37,12 +40,14 @@ module Eyecare
37
40
 
38
41
  beep_start = DEFAULT_BEEP_START
39
42
  beep_end = DEFAULT_BEEP_END
43
+ beep_player = DEFAULT_BEEP_PLAYER
40
44
  if options[:beep]
41
45
  beep_start = options[:beep][:start] if options[:beep][:start]
42
46
  beep_end = options[:beep][:end] if options[:beep][:end]
47
+ beep_player = options[:beep][:player] if options[:beep][:player]
43
48
  end
44
49
 
45
- @beep = Beep.new(start: beep_start, end: beep_end)
50
+ @beep = Beep.new(start: beep_start, end: beep_end, player: beep_player)
46
51
  self
47
52
  end
48
53
 
data/lib/eyecare/audio.rb CHANGED
@@ -1,13 +1,29 @@
1
1
  module Eyecare
2
2
  class Audio
3
3
  attr_accessor :filename
4
- def initialize(filename)
4
+ attr_accessor :player
5
+
6
+ DEFAULT_PLAYER = 'aplay :file'
7
+
8
+ def initialize(filename, player = nil)
5
9
  @filename = filename
10
+ @player = player ? player : DEFAULT_PLAYER
6
11
  end
7
12
 
8
13
  def play
9
- pid = spawn("aplay #{filename} > /dev/null 2>&1")
14
+ return unless player
15
+ pid = spawn(player_cmd + ' > /dev/null 2>&1')
10
16
  Process.detach(pid)
11
17
  end
18
+
19
+ private
20
+ def player_cmd
21
+ return @player_cmd if @player_cmd
22
+ @player_cmd = @player ? @player : DEFAULT_PLAYER
23
+ @player_cmd = @player_cmd.gsub(/:filename/, ':file')
24
+ .gsub(/:filepath/, ':file')
25
+ .gsub(/:file_path/, ':file')
26
+ .gsub(/:file/, filename)
27
+ end
12
28
  end
13
29
  end
@@ -1,18 +1,12 @@
1
1
  require 'yaml'
2
2
  require 'chronic_duration'
3
+ require 'active_support/core_ext/hash/keys'
4
+ require 'active_support/core_ext/hash/compact'
5
+ require 'active_support/core_ext/hash/deep_merge'
3
6
  require 'eyecare'
4
7
 
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
8
 
9
+ module Eyecare
16
10
  class Config
17
11
  ASSETS_PATH = File.expand_path(File.join(File.dirname(__FILE__), 'assets'))
18
12
  IMAGES_PATH = File.join(ASSETS_PATH, 'images')
@@ -25,6 +19,7 @@ module Eyecare
25
19
  interval: 20 * 60,
26
20
  icon: File.join(IMAGES_PATH, 'eyecare.png'),
27
21
  beep: {
22
+ player: "aplay :file",
28
23
  start: File.join(AUDIOS_PATH, 'beep_start.wav'),
29
24
  end: File.join(AUDIOS_PATH, 'beep_end.wav')
30
25
  }
@@ -33,12 +28,14 @@ module Eyecare
33
28
  pid_file: File.expand_path(File.join('.eyecare', 'eyecare.pid'), '~')
34
29
  }
35
30
 
31
+ attr_reader :options
32
+
36
33
  def initialize(options = {})
37
34
  @options = DEFAULTS
38
- if options.respond_to?(:symbolize_keys)
39
- options = options.symbolize_keys
35
+ if options.respond_to?(:deep_symbolize_keys)
36
+ options = options.deep_symbolize_keys.deep_compact
40
37
  options = parse_duration_values(options)
41
- @options.merge!(options)
38
+ @options = @options.deep_merge(options)
42
39
  end
43
40
  end
44
41
 
@@ -59,8 +56,11 @@ module Eyecare
59
56
  return options unless options.is_a?(Hash)
60
57
  if options[:alert].is_a?(Hash)
61
58
  [: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
59
+ if options[:alert][k]
60
+ options[:alert][k] = ChronicDuration.parse(options[:alert][k]) if options[:alert][k].is_a?(String)
61
+ options[:alert][k] = options[:alert][k].to_i
62
+ options[:alert].delete(k) if options[:alert][k] == 0
63
+ end
64
64
  end
65
65
  end
66
66
  options
@@ -1,3 +1,3 @@
1
1
  module Eyecare
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/test/alert_test.rb CHANGED
@@ -3,10 +3,13 @@ require 'eyecare/alert'
3
3
 
4
4
  describe Eyecare::Alert do
5
5
  let(:config) do
6
- {
6
+ {
7
7
  message: 'Blink blink',
8
8
  timeout: 40,
9
- interval: 1800
9
+ interval: 1800,
10
+ beep: {
11
+ player: 'fooplayer :file'
12
+ }
10
13
  }
11
14
  end
12
15
 
@@ -15,6 +18,7 @@ describe Eyecare::Alert do
15
18
  alert.message.wont_be_empty
16
19
  alert.message.must_equal Eyecare::Alert::DEFAULT_MESSAGE
17
20
  alert.timeout.must_equal Eyecare::Alert::DEFAULT_TIMEOUT
21
+ alert.beep.player.must_equal Eyecare::Alert::DEFAULT_BEEP_PLAYER
18
22
  end
19
23
 
20
24
  it 'correctly initialized from config hash' do
@@ -23,5 +27,6 @@ describe Eyecare::Alert do
23
27
  alert.message.wont_be_empty
24
28
  alert.message.must_equal config[:message]
25
29
  alert.timeout.must_equal config[:timeout]
30
+ alert.beep.player.must_equal config[:beep][:player]
26
31
  end
27
32
  end
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+ require 'eyecare/audio'
3
+
4
+ describe Eyecare::Alert do
5
+ it "player command is always valid" do
6
+ audio = Eyecare::Audio.new('foo.wav', 'fooplayer :file')
7
+ player_cmd = audio.send(:player_cmd)
8
+ player_cmd.must_equal 'fooplayer foo.wav'
9
+
10
+ audio = Eyecare::Audio.new('foo.wav', 'fooplayer :filename')
11
+ player_cmd = audio.send(:player_cmd)
12
+ player_cmd.must_equal 'fooplayer foo.wav'
13
+ end
14
+ end
data/test/config_test.rb CHANGED
@@ -55,8 +55,8 @@ describe Eyecare::Config do
55
55
  %"
56
56
  alert:
57
57
  message: 'This is a test message'
58
- timeout:
59
- interval:
58
+ timeout:
59
+ interval:
60
60
  beep:
61
61
  start: /path/to/beep/start.wav
62
62
  end: /path/to/beep/end.wav
@@ -81,8 +81,8 @@ describe Eyecare::Config do
81
81
  end
82
82
 
83
83
  it 'is malformed' do
84
- config = Eyecare::Config.load_from_text('fdaf-fff:fsdafd')
85
- assert_default(config)
84
+ config = Eyecare::Config.load_from_text(YAML.load('fdaf-fff:fsdafd'))
85
+ assert_default(config)
86
86
  end
87
87
 
88
88
  it 'is loaded from file' do
@@ -90,7 +90,7 @@ describe Eyecare::Config do
90
90
  f = Tempfile.new('config')
91
91
  f.write(config_text)
92
92
  f.close
93
-
93
+
94
94
  config = Eyecare::Config.load_from_file(f.path)
95
95
  f.unlink
96
96
  config[:alert][:message].wont_be_empty
data/test/eyecare_test.rb CHANGED
@@ -4,7 +4,12 @@ require 'tempfile'
4
4
  describe Eyecare do
5
5
  describe 'config' do
6
6
  it 'is default if config file is missing' do
7
- config = Eyecare.config
7
+ stub = Eyecare.dup
8
+ stub.instance_eval do
9
+ @config_path = ''
10
+ end
11
+
12
+ config = stub.config
8
13
  config[:alert][:message].wont_be_empty
9
14
  config[:alert][:message].must_equal Eyecare::Config::DEFAULTS[:alert][:message]
10
15
  config[:alert][:timeout].must_equal Eyecare::Config::DEFAULTS[:alert][:timeout]
@@ -14,7 +19,7 @@ describe Eyecare do
14
19
 
15
20
  it 'is loaded from file if file exists' do
16
21
  config_yml = %"
17
- alert:
22
+ alert:
18
23
  message: 'Yadayada'
19
24
  timeout: 10
20
25
  interval: 1000
@@ -24,13 +29,13 @@ describe Eyecare do
24
29
  config_file.write(config_yml)
25
30
  config_file.close
26
31
 
27
- EyecareStub = Eyecare.dup
28
- EyecareStub.instance_eval do
32
+ stub = Eyecare.dup
33
+ stub.instance_eval do
29
34
  @config = nil
30
35
  @config_path = config_file.path
31
36
  end
32
37
 
33
- config = EyecareStub.config
38
+ config = stub.config
34
39
 
35
40
  config[:alert][:message].wont_be_empty
36
41
  config[:alert][:message].must_equal 'Yadayada'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eyecare
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.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-19 00:00:00.000000000 Z
11
+ date: 2015-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -120,6 +134,7 @@ files:
120
134
  - lib/eyecare/daemon.rb
121
135
  - lib/eyecare/version.rb
122
136
  - test/alert_test.rb
137
+ - test/audio_test.rb
123
138
  - test/config_test.rb
124
139
  - test/eyecare_test.rb
125
140
  - test/test_helper.rb
@@ -143,12 +158,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
143
158
  version: '0'
144
159
  requirements: []
145
160
  rubyforge_project:
146
- rubygems_version: 2.4.1
161
+ rubygems_version: 2.4.5
147
162
  signing_key:
148
163
  specification_version: 4
149
164
  summary: Protect your eyes
150
165
  test_files:
151
166
  - test/alert_test.rb
167
+ - test/audio_test.rb
152
168
  - test/config_test.rb
153
169
  - test/eyecare_test.rb
154
170
  - test/test_helper.rb