guard 0.7.0 → 0.8.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.
@@ -1,41 +1,60 @@
1
1
  module Guard
2
+
3
+ # Listener implementation for Mac OS X `FSEvents`.
4
+ #
2
5
  class Darwin < Listener
3
6
 
7
+ # Initialize the Listener.
8
+ #
4
9
  def initialize(*)
5
10
  super
6
11
  @fsevent = FSEvent.new
7
12
  end
8
13
 
9
- def worker
10
- @fsevent
11
- end
12
-
14
+ # Start the listener.
15
+ #
13
16
  def start
14
17
  super
15
18
  worker.run
16
19
  end
17
20
 
21
+ # Stop the listener.
22
+ #
18
23
  def stop
19
24
  super
20
25
  worker.stop
21
26
  end
22
27
 
28
+ # Check if the listener is usable on the current OS.
29
+ #
30
+ # @return [Boolean] whether usable or not
31
+ #
23
32
  def self.usable?
24
33
  require 'rb-fsevent'
25
34
  if !defined?(FSEvent::VERSION) || (defined?(Gem::Version) &&
26
35
  Gem::Version.new(FSEvent::VERSION) < Gem::Version.new('0.4.0'))
27
- UI.info "Please update rb-fsevent (>= 0.4.0)"
36
+ UI.info 'Please update rb-fsevent (>= 0.4.0)'
28
37
  false
29
38
  else
30
39
  true
31
40
  end
32
41
  rescue LoadError
33
- UI.info "Please install rb-fsevent gem for Mac OSX FSEvents support"
42
+ UI.info 'Please install rb-fsevent gem for Mac OSX FSEvents support'
34
43
  false
35
44
  end
36
45
 
37
- private
46
+ private
47
+
48
+ # Get the listener worker.
49
+ #
50
+ def worker
51
+ @fsevent
52
+ end
38
53
 
54
+ # Watch the given directory for file changes.
55
+ #
56
+ # @param [String] directory the directory to watch
57
+ #
39
58
  def watch(directory)
40
59
  worker.watch(directory) do |modified_dirs|
41
60
  files = modified_files(modified_dirs)
@@ -1,6 +1,11 @@
1
1
  module Guard
2
+
3
+ # Listener implementation for Linux `inotify`.
4
+ #
2
5
  class Linux < Listener
3
6
 
7
+ # Initialize the Listener.
8
+ #
4
9
  def initialize(*)
5
10
  super
6
11
  @inotify = INotify::Notifier.new
@@ -8,44 +13,53 @@ module Guard
8
13
  @latency = 0.5
9
14
  end
10
15
 
16
+ # Start the listener.
17
+ #
11
18
  def start
12
19
  @stop = false
13
20
  super
14
21
  watch_change unless watch_change?
15
22
  end
16
23
 
24
+ # Stop the listener.
25
+ #
17
26
  def stop
18
27
  super
19
28
  @stop = true
20
29
  sleep(@latency)
21
30
  end
22
31
 
32
+ # Check if the listener is usable on the current OS.
33
+ #
34
+ # @return [Boolean] whether usable or not
35
+ #
23
36
  def self.usable?
24
37
  require 'rb-inotify'
25
38
  if !defined?(INotify::VERSION) || (defined?(Gem::Version) &&
26
39
  Gem::Version.new(INotify::VERSION.join('.')) < Gem::Version.new('0.8.5'))
27
- UI.info "Please update rb-inotify (>= 0.8.5)"
40
+ UI.info 'Please update rb-inotify (>= 0.8.5)'
28
41
  false
29
42
  else
30
43
  true
31
44
  end
32
45
  rescue LoadError
33
- UI.info "Please install rb-inotify gem for Linux inotify support"
46
+ UI.info 'Please install rb-inotify gem for Linux inotify support'
34
47
  false
35
48
  end
36
49
 
37
- def watch_change?
38
- !!@watch_change
39
- end
40
-
41
- private
50
+ private
42
51
 
52
+ # Get the listener worker.
53
+ #
43
54
  def worker
44
55
  @inotify
45
56
  end
46
57
 
58
+ # Watch the given directory for file changes.
59
+ #
60
+ # @param [String] directory the directory to watch
61
+ #
47
62
  def watch(directory)
48
- # The event selection is based on https://github.com/guard/guard/wiki/Analysis-of-inotify-events-for-different-editors
49
63
  worker.watch(directory, :recursive, :attrib, :create, :move_self, :close_write) do |event|
50
64
  unless event.name == "" # Event on root directory
51
65
  @files << event.absolute_name
@@ -54,6 +68,16 @@ module Guard
54
68
  rescue Interrupt
55
69
  end
56
70
 
71
+ # Test if inotify is watching for changes.
72
+ #
73
+ # @return [Boolean] whether inotify is active or not
74
+ #
75
+ def watch_change?
76
+ !!@watch_change
77
+ end
78
+
79
+ # Watch for file system changes.
80
+ #
57
81
  def watch_change
58
82
  @watch_change = true
59
83
  until @stop
@@ -1,24 +1,46 @@
1
1
  module Guard
2
+
3
+ # Polling listener that works cross-platform and
4
+ # has no dependencies. This is the listener that
5
+ # uses the most CPU processing power and has higher
6
+ # file IO that the other implementations.
7
+ #
2
8
  class Polling < Listener
3
9
 
10
+ # Initialize the Listener.
11
+ #
4
12
  def initialize(*)
5
13
  super
6
14
  @latency = 1.5
7
15
  end
8
16
 
17
+ # Start the listener.
18
+ #
9
19
  def start
10
20
  @stop = false
11
21
  super
12
22
  watch_change
13
23
  end
14
24
 
25
+ # Stop the listener.
26
+ #
15
27
  def stop
16
28
  super
17
29
  @stop = true
18
30
  end
19
31
 
20
- private
32
+ # Watch the given directory for file changes.
33
+ #
34
+ # @param [String] directory the directory to watch
35
+ #
36
+ def watch(directory)
37
+ @existing = all_files
38
+ end
39
+
40
+ private
21
41
 
42
+ # Watch for file system changes.
43
+ #
22
44
  def watch_change
23
45
  until @stop
24
46
  start = Time.now.to_f
@@ -29,9 +51,5 @@ module Guard
29
51
  end
30
52
  end
31
53
 
32
- def watch(directory)
33
- @existing = all_files
34
- end
35
-
36
54
  end
37
55
  end
@@ -1,35 +1,48 @@
1
1
  module Guard
2
+
3
+ # Listener implementation for Windows `fchange`.
4
+ #
2
5
  class Windows < Listener
3
6
 
7
+ # Initialize the Listener.
8
+ #
4
9
  def initialize(*)
5
10
  super
6
11
  @fchange = FChange::Notifier.new
7
12
  end
8
13
 
14
+ # Start the listener.
15
+ #
9
16
  def start
10
17
  super
11
18
  worker.run
12
19
  end
13
20
 
21
+ # Stop the listener.
22
+ #
14
23
  def stop
15
24
  super
16
25
  worker.stop
17
26
  end
18
27
 
28
+ # Check if the listener is usable on the current OS.
29
+ #
30
+ # @return [Boolean] whether usable or not
31
+ #
19
32
  def self.usable?
20
33
  require 'rb-fchange'
21
34
  true
22
35
  rescue LoadError
23
- UI.info "Please install rb-fchange gem for Windows file events support"
36
+ UI.info 'Please install rb-fchange gem for Windows file events support'
24
37
  false
25
38
  end
26
39
 
27
- private
28
-
29
- def worker
30
- @fchange
31
- end
40
+ private
32
41
 
42
+ # Watch the given directory for file changes.
43
+ #
44
+ # @param [String] directory the directory to watch
45
+ #
33
46
  def watch(directory)
34
47
  worker.watch(directory, :all_events, :recursive) do |event|
35
48
  paths = [File.expand_path(event.watcher.path)]
@@ -38,5 +51,11 @@ module Guard
38
51
  end
39
52
  end
40
53
 
54
+ # Get the listener worker.
55
+ #
56
+ def worker
57
+ @fchange
58
+ end
59
+
41
60
  end
42
61
  end
@@ -3,13 +3,29 @@ require 'pathname'
3
3
  require 'guard/ui'
4
4
 
5
5
  module Guard
6
+
7
+ # The notifier class handles cross-platform system notifications that supports:
8
+ #
9
+ # - Growl on Mac OS X
10
+ # - Libnotify on Linux
11
+ # - Notifu on Windows
12
+ #
6
13
  module Notifier
14
+
15
+ # Application name as shown in the specific notification settings
7
16
  APPLICATION_NAME = "Guard"
8
17
 
18
+ # Turn notifications off.
19
+ #
9
20
  def self.turn_off
10
21
  ENV["GUARD_NOTIFY"] = 'false'
11
22
  end
12
23
 
24
+ # Turn notifications on. This tries to load the platform
25
+ # specific notification library.
26
+ #
27
+ # @return [Boolean] whether the notification could be enabled.
28
+ #
13
29
  def self.turn_on
14
30
  ENV["GUARD_NOTIFY"] = 'true'
15
31
  case RbConfig::CONFIG['target_os']
@@ -22,6 +38,14 @@ module Guard
22
38
  end
23
39
  end
24
40
 
41
+ # Show a message with the system notification.
42
+ #
43
+ # @see .image_path
44
+ #
45
+ # @param [String] the message to show
46
+ # @option options [Symbol, String] image the image symbol or path to an image
47
+ # @option options [String] title the notification title
48
+ #
25
49
  def self.notify(message, options = {})
26
50
  if enabled?
27
51
  image = options.delete(:image) || :success
@@ -38,13 +62,24 @@ module Guard
38
62
  end
39
63
  end
40
64
 
65
+ # Test if the notifications are enabled and available.
66
+ #
67
+ # @return [Boolean] whether the notifications are available
68
+ #
41
69
  def self.enabled?
42
70
  ENV["GUARD_NOTIFY"] == 'true'
43
71
  end
44
72
 
45
73
  private
46
74
 
47
- def self.notify_mac(title, message, image, options)
75
+ # Send a message to Growl either with the `growl` gem or the `growl_notify` gem.
76
+ #
77
+ # @param [String] title the notification title
78
+ # @param [String] message the message to show
79
+ # @param [Symbol, String] the image to user
80
+ # @param [Hash] options the growl options
81
+ #
82
+ def self.notify_mac(title, message, image, options = {})
48
83
  require_growl # need for guard-rspec formatter that is called out of guard scope
49
84
 
50
85
  default_options = { :title => title, :icon => image_path(image), :name => APPLICATION_NAME }
@@ -61,18 +96,43 @@ module Guard
61
96
  end
62
97
  end
63
98
 
64
- def self.notify_linux(title, message, image, options)
99
+ # Send a message to libnotify.
100
+ #
101
+ # @param [String] title the notification title
102
+ # @param [String] message the message to show
103
+ # @param [Symbol, String] the image to user
104
+ # @param [Hash] options the libnotify options
105
+ #
106
+ def self.notify_linux(title, message, image, options = {})
65
107
  require_libnotify # need for guard-rspec formatter that is called out of guard scope
66
108
  default_options = { :body => message, :summary => title, :icon_path => image_path(image), :transient => true }
67
109
  Libnotify.show default_options.merge(options) if enabled?
68
110
  end
69
111
 
70
- def self.notify_windows(title, message, image, options)
112
+ # Send a message to notifu.
113
+ #
114
+ # @param [String] title the notification title
115
+ # @param [String] message the message to show
116
+ # @param [Symbol, String] the image to user
117
+ # @param [Hash] options the notifu options
118
+ #
119
+ def self.notify_windows(title, message, image, options = {})
71
120
  require_rbnotifu # need for guard-rspec formatter that is called out of guard scope
72
121
  default_options = { :message => message, :title => title, :type => image_level(image), :time => 3 }
73
122
  Notifu.show default_options.merge(options) if enabled?
74
123
  end
75
124
 
125
+ # Get the image path for an image symbol.
126
+ #
127
+ # Known symbols are:
128
+ #
129
+ # - failed
130
+ # - pending
131
+ # - success
132
+ #
133
+ # @param [Symbol] image the image name
134
+ # @return [String] the image path
135
+ #
76
136
  def self.image_path(image)
77
137
  images_path = Pathname.new(File.dirname(__FILE__)).join('../../images')
78
138
  case image
@@ -88,6 +148,11 @@ module Guard
88
148
  end
89
149
  end
90
150
 
151
+ # The notification level type for the given image.
152
+ #
153
+ # @param [Symbol] image the image
154
+ # @return [Symbol] the level
155
+ #
91
156
  def self.image_level(image)
92
157
  case image
93
158
  when :failed
@@ -101,6 +166,9 @@ module Guard
101
166
  end
102
167
  end
103
168
 
169
+ # Try to safely load growl and turns notifications
170
+ # off on load failure.
171
+ #
104
172
  def self.require_growl
105
173
  begin
106
174
  require 'growl_notify'
@@ -119,6 +187,9 @@ module Guard
119
187
  UI.info "Please install growl_notify or growl gem for Mac OS X notification support and add it to your Gemfile"
120
188
  end
121
189
 
190
+ # Try to safely load libnotify and turns notifications
191
+ # off on load failure.
192
+ #
122
193
  def self.require_libnotify
123
194
  require 'libnotify'
124
195
  rescue LoadError
@@ -126,11 +197,15 @@ module Guard
126
197
  UI.info "Please install libnotify gem for Linux notification support and add it to your Gemfile"
127
198
  end
128
199
 
200
+ # Try to safely load rb-notifu and turns notifications
201
+ # off on load failure.
202
+ #
129
203
  def self.require_rbnotifu
130
204
  require 'rb-notifu'
131
205
  rescue LoadError
132
206
  turn_off
133
207
  UI.info "Please install rb-notifu gem for Windows notification support and add it to your Gemfile"
134
208
  end
209
+
135
210
  end
136
211
  end
data/lib/guard/ui.rb CHANGED
@@ -1,88 +1,88 @@
1
1
  module Guard
2
- module UI
3
-
4
- ANSI_ESCAPE_BRIGHT = "1"
5
-
6
- ANSI_ESCAPE_BLACK = "30"
7
- ANSI_ESCAPE_RED = "31"
8
- ANSI_ESCAPE_GREEN = "32"
9
- ANSI_ESCAPE_YELLOW = "33"
10
- ANSI_ESCAPE_BLUE = "34"
11
- ANSI_ESCAPE_MAGENTA = "35"
12
- ANSI_ESCAPE_CYAN = "36"
13
- ANSI_ESCAPE_WHITE = "37"
14
-
15
- ANSI_ESCAPE_BGBLACK = "40"
16
- ANSI_ESCAPE_BGRED = "41"
17
- ANSI_ESCAPE_BGGREEN = "42"
18
- ANSI_ESCAPE_BGYELLOW = "43"
19
- ANSI_ESCAPE_BGBLUE = "44"
20
- ANSI_ESCAPE_BGMAGENTA = "45"
21
- ANSI_ESCAPE_BGCYAN = "46"
22
- ANSI_ESCAPE_BGWHITE = "47"
23
2
 
3
+ # The UI class helps to format messages for the user.
4
+ #
5
+ module UI
24
6
  class << self
25
7
 
26
8
  color_enabled = nil
27
9
 
28
- def info(message, options = {})
29
- unless ENV["GUARD_ENV"] == "test"
10
+ # Show an info message.
11
+ #
12
+ # @param [String] message the message to show
13
+ # @option options [Boolean] reset whether to clean the output before
14
+ #
15
+ def info(message, options = { })
16
+ unless ENV['GUARD_ENV'] == 'test'
30
17
  reset_line if options[:reset]
31
18
  puts color(message) if message != ''
32
19
  end
33
20
  end
34
21
 
35
- def error(message, options={})
36
- unless ENV["GUARD_ENV"] == "test"
22
+ # Show a red error message that is prefixed with ERROR.
23
+ #
24
+ # @param [String] message the message to show
25
+ # @option options [Boolean] reset whether to clean the output before
26
+ #
27
+ def error(message, options = { })
28
+ unless ENV['GUARD_ENV'] == 'test'
37
29
  reset_line if options[:reset]
38
30
  puts color('ERROR: ', :red) + message
39
31
  end
40
32
  end
41
33
 
42
- def deprecation(message, options = {})
43
- unless ENV["GUARD_ENV"] == "test"
34
+ # Show a red deprecation message that is prefixed with DEPRECATION.
35
+ #
36
+ # @param [String] message the message to show
37
+ # @option options [Boolean] reset whether to clean the output before
38
+ #
39
+ def deprecation(message, options = { })
40
+ unless ENV['GUARD_ENV'] == 'test'
44
41
  reset_line if options[:reset]
45
42
  puts color('DEPRECATION: ', :red) + message
46
43
  end
47
44
  end
48
45
 
49
- def debug(message, options={})
50
- unless ENV["GUARD_ENV"] == "test"
46
+ # Show a debug message that is prefixed with DEBUG and a timestamp.
47
+ #
48
+ # @param [String] message the message to show
49
+ # @option options [Boolean] reset whether to clean the output before
50
+ #
51
+ def debug(message, options = { })
52
+ unless ENV['GUARD_ENV'] == 'test'
51
53
  reset_line if options[:reset]
52
54
  puts color("DEBUG (#{Time.now.strftime('%T')}): ", :yellow) + message if ::Guard.options && ::Guard.options[:debug]
53
55
  end
54
56
  end
55
57
 
58
+ # Reset a line.
59
+ #
56
60
  def reset_line
57
61
  print(color_enabled? ? "\r\e[0m" : "\r\n")
58
62
  end
59
63
 
64
+ # Clear the output.
65
+ #
60
66
  def clear
61
- system("clear;")
67
+ system('clear;')
62
68
  end
63
69
 
64
- private
70
+ private
65
71
 
72
+ # Reset a color sequence.
73
+ #
66
74
  # @deprecated
75
+ # @param [String] text the text
76
+ #
67
77
  def reset_color(text)
68
- deprecation('UI.reset_color(text) is deprecated, please use color(text, "") instead.')
69
- color(text, "")
70
- end
71
-
72
- def color(text, *color_options)
73
- color_code = ""
74
- color_options.each do |color_option|
75
- color_option = color_option.to_s
76
- if color_option != ""
77
- if !(color_option =~ /\d+/)
78
- color_option = const_get("ANSI_ESCAPE_#{color_option.upcase}")
79
- end
80
- color_code += ";" + color_option
81
- end
82
- end
83
- color_enabled? ? "\e[0#{color_code}m#{text}\e[0m" : text
78
+ deprecation('UI.reset_color(text) is deprecated, please use color(text, ' ') instead.')
79
+ color(text, '')
84
80
  end
85
81
 
82
+ # Checks if color output can be enabled.
83
+ #
84
+ # @return [Boolean] whether color is enabled or not
85
+ #
86
86
  def color_enabled?
87
87
  if @color_enabled.nil?
88
88
  if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
@@ -102,9 +102,87 @@ module Guard
102
102
  @color_enabled = true
103
103
  end
104
104
  end
105
+
105
106
  @color_enabled
106
107
  end
107
108
 
109
+ # Colorizes a text message. See the constant in the UI class for possible
110
+ # color_options parameters. You can pass optionally :bright, a foreground
111
+ # color and a background color.
112
+ #
113
+ # @example
114
+ #
115
+ # color('Hello World', :red, :bright)
116
+ #
117
+ # @param [String] the text to colorize
118
+ # @param [Array] color_options the color options
119
+ #
120
+ def color(text, *color_options)
121
+ color_code = ''
122
+ color_options.each do |color_option|
123
+ color_option = color_option.to_s
124
+ if color_option != ''
125
+ if !(color_option =~ /\d+/)
126
+ color_option = const_get("ANSI_ESCAPE_#{ color_option.upcase }")
127
+ end
128
+ color_code += ';' + color_option
129
+ end
130
+ end
131
+ color_enabled? ? "\e[0#{ color_code }m#{ text }\e[0m" : text
132
+ end
133
+
108
134
  end
135
+
136
+ # Brighten the color
137
+ ANSI_ESCAPE_BRIGHT = '1'
138
+
139
+ # Black foreground color
140
+ ANSI_ESCAPE_BLACK = '30'
141
+
142
+ # Red foreground color
143
+ ANSI_ESCAPE_RED = '31'
144
+
145
+ # Green foreground color
146
+ ANSI_ESCAPE_GREEN = '32'
147
+
148
+ # Yellow foreground color
149
+ ANSI_ESCAPE_YELLOW = '33'
150
+
151
+ # Blue foreground color
152
+ ANSI_ESCAPE_BLUE = '34'
153
+
154
+ # Magenta foreground color
155
+ ANSI_ESCAPE_MAGENTA = '35'
156
+
157
+ # Cyan foreground color
158
+ ANSI_ESCAPE_CYAN = '36'
159
+
160
+ # White foreground color
161
+ ANSI_ESCAPE_WHITE = '37'
162
+
163
+ # Black background color
164
+ ANSI_ESCAPE_BGBLACK = '40'
165
+
166
+ # Red background color
167
+ ANSI_ESCAPE_BGRED = '41'
168
+
169
+ # Green background color
170
+ ANSI_ESCAPE_BGGREEN = '42'
171
+
172
+ # Yellow background color
173
+ ANSI_ESCAPE_BGYELLOW = '43'
174
+
175
+ # Blue background color
176
+ ANSI_ESCAPE_BGBLUE = '44'
177
+
178
+ # Magenta background color
179
+ ANSI_ESCAPE_BGMAGENTA = '45'
180
+
181
+ # Cyan background color
182
+ ANSI_ESCAPE_BGCYAN = '46'
183
+
184
+ # White background color
185
+ ANSI_ESCAPE_BGWHITE = '47'
186
+
109
187
  end
110
188
  end