guard 2.8.2 → 2.9.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 +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
|