guard 0.8.4 → 0.8.5
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.
- data/CHANGELOG.md +348 -330
- data/LICENSE +19 -19
- data/README.md +464 -431
- data/bin/guard +6 -6
- data/lib/guard.rb +452 -414
- data/lib/guard/cli.rb +119 -178
- data/lib/guard/dsl.rb +370 -370
- data/lib/guard/dsl_describer.rb +150 -60
- data/lib/guard/group.rb +37 -37
- data/lib/guard/guard.rb +129 -113
- data/lib/guard/hook.rb +118 -118
- data/lib/guard/interactor.rb +110 -44
- data/lib/guard/listener.rb +350 -350
- data/lib/guard/listeners/darwin.rb +66 -66
- data/lib/guard/listeners/linux.rb +97 -97
- data/lib/guard/listeners/polling.rb +55 -55
- data/lib/guard/listeners/windows.rb +61 -61
- data/lib/guard/notifier.rb +290 -211
- data/lib/guard/templates/Guardfile +2 -2
- data/lib/guard/ui.rb +193 -188
- data/lib/guard/version.rb +6 -6
- data/lib/guard/watcher.rb +114 -110
- data/man/guard.1 +93 -93
- data/man/guard.1.html +176 -176
- metadata +107 -59
@@ -1,2 +1,2 @@
|
|
1
|
-
# A sample Guardfile
|
2
|
-
# More info at https://github.com/guard/guard#readme
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
data/lib/guard/ui.rb
CHANGED
@@ -1,188 +1,193 @@
|
|
1
|
-
module Guard
|
2
|
-
|
3
|
-
# The UI class helps to format messages for the user.
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
#
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
# @
|
119
|
-
#
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
1
|
+
module Guard
|
2
|
+
|
3
|
+
# The UI class helps to format messages for the user. Everything that is logged
|
4
|
+
# through this class is considered either as an error message or a diagnostic
|
5
|
+
# message and is written to standard error (STDERR).
|
6
|
+
#
|
7
|
+
# If your Guard does some output that is piped into another process for further
|
8
|
+
# processing, please just write it to STDOUT with `puts`.
|
9
|
+
#
|
10
|
+
module UI
|
11
|
+
class << self
|
12
|
+
|
13
|
+
color_enabled = nil
|
14
|
+
|
15
|
+
# Show an info message.
|
16
|
+
#
|
17
|
+
# @param [String] message the message to show
|
18
|
+
# @option options [Boolean] reset whether to clean the output before
|
19
|
+
#
|
20
|
+
def info(message, options = { })
|
21
|
+
unless ENV['GUARD_ENV'] == 'test'
|
22
|
+
reset_line if options[:reset]
|
23
|
+
STDERR.puts color(message) if message != ''
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Show a red error message that is prefixed with ERROR.
|
28
|
+
#
|
29
|
+
# @param [String] message the message to show
|
30
|
+
# @option options [Boolean] reset whether to clean the output before
|
31
|
+
#
|
32
|
+
def error(message, options = { })
|
33
|
+
unless ENV['GUARD_ENV'] == 'test'
|
34
|
+
reset_line if options[:reset]
|
35
|
+
STDERR.puts color('ERROR: ', :red) + message
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Show a red deprecation message that is prefixed with DEPRECATION.
|
40
|
+
#
|
41
|
+
# @param [String] message the message to show
|
42
|
+
# @option options [Boolean] reset whether to clean the output before
|
43
|
+
#
|
44
|
+
def deprecation(message, options = { })
|
45
|
+
unless ENV['GUARD_ENV'] == 'test'
|
46
|
+
reset_line if options[:reset]
|
47
|
+
STDERR.puts color('DEPRECATION: ', :red) + message
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Show a debug message that is prefixed with DEBUG and a timestamp.
|
52
|
+
#
|
53
|
+
# @param [String] message the message to show
|
54
|
+
# @option options [Boolean] reset whether to clean the output before
|
55
|
+
#
|
56
|
+
def debug(message, options = { })
|
57
|
+
unless ENV['GUARD_ENV'] == 'test'
|
58
|
+
reset_line if options[:reset]
|
59
|
+
STDERR.puts color("DEBUG (#{Time.now.strftime('%T')}): ", :yellow) + message if ::Guard.options && ::Guard.options[:debug]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Reset a line.
|
64
|
+
#
|
65
|
+
def reset_line
|
66
|
+
STDERR.print(color_enabled? ? "\r\e[0m" : "\r\n")
|
67
|
+
end
|
68
|
+
|
69
|
+
# Clear the output.
|
70
|
+
#
|
71
|
+
def clear
|
72
|
+
system('clear;')
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# Reset a color sequence.
|
78
|
+
#
|
79
|
+
# @deprecated
|
80
|
+
# @param [String] text the text
|
81
|
+
#
|
82
|
+
def reset_color(text)
|
83
|
+
deprecation('UI.reset_color(text) is deprecated, please use color(text, ' ') instead.')
|
84
|
+
color(text, '')
|
85
|
+
end
|
86
|
+
|
87
|
+
# Checks if color output can be enabled.
|
88
|
+
#
|
89
|
+
# @return [Boolean] whether color is enabled or not
|
90
|
+
#
|
91
|
+
def color_enabled?
|
92
|
+
if @color_enabled.nil?
|
93
|
+
if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
94
|
+
if ENV['ANSICON']
|
95
|
+
@color_enabled = true
|
96
|
+
else
|
97
|
+
begin
|
98
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
99
|
+
require 'Win32/Console/ANSI'
|
100
|
+
@color_enabled = true
|
101
|
+
rescue LoadError
|
102
|
+
@color_enabled = false
|
103
|
+
info "You must 'gem install win32console' to use color on Windows"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
else
|
107
|
+
@color_enabled = true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
@color_enabled
|
112
|
+
end
|
113
|
+
|
114
|
+
# Colorizes a text message. See the constant in the UI class for possible
|
115
|
+
# color_options parameters. You can pass optionally :bright, a foreground
|
116
|
+
# color and a background color.
|
117
|
+
#
|
118
|
+
# @example
|
119
|
+
#
|
120
|
+
# color('Hello World', :red, :bright)
|
121
|
+
#
|
122
|
+
# @param [String] the text to colorize
|
123
|
+
# @param [Array] color_options the color options
|
124
|
+
#
|
125
|
+
def color(text, *color_options)
|
126
|
+
color_code = ''
|
127
|
+
color_options.each do |color_option|
|
128
|
+
color_option = color_option.to_s
|
129
|
+
if color_option != ''
|
130
|
+
if !(color_option =~ /\d+/)
|
131
|
+
color_option = const_get("ANSI_ESCAPE_#{ color_option.upcase }")
|
132
|
+
end
|
133
|
+
color_code += ';' + color_option
|
134
|
+
end
|
135
|
+
end
|
136
|
+
color_enabled? ? "\e[0#{ color_code }m#{ text }\e[0m" : text
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
# Brighten the color
|
142
|
+
ANSI_ESCAPE_BRIGHT = '1'
|
143
|
+
|
144
|
+
# Black foreground color
|
145
|
+
ANSI_ESCAPE_BLACK = '30'
|
146
|
+
|
147
|
+
# Red foreground color
|
148
|
+
ANSI_ESCAPE_RED = '31'
|
149
|
+
|
150
|
+
# Green foreground color
|
151
|
+
ANSI_ESCAPE_GREEN = '32'
|
152
|
+
|
153
|
+
# Yellow foreground color
|
154
|
+
ANSI_ESCAPE_YELLOW = '33'
|
155
|
+
|
156
|
+
# Blue foreground color
|
157
|
+
ANSI_ESCAPE_BLUE = '34'
|
158
|
+
|
159
|
+
# Magenta foreground color
|
160
|
+
ANSI_ESCAPE_MAGENTA = '35'
|
161
|
+
|
162
|
+
# Cyan foreground color
|
163
|
+
ANSI_ESCAPE_CYAN = '36'
|
164
|
+
|
165
|
+
# White foreground color
|
166
|
+
ANSI_ESCAPE_WHITE = '37'
|
167
|
+
|
168
|
+
# Black background color
|
169
|
+
ANSI_ESCAPE_BGBLACK = '40'
|
170
|
+
|
171
|
+
# Red background color
|
172
|
+
ANSI_ESCAPE_BGRED = '41'
|
173
|
+
|
174
|
+
# Green background color
|
175
|
+
ANSI_ESCAPE_BGGREEN = '42'
|
176
|
+
|
177
|
+
# Yellow background color
|
178
|
+
ANSI_ESCAPE_BGYELLOW = '43'
|
179
|
+
|
180
|
+
# Blue background color
|
181
|
+
ANSI_ESCAPE_BGBLUE = '44'
|
182
|
+
|
183
|
+
# Magenta background color
|
184
|
+
ANSI_ESCAPE_BGMAGENTA = '45'
|
185
|
+
|
186
|
+
# Cyan background color
|
187
|
+
ANSI_ESCAPE_BGCYAN = '46'
|
188
|
+
|
189
|
+
# White background color
|
190
|
+
ANSI_ESCAPE_BGWHITE = '47'
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
data/lib/guard/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
module Guard
|
2
|
-
unless defined? Guard::VERSION
|
3
|
-
# The current gem version of Guard
|
4
|
-
VERSION = '0.8.
|
5
|
-
end
|
6
|
-
end
|
1
|
+
module Guard
|
2
|
+
unless defined? Guard::VERSION
|
3
|
+
# The current gem version of Guard
|
4
|
+
VERSION = '0.8.5'
|
5
|
+
end
|
6
|
+
end
|
data/lib/guard/watcher.rb
CHANGED
@@ -1,110 +1,114 @@
|
|
1
|
-
module Guard
|
2
|
-
|
3
|
-
# The watcher defines a RegExp that will be matched against file system modifications.
|
4
|
-
# When a watcher matches a change, an optional action block is executed to enable
|
5
|
-
# processing the file system change result.
|
6
|
-
#
|
7
|
-
class Watcher
|
8
|
-
|
9
|
-
attr_accessor :pattern, :action
|
10
|
-
|
11
|
-
# Initialize a file watcher.
|
12
|
-
#
|
13
|
-
# @param [String, Regexp] pattern the pattern to be watched by the guard
|
14
|
-
# @param [Block] action the action to execute before passing the result to the Guard
|
15
|
-
#
|
16
|
-
def initialize(pattern, action = nil)
|
17
|
-
@pattern, @action = pattern, action
|
18
|
-
@@warning_printed ||= false
|
19
|
-
|
20
|
-
# deprecation warning
|
21
|
-
if @pattern.is_a?(String) && @pattern =~ /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/
|
22
|
-
unless @@warning_printed
|
23
|
-
UI.info "*"*20 + "\nDEPRECATION WARNING!\n" + "*"*20
|
24
|
-
UI.info <<-MSG
|
25
|
-
You have a string in your Guardfile watch patterns that seem to represent a Regexp.
|
26
|
-
Guard matches String with == and Regexp with Regexp#match.
|
27
|
-
You should either use plain String (without Regexp special characters) or real Regexp.
|
28
|
-
MSG
|
29
|
-
@@warning_printed = true
|
30
|
-
end
|
31
|
-
|
32
|
-
UI.info "\"#{@pattern}\" has been converted to #{ Regexp.new(@pattern).inspect }\n"
|
33
|
-
@pattern = Regexp.new(@pattern)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Finds the files that matches a Guard.
|
38
|
-
#
|
39
|
-
# @param [Guard::Guard] guard the guard which watchers are used
|
40
|
-
# @param [Array<String>] files the changed files
|
41
|
-
# @return [Array<
|
42
|
-
#
|
43
|
-
def self.match_files(guard, files)
|
44
|
-
guard.watchers.inject([]) do |paths, watcher|
|
45
|
-
files.each do |file|
|
46
|
-
if matches = watcher.match_file?(file)
|
47
|
-
if watcher.action
|
48
|
-
result = watcher.call_action(matches)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
#
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
#
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
#
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
end
|
1
|
+
module Guard
|
2
|
+
|
3
|
+
# The watcher defines a RegExp that will be matched against file system modifications.
|
4
|
+
# When a watcher matches a change, an optional action block is executed to enable
|
5
|
+
# processing the file system change result.
|
6
|
+
#
|
7
|
+
class Watcher
|
8
|
+
|
9
|
+
attr_accessor :pattern, :action
|
10
|
+
|
11
|
+
# Initialize a file watcher.
|
12
|
+
#
|
13
|
+
# @param [String, Regexp] pattern the pattern to be watched by the guard
|
14
|
+
# @param [Block] action the action to execute before passing the result to the Guard
|
15
|
+
#
|
16
|
+
def initialize(pattern, action = nil)
|
17
|
+
@pattern, @action = pattern, action
|
18
|
+
@@warning_printed ||= false
|
19
|
+
|
20
|
+
# deprecation warning
|
21
|
+
if @pattern.is_a?(String) && @pattern =~ /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/
|
22
|
+
unless @@warning_printed
|
23
|
+
UI.info "*"*20 + "\nDEPRECATION WARNING!\n" + "*"*20
|
24
|
+
UI.info <<-MSG
|
25
|
+
You have a string in your Guardfile watch patterns that seem to represent a Regexp.
|
26
|
+
Guard matches String with == and Regexp with Regexp#match.
|
27
|
+
You should either use plain String (without Regexp special characters) or real Regexp.
|
28
|
+
MSG
|
29
|
+
@@warning_printed = true
|
30
|
+
end
|
31
|
+
|
32
|
+
UI.info "\"#{@pattern}\" has been converted to #{ Regexp.new(@pattern).inspect }\n"
|
33
|
+
@pattern = Regexp.new(@pattern)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Finds the files that matches a Guard.
|
38
|
+
#
|
39
|
+
# @param [Guard::Guard] guard the guard which watchers are used
|
40
|
+
# @param [Array<String>] files the changed files
|
41
|
+
# @return [Array<Object>] the matched watcher response
|
42
|
+
#
|
43
|
+
def self.match_files(guard, files)
|
44
|
+
guard.watchers.inject([]) do |paths, watcher|
|
45
|
+
files.each do |file|
|
46
|
+
if matches = watcher.match_file?(file)
|
47
|
+
if watcher.action
|
48
|
+
result = watcher.call_action(matches)
|
49
|
+
if guard.options[:any_return]
|
50
|
+
paths << result
|
51
|
+
elsif result.respond_to?(:empty?) && !result.empty?
|
52
|
+
paths << Array(result)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
paths << matches[0]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
guard.options[:any_return] ? paths : paths.flatten.map { |p| p.to_s }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Test if a file would be matched by any of the Guards watchers.
|
65
|
+
#
|
66
|
+
# @param [Array<Guard::Guard>] guards the guards to use the watchers from
|
67
|
+
# @param [Array<String>] files the files to test
|
68
|
+
# @return [Boolean] Whether a file matches
|
69
|
+
#
|
70
|
+
def self.match_files?(guards, files)
|
71
|
+
guards.any? do |guard|
|
72
|
+
guard.watchers.any? do |watcher|
|
73
|
+
files.any? { |file| watcher.match_file?(file) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Test the watchers pattern against a file.
|
79
|
+
#
|
80
|
+
# @param [String] file the file to test
|
81
|
+
# @return [Boolean] whether the given file is matched
|
82
|
+
#
|
83
|
+
def match_file?(file)
|
84
|
+
if @pattern.is_a?(Regexp)
|
85
|
+
file.match(@pattern)
|
86
|
+
else
|
87
|
+
file == @pattern ? [file] : nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Test if any of the files is the Guardfile.
|
92
|
+
#
|
93
|
+
# @param [Array<String>] the files to test
|
94
|
+
# @return [Boolean] whether one of these files is the Guardfile
|
95
|
+
#
|
96
|
+
def self.match_guardfile?(files)
|
97
|
+
files.any? { |file| "#{ Dir.pwd }/#{ file }" == Dsl.guardfile_path }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Executes a watcher action.
|
101
|
+
#
|
102
|
+
# @param [String, MatchData] the matched path or the match from the Regex
|
103
|
+
# @return [String] the final paths
|
104
|
+
#
|
105
|
+
def call_action(matches)
|
106
|
+
begin
|
107
|
+
@action.arity > 0 ? @action.call(matches) : @action.call
|
108
|
+
rescue Exception => e
|
109
|
+
UI.error "Problem with watch action!\n#{ e.message }\n\n#{ e.backtrace.join("\n") }"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|