guard 1.4.0 → 2.18.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -677
- data/LICENSE +4 -2
- data/README.md +91 -753
- data/bin/_guard-core +11 -0
- data/bin/guard +108 -3
- data/lib/guard/aruba_adapter.rb +59 -0
- data/lib/guard/cli/environments/bundler.rb +22 -0
- data/lib/guard/cli/environments/evaluate_only.rb +35 -0
- data/lib/guard/cli/environments/valid.rb +69 -0
- data/lib/guard/cli.rb +129 -128
- data/lib/guard/commander.rb +104 -0
- data/lib/guard/commands/all.rb +37 -0
- data/lib/guard/commands/change.rb +31 -0
- data/lib/guard/commands/notification.rb +26 -0
- data/lib/guard/commands/pause.rb +29 -0
- data/lib/guard/commands/reload.rb +36 -0
- data/lib/guard/commands/scope.rb +38 -0
- data/lib/guard/commands/show.rb +24 -0
- data/lib/guard/config.rb +18 -0
- data/lib/guard/deprecated/dsl.rb +45 -0
- data/lib/guard/deprecated/evaluator.rb +39 -0
- data/lib/guard/deprecated/guard.rb +328 -0
- data/lib/guard/deprecated/guardfile.rb +84 -0
- data/lib/guard/deprecated/watcher.rb +27 -0
- data/lib/guard/dsl.rb +332 -363
- data/lib/guard/dsl_describer.rb +132 -122
- data/lib/guard/dsl_reader.rb +51 -0
- data/lib/guard/group.rb +34 -14
- data/lib/guard/guardfile/evaluator.rb +232 -0
- data/lib/guard/guardfile/generator.rb +128 -0
- data/lib/guard/guardfile.rb +24 -60
- data/lib/guard/interactor.rb +31 -255
- data/lib/guard/internals/debugging.rb +68 -0
- data/lib/guard/internals/groups.rb +40 -0
- data/lib/guard/internals/helpers.rb +13 -0
- data/lib/guard/internals/plugins.rb +53 -0
- data/lib/guard/internals/queue.rb +51 -0
- data/lib/guard/internals/scope.rb +121 -0
- data/lib/guard/internals/session.rb +180 -0
- data/lib/guard/internals/state.rb +25 -0
- data/lib/guard/internals/tracing.rb +33 -0
- data/lib/guard/internals/traps.rb +10 -0
- data/lib/guard/jobs/base.rb +21 -0
- data/lib/guard/jobs/pry_wrapper.rb +336 -0
- data/lib/guard/jobs/sleep.rb +26 -0
- data/lib/guard/notifier.rb +46 -212
- data/lib/guard/options.rb +22 -0
- data/lib/guard/plugin.rb +303 -0
- data/lib/guard/plugin_util.rb +191 -0
- data/lib/guard/rake_task.rb +42 -0
- data/lib/guard/runner.rb +80 -140
- data/lib/guard/templates/Guardfile +14 -0
- data/lib/guard/terminal.rb +13 -0
- data/lib/guard/ui/colors.rb +56 -0
- data/lib/guard/ui/config.rb +70 -0
- data/lib/guard/ui/logger.rb +30 -0
- data/lib/guard/ui.rb +163 -128
- data/lib/guard/version.rb +1 -2
- data/lib/guard/watcher/pattern/deprecated_regexp.rb +45 -0
- data/lib/guard/watcher/pattern/match_result.rb +18 -0
- data/lib/guard/watcher/pattern/matcher.rb +33 -0
- data/lib/guard/watcher/pattern/pathname_path.rb +15 -0
- data/lib/guard/watcher/pattern/simple_path.rb +23 -0
- data/lib/guard/watcher/pattern.rb +24 -0
- data/lib/guard/watcher.rb +52 -95
- data/lib/guard.rb +108 -376
- data/lib/tasks/releaser.rb +116 -0
- data/man/guard.1 +12 -9
- data/man/guard.1.html +18 -12
- metadata +148 -77
- data/images/guard.png +0 -0
- data/lib/guard/guard.rb +0 -156
- data/lib/guard/hook.rb +0 -120
- data/lib/guard/interactors/coolline.rb +0 -64
- data/lib/guard/interactors/helpers/completion.rb +0 -32
- data/lib/guard/interactors/helpers/terminal.rb +0 -46
- data/lib/guard/interactors/readline.rb +0 -94
- data/lib/guard/interactors/simple.rb +0 -19
- data/lib/guard/notifiers/emacs.rb +0 -69
- data/lib/guard/notifiers/gntp.rb +0 -118
- data/lib/guard/notifiers/growl.rb +0 -99
- data/lib/guard/notifiers/growl_notify.rb +0 -92
- data/lib/guard/notifiers/libnotify.rb +0 -96
- data/lib/guard/notifiers/notifysend.rb +0 -84
- data/lib/guard/notifiers/rb_notifu.rb +0 -102
- data/lib/guard/notifiers/terminal_notifier.rb +0 -66
- data/lib/guard/notifiers/tmux.rb +0 -69
- data/lib/guard/version.rbc +0 -130
data/lib/guard/dsl.rb
CHANGED
@@ -1,469 +1,438 @@
|
|
1
|
-
|
1
|
+
require "guard/guardfile/evaluator"
|
2
|
+
require "guard/interactor"
|
3
|
+
require "guard/notifier"
|
4
|
+
require "guard/ui"
|
5
|
+
require "guard/watcher"
|
6
|
+
|
7
|
+
require "guard/deprecated/dsl" unless Guard::Config.new.strict?
|
8
|
+
require "guard"
|
2
9
|
|
3
|
-
|
4
|
-
# the
|
10
|
+
module Guard
|
11
|
+
# The Dsl class provides the methods that are used in each `Guardfile` to
|
12
|
+
# describe the behaviour of Guard.
|
5
13
|
#
|
6
|
-
# The main keywords of the DSL are
|
7
|
-
# the used Guard plugins and the file changes they are watching.
|
14
|
+
# The main keywords of the DSL are {#guard} and {#watch}. These are necessary
|
15
|
+
# to define the used Guard plugins and the file changes they are watching.
|
8
16
|
#
|
9
|
-
# You can optionally group the Guard plugins with the
|
10
|
-
# with the
|
17
|
+
# You can optionally group the Guard plugins with the {#group} keyword and
|
18
|
+
# ignore and filter certain paths with the {#ignore} and {#filter} keywords.
|
11
19
|
#
|
12
|
-
# You can set your preferred system notification library with
|
13
|
-
# some optional configuration options for the library. If you don't
|
14
|
-
# Guard will automatically pick one with default options
|
15
|
-
# specify `:off` as library).
|
20
|
+
# You can set your preferred system notification library with {#notification}
|
21
|
+
# and pass some optional configuration options for the library. If you don't
|
22
|
+
# configure a library, Guard will automatically pick one with default options
|
23
|
+
# (if you don't want notifications, specify `:off` as library). Please see
|
24
|
+
# {Notifier} for more information about the supported libraries.
|
16
25
|
#
|
17
|
-
# A more advanced DSL use is the
|
18
|
-
# code before or after any of the
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
26
|
+
# A more advanced DSL use is the {#callback} keyword that allows you to
|
27
|
+
# execute arbitrary code before or after any of the {Plugin#start},
|
28
|
+
# {Plugin#stop}, {Plugin#reload}, {Plugin#run_all},
|
29
|
+
# {Plugin#run_on_changes}, {Plugin#run_on_additions},
|
30
|
+
# {Plugin#run_on_modifications} and {Plugin#run_on_removals}
|
31
|
+
# Guard plugins method.
|
32
|
+
# You can even insert more hooks inside these methods. Please [checkout the
|
33
|
+
# Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for
|
34
|
+
# more details.
|
22
35
|
#
|
23
36
|
# The DSL will also evaluate normal Ruby code.
|
24
37
|
#
|
25
38
|
# There are two possible locations for the `Guardfile`:
|
26
|
-
# - The `Guardfile` in the current directory where Guard has been started
|
27
|
-
# - The `.Guardfile` in your home directory.
|
28
|
-
#
|
29
|
-
# In addition, if a user configuration `.guard.rb` in your home directory is found, it will
|
30
|
-
# be appended to the current project `Guardfile`.
|
31
|
-
#
|
32
|
-
# @example A sample of a complex Guardfile
|
33
|
-
#
|
34
|
-
# notification :growl
|
35
|
-
#
|
36
|
-
# group 'frontend' do
|
37
|
-
# guard 'passenger', :ping => true do
|
38
|
-
# watch('config/application.rb')
|
39
|
-
# watch('config/environment.rb')
|
40
|
-
# watch(%r{^config/environments/.+\.rb})
|
41
|
-
# watch(%r{^config/initializers/.+\.rb})
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# guard 'livereload', :apply_js_live => false do
|
45
|
-
# watch(%r{^app/.+\.(erb|haml)})
|
46
|
-
# watch(%r{^app/helpers/.+\.rb})
|
47
|
-
# watch(%r{^public/javascripts/.+\.js})
|
48
|
-
# watch(%r{^public/stylesheets/.+\.css})
|
49
|
-
# watch(%r{^public/.+\.html})
|
50
|
-
# watch(%r{^config/locales/.+\.yml})
|
51
|
-
# end
|
52
|
-
# end
|
53
|
-
#
|
54
|
-
# group 'backend' do
|
55
|
-
# # Reload the bundle when the Gemfile is modified
|
56
|
-
# guard 'bundler' do
|
57
|
-
# watch('Gemfile')
|
58
|
-
# end
|
59
39
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
# watch('config/application.rb')
|
64
|
-
# watch('config/environment.rb')
|
65
|
-
# watch(%r{^config/environments/.+\.rb})
|
66
|
-
# watch(%r{^config/initializers/.+\.rb})
|
67
|
-
# watch('spec/spec_helper.rb')
|
68
|
-
# end
|
40
|
+
# * The `Guardfile` or `guardfile.rb` in the current directory where Guard
|
41
|
+
# has been started
|
42
|
+
# * The `.Guardfile` in your home directory.
|
69
43
|
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
# watch('spec/spec_helper.rb') { "spec" }
|
73
|
-
# watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
74
|
-
# watch('config/routes.rb') { "spec/routing" }
|
75
|
-
# watch(%r{^spec/support/(controllers|acceptance)_helpers\.rb}) { |m| "spec/#{m[1]}" }
|
76
|
-
# watch(%r{^spec/.+_spec\.rb})
|
44
|
+
# In addition, if a user configuration `.guard.rb` in your home directory is
|
45
|
+
# found, it will be appended to the current project `Guardfile`.
|
77
46
|
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
# watch(%r{^app/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
|
81
|
-
# watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
82
|
-
# end
|
83
|
-
# end
|
47
|
+
# @see https://github.com/guard/guard/wiki/Guardfile-examples
|
84
48
|
#
|
85
49
|
class Dsl
|
50
|
+
Deprecated::Dsl.add_deprecated(self) unless Config.new.strict?
|
86
51
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
require 'guard/notifier'
|
91
|
-
require 'guard/ui'
|
92
|
-
require 'guard/watcher'
|
93
|
-
|
94
|
-
# Deprecation message for the `ignore_paths` method
|
95
|
-
IGNORE_PATHS_DEPRECATION = <<-EOS.gsub(/^\s*/, '')
|
96
|
-
Starting with Guard v1.1 the use of the 'ignore_paths' Guardfile dsl method is deprecated.
|
97
|
-
|
98
|
-
Please replace that method with the better 'ignore' or/and 'filter' methods.
|
99
|
-
Documentation on the README: https://github.com/guard/guard#guardfile-dsl-ignore
|
100
|
-
EOS
|
101
|
-
|
102
|
-
class << self
|
103
|
-
|
104
|
-
@@options = nil
|
105
|
-
|
106
|
-
# Evaluate the DSL methods in the `Guardfile`.
|
107
|
-
#
|
108
|
-
# @option options [Array<Symbol,String>] groups the groups to evaluate
|
109
|
-
# @option options [String] guardfile the path to a valid Guardfile
|
110
|
-
# @option options [String] guardfile_contents a string representing the content of a valid Guardfile
|
111
|
-
# @raise [ArgumentError] when options are not a Hash
|
112
|
-
#
|
113
|
-
def evaluate_guardfile(options = {})
|
114
|
-
raise ArgumentError.new('No option hash passed to evaluate_guardfile!') unless options.is_a?(Hash)
|
115
|
-
|
116
|
-
@@options = options.dup
|
117
|
-
|
118
|
-
fetch_guardfile_contents
|
119
|
-
instance_eval_guardfile(guardfile_contents_with_user_config)
|
120
|
-
end
|
121
|
-
|
122
|
-
# Re-evaluate the `Guardfile` to update the current Guard configuration.
|
123
|
-
#
|
124
|
-
def reevaluate_guardfile
|
125
|
-
before_reevaluate_guardfile
|
126
|
-
::Guard::Dsl.evaluate_guardfile(@@options)
|
127
|
-
after_reevaluate_guardfile
|
128
|
-
end
|
129
|
-
|
130
|
-
# Stop Guard and clear internal state
|
131
|
-
# before the Guardfile will be re-evaluated.
|
132
|
-
#
|
133
|
-
def before_reevaluate_guardfile
|
134
|
-
::Guard.runner.run(:stop)
|
135
|
-
::Guard.guards.clear
|
136
|
-
::Guard.setup_groups
|
137
|
-
::Guard::Notifier.clear_notifications
|
138
|
-
|
139
|
-
@@options.delete(:guardfile_contents)
|
140
|
-
end
|
141
|
-
|
142
|
-
# Start Guard and notification and show a message
|
143
|
-
# after the Guardfile has been re-evaluated.
|
144
|
-
#
|
145
|
-
def after_reevaluate_guardfile
|
146
|
-
::Guard::Notifier.turn_on if ::Guard::Notifier.enabled?
|
147
|
-
|
148
|
-
if ::Guard.guards.empty?
|
149
|
-
::Guard::Notifier.notify('No guards found in Guardfile, please add at least one.', :title => 'Guard re-evaluate', :image => :failed)
|
150
|
-
else
|
151
|
-
msg = 'Guardfile has been re-evaluated.'
|
152
|
-
::Guard::UI.info(msg)
|
153
|
-
::Guard::Notifier.notify(msg, :title => 'Guard re-evaluate')
|
154
|
-
|
155
|
-
::Guard.runner.run(:start)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Evaluate the content of the `Guardfile`.
|
160
|
-
#
|
161
|
-
# @param [String] contents the content to evaluate.
|
162
|
-
#
|
163
|
-
def instance_eval_guardfile(contents)
|
164
|
-
new.instance_eval(contents, @@options[:guardfile_path], 1)
|
165
|
-
rescue
|
166
|
-
::Guard::UI.error "Invalid Guardfile, original error is:\n#{ $! }"
|
167
|
-
end
|
168
|
-
|
169
|
-
# Test if the current `Guardfile` contains a specific Guard plugin.
|
170
|
-
#
|
171
|
-
# @param [String] guard_name the name of the Guard
|
172
|
-
# @return [Boolean] whether the Guard has been declared
|
173
|
-
#
|
174
|
-
def guardfile_include?(guard_name)
|
175
|
-
guardfile_contents.match(/^guard\s*\(?\s*['":]#{ guard_name }['"]?/)
|
176
|
-
end
|
177
|
-
|
178
|
-
# Read the current `Guardfile` content.
|
179
|
-
#
|
180
|
-
# @param [String] guardfile_path the path to the Guardfile
|
181
|
-
#
|
182
|
-
def read_guardfile(guardfile_path)
|
183
|
-
@@options[:guardfile_path] = guardfile_path
|
184
|
-
@@options[:guardfile_contents] = File.read(guardfile_path)
|
185
|
-
rescue
|
186
|
-
::Guard::UI.error("Error reading file #{ guardfile_path }")
|
187
|
-
exit 1
|
188
|
-
end
|
189
|
-
|
190
|
-
# Get the content to evaluate and stores it into
|
191
|
-
# the options as `:guardfile_contents`.
|
192
|
-
#
|
193
|
-
def fetch_guardfile_contents
|
194
|
-
if @@options[:guardfile_contents]
|
195
|
-
::Guard::UI.info 'Using inline Guardfile.'
|
196
|
-
@@options[:guardfile_path] = 'Inline Guardfile'
|
197
|
-
|
198
|
-
elsif @@options[:guardfile]
|
199
|
-
if File.exist?(@@options[:guardfile])
|
200
|
-
read_guardfile(@@options[:guardfile])
|
201
|
-
::Guard::UI.info "Using Guardfile at #{ @@options[:guardfile] }."
|
202
|
-
else
|
203
|
-
::Guard::UI.error "No Guardfile exists at #{ @@options[:guardfile] }."
|
204
|
-
exit 1
|
205
|
-
end
|
206
|
-
|
207
|
-
else
|
208
|
-
if File.exist?(guardfile_default_path)
|
209
|
-
read_guardfile(guardfile_default_path)
|
210
|
-
else
|
211
|
-
::Guard::UI.error 'No Guardfile found, please create one with `guard init`.'
|
212
|
-
exit 1
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
unless guardfile_contents_usable?
|
217
|
-
::Guard::UI.error 'No Guard plugins found in Guardfile, please add at least one.'
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
# Get the content of the `Guardfile`.
|
222
|
-
#
|
223
|
-
# @return [String] the Guardfile content
|
224
|
-
#
|
225
|
-
def guardfile_contents
|
226
|
-
@@options ? @@options[:guardfile_contents] : ''
|
227
|
-
end
|
228
|
-
|
229
|
-
# Get the content of the `Guardfile` and the global
|
230
|
-
# user configuration file.
|
231
|
-
#
|
232
|
-
# @see #user_config_path
|
233
|
-
#
|
234
|
-
# @return [String] the Guardfile content
|
235
|
-
#
|
236
|
-
def guardfile_contents_with_user_config
|
237
|
-
config = File.read(user_config_path) if File.exist?(user_config_path)
|
238
|
-
[guardfile_contents, config].join("\n")
|
239
|
-
end
|
240
|
-
|
241
|
-
# Get the file path to the project `Guardfile`.
|
242
|
-
#
|
243
|
-
# @return [String] the path to the Guardfile
|
244
|
-
#
|
245
|
-
def guardfile_path
|
246
|
-
@@options ? @@options[:guardfile_path] : ''
|
247
|
-
end
|
248
|
-
|
249
|
-
# Tests if the current `Guardfile` content is usable.
|
250
|
-
#
|
251
|
-
# @return [Boolean] if the Guardfile is usable
|
252
|
-
#
|
253
|
-
def guardfile_contents_usable?
|
254
|
-
guardfile_contents && guardfile_contents.size >= 'guard :a'.size # Smallest Guard definition
|
255
|
-
end
|
256
|
-
|
257
|
-
# Gets the default path of the `Guardfile`. This returns the `Guardfile`
|
258
|
-
# from the current directory when existing, or the global `.Guardfile`
|
259
|
-
# at the home directory.
|
260
|
-
#
|
261
|
-
# @return [String] the path to the Guardfile
|
262
|
-
#
|
263
|
-
def guardfile_default_path
|
264
|
-
File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
|
265
|
-
end
|
266
|
-
|
267
|
-
private
|
268
|
-
|
269
|
-
# The path to the `Guardfile` that is located at
|
270
|
-
# the directory, where Guard has been started from.
|
271
|
-
#
|
272
|
-
# @return [String] the path to the local Guardfile
|
273
|
-
#
|
274
|
-
def local_guardfile_path
|
275
|
-
File.join(Dir.pwd, 'Guardfile')
|
276
|
-
end
|
277
|
-
|
278
|
-
# The path to the `.Guardfile` that is located at
|
279
|
-
# the users home directory.
|
280
|
-
#
|
281
|
-
# @return [String] the path to ~/.Guardfile
|
282
|
-
#
|
283
|
-
def home_guardfile_path
|
284
|
-
File.expand_path(File.join('~', '.Guardfile'))
|
285
|
-
end
|
52
|
+
# Wrap exceptions during parsing Guardfile
|
53
|
+
class Error < RuntimeError
|
54
|
+
end
|
286
55
|
|
287
|
-
|
288
|
-
|
289
|
-
#
|
290
|
-
# @return [String] the path to ~/.guard.rb
|
291
|
-
#
|
292
|
-
def user_config_path
|
293
|
-
File.expand_path(File.join('~', '.guard.rb'))
|
294
|
-
end
|
56
|
+
WARN_INVALID_LOG_LEVEL = "Invalid log level `%s` ignored. "\
|
57
|
+
"Please use either :debug, :info, :warn or :error."
|
295
58
|
|
296
|
-
|
59
|
+
WARN_INVALID_LOG_OPTIONS = "You cannot specify the logger options"\
|
60
|
+
" :only and :except at the same time."
|
297
61
|
|
298
62
|
# Set notification options for the system notifications.
|
299
|
-
# You can set multiple
|
63
|
+
# You can set multiple notifications, which allows you to show local
|
300
64
|
# system notifications and remote notifications with separate libraries.
|
301
65
|
# You can also pass `:off` as library to turn off notifications.
|
302
66
|
#
|
303
67
|
# @example Define multiple notifications
|
304
|
-
# notification :
|
305
|
-
# notification :ruby_gntp, :
|
306
|
-
#
|
307
|
-
# @see Guard::Notifier for available notifier and its options.
|
68
|
+
# notification :ruby_gntp
|
69
|
+
# notification :ruby_gntp, host: '192.168.1.5'
|
308
70
|
#
|
309
71
|
# @param [Symbol, String] notifier the name of the notifier to use
|
310
|
-
# @param [Hash]
|
72
|
+
# @param [Hash] opts the notification library options
|
311
73
|
#
|
312
|
-
|
313
|
-
|
74
|
+
# @see Guard::Notifier for available notifier and its options.
|
75
|
+
#
|
76
|
+
def notification(notifier, opts = {})
|
77
|
+
Guard.state.session.guardfile_notification = { notifier.to_sym => opts }
|
314
78
|
end
|
315
79
|
|
316
|
-
# Sets the interactor
|
80
|
+
# Sets the interactor options or disable the interactor.
|
317
81
|
#
|
318
|
-
# @example
|
319
|
-
# interactor :
|
320
|
-
#
|
321
|
-
# @example Use the gets interactor
|
322
|
-
# interactor :gets
|
82
|
+
# @example Pass options to the interactor
|
83
|
+
# interactor option1: 'value1', option2: 'value2'
|
323
84
|
#
|
324
85
|
# @example Turn off interactions
|
325
86
|
# interactor :off
|
326
87
|
#
|
327
|
-
|
328
|
-
|
88
|
+
# @param [Symbol, Hash] options either `:off` or a Hash with interactor
|
89
|
+
# options
|
90
|
+
#
|
91
|
+
def interactor(options)
|
92
|
+
# TODO: remove dependency on Interactor (let session handle this)
|
93
|
+
case options
|
94
|
+
when :off
|
95
|
+
Interactor.enabled = false
|
96
|
+
when Hash
|
97
|
+
Interactor.options = options
|
98
|
+
end
|
329
99
|
end
|
330
100
|
|
331
|
-
# Declares a group of Guard plugins to be run with `guard start --group
|
101
|
+
# Declares a group of Guard plugins to be run with `guard start --group
|
102
|
+
# group_name`.
|
332
103
|
#
|
333
104
|
# @example Declare two groups of Guard plugins
|
334
|
-
#
|
335
|
-
#
|
336
|
-
# guard
|
337
|
-
# guard 'rspec'
|
105
|
+
# group :backend do
|
106
|
+
# guard :spork
|
107
|
+
# guard :rspec
|
338
108
|
# end
|
339
109
|
#
|
340
|
-
# group
|
341
|
-
# guard
|
342
|
-
# guard
|
110
|
+
# group :frontend do
|
111
|
+
# guard :passenger
|
112
|
+
# guard :livereload
|
343
113
|
# end
|
344
114
|
#
|
345
|
-
# @param [Symbol, String] name the group name called
|
115
|
+
# @param [Symbol, String, Array<Symbol, String>] name the group name called
|
116
|
+
# from the CLI
|
346
117
|
# @param [Hash] options the options accepted by the group
|
347
|
-
# @yield a block where you can declare several
|
118
|
+
# @yield a block where you can declare several Guard plugins
|
348
119
|
#
|
120
|
+
# @see Group
|
349
121
|
# @see Guard.add_group
|
350
|
-
# @see
|
351
|
-
# @see Guard::DslDescriber
|
122
|
+
# @see #guard
|
352
123
|
#
|
353
|
-
def group(
|
354
|
-
|
355
|
-
|
124
|
+
def group(*args)
|
125
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
126
|
+
groups = args
|
356
127
|
|
357
|
-
|
358
|
-
|
359
|
-
|
128
|
+
groups.each do |group|
|
129
|
+
next unless group.to_sym == :all
|
130
|
+
fail ArgumentError, "'all' is not an allowed group name!"
|
131
|
+
end
|
360
132
|
|
361
|
-
|
133
|
+
if block_given?
|
134
|
+
groups.each do |group|
|
135
|
+
# TODO: let groups be added *after* evaluation
|
136
|
+
Guard.state.session.groups.add(group, options)
|
137
|
+
end
|
362
138
|
|
363
|
-
@
|
139
|
+
@current_groups ||= []
|
140
|
+
@current_groups.push(groups)
|
141
|
+
|
142
|
+
yield
|
143
|
+
|
144
|
+
@current_groups.pop
|
145
|
+
else
|
146
|
+
UI.error \
|
147
|
+
"No Guard plugins found in the group '#{ groups.join(', ') }',"\
|
148
|
+
" please add at least one."
|
364
149
|
end
|
365
150
|
end
|
366
151
|
|
367
|
-
#
|
152
|
+
# Declares a Guard plugin to be used when running `guard start`.
|
368
153
|
#
|
369
154
|
# The name parameter is usually the name of the gem without
|
370
155
|
# the 'guard-' prefix.
|
371
156
|
#
|
372
157
|
# The available options are different for each Guard implementation.
|
373
158
|
#
|
374
|
-
# @example Declare a Guard
|
159
|
+
# @example Declare a Guard without `watch` patterns
|
160
|
+
# guard :rspec
|
375
161
|
#
|
376
|
-
#
|
162
|
+
# @example Declare a Guard with a `watch` pattern
|
163
|
+
# guard :rspec do
|
164
|
+
# watch %r{.*_spec.rb}
|
377
165
|
# end
|
378
166
|
#
|
379
|
-
# @param [String] name the Guard name
|
380
|
-
# @param [Hash] options the options accepted by the Guard
|
167
|
+
# @param [String] name the Guard plugin name
|
168
|
+
# @param [Hash] options the options accepted by the Guard plugin
|
381
169
|
# @yield a block where you can declare several watch patterns and actions
|
382
170
|
#
|
383
|
-
# @see
|
384
|
-
# @see
|
385
|
-
# @see
|
386
|
-
# @see
|
171
|
+
# @see Plugin
|
172
|
+
# @see Guard.add_plugin
|
173
|
+
# @see #watch
|
174
|
+
# @see #group
|
387
175
|
#
|
388
176
|
def guard(name, options = {})
|
389
|
-
@
|
390
|
-
@callbacks = []
|
391
|
-
@current_group ||= :default
|
177
|
+
@plugin_options = options.merge(watchers: [], callbacks: [])
|
392
178
|
|
393
179
|
yield if block_given?
|
394
180
|
|
395
|
-
|
396
|
-
|
181
|
+
@current_groups ||= []
|
182
|
+
groups = @current_groups && @current_groups.last || [:default]
|
183
|
+
groups.each do |group|
|
184
|
+
opts = @plugin_options.merge(group: group)
|
185
|
+
# TODO: let plugins be added *after* evaluation
|
186
|
+
Guard.state.session.plugins.add(name, opts)
|
187
|
+
end
|
188
|
+
|
189
|
+
@plugin_options = nil
|
397
190
|
end
|
398
191
|
|
399
|
-
#
|
192
|
+
# Defines a pattern to be watched in order to run actions on file
|
193
|
+
# modification.
|
400
194
|
#
|
401
195
|
# @example Declare watchers for a Guard
|
402
|
-
#
|
403
|
-
# guard 'rspec' do
|
196
|
+
# guard :rspec do
|
404
197
|
# watch('spec/spec_helper.rb')
|
405
198
|
# watch(%r{^.+_spec.rb})
|
406
|
-
# watch(%r{^app/controllers/(.+).rb})
|
199
|
+
# watch(%r{^app/controllers/(.+).rb}) do |m|
|
200
|
+
# 'spec/acceptance/#{m[1]}s_spec.rb'
|
201
|
+
# end
|
407
202
|
# end
|
408
203
|
#
|
409
|
-
# @
|
204
|
+
# @example Declare global watchers outside of a Guard
|
205
|
+
# watch(%r{^(.+)$}) { |m| puts "#{m[1]} changed." }
|
206
|
+
#
|
207
|
+
# @param [String, Regexp] pattern the pattern that Guard must watch for
|
208
|
+
# modification
|
209
|
+
#
|
410
210
|
# @yield a block to be run when the pattern is matched
|
411
211
|
# @yieldparam [MatchData] m matches of the pattern
|
412
|
-
# @yieldreturn a directory, a filename, an array of
|
212
|
+
# @yieldreturn a directory, a filename, an array of
|
213
|
+
# directories / filenames, or nothing (can be an arbitrary command)
|
413
214
|
#
|
414
215
|
# @see Guard::Watcher
|
415
|
-
# @see
|
216
|
+
# @see #guard
|
416
217
|
#
|
417
218
|
def watch(pattern, &action)
|
418
|
-
|
219
|
+
# Allow watches in the global scope (to execute arbitrary commands) by
|
220
|
+
# building a generic Guard::Plugin.
|
221
|
+
@plugin_options ||= nil
|
222
|
+
return guard(:plugin) { watch(pattern, &action) } unless @plugin_options
|
223
|
+
|
224
|
+
@plugin_options[:watchers] << Watcher.new(pattern, action)
|
419
225
|
end
|
420
226
|
|
421
|
-
#
|
422
|
-
# the `start`, `stop`, `reload`, `run_all`, `run_on_changes
|
423
|
-
# and `run_on_removals` plugin
|
227
|
+
# Defines a callback to execute arbitrary code before or after any of
|
228
|
+
# the `start`, `stop`, `reload`, `run_all`, `run_on_changes`,
|
229
|
+
# `run_on_additions`, `run_on_modifications` and `run_on_removals` plugin
|
230
|
+
# method.
|
424
231
|
#
|
425
|
-
# @
|
426
|
-
#
|
232
|
+
# @example Add callback before the `reload` action.
|
233
|
+
# callback(:reload_begin) { puts "Let's reload!" }
|
427
234
|
#
|
428
|
-
# @
|
235
|
+
# @example Add callback before the `start` and `stop` actions.
|
429
236
|
#
|
430
|
-
|
431
|
-
|
432
|
-
|
237
|
+
# my_lambda = lambda do |plugin, event, *args|
|
238
|
+
# puts "Let's #{event} #{plugin} with #{args}!"
|
239
|
+
# end
|
240
|
+
#
|
241
|
+
# callback(my_lambda, [:start_begin, :start_end])
|
242
|
+
#
|
243
|
+
# @param [Array] args the callback arguments
|
244
|
+
# @yield a callback block
|
245
|
+
#
|
246
|
+
def callback(*args, &block)
|
247
|
+
@plugin_options ||= nil
|
248
|
+
fail "callback must be called within a guard block" unless @plugin_options
|
249
|
+
|
250
|
+
block, events = if args.size > 1
|
251
|
+
# block must be the first argument in that case, the
|
252
|
+
# yielded block is ignored
|
253
|
+
args
|
254
|
+
else
|
255
|
+
[block, args[0]]
|
256
|
+
end
|
257
|
+
@plugin_options[:callbacks] << { events: events, listener: block }
|
433
258
|
end
|
434
259
|
|
435
|
-
#
|
260
|
+
# Ignores certain paths globally.
|
436
261
|
#
|
437
262
|
# @example Ignore some paths
|
438
|
-
#
|
263
|
+
# ignore %r{^ignored/path/}, /man/
|
439
264
|
#
|
440
|
-
# @param [
|
265
|
+
# @param [Regexp] regexps a pattern (or list of patterns) for ignoring paths
|
441
266
|
#
|
442
|
-
def
|
443
|
-
|
267
|
+
def ignore(*regexps)
|
268
|
+
# TODO: use guardfile results class
|
269
|
+
Guard.state.session.guardfile_ignore = regexps
|
444
270
|
end
|
445
271
|
|
446
|
-
#
|
272
|
+
# TODO: deprecate
|
273
|
+
alias filter ignore
|
274
|
+
|
275
|
+
# Replaces ignored paths globally
|
447
276
|
#
|
448
|
-
# @example Ignore
|
449
|
-
# ignore %r{^ignored/path/}, /man/
|
277
|
+
# @example Ignore only these paths
|
278
|
+
# ignore! %r{^ignored/path/}, /man/
|
450
279
|
#
|
451
|
-
# @param [Regexp] regexps a pattern for ignoring paths
|
280
|
+
# @param [Regexp] regexps a pattern (or list of patterns) for ignoring paths
|
452
281
|
#
|
453
|
-
def ignore(*regexps)
|
454
|
-
|
282
|
+
def ignore!(*regexps)
|
283
|
+
@ignore_regexps ||= []
|
284
|
+
@ignore_regexps << regexps
|
285
|
+
# TODO: use guardfile results class
|
286
|
+
Guard.state.session.guardfile_ignore_bang = @ignore_regexps
|
287
|
+
end
|
288
|
+
|
289
|
+
# TODO: deprecate
|
290
|
+
alias filter! ignore!
|
291
|
+
|
292
|
+
# Configures the Guard logger.
|
293
|
+
#
|
294
|
+
# * Log level must be either `:debug`, `:info`, `:warn` or `:error`.
|
295
|
+
# * Template supports the following placeholders: `:time`, `:severity`,
|
296
|
+
# `:progname`, `:pid`, `:unit_of_work_id` and `:message`.
|
297
|
+
# * Time format directives are the same as `Time#strftime` or
|
298
|
+
# `:milliseconds`.
|
299
|
+
# * The `:only` and `:except` options must be a `RegExp`.
|
300
|
+
#
|
301
|
+
# @example Set the log level
|
302
|
+
# logger level: :warn
|
303
|
+
#
|
304
|
+
# @example Set a custom log template
|
305
|
+
# logger template: '[Guard - :severity - :progname - :time] :message'
|
306
|
+
#
|
307
|
+
# @example Set a custom time format
|
308
|
+
# logger time_format: '%h'
|
309
|
+
#
|
310
|
+
# @example Limit logging to a Guard plugin
|
311
|
+
# logger only: :jasmine
|
312
|
+
#
|
313
|
+
# @example Log all but not the messages from a specific Guard plugin
|
314
|
+
# logger except: :jasmine
|
315
|
+
#
|
316
|
+
# @param [Hash] options the log options
|
317
|
+
# @option options [String, Symbol] level the log level
|
318
|
+
# @option options [String] template the logger template
|
319
|
+
# @option options [String, Symbol] time_format the time format
|
320
|
+
# @option options [Regexp] only show only messages from the matching Guard
|
321
|
+
# plugin
|
322
|
+
# @option options [Regexp] except does not show messages from the matching
|
323
|
+
# Guard plugin
|
324
|
+
#
|
325
|
+
def logger(options)
|
326
|
+
if options[:level]
|
327
|
+
options[:level] = options[:level].to_sym
|
328
|
+
|
329
|
+
unless [:debug, :info, :warn, :error].include? options[:level]
|
330
|
+
UI.warning(format(WARN_INVALID_LOG_LEVEL, options[:level]))
|
331
|
+
options.delete :level
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
if options[:only] && options[:except]
|
336
|
+
UI.warning WARN_INVALID_LOG_OPTIONS
|
337
|
+
|
338
|
+
options.delete :only
|
339
|
+
options.delete :except
|
340
|
+
end
|
341
|
+
|
342
|
+
# Convert the :only and :except options to a regular expression
|
343
|
+
[:only, :except].each do |name|
|
344
|
+
next unless options[name]
|
345
|
+
|
346
|
+
list = [].push(options[name]).flatten.map do |plugin|
|
347
|
+
Regexp.escape(plugin.to_s)
|
348
|
+
end
|
349
|
+
|
350
|
+
options[name] = Regexp.new(list.join("|"), Regexp::IGNORECASE)
|
351
|
+
end
|
352
|
+
|
353
|
+
UI.options = UI.options.merge(options)
|
455
354
|
end
|
456
355
|
|
457
|
-
#
|
356
|
+
# Sets the default scope on startup
|
458
357
|
#
|
459
|
-
# @example
|
460
|
-
#
|
358
|
+
# @example Scope Guard to a single group
|
359
|
+
# scope group: :frontend
|
461
360
|
#
|
462
|
-
# @
|
361
|
+
# @example Scope Guard to multiple groups
|
362
|
+
# scope groups: [:specs, :docs]
|
463
363
|
#
|
464
|
-
|
465
|
-
|
364
|
+
# @example Scope Guard to a single plugin
|
365
|
+
# scope plugin: :test
|
366
|
+
#
|
367
|
+
# @example Scope Guard to multiple plugins
|
368
|
+
# scope plugins: [:jasmine, :rspec]
|
369
|
+
#
|
370
|
+
# @param [Hash] scope the scope for the groups and plugins
|
371
|
+
#
|
372
|
+
def scope(scope = {})
|
373
|
+
# TODO: use a Guardfile::Results class
|
374
|
+
Guard.state.session.guardfile_scope(scope)
|
375
|
+
end
|
376
|
+
|
377
|
+
def evaluate(contents, filename, lineno) # :nodoc
|
378
|
+
instance_eval(contents, filename.to_s, lineno)
|
379
|
+
rescue StandardError, ScriptError => e
|
380
|
+
prefix = "\n\t(dsl)> "
|
381
|
+
cleaned_backtrace = _cleanup_backtrace(e.backtrace)
|
382
|
+
backtrace = "#{prefix}#{cleaned_backtrace.join(prefix)}"
|
383
|
+
msg = "Invalid Guardfile, original error is: \n\n%s, \nbacktrace: %s"
|
384
|
+
raise Error, format(msg, e, backtrace)
|
466
385
|
end
|
467
386
|
|
387
|
+
# Sets the directories to pass to Listen
|
388
|
+
#
|
389
|
+
# @example watch only given directories
|
390
|
+
# directories %w(lib specs)
|
391
|
+
#
|
392
|
+
# @param [Array] directories directories for Listen to watch
|
393
|
+
#
|
394
|
+
def directories(directories)
|
395
|
+
directories.each do |dir|
|
396
|
+
fail "Directory #{dir.inspect} does not exist!" unless Dir.exist?(dir)
|
397
|
+
end
|
398
|
+
Guard.state.session.watchdirs = directories
|
399
|
+
end
|
400
|
+
|
401
|
+
# Sets Guard to clear the screen before every task is run
|
402
|
+
#
|
403
|
+
# @example switching clearing the screen on
|
404
|
+
# clearing(:on)
|
405
|
+
#
|
406
|
+
# @param [Symbol] on ':on' to turn on, ':off' (default) to turn off
|
407
|
+
#
|
408
|
+
def clearing(on)
|
409
|
+
Guard.state.session.clearing(on == :on)
|
410
|
+
end
|
411
|
+
|
412
|
+
private
|
413
|
+
|
414
|
+
def _cleanup_backtrace(backtrace)
|
415
|
+
dirs = { File.realpath(Dir.pwd) => ".", }
|
416
|
+
|
417
|
+
gem_env = ENV["GEM_HOME"] || ""
|
418
|
+
dirs[gem_env] = "$GEM_HOME" unless gem_env.empty?
|
419
|
+
|
420
|
+
gem_paths = (ENV["GEM_PATH"] || "").split(File::PATH_SEPARATOR)
|
421
|
+
gem_paths.each_with_index do |path, index|
|
422
|
+
dirs[path] = "$GEM_PATH[#{index}]"
|
423
|
+
end
|
424
|
+
|
425
|
+
backtrace.dup.map do |raw_line|
|
426
|
+
path = nil
|
427
|
+
symlinked_path = raw_line.split(":").first
|
428
|
+
begin
|
429
|
+
path = raw_line.sub(symlinked_path, File.realpath(symlinked_path))
|
430
|
+
dirs.detect { |dir, name| path.sub!(File.realpath(dir), name) }
|
431
|
+
path
|
432
|
+
rescue Errno::ENOENT
|
433
|
+
path || symlinked_path
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
468
437
|
end
|
469
438
|
end
|