guard 2.6.1 → 2.7.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.
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