guard 2.6.1 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +73 -58
  3. data/bin/guard +2 -2
  4. data/lib/guard.rb +64 -59
  5. data/lib/guard/cli.rb +66 -60
  6. data/lib/guard/cli.rb.orig +215 -0
  7. data/lib/guard/commander.rb +45 -69
  8. data/lib/guard/commands/all.rb +21 -19
  9. data/lib/guard/commands/change.rb +17 -22
  10. data/lib/guard/commands/notification.rb +15 -16
  11. data/lib/guard/commands/pause.rb +14 -15
  12. data/lib/guard/commands/reload.rb +19 -20
  13. data/lib/guard/commands/scope.rb +23 -19
  14. data/lib/guard/commands/show.rb +13 -16
  15. data/lib/guard/deprecated_methods.rb +6 -10
  16. data/lib/guard/deprecator.rb +52 -37
  17. data/lib/guard/dsl.rb +55 -33
  18. data/lib/guard/dsl_describer.rb +83 -31
  19. data/lib/guard/dsl_describer.rb.orig +184 -0
  20. data/lib/guard/group.rb +7 -6
  21. data/lib/guard/guard.rb +4 -4
  22. data/lib/guard/guard.rb.orig +42 -0
  23. data/lib/guard/guardfile.rb +12 -13
  24. data/lib/guard/guardfile/evaluator.rb +77 -55
  25. data/lib/guard/guardfile/evaluator.rb.orig +275 -0
  26. data/lib/guard/guardfile/generator.rb +25 -20
  27. data/lib/guard/interactor.rb +52 -293
  28. data/lib/guard/interactor.rb.orig +85 -0
  29. data/lib/guard/jobs/base.rb +21 -0
  30. data/lib/guard/jobs/pry_wrapper.rb +290 -0
  31. data/lib/guard/jobs/pry_wrapper.rb.orig +293 -0
  32. data/lib/guard/jobs/sleep.rb +25 -0
  33. data/lib/guard/notifier.rb +42 -39
  34. data/lib/guard/notifiers/base.rb +25 -24
  35. data/lib/guard/notifiers/emacs.rb +30 -24
  36. data/lib/guard/notifiers/file_notifier.rb +3 -7
  37. data/lib/guard/notifiers/gntp.rb +22 -22
  38. data/lib/guard/notifiers/growl.rb +16 -15
  39. data/lib/guard/notifiers/libnotify.rb +7 -10
  40. data/lib/guard/notifiers/notifysend.rb +15 -14
  41. data/lib/guard/notifiers/rb_notifu.rb +8 -10
  42. data/lib/guard/notifiers/terminal_notifier.rb +15 -11
  43. data/lib/guard/notifiers/terminal_title.rb +4 -8
  44. data/lib/guard/notifiers/tmux.rb +104 -71
  45. data/lib/guard/options.rb +1 -5
  46. data/lib/guard/plugin.rb +1 -3
  47. data/lib/guard/plugin/base.rb +12 -9
  48. data/lib/guard/plugin/hooker.rb +1 -5
  49. data/lib/guard/plugin_util.rb +46 -25
  50. data/lib/guard/plugin_util.rb.orig +178 -0
  51. data/lib/guard/rake_task.rb +4 -7
  52. data/lib/guard/reevaluator.rb +13 -0
  53. data/lib/guard/runner.rb +50 -78
  54. data/lib/guard/runner.rb.orig +200 -0
  55. data/lib/guard/setuper.rb +199 -130
  56. data/lib/guard/setuper.rb.orig +348 -0
  57. data/lib/guard/sheller.rb +107 -0
  58. data/lib/guard/tags +367 -0
  59. data/lib/guard/ui.rb +50 -38
  60. data/lib/guard/ui.rb.orig +254 -0
  61. data/lib/guard/ui/colors.rb +17 -21
  62. data/lib/guard/version.rb +1 -1
  63. data/lib/guard/version.rb.orig +3 -0
  64. data/lib/guard/watcher.rb +49 -62
  65. metadata +21 -4
  66. data/lib/guard/notifiers/growl_notify.rb +0 -93
@@ -0,0 +1,254 @@
1
+ require "lumberjack"
2
+
3
+ require "guard/options"
4
+ require "guard/ui/colors"
5
+
6
+ module Guard
7
+ # The UI class helps to format messages for the user. Everything that is
8
+ # logged through this class is considered either as an error message or a
9
+ # diagnostic message and is written to standard error ($stderr).
10
+ #
11
+ # If your Guard plugin does some output that is piped into another process
12
+ # for further processing, please just write it to STDOUT with `puts`.
13
+ #
14
+ module UI
15
+ include Colors
16
+
17
+ class << self
18
+ # Get the Guard::UI logger instance
19
+ #
20
+ def logger
21
+ @logger ||= begin
22
+ Lumberjack::Logger.new(
23
+ options.fetch(:device) { $stderr },
24
+ options)
25
+ end
26
+ end
27
+
28
+ # Get the logger options
29
+ #
30
+ # @return [Hash] the logger options
31
+ #
32
+ def options
33
+ @options ||= ::Guard::Options.new(
34
+ level: :info,
35
+ template: ":time - :severity - :message",
36
+ time_format: "%H:%M:%S")
37
+ end
38
+
39
+ # Set the logger options
40
+ #
41
+ # @param [Hash] options the logger options
42
+ # @option options [Symbol] level the log level
43
+ # @option options [String] template the logger template
44
+ # @option options [String] time_format the time format
45
+ #
46
+ def options=(options)
47
+ @options = ::Guard::Options.new(options)
48
+ end
49
+
50
+ # Show an info message.
51
+ #
52
+ # @param [String] message the message to show
53
+ # @option options [Boolean] reset whether to clean the output before
54
+ # @option options [String] plugin manually define the calling plugin
55
+ #
56
+ def info(message, options = {})
57
+ _filtered_logger_message(message, :info, nil, options)
58
+ end
59
+
60
+ # Show a yellow warning message that is prefixed with WARNING.
61
+ #
62
+ # @param [String] message the message to show
63
+ # @option options [Boolean] reset whether to clean the output before
64
+ # @option options [String] plugin manually define the calling plugin
65
+ #
66
+ def warning(message, options = {})
67
+ _filtered_logger_message(message, :warn, :yellow, options)
68
+ end
69
+
70
+ # Show a red error message that is prefixed with ERROR.
71
+ #
72
+ # @param [String] message the message to show
73
+ # @option options [Boolean] reset whether to clean the output before
74
+ # @option options [String] plugin manually define the calling plugin
75
+ #
76
+ def error(message, options = {})
77
+ _filtered_logger_message(message, :error, :red, options)
78
+ end
79
+
80
+ # Show a red deprecation message that is prefixed with DEPRECATION.
81
+ # It has a log level of `warn`.
82
+ #
83
+ # @param [String] message the message to show
84
+ # @option options [Boolean] reset whether to clean the output before
85
+ # @option options [String] plugin manually define the calling plugin
86
+ #
87
+ def deprecation(message, options = {})
88
+ warning(message, options) if ::Guard.options[:show_deprecations]
89
+ end
90
+
91
+ # Show a debug message that is prefixed with DEBUG and a timestamp.
92
+ #
93
+ # @param [String] message the message to show
94
+ # @option options [Boolean] reset whether to clean the output before
95
+ # @option options [String] plugin manually define the calling plugin
96
+ #
97
+ def debug(message, options = {})
98
+ _filtered_logger_message(message, :debug, :yellow, options)
99
+ end
100
+
101
+ # Reset a line.
102
+ #
103
+ def reset_line
104
+ $stderr.print(color_enabled? ? "\r\e[0m" : "\r\n")
105
+ end
106
+
107
+ # Clear the output if clearable.
108
+ #
109
+ def clear(options = {})
110
+ return unless ::Guard.options[:clear] && (@clearable || options[:force])
111
+ @clearable = false
112
+ ::Guard::Sheller.run("clear;")
113
+ end
114
+
115
+ # Allow the screen to be cleared again.
116
+ #
117
+ def clearable
118
+ @clearable = true
119
+ end
120
+
121
+ # Show a scoped action message.
122
+ #
123
+ # @param [String] action the action to show
124
+ # @param [Hash] scope hash with a guard or a group scope
125
+ #
126
+ def action_with_scopes(action, scope)
127
+ first_non_blank_scope = _first_non_blank_scope(scope)
128
+ unless first_non_blank_scope.nil?
129
+ scope_message = first_non_blank_scope.map(&:title).join(", ")
130
+ end
131
+
132
+ info "#{ action } #{ scope_message || "all" }"
133
+ end
134
+
135
+ private
136
+
137
+ # Returns the first non-blank scope by searching in the given `scope`
138
+ # hash and in Guard.scope. Returns nil if no non-blank scope is found.
139
+ #
140
+ def _first_non_blank_scope(scope)
141
+ [:plugins, :groups].each do |scope_name|
142
+ s = scope[scope_name] || ::Guard.scope[scope_name]
143
+ return s if !s.nil? && !s.empty?
144
+ end
145
+
146
+ nil
147
+ end
148
+
149
+ # Filters log messages depending on either the
150
+ # `:only`` or `:except` option.
151
+ #
152
+ # @param [String] plugin the calling plugin name
153
+ # @yield When the message should be logged
154
+ # @yieldparam [String] param the calling plugin name
155
+ #
156
+ def _filter(plugin)
157
+ only = options[:only]
158
+ except = options[:except]
159
+ plugin ||= calling_plugin_name
160
+
161
+ match = !(only || except)
162
+ match ||= (only && only.match(plugin))
163
+ match ||= (except && !except.match(plugin))
164
+ return unless match
165
+ yield plugin
166
+ end
167
+
168
+ # Display a message of the type `method` and with the color `color_name`
169
+ # (no color by default) conditionnaly given a `plugin_name`.
170
+ #
171
+ # @param [String] plugin_name the calling plugin name
172
+ # @option options [Boolean] reset whether to clean the output before
173
+ # @option options [String] plugin manually define the calling plugin
174
+ #
175
+ def _filtered_logger_message(message, method, color_name, options = {})
176
+ message = color(message, color_name) if color_name
177
+
178
+ _filter(options[:plugin]) do |plugin|
179
+ reset_line if options[:reset]
180
+ logger.send(method, message, plugin)
181
+ end
182
+ end
183
+
184
+ # Tries to extract the calling Guard plugin name
185
+ # from the call stack.
186
+ #
187
+ # @param [Integer] depth the stack depth
188
+ # @return [String] the Guard plugin name
189
+ #
190
+ def calling_plugin_name(depth = 2)
191
+ name = /(guard\/[a-z_]*)(\/[a-z_]*)?.rb:/i.match(caller[depth])
192
+ return "Guard" unless name
193
+ name[1].split("/").map do |part|
194
+ part.split(/[^a-z0-9]/i).map(&:capitalize).join
195
+ end.join("::")
196
+ end
197
+
198
+ # Checks if color output can be enabled.
199
+ #
200
+ # @return [Boolean] whether color is enabled or not
201
+ #
202
+ def color_enabled?
203
+ @color_enabled_initialized ||= false
204
+ @color_enabled = nil unless @color_enabled_initialized
205
+ @color_enabled_initialized = true
206
+ if @color_enabled.nil?
207
+ if RbConfig::CONFIG["target_os"] =~ /mswin|mingw/i
208
+ if ENV["ANSICON"]
209
+ @color_enabled = true
210
+ else
211
+ begin
212
+ require "rubygems" unless ENV["NO_RUBYGEMS"]
213
+ require "Win32/Console/ANSI"
214
+ @color_enabled = true
215
+ rescue LoadError
216
+ @color_enabled = false
217
+ info "Run 'gem install win32console' to use color on Windows"
218
+ end
219
+ end
220
+ else
221
+ @color_enabled = true
222
+ end
223
+ end
224
+
225
+ @color_enabled
226
+ end
227
+
228
+ # Colorizes a text message. See the constant in the UI class for possible
229
+ # color_options parameters. You can pass optionally :bright, a foreground
230
+ # color and a background color.
231
+ #
232
+ # @example
233
+ #
234
+ # color('Hello World', :red, :bright)
235
+ #
236
+ # @param [String] text the text to colorize
237
+ # @param [Array] color_options the color options
238
+ #
239
+ def color(text, *color_options)
240
+ color_code = ""
241
+ color_options.each do |color_option|
242
+ color_option = color_option.to_s
243
+ next if color_option == ""
244
+
245
+ unless color_option =~ /\d+/
246
+ color_option = const_get("ANSI_ESCAPE_#{ color_option.upcase }")
247
+ end
248
+ color_code += ";" + color_option
249
+ end
250
+ color_enabled? ? "\e[0#{ color_code }m#{ text }\e[0m" : text
251
+ end
252
+ end
253
+ end
254
+ end
@@ -1,60 +1,56 @@
1
1
  module Guard
2
2
  module UI
3
-
4
3
  module Colors
5
-
6
4
  # Brighten the color
7
- ANSI_ESCAPE_BRIGHT = '1'
5
+ ANSI_ESCAPE_BRIGHT = "1"
8
6
 
9
7
  # Black foreground color
10
- ANSI_ESCAPE_BLACK = '30'
8
+ ANSI_ESCAPE_BLACK = "30"
11
9
 
12
10
  # Red foreground color
13
- ANSI_ESCAPE_RED = '31'
11
+ ANSI_ESCAPE_RED = "31"
14
12
 
15
13
  # Green foreground color
16
- ANSI_ESCAPE_GREEN = '32'
14
+ ANSI_ESCAPE_GREEN = "32"
17
15
 
18
16
  # Yellow foreground color
19
- ANSI_ESCAPE_YELLOW = '33'
17
+ ANSI_ESCAPE_YELLOW = "33"
20
18
 
21
19
  # Blue foreground color
22
- ANSI_ESCAPE_BLUE = '34'
20
+ ANSI_ESCAPE_BLUE = "34"
23
21
 
24
22
  # Magenta foreground color
25
- ANSI_ESCAPE_MAGENTA = '35'
23
+ ANSI_ESCAPE_MAGENTA = "35"
26
24
 
27
25
  # Cyan foreground color
28
- ANSI_ESCAPE_CYAN = '36'
26
+ ANSI_ESCAPE_CYAN = "36"
29
27
 
30
28
  # White foreground color
31
- ANSI_ESCAPE_WHITE = '37'
29
+ ANSI_ESCAPE_WHITE = "37"
32
30
 
33
31
  # Black background color
34
- ANSI_ESCAPE_BGBLACK = '40'
32
+ ANSI_ESCAPE_BGBLACK = "40"
35
33
 
36
34
  # Red background color
37
- ANSI_ESCAPE_BGRED = '41'
35
+ ANSI_ESCAPE_BGRED = "41"
38
36
 
39
37
  # Green background color
40
- ANSI_ESCAPE_BGGREEN = '42'
38
+ ANSI_ESCAPE_BGGREEN = "42"
41
39
 
42
40
  # Yellow background color
43
- ANSI_ESCAPE_BGYELLOW = '43'
41
+ ANSI_ESCAPE_BGYELLOW = "43"
44
42
 
45
43
  # Blue background color
46
- ANSI_ESCAPE_BGBLUE = '44'
44
+ ANSI_ESCAPE_BGBLUE = "44"
47
45
 
48
46
  # Magenta background color
49
- ANSI_ESCAPE_BGMAGENTA = '45'
47
+ ANSI_ESCAPE_BGMAGENTA = "45"
50
48
 
51
49
  # Cyan background color
52
- ANSI_ESCAPE_BGCYAN = '46'
50
+ ANSI_ESCAPE_BGCYAN = "46"
53
51
 
54
52
  # White background color
55
- ANSI_ESCAPE_BGWHITE = '47'
56
-
53
+ ANSI_ESCAPE_BGWHITE = "47"
57
54
  end
58
-
59
55
  end
60
56
  end
data/lib/guard/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Guard
2
- VERSION = '2.6.1'
2
+ VERSION = "2.7.0"
3
3
  end
@@ -0,0 +1,3 @@
1
+ module Guard
2
+ VERSION = "2.6.1"
3
+ end
data/lib/guard/watcher.rb CHANGED
@@ -1,14 +1,12 @@
1
- require 'guard/ui'
1
+ require "guard/ui"
2
2
 
3
3
  module Guard
4
-
5
4
  # The watcher defines a RegExp that will be matched against file system
6
5
  # modifications.
7
6
  # When a watcher matches a change, an optional action block is executed to
8
7
  # enable processing the file system change result.
9
8
  #
10
9
  class Watcher
11
-
12
10
  attr_accessor :pattern, :action
13
11
 
14
12
  # Initializes a file watcher.
@@ -23,20 +21,26 @@ module Guard
23
21
  @@warning_printed ||= false
24
22
 
25
23
  # deprecation warning
26
- if @pattern.is_a?(String) && @pattern =~ /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/
27
- unless @@warning_printed
28
- ::Guard::UI.info "*"*20 + "\nDEPRECATION WARNING!\n" + "*"*20
29
- ::Guard::UI.info <<-MSG
30
- You have a string in your Guardfile watch patterns that seem to represent a Regexp.
24
+ regexp = /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/
25
+ return unless @pattern.is_a?(String) && @pattern =~ regexp
26
+
27
+ unless @@warning_printed
28
+ ::Guard::UI.info "*" * 20 + "\nDEPRECATION WARNING!\n" + "*" * 20
29
+ ::Guard::UI.info <<-MSG
30
+ You have a string in your Guardfile watch patterns that seem to
31
+ represent a Regexp.
32
+
31
33
  Guard matches String with == and Regexp with Regexp#match.
32
- You should either use plain String (without Regexp special characters) or real Regexp.
33
- MSG
34
- @@warning_printed = true
35
- end
36
34
 
37
- ::Guard::UI.info "\"#{@pattern}\" has been converted to #{ Regexp.new(@pattern).inspect }\n"
38
- @pattern = Regexp.new(@pattern)
35
+ You should either use plain String (without Regexp special
36
+ characters) or real Regexp.
37
+ MSG
38
+ @@warning_printed = true
39
39
  end
40
+
41
+ new_regexp = Regexp.new(@pattern).inspect
42
+ ::Guard::UI.info "\"#{@pattern}\" has been converted to #{ new_regexp }\n"
43
+ @pattern = Regexp.new(@pattern)
40
44
  end
41
45
 
42
46
  # Finds the files that matches a Guard plugin.
@@ -48,38 +52,28 @@ module Guard
48
52
  def self.match_files(guard, files)
49
53
  return [] if files.empty?
50
54
 
51
- guard.watchers.inject([]) do |paths, watcher|
52
- files.each do |file|
53
- if matches = watcher.match(file)
54
- if watcher.action
55
- result = watcher.call_action(matches)
56
- if guard.options[:any_return]
57
- paths << result
58
- elsif result.respond_to?(:empty?) && !result.empty?
59
- paths << Array(result)
60
- end
55
+ files.inject([]) do |paths, file|
56
+ guard.watchers.each do |watcher|
57
+ matches = watcher.match(file)
58
+ next unless matches
59
+
60
+ if watcher.action
61
+ result = watcher.call_action(matches)
62
+ if guard.options[:any_return]
63
+ paths << result
64
+ elsif result.respond_to?(:empty?) && !result.empty?
65
+ paths << Array(result)
61
66
  else
62
- paths << matches[0]
67
+ next
63
68
  end
69
+ else
70
+ paths << matches[0]
64
71
  end
65
- end
66
72
 
67
- guard.options[:any_return] ? paths : paths.flatten.map { |p| p.to_s }
68
- end
69
- end
70
-
71
- # Tests if a file would be matched by any of the Guard plugin watchers.
72
- #
73
- # @param [Array<Guard::Plugin>] plugins the Guard plugins to use the
74
- # watchers from
75
- # @param [Array<String>] files the files to test
76
- # @return [Boolean] Whether a file matches
77
- #
78
- def self.match_files?(plugins, files)
79
- plugins.any? do |plugin|
80
- plugin.watchers.any? do |watcher|
81
- files.any? { |file| watcher.match(file) }
73
+ break if guard.options[:first_match]
82
74
  end
75
+
76
+ guard.options[:any_return] ? paths : paths.flatten.map(&:to_s)
83
77
  end
84
78
  end
85
79
 
@@ -89,7 +83,8 @@ module Guard
89
83
  # @return [Boolean] whether one of these files is the Guardfile
90
84
  #
91
85
  def self.match_guardfile?(files)
92
- files.any? { |file| File.expand_path(file) == ::Guard.evaluator.guardfile_path }
86
+ path = ::Guard.evaluator.guardfile_path
87
+ files.any? { |file| File.expand_path(file) == path }
93
88
  end
94
89
 
95
90
  # Test the watchers pattern against a file.
@@ -98,19 +93,14 @@ module Guard
98
93
  # @return [Array<String>] an array of matches (or containing a single path
99
94
  # if the pattern is a string)
100
95
  #
101
- def match(file)
102
- f = file
103
- deleted = file.start_with?('!')
104
- f = deleted ? f[1..-1] : f
105
- if @pattern.is_a?(Regexp)
106
- if m = f.match(@pattern)
107
- m = m.to_a
108
- m[0] = file
109
- m
110
- end
111
- else
112
- f == @pattern ? [file] : nil
113
- end
96
+ def match(string_or_pathname)
97
+ # TODO: use only match() - and show fnmatch example
98
+ file = string_or_pathname.to_s
99
+ return (file == @pattern ? [file] : nil) unless @pattern.is_a?(Regexp)
100
+ return unless (m = @pattern.match(file))
101
+ m = m.to_a
102
+ m[0] = file
103
+ m
114
104
  end
115
105
 
116
106
  # Executes a watcher action.
@@ -120,13 +110,10 @@ module Guard
120
110
  # @return [String] the final paths
121
111
  #
122
112
  def call_action(matches)
123
- begin
124
- @action.arity > 0 ? @action.call(matches) : @action.call
125
- rescue Exception => ex
126
- ::Guard::UI.error "Problem with watch action!\n#{ ex.message }"
127
- ::Guard::UI.error ex.backtrace.join("\n")
128
- end
113
+ @action.arity > 0 ? @action.call(matches) : @action.call
114
+ rescue => ex
115
+ ::Guard::UI.error "Problem with watch action!\n#{ ex.message }"
116
+ ::Guard::UI.error ex.backtrace.join("\n")
129
117
  end
130
-
131
118
  end
132
119
  end