guard 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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