guard 2.8.2 → 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +0 -7
- data/lib/guard.rb +220 -152
- data/lib/guard.rb.orig +213 -155
- data/lib/guard/aruba_adapter.rb +2 -2
- data/lib/guard/cli.rb +8 -13
- data/lib/guard/cli.rb.orig +12 -10
- data/lib/guard/commander.rb +15 -7
- data/lib/guard/commands/all.rb +3 -0
- data/lib/guard/commands/change.rb +3 -0
- data/lib/guard/commands/pause.rb +2 -0
- data/lib/guard/commands/reload.rb +4 -0
- data/lib/guard/commands/scope.rb +3 -0
- data/lib/guard/config.rb +24 -0
- data/lib/guard/deprecated/dsl.rb +45 -0
- data/lib/guard/deprecated/guard.rb +166 -0
- data/lib/guard/deprecated/guardfile.rb +84 -0
- data/lib/guard/dsl.rb +24 -13
- data/lib/guard/dsl.rb.orig +378 -0
- data/lib/guard/dsl_describer.rb +8 -2
- data/lib/guard/dsl_describer.rb.orig +11 -3
- data/lib/guard/guardfile.rb +32 -44
- data/lib/guard/guardfile/evaluator.rb +13 -6
- data/lib/guard/guardfile/generator.rb +4 -3
- data/lib/guard/interactor.rb +7 -3
- data/lib/guard/internals/debugging.rb +1 -0
- data/lib/guard/internals/environment.rb +93 -0
- data/lib/guard/internals/helpers.rb +13 -0
- data/lib/guard/internals/traps.rb +10 -0
- data/lib/guard/jobs/pry_wrapper.rb +4 -3
- data/lib/guard/jobs/sleep.rb +2 -0
- data/lib/guard/metadata.rb +190 -0
- data/lib/guard/notifier.rb +124 -99
- data/lib/guard/notifier.rb.orig +124 -99
- data/lib/guard/notifier/detected.rb +83 -0
- data/lib/guard/notifiers/emacs.rb +2 -1
- data/lib/guard/notifiers/tmux.rb +173 -177
- data/lib/guard/plugin/base.rb +2 -0
- data/lib/guard/plugin_util.rb +26 -32
- data/lib/guard/reevaluator.rb +3 -3
- data/lib/guard/reevaluator.rb.orig +22 -0
- data/lib/guard/runner.rb +1 -0
- data/lib/guard/session.rb +5 -0
- data/lib/guard/sheller.rb +2 -2
- data/lib/guard/templates/Guardfile +4 -0
- data/lib/guard/templates/Guardfile.orig +2 -0
- data/lib/guard/terminal.rb +1 -0
- data/lib/guard/ui.rb +4 -1
- data/lib/guard/version.rb +1 -1
- data/lib/guard/version.rb.orig +1 -1
- data/lib/guard/watcher.rb +3 -1
- data/lib/guard/watcher.rb.orig +122 -0
- data/man/guard.1 +1 -4
- data/man/guard.1.html +1 -4
- metadata +17 -25
- data/lib/guard/commander.rb.orig +0 -103
- data/lib/guard/commands/all.rb.orig +0 -36
- data/lib/guard/commands/reload.rb.orig +0 -34
- data/lib/guard/commands/scope.rb.orig +0 -36
- data/lib/guard/deprecated_methods.rb +0 -72
- data/lib/guard/deprecated_methods.rb.orig +0 -71
- data/lib/guard/deprecator.rb +0 -133
- data/lib/guard/deprecator.rb.orig +0 -206
- data/lib/guard/guard.rb +0 -100
- data/lib/guard/guard.rb.orig +0 -42
- data/lib/guard/guardfile.rb.orig +0 -43
- data/lib/guard/guardfile/evaluator.rb.orig +0 -275
- data/lib/guard/internals/debugging.rb.orig +0 -0
- data/lib/guard/internals/environment.rb.orig +0 -0
- data/lib/guard/internals/tracing.rb.orig +0 -0
- data/lib/guard/notifiers/base.rb.orig +0 -221
- data/lib/guard/notifiers/tmux.rb.orig +0 -339
- data/lib/guard/plugin_util.rb.orig +0 -186
- data/lib/guard/runner.rb.orig +0 -210
- data/lib/guard/setuper.rb +0 -359
- data/lib/guard/setuper.rb.orig +0 -395
- data/lib/guard/ui.rb.orig +0 -278
data/lib/guard/setuper.rb.orig
DELETED
@@ -1,395 +0,0 @@
|
|
1
|
-
require "thread"
|
2
|
-
require "listen"
|
3
|
-
require "guard/options"
|
4
|
-
|
5
|
-
module Guard
|
6
|
-
# Sets up initial variables and options
|
7
|
-
module Setuper
|
8
|
-
DEFAULT_OPTIONS = {
|
9
|
-
clear: false,
|
10
|
-
notify: true,
|
11
|
-
debug: false,
|
12
|
-
group: [],
|
13
|
-
plugin: [],
|
14
|
-
watchdir: nil,
|
15
|
-
guardfile: nil,
|
16
|
-
no_interactions: false,
|
17
|
-
no_bundler_warning: false,
|
18
|
-
show_deprecations: false,
|
19
|
-
latency: nil,
|
20
|
-
force_polling: false,
|
21
|
-
wait_for_delay: nil,
|
22
|
-
listen_on: nil
|
23
|
-
}
|
24
|
-
DEFAULT_GROUPS = [:common, :default]
|
25
|
-
|
26
|
-
# Initializes the Guard singleton:
|
27
|
-
#
|
28
|
-
# * Initialize the internal Guard state;
|
29
|
-
# * Create the interactor
|
30
|
-
# * Select and initialize the file change listener.
|
31
|
-
#
|
32
|
-
# @option options [Boolean] clear if auto clear the UI should be done
|
33
|
-
# @option options [Boolean] notify if system notifications should be shown
|
34
|
-
# @option options [Boolean] debug if debug output should be shown
|
35
|
-
# @option options [Array<String>] group the list of groups to start
|
36
|
-
# @option options [Array<String>] watchdir the directories to watch
|
37
|
-
# @option options [String] guardfile the path to the Guardfile
|
38
|
-
# @option options [Boolean] watch_all_modifications **[deprecated]** watches all file modifications if true
|
39
|
-
# @option options [Boolean] no_vendor **[deprecated]** ignore vendored dependencies
|
40
|
-
#
|
41
|
-
# @return [Guard] the Guard singleton
|
42
|
-
#
|
43
|
-
|
44
|
-
# TODO: this method has too many instance variables
|
45
|
-
# and some are mock and leak between tests,
|
46
|
-
# so ideally there should be a guard "instance"
|
47
|
-
# object that can be created anew between tests
|
48
|
-
def setup(opts = {})
|
49
|
-
reset_options(opts)
|
50
|
-
reset_evaluator(opts)
|
51
|
-
|
52
|
-
@queue = Queue.new
|
53
|
-
@runner = ::Guard::Runner.new
|
54
|
-
@watchdirs = _setup_watchdirs
|
55
|
-
|
56
|
-
::Guard::UI.setup(options)
|
57
|
-
|
58
|
-
<<<<<<< HEAD
|
59
|
-
_setup_debug if options[:debug]
|
60
|
-
@listener = _setup_listener
|
61
|
-
_setup_signal_traps
|
62
|
-
=======
|
63
|
-
::Guard::Deprecator.deprecated_options_warning(options)
|
64
|
-
::Guard::Deprecator.deprecated_plugin_methods_warning
|
65
|
-
|
66
|
-
_setup_notifier
|
67
|
-
_setup_interactor
|
68
|
-
>>>>>>> parent of a5162d2... Remove deprecated methods and options. Fixes #425.
|
69
|
-
|
70
|
-
_load_guardfile
|
71
|
-
@interactor = _setup_interactor
|
72
|
-
self
|
73
|
-
end
|
74
|
-
|
75
|
-
attr_reader :options, :interactor
|
76
|
-
|
77
|
-
# Used only by tests (for all I know...)
|
78
|
-
def clear_options
|
79
|
-
@options = nil
|
80
|
-
end
|
81
|
-
|
82
|
-
# Initializes the groups array with the default group(s).
|
83
|
-
#
|
84
|
-
# @see DEFAULT_GROUPS
|
85
|
-
#
|
86
|
-
def reset_groups
|
87
|
-
@groups = DEFAULT_GROUPS.map { |name| Group.new(name) }
|
88
|
-
end
|
89
|
-
|
90
|
-
# Initializes the plugins array to an empty array.
|
91
|
-
#
|
92
|
-
# @see Guard.plugins
|
93
|
-
#
|
94
|
-
def reset_plugins
|
95
|
-
@plugins = []
|
96
|
-
end
|
97
|
-
|
98
|
-
# Initializes the scope hash to `{ groups: [], plugins: [] }`.
|
99
|
-
#
|
100
|
-
# @see Guard.setup_scope
|
101
|
-
#
|
102
|
-
def reset_scope
|
103
|
-
# calls Guard.scope=() to set the instance variable directly, as opposed
|
104
|
-
# to Guard.scope()
|
105
|
-
::Guard.scope = { groups: [], plugins: [] }
|
106
|
-
end
|
107
|
-
|
108
|
-
# Used to merge CLI options with Setuper defaults
|
109
|
-
def reset_options(new_options)
|
110
|
-
@options = ::Guard::Options.new(new_options, DEFAULT_OPTIONS)
|
111
|
-
end
|
112
|
-
|
113
|
-
# TODO: code smell - too many reset_* methods
|
114
|
-
def reset_evaluator(new_options)
|
115
|
-
@evaluator = ::Guard::Guardfile::Evaluator.new(new_options)
|
116
|
-
end
|
117
|
-
|
118
|
-
def save_scope
|
119
|
-
# This actually replaces scope from command line,
|
120
|
-
# so scope set by 'scope' Pry command will be reset
|
121
|
-
@saved_scope = _prepare_scope(::Guard.scope)
|
122
|
-
end
|
123
|
-
|
124
|
-
def restore_scope
|
125
|
-
::Guard.setup_scope(@saved_scope)
|
126
|
-
end
|
127
|
-
|
128
|
-
attr_reader :watchdirs
|
129
|
-
|
130
|
-
# Stores the scopes defined by the user via the `--group` / `-g` option (to
|
131
|
-
# run only a specific group) or the `--plugin` / `-P` option (to run only a
|
132
|
-
# specific plugin).
|
133
|
-
#
|
134
|
-
# @see CLI#start
|
135
|
-
# @see Dsl#scope
|
136
|
-
#
|
137
|
-
def setup_scope(scope = {})
|
138
|
-
# TODO: there should be a special Scope class instead
|
139
|
-
scope = _prepare_scope(scope)
|
140
|
-
|
141
|
-
::Guard.scope = {
|
142
|
-
groups: scope[:groups].map { |item| ::Guard.add_group(item) },
|
143
|
-
plugins: scope[:plugins].map { |item| ::Guard.plugin(item) },
|
144
|
-
}
|
145
|
-
end
|
146
|
-
|
147
|
-
# Evaluates the Guardfile content. It displays an error message if no
|
148
|
-
# Guard plugins are instantiated after the Guardfile evaluation.
|
149
|
-
#
|
150
|
-
# @see Guard::Guardfile::Evaluator#evaluate_guardfile
|
151
|
-
#
|
152
|
-
def evaluate_guardfile
|
153
|
-
evaluator.evaluate_guardfile
|
154
|
-
msg = "No plugins found in Guardfile, please add at least one."
|
155
|
-
::Guard::UI.error msg unless _pluginless_guardfile?
|
156
|
-
end
|
157
|
-
|
158
|
-
# Asynchronously trigger changes
|
159
|
-
#
|
160
|
-
# Currently supported args:
|
161
|
-
#
|
162
|
-
# old style hash: {modified: ['foo'], added: ['bar'], removed: []}
|
163
|
-
#
|
164
|
-
# new style signals with args: [:guard_pause, :unpaused ]
|
165
|
-
#
|
166
|
-
def async_queue_add(changes)
|
167
|
-
@queue << changes
|
168
|
-
|
169
|
-
# Putting interactor in background puts guard into foreground
|
170
|
-
# so it can handle change notifications
|
171
|
-
Thread.new { interactor.background }
|
172
|
-
end
|
173
|
-
|
174
|
-
def pending_changes?
|
175
|
-
! @queue.empty?
|
176
|
-
end
|
177
|
-
|
178
|
-
def add_builtin_plugins(guardfile)
|
179
|
-
return unless guardfile
|
180
|
-
|
181
|
-
pattern = _relative_pathname(guardfile).to_s
|
182
|
-
watcher = ::Guard::Watcher.new(pattern)
|
183
|
-
::Guard.add_plugin(:reevaluator, watchers: [watcher], group: :common)
|
184
|
-
end
|
185
|
-
|
186
|
-
private
|
187
|
-
|
188
|
-
# Sets up various debug behaviors:
|
189
|
-
#
|
190
|
-
# * Abort threads on exception;
|
191
|
-
# * Set the logging level to `:debug`;
|
192
|
-
# * Modify the system and ` methods to log themselves before being executed
|
193
|
-
#
|
194
|
-
# @see #_debug_command_execution
|
195
|
-
#
|
196
|
-
def _setup_debug
|
197
|
-
Thread.abort_on_exception = true
|
198
|
-
::Guard::UI.options[:level] = :debug
|
199
|
-
_debug_command_execution
|
200
|
-
end
|
201
|
-
|
202
|
-
# Initializes the listener and registers a callback for changes.
|
203
|
-
#
|
204
|
-
def _setup_listener
|
205
|
-
if options[:listen_on]
|
206
|
-
Listen.on(options[:listen_on], &_listener_callback)
|
207
|
-
else
|
208
|
-
listener_options = {}
|
209
|
-
[:latency, :force_polling, :wait_for_delay].each do |option|
|
210
|
-
listener_options[option] = options[option] if options[option]
|
211
|
-
end
|
212
|
-
listen_args = watchdirs + [listener_options]
|
213
|
-
Listen.to(*listen_args, &_listener_callback)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
# Process the change queue, running tasks within the main Guard thread
|
218
|
-
def _process_queue
|
219
|
-
actions, changes = [], { modified: [], added: [], removed: [] }
|
220
|
-
|
221
|
-
while pending_changes?
|
222
|
-
if (item = @queue.pop).first.is_a?(Symbol)
|
223
|
-
actions << item
|
224
|
-
else
|
225
|
-
item.each { |key, value| changes[key] += value }
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
_run_actions(actions)
|
230
|
-
return if changes.values.all?(&:empty?)
|
231
|
-
runner.run_on_changes(*changes.values)
|
232
|
-
end
|
233
|
-
|
234
|
-
# Sets up traps to catch signals used to control Guard.
|
235
|
-
#
|
236
|
-
# Currently two signals are caught:
|
237
|
-
# - `USR1` which pauses listening to changes.
|
238
|
-
# - `USR2` which resumes listening to changes.
|
239
|
-
# - 'INT' which is delegated to Pry if active, otherwise stops Guard.
|
240
|
-
#
|
241
|
-
def _setup_signal_traps
|
242
|
-
return if defined?(JRUBY_VERSION)
|
243
|
-
|
244
|
-
if Signal.list.keys.include?("USR1")
|
245
|
-
Signal.trap("USR1") { async_queue_add([:guard_pause, :paused]) }
|
246
|
-
end
|
247
|
-
|
248
|
-
if Signal.list.keys.include?("USR2")
|
249
|
-
Signal.trap("USR2") { async_queue_add([:guard_pause, :unpaused]) }
|
250
|
-
end
|
251
|
-
|
252
|
-
return unless Signal.list.keys.include?("INT")
|
253
|
-
Signal.trap("INT") { interactor.handle_interrupt }
|
254
|
-
end
|
255
|
-
|
256
|
-
# Enables or disables the notifier based on user's configurations.
|
257
|
-
#
|
258
|
-
def _setup_notifier
|
259
|
-
if options[:notify] && ENV["GUARD_NOTIFY"] != "false"
|
260
|
-
::Guard::Notifier.turn_on
|
261
|
-
else
|
262
|
-
::Guard::Notifier.turn_off
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
# Adds a command logger in debug mode. This wraps common command
|
267
|
-
# execution functions and logs the executed command before execution.
|
268
|
-
#
|
269
|
-
def _debug_command_execution
|
270
|
-
Kernel.send(:alias_method, :original_system, :system)
|
271
|
-
Kernel.send(:define_method, :system) do |command, *args|
|
272
|
-
::Guard::UI.debug "Command execution: #{ command } #{ args.join(" ") }"
|
273
|
-
Kernel.send :original_system, command, *args
|
274
|
-
end
|
275
|
-
|
276
|
-
Kernel.send(:alias_method, :original_backtick, :'`')
|
277
|
-
Kernel.send(:define_method, :'`') do |command|
|
278
|
-
::Guard::UI.debug "Command execution: #{ command }"
|
279
|
-
Kernel.send :original_backtick, command
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
# TODO: Guard::Watch or Guard::Scope should provide this
|
284
|
-
def _scoped_watchers
|
285
|
-
watchers = []
|
286
|
-
runner.send(:_scoped_plugins) { |guard| watchers += guard.watchers }
|
287
|
-
watchers
|
288
|
-
end
|
289
|
-
|
290
|
-
# Check if any of the changes are actually watched for
|
291
|
-
def _relevant_changes?(changes)
|
292
|
-
files = changes.values.flatten(1)
|
293
|
-
watchers = _scoped_watchers
|
294
|
-
watchers.any? { |watcher| files.any? { |file| watcher.match(file) } }
|
295
|
-
end
|
296
|
-
|
297
|
-
def _relative_pathname(path)
|
298
|
-
full_path = Pathname(path)
|
299
|
-
full_path.relative_path_from(Pathname.pwd)
|
300
|
-
rescue ArgumentError
|
301
|
-
full_path
|
302
|
-
end
|
303
|
-
|
304
|
-
def _relative_pathnames(paths)
|
305
|
-
paths.map { |path| _relative_pathname(path) }
|
306
|
-
end
|
307
|
-
|
308
|
-
def _run_actions(actions)
|
309
|
-
actions.each do |action_args|
|
310
|
-
args = action_args.dup
|
311
|
-
namespaced_action = args.shift
|
312
|
-
action = namespaced_action.to_s.sub(/^guard_/, "")
|
313
|
-
if ::Guard.respond_to?(action)
|
314
|
-
::Guard.send(action, *args)
|
315
|
-
else
|
316
|
-
fail "Unknown action: #{action.inspect}"
|
317
|
-
end
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
def _setup_watchdirs
|
322
|
-
dirs = Array(options[:watchdir])
|
323
|
-
dirs.empty? ? [Dir.pwd] : dirs.map { |dir| File.expand_path dir }
|
324
|
-
end
|
325
|
-
|
326
|
-
def _listener_callback
|
327
|
-
lambda do |modified, added, removed|
|
328
|
-
relative_paths = {
|
329
|
-
modified: _relative_pathnames(modified),
|
330
|
-
added: _relative_pathnames(added),
|
331
|
-
removed: _relative_pathnames(removed)
|
332
|
-
}
|
333
|
-
|
334
|
-
async_queue_add(relative_paths) if _relevant_changes?(relative_paths)
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
def _reset_all
|
339
|
-
reset_groups
|
340
|
-
reset_plugins
|
341
|
-
reset_scope
|
342
|
-
end
|
343
|
-
|
344
|
-
def _setup_interactor
|
345
|
-
::Guard::Interactor.new(options[:no_interactions])
|
346
|
-
end
|
347
|
-
|
348
|
-
def _load_guardfile
|
349
|
-
_reset_all
|
350
|
-
evaluate_guardfile
|
351
|
-
setup_scope
|
352
|
-
_setup_notifier
|
353
|
-
end
|
354
|
-
|
355
|
-
def _prepare_scope(scope)
|
356
|
-
fail "Guard::setup() not called!" if options.nil?
|
357
|
-
plugins = Array(options[:plugin])
|
358
|
-
plugins = Array(scope[:plugins] || scope[:plugin]) if plugins.empty?
|
359
|
-
|
360
|
-
# Convert objects to names
|
361
|
-
plugins.map! { |p| p.respond_to?(:name) ? p.name : p }
|
362
|
-
|
363
|
-
groups = Array(options[:group])
|
364
|
-
groups = Array(scope[:groups] || scope[:group]) if groups.empty?
|
365
|
-
|
366
|
-
# Convert objects to names
|
367
|
-
groups.map! { |g| g.respond_to?(:name) ? g.name : g }
|
368
|
-
|
369
|
-
{ plugins: plugins, groups: groups }
|
370
|
-
end
|
371
|
-
|
372
|
-
def _pluginless_guardfile?
|
373
|
-
# no Reevaluator means there was no Guardfile configured that could be
|
374
|
-
# reevaluated, so we don't have a pluginless guardfile, because we don't
|
375
|
-
# have a Guardfile to begin with...
|
376
|
-
#
|
377
|
-
# But, if we have a Guardfile, we'll at least have the built-in
|
378
|
-
# Reevaluator, so the following will work:
|
379
|
-
|
380
|
-
plugins.map(&:name) != ["reevaluator"]
|
381
|
-
end
|
382
|
-
|
383
|
-
def _reset_for_tests
|
384
|
-
@options = nil
|
385
|
-
@queue = nil
|
386
|
-
@runner = nil
|
387
|
-
@evaluator = nil
|
388
|
-
@watchdirs = nil
|
389
|
-
@watchdirs = nil
|
390
|
-
@listener = nil
|
391
|
-
@interactor = nil
|
392
|
-
@scope = nil
|
393
|
-
end
|
394
|
-
end
|
395
|
-
end
|
data/lib/guard/ui.rb.orig
DELETED
@@ -1,278 +0,0 @@
|
|
1
|
-
require "lumberjack"
|
2
|
-
|
3
|
-
require "guard/options"
|
4
|
-
require "guard/ui/colors"
|
5
|
-
|
6
|
-
require "guard/terminal"
|
7
|
-
|
8
|
-
module Guard
|
9
|
-
# The UI class helps to format messages for the user. Everything that is
|
10
|
-
# logged through this class is considered either as an error message or a
|
11
|
-
# diagnostic message and is written to standard error ($stderr).
|
12
|
-
#
|
13
|
-
# If your Guard plugin does some output that is piped into another process
|
14
|
-
# for further processing, please just write it to STDOUT with `puts`.
|
15
|
-
#
|
16
|
-
module UI
|
17
|
-
include Colors
|
18
|
-
|
19
|
-
class << self
|
20
|
-
# Get the Guard::UI logger instance
|
21
|
-
#
|
22
|
-
def logger
|
23
|
-
@logger ||= begin
|
24
|
-
Lumberjack::Logger.new(
|
25
|
-
options.fetch(:device) { $stderr },
|
26
|
-
options)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# Since logger is global, for Aruba in-process to properly
|
31
|
-
# separate output between calls, we need to reset
|
32
|
-
#
|
33
|
-
# We don't use logger=() since it's expected to be a Lumberjack instance
|
34
|
-
def reset_logger
|
35
|
-
@logger = nil
|
36
|
-
end
|
37
|
-
|
38
|
-
# Get the logger options
|
39
|
-
#
|
40
|
-
# @return [Hash] the logger options
|
41
|
-
#
|
42
|
-
def options
|
43
|
-
@options ||= ::Guard::Options.new(
|
44
|
-
level: :info,
|
45
|
-
template: ":time - :severity - :message",
|
46
|
-
time_format: "%H:%M:%S")
|
47
|
-
end
|
48
|
-
|
49
|
-
# Set the logger options
|
50
|
-
#
|
51
|
-
# @param [Hash] options the logger options
|
52
|
-
# @option options [Symbol] level the log level
|
53
|
-
# @option options [String] template the logger template
|
54
|
-
# @option options [String] time_format the time format
|
55
|
-
#
|
56
|
-
def options=(options)
|
57
|
-
@options = ::Guard::Options.new(options)
|
58
|
-
end
|
59
|
-
|
60
|
-
# Show an info message.
|
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 info(message, options = {})
|
67
|
-
_filtered_logger_message(message, :info, nil, options)
|
68
|
-
end
|
69
|
-
|
70
|
-
# Show a yellow warning message that is prefixed with WARNING.
|
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 warning(message, options = {})
|
77
|
-
_filtered_logger_message(message, :warn, :yellow, options)
|
78
|
-
end
|
79
|
-
|
80
|
-
# Show a red error message that is prefixed with ERROR.
|
81
|
-
#
|
82
|
-
# @param [String] message the message to show
|
83
|
-
# @option options [Boolean] reset whether to clean the output before
|
84
|
-
# @option options [String] plugin manually define the calling plugin
|
85
|
-
#
|
86
|
-
def error(message, options = {})
|
87
|
-
_filtered_logger_message(message, :error, :red, options)
|
88
|
-
end
|
89
|
-
|
90
|
-
# Show a red deprecation message that is prefixed with DEPRECATION.
|
91
|
-
# It has a log level of `warn`.
|
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 deprecation(message, options = {})
|
98
|
-
msg = "neither ::Guard.setup nor ::Guard.reset_options was called"
|
99
|
-
fail msg if ::Guard.options.nil?
|
100
|
-
warning(message, options) if ::Guard.options[:show_deprecations]
|
101
|
-
end
|
102
|
-
|
103
|
-
# Show a debug message that is prefixed with DEBUG and a timestamp.
|
104
|
-
#
|
105
|
-
# @param [String] message the message to show
|
106
|
-
# @option options [Boolean] reset whether to clean the output before
|
107
|
-
# @option options [String] plugin manually define the calling plugin
|
108
|
-
#
|
109
|
-
def debug(message, options = {})
|
110
|
-
_filtered_logger_message(message, :debug, :yellow, options)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Reset a line.
|
114
|
-
#
|
115
|
-
def reset_line
|
116
|
-
$stderr.print(color_enabled? ? "\r\e[0m" : "\r\n")
|
117
|
-
end
|
118
|
-
|
119
|
-
# Clear the output if clearable.
|
120
|
-
#
|
121
|
-
def clear(opts = {})
|
122
|
-
return unless ::Guard.options[:clear]
|
123
|
-
|
124
|
-
fail "UI not set up!" if @clearable.nil?
|
125
|
-
return unless @clearable || opts[:force]
|
126
|
-
|
127
|
-
@clearable = false
|
128
|
-
::Guard::Terminal.clear
|
129
|
-
rescue Errno::ENOENT => e
|
130
|
-
warning("Failed to clear the screen: #{e.inspect}")
|
131
|
-
end
|
132
|
-
|
133
|
-
# TODO: arguments: UI uses Guard::options anyway
|
134
|
-
def setup(_options)
|
135
|
-
@clearable = false
|
136
|
-
clear(force: true)
|
137
|
-
end
|
138
|
-
|
139
|
-
# Allow the screen to be cleared again.
|
140
|
-
#
|
141
|
-
def clearable
|
142
|
-
@clearable = true
|
143
|
-
end
|
144
|
-
|
145
|
-
# Show a scoped action message.
|
146
|
-
#
|
147
|
-
# @param [String] action the action to show
|
148
|
-
# @param [Hash] scope hash with a guard or a group scope
|
149
|
-
#
|
150
|
-
def action_with_scopes(action, scope)
|
151
|
-
first_non_blank_scope = _first_non_blank_scope(scope)
|
152
|
-
unless first_non_blank_scope.nil?
|
153
|
-
scope_message = first_non_blank_scope.map(&:title).join(", ")
|
154
|
-
end
|
155
|
-
|
156
|
-
info "#{ action } #{ scope_message || "all" }"
|
157
|
-
end
|
158
|
-
|
159
|
-
private
|
160
|
-
|
161
|
-
# Returns the first non-blank scope by searching in the given `scope`
|
162
|
-
# hash and in Guard.scope. Returns nil if no non-blank scope is found.
|
163
|
-
#
|
164
|
-
def _first_non_blank_scope(scope)
|
165
|
-
[:plugins, :groups].each do |scope_name|
|
166
|
-
s = scope[scope_name] || ::Guard.scope[scope_name]
|
167
|
-
return s if !s.nil? && !s.empty?
|
168
|
-
end
|
169
|
-
|
170
|
-
nil
|
171
|
-
end
|
172
|
-
|
173
|
-
# Filters log messages depending on either the
|
174
|
-
# `:only`` or `:except` option.
|
175
|
-
#
|
176
|
-
# @param [String] plugin the calling plugin name
|
177
|
-
# @yield When the message should be logged
|
178
|
-
# @yieldparam [String] param the calling plugin name
|
179
|
-
#
|
180
|
-
def _filter(plugin)
|
181
|
-
only = options[:only]
|
182
|
-
except = options[:except]
|
183
|
-
plugin ||= calling_plugin_name
|
184
|
-
|
185
|
-
match = !(only || except)
|
186
|
-
match ||= (only && only.match(plugin))
|
187
|
-
match ||= (except && !except.match(plugin))
|
188
|
-
return unless match
|
189
|
-
yield plugin
|
190
|
-
end
|
191
|
-
|
192
|
-
# Display a message of the type `method` and with the color `color_name`
|
193
|
-
# (no color by default) conditionnaly given a `plugin_name`.
|
194
|
-
#
|
195
|
-
# @param [String] plugin_name the calling plugin name
|
196
|
-
# @option options [Boolean] reset whether to clean the output before
|
197
|
-
# @option options [String] plugin manually define the calling plugin
|
198
|
-
#
|
199
|
-
def _filtered_logger_message(message, method, color_name, options = {})
|
200
|
-
message = color(message, color_name) if color_name
|
201
|
-
|
202
|
-
_filter(options[:plugin]) do |plugin|
|
203
|
-
reset_line if options[:reset]
|
204
|
-
logger.send(method, message, plugin)
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
# Tries to extract the calling Guard plugin name
|
209
|
-
# from the call stack.
|
210
|
-
#
|
211
|
-
# @param [Integer] depth the stack depth
|
212
|
-
# @return [String] the Guard plugin name
|
213
|
-
#
|
214
|
-
def calling_plugin_name(depth = 2)
|
215
|
-
name = /(guard\/[a-z_]*)(\/[a-z_]*)?.rb:/i.match(caller[depth])
|
216
|
-
return "Guard" unless name
|
217
|
-
name[1].split("/").map do |part|
|
218
|
-
part.split(/[^a-z0-9]/i).map(&:capitalize).join
|
219
|
-
end.join("::")
|
220
|
-
end
|
221
|
-
|
222
|
-
# Checks if color output can be enabled.
|
223
|
-
#
|
224
|
-
# @return [Boolean] whether color is enabled or not
|
225
|
-
#
|
226
|
-
def color_enabled?
|
227
|
-
@color_enabled_initialized ||= false
|
228
|
-
@color_enabled = nil unless @color_enabled_initialized
|
229
|
-
@color_enabled_initialized = true
|
230
|
-
if @color_enabled.nil?
|
231
|
-
if Gem.win_platform?
|
232
|
-
if ENV["ANSICON"]
|
233
|
-
@color_enabled = true
|
234
|
-
else
|
235
|
-
begin
|
236
|
-
require "rubygems" unless ENV["NO_RUBYGEMS"]
|
237
|
-
require "Win32/Console/ANSI"
|
238
|
-
@color_enabled = true
|
239
|
-
rescue LoadError
|
240
|
-
@color_enabled = false
|
241
|
-
info "Run 'gem install win32console' to use color on Windows"
|
242
|
-
end
|
243
|
-
end
|
244
|
-
else
|
245
|
-
@color_enabled = true
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
@color_enabled
|
250
|
-
end
|
251
|
-
|
252
|
-
# Colorizes a text message. See the constant in the UI class for possible
|
253
|
-
# color_options parameters. You can pass optionally :bright, a foreground
|
254
|
-
# color and a background color.
|
255
|
-
#
|
256
|
-
# @example
|
257
|
-
#
|
258
|
-
# color('Hello World', :red, :bright)
|
259
|
-
#
|
260
|
-
# @param [String] text the text to colorize
|
261
|
-
# @param [Array] color_options the color options
|
262
|
-
#
|
263
|
-
def color(text, *color_options)
|
264
|
-
color_code = ""
|
265
|
-
color_options.each do |color_option|
|
266
|
-
color_option = color_option.to_s
|
267
|
-
next if color_option == ""
|
268
|
-
|
269
|
-
unless color_option =~ /\d+/
|
270
|
-
color_option = const_get("ANSI_ESCAPE_#{ color_option.upcase }")
|
271
|
-
end
|
272
|
-
color_code += ";" + color_option
|
273
|
-
end
|
274
|
-
color_enabled? ? "\e[0#{ color_code }m#{ text }\e[0m" : text
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|
278
|
-
end
|