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.rb.orig
CHANGED
@@ -1,213 +1,271 @@
|
|
1
|
-
require "
|
1
|
+
require "thread"
|
2
|
+
require "listen"
|
3
|
+
|
4
|
+
require "guard/config"
|
5
|
+
require "guard/deprecated/guard" unless Guard::Config.new.strict?
|
6
|
+
|
7
|
+
require "guard/internals/debugging"
|
8
|
+
require "guard/internals/traps"
|
9
|
+
require "guard/internals/helpers"
|
10
|
+
|
11
|
+
require "guard/metadata"
|
12
|
+
require "guard/options"
|
2
13
|
|
3
14
|
require "guard/commander"
|
4
|
-
require "guard/deprecated_methods"
|
5
|
-
require "guard/deprecator"
|
6
15
|
require "guard/dsl"
|
7
|
-
require "guard/group"
|
8
|
-
require "guard/guardfile"
|
9
16
|
require "guard/interactor"
|
10
17
|
require "guard/notifier"
|
11
18
|
require "guard/plugin_util"
|
12
19
|
require "guard/runner"
|
13
|
-
require "guard/setuper"
|
14
20
|
require "guard/sheller"
|
15
21
|
require "guard/ui"
|
16
22
|
require "guard/watcher"
|
17
|
-
require "guard/reevaluator"
|
18
23
|
|
19
24
|
# Guard is the main module for all Guard related modules and classes.
|
20
25
|
# Also Guard plugins should use this namespace.
|
21
26
|
#
|
22
27
|
module Guard
|
23
|
-
|
24
|
-
|
25
|
-
extend Commander
|
26
|
-
extend DeprecatedMethods
|
27
|
-
extend Setuper
|
28
|
+
Deprecated::Guard.add_deprecated(self) unless Config.new.strict?
|
28
29
|
|
29
30
|
class << self
|
30
|
-
|
31
|
+
attr_reader :listener
|
31
32
|
|
32
|
-
|
33
|
-
@scope = new_scope
|
34
|
-
@scope.dup.freeze
|
35
|
-
end
|
33
|
+
include Internals::Helpers
|
36
34
|
|
37
|
-
|
38
|
-
<<<<<<< HEAD
|
39
|
-
fail "::Guard.setup() not called" if @scope.nil?
|
40
|
-
=======
|
41
|
-
>>>>>>> origin/api_safe_refactoring
|
42
|
-
@scope.dup.freeze
|
43
|
-
end
|
44
|
-
attr_reader :runner, :listener
|
45
|
-
|
46
|
-
# Smart accessor for retrieving specific plugins at once.
|
47
|
-
#
|
48
|
-
# @see Guard.plugin
|
49
|
-
# @see Guard.group
|
50
|
-
# @see Guard.groups
|
51
|
-
#
|
52
|
-
# @example Filter plugins by String or Symbol
|
53
|
-
# Guard.plugins('rspec')
|
54
|
-
# Guard.plugins(:rspec)
|
35
|
+
# Initializes the Guard singleton:
|
55
36
|
#
|
56
|
-
#
|
57
|
-
#
|
37
|
+
# * Initialize the internal Guard state;
|
38
|
+
# * Create the interactor
|
39
|
+
# * Select and initialize the file change listener.
|
58
40
|
#
|
59
|
-
# @
|
60
|
-
#
|
41
|
+
# @option options [Boolean] clear if auto clear the UI should be done
|
42
|
+
# @option options [Boolean] notify if system notifications should be shown
|
43
|
+
# @option options [Boolean] debug if debug output should be shown
|
44
|
+
# @option options [Array<String>] group the list of groups to start
|
45
|
+
# @option options [Array<String>] watchdir the directories to watch
|
46
|
+
# @option options [String] guardfile the path to the Guardfile
|
61
47
|
#
|
62
|
-
# @
|
63
|
-
# plugins
|
64
|
-
# @return [Plugin, Array<Plugin>] the filtered plugin(s)
|
48
|
+
# @return [Guard] the Guard singleton
|
65
49
|
#
|
66
|
-
def plugins(filter = nil)
|
67
|
-
@plugins ||= []
|
68
50
|
|
69
|
-
|
51
|
+
# TODO: this method has too many instance variables
|
52
|
+
# and some are mock and leak between tests,
|
53
|
+
# so ideally there should be a guard "instance"
|
54
|
+
# object that can be created anew between tests
|
55
|
+
def setup(opts = {})
|
56
|
+
# NOTE: must be set before anything calls Guard.options
|
57
|
+
reset_options(opts)
|
70
58
|
|
71
|
-
|
72
|
-
|
73
|
-
@plugins.select do |plugin|
|
74
|
-
plugin.name == filter.to_s.downcase.gsub("-", "")
|
75
|
-
end
|
76
|
-
when Regexp
|
77
|
-
@plugins.select do |plugin|
|
78
|
-
plugin.name =~ filter
|
79
|
-
end
|
80
|
-
when Hash
|
81
|
-
@plugins.select do |plugin|
|
82
|
-
filter.all? do |k, v|
|
83
|
-
case k
|
84
|
-
when :name
|
85
|
-
plugin.name == v.to_s.downcase.gsub("-", "")
|
86
|
-
when :group
|
87
|
-
plugin.group.name == v.to_sym
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
59
|
+
# NOTE: must be set before anything calls Guard::UI.debug
|
60
|
+
::Guard::Internals::Debugging.start if options[:debug]
|
92
61
|
|
93
|
-
|
62
|
+
@queue = Queue.new
|
63
|
+
self.watchdirs = Array(options[:watchdir])
|
64
|
+
|
65
|
+
::Guard::UI.reset_and_clear
|
66
|
+
|
67
|
+
@listener = _setup_listener
|
68
|
+
|
69
|
+
_reset_all
|
70
|
+
evaluate_guardfile
|
71
|
+
setup_scope
|
72
|
+
|
73
|
+
::Guard::Notifier.connect(notify: options[:notify])
|
74
|
+
|
75
|
+
traps = Internals::Traps
|
76
|
+
traps.handle("USR1") { async_queue_add([:guard_pause, :paused]) }
|
77
|
+
traps.handle("USR2") { async_queue_add([:guard_pause, :unpaused]) }
|
78
|
+
|
79
|
+
@interactor = ::Guard::Interactor.new(options[:no_interactions])
|
80
|
+
traps.handle("INT") { @interactor.handle_interrupt }
|
81
|
+
|
82
|
+
self
|
94
83
|
end
|
95
84
|
|
96
|
-
|
85
|
+
attr_reader :interactor
|
86
|
+
|
87
|
+
# Used only by tests (for all I know...)
|
88
|
+
def clear_options
|
89
|
+
@options = nil
|
90
|
+
end
|
91
|
+
|
92
|
+
# Initializes the groups array with the default group(s).
|
97
93
|
#
|
98
|
-
# @see
|
99
|
-
# @see Guard.group
|
100
|
-
# @see Guard.groups
|
94
|
+
# @see DEFAULT_GROUPS
|
101
95
|
#
|
102
|
-
|
103
|
-
|
104
|
-
|
96
|
+
def reset_groups
|
97
|
+
@groups = DEFAULT_GROUPS.map { |name| Group.new(name) }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Initializes the plugins array to an empty array.
|
105
101
|
#
|
106
|
-
# @
|
107
|
-
# Guard.plugin(/rsp.+/)
|
102
|
+
# @see Guard.plugins
|
108
103
|
#
|
109
|
-
|
110
|
-
|
104
|
+
def reset_plugins
|
105
|
+
@plugins = []
|
106
|
+
end
|
107
|
+
|
108
|
+
attr_reader :watchdirs
|
109
|
+
|
110
|
+
# Stores the scopes defined by the user via the `--group` / `-g` option (to
|
111
|
+
# run only a specific group) or the `--plugin` / `-P` option (to run only a
|
112
|
+
# specific plugin).
|
111
113
|
#
|
112
|
-
# @
|
113
|
-
#
|
114
|
-
# @return [Plugin, nil] the plugin found, nil otherwise
|
114
|
+
# @see CLI#start
|
115
|
+
# @see Dsl#scope
|
115
116
|
#
|
116
|
-
def
|
117
|
-
|
117
|
+
def setup_scope(scope = {})
|
118
|
+
# TODO: there should be a special Scope class instead
|
119
|
+
scope = _prepare_scope(scope)
|
120
|
+
|
121
|
+
::Guard.scope = {
|
122
|
+
groups: scope[:groups].map { |item| ::Guard.add_group(item) },
|
123
|
+
plugins: scope[:plugins].map { |item| ::Guard.plugin(item) },
|
124
|
+
}
|
118
125
|
end
|
119
126
|
|
120
|
-
#
|
127
|
+
# Evaluates the Guardfile content. It displays an error message if no
|
128
|
+
# Guard plugins are instantiated after the Guardfile evaluation.
|
121
129
|
#
|
122
|
-
# @see Guard
|
123
|
-
#
|
124
|
-
|
130
|
+
# @see Guard::Guardfile::Evaluator#evaluate_guardfile
|
131
|
+
#
|
132
|
+
def evaluate_guardfile
|
133
|
+
evaluator = Guard::Guardfile::Evaluator.new(options)
|
134
|
+
evaluator.evaluate_guardfile
|
135
|
+
msg = "No plugins found in Guardfile, please add at least one."
|
136
|
+
::Guard::UI.error msg if _pluginless_guardfile?
|
137
|
+
end
|
138
|
+
|
139
|
+
# Asynchronously trigger changes
|
125
140
|
#
|
126
|
-
#
|
127
|
-
# Guard.groups('backend')
|
128
|
-
# Guard.groups(:backend)
|
141
|
+
# Currently supported args:
|
129
142
|
#
|
130
|
-
#
|
131
|
-
# Guard.groups(/(back|front)end/)
|
143
|
+
# old style hash: {modified: ['foo'], added: ['bar'], removed: []}
|
132
144
|
#
|
133
|
-
#
|
134
|
-
# @return [Array<Group>] the filtered group(s)
|
145
|
+
# new style signals with args: [:guard_pause, :unpaused ]
|
135
146
|
#
|
136
|
-
def
|
137
|
-
@
|
147
|
+
def async_queue_add(changes)
|
148
|
+
@queue << changes
|
138
149
|
|
139
|
-
|
150
|
+
# Putting interactor in background puts guard into foreground
|
151
|
+
# so it can handle change notifications
|
152
|
+
Thread.new { interactor.background }
|
153
|
+
end
|
140
154
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
155
|
+
def pending_changes?
|
156
|
+
! @queue.empty?
|
157
|
+
end
|
158
|
+
|
159
|
+
def watchdirs=(dirs)
|
160
|
+
dirs = [Dir.pwd] if dirs.empty?
|
161
|
+
@watchdirs = dirs.map { |dir| File.expand_path dir }
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
# Initializes the listener and registers a callback for changes.
|
167
|
+
#
|
168
|
+
def _setup_listener
|
169
|
+
if options[:listen_on]
|
170
|
+
Listen.on(options[:listen_on], &_listener_callback)
|
146
171
|
else
|
147
|
-
|
172
|
+
listener_options = {}
|
173
|
+
[:latency, :force_polling, :wait_for_delay].each do |option|
|
174
|
+
listener_options[option] = options[option] if options[option]
|
175
|
+
end
|
176
|
+
listen_args = watchdirs + [listener_options]
|
177
|
+
Listen.to(*listen_args, &_listener_callback)
|
148
178
|
end
|
149
179
|
end
|
150
180
|
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
#
|
167
|
-
def group(filter)
|
168
|
-
groups(filter).first
|
181
|
+
# Process the change queue, running tasks within the main Guard thread
|
182
|
+
def _process_queue
|
183
|
+
actions, changes = [], { modified: [], added: [], removed: [] }
|
184
|
+
|
185
|
+
while pending_changes?
|
186
|
+
if (item = @queue.pop).first.is_a?(Symbol)
|
187
|
+
actions << item
|
188
|
+
else
|
189
|
+
item.each { |key, value| changes[key] += value }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
_run_actions(actions)
|
194
|
+
return if changes.values.all?(&:empty?)
|
195
|
+
Runner.new.run_on_changes(*changes.values)
|
169
196
|
end
|
170
197
|
|
171
|
-
#
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
# @option options [Array<Watcher>] watchers the list of declared watchers
|
177
|
-
# @option options [Array<Hash>] callbacks the list of callbacks
|
178
|
-
# @return [Plugin] the added Guard plugin
|
179
|
-
# @see Plugin
|
180
|
-
#
|
181
|
-
def add_plugin(name, options = {})
|
182
|
-
instance = ::Guard::PluginUtil.new(name).initialize_plugin(options)
|
183
|
-
@plugins << instance
|
184
|
-
instance
|
198
|
+
# TODO: Guard::Watch or Guard::Scope should provide this
|
199
|
+
def _scoped_watchers
|
200
|
+
watchers = []
|
201
|
+
Runner.new.send(:_scoped_plugins) { |guard| watchers += guard.watchers }
|
202
|
+
watchers
|
185
203
|
end
|
186
204
|
|
187
|
-
#
|
188
|
-
def
|
189
|
-
|
190
|
-
|
205
|
+
# Check if any of the changes are actually watched for
|
206
|
+
def _relevant_changes?(changes)
|
207
|
+
files = changes.values.flatten(1)
|
208
|
+
watchers = _scoped_watchers
|
209
|
+
watchers.any? { |watcher| files.any? { |file| watcher.match(file) } }
|
191
210
|
end
|
192
211
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
212
|
+
def _relative_pathnames(paths)
|
213
|
+
paths.map { |path| _relative_pathname(path) }
|
214
|
+
end
|
215
|
+
|
216
|
+
def _run_actions(actions)
|
217
|
+
actions.each do |action_args|
|
218
|
+
args = action_args.dup
|
219
|
+
namespaced_action = args.shift
|
220
|
+
action = namespaced_action.to_s.sub(/^guard_/, "")
|
221
|
+
if ::Guard.respond_to?(action)
|
222
|
+
::Guard.send(action, *args)
|
223
|
+
else
|
224
|
+
fail "Unknown action: #{action.inspect}"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def _listener_callback
|
230
|
+
lambda do |modified, added, removed|
|
231
|
+
relative_paths = {
|
232
|
+
modified: _relative_pathnames(modified),
|
233
|
+
added: _relative_pathnames(added),
|
234
|
+
removed: _relative_pathnames(removed)
|
235
|
+
}
|
236
|
+
|
237
|
+
async_queue_add(relative_paths) if _relevant_changes?(relative_paths)
|
208
238
|
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def _reset_all
|
242
|
+
reset_groups
|
243
|
+
reset_plugins
|
244
|
+
reset_scope
|
245
|
+
end
|
246
|
+
|
247
|
+
def _pluginless_guardfile?
|
248
|
+
# no Reevaluator means there was no Guardfile configured that could be
|
249
|
+
# reevaluated, so we don't have a pluginless guardfile, because we don't
|
250
|
+
# have a Guardfile to begin with...
|
251
|
+
#
|
252
|
+
# But, if we have a Guardfile, we'll at least have the built-in
|
253
|
+
# Reevaluator, so the following will work:
|
254
|
+
|
255
|
+
# TODO: this is a workaround for tests
|
256
|
+
return true if plugins.empty?
|
257
|
+
|
258
|
+
plugins.map(&:name) == ["reevaluator"]
|
259
|
+
end
|
209
260
|
|
210
|
-
|
261
|
+
def _reset_for_tests
|
262
|
+
@options = nil
|
263
|
+
@queue = nil
|
264
|
+
@watchdirs = nil
|
265
|
+
@watchdirs = nil
|
266
|
+
@listener = nil
|
267
|
+
@interactor = nil
|
268
|
+
@scope = nil
|
211
269
|
end
|
212
270
|
end
|
213
271
|
end
|
data/lib/guard/aruba_adapter.rb
CHANGED
@@ -38,8 +38,8 @@ module Guard
|
|
38
38
|
# The ruby interpreter would pipe this to STDERR and exit 1 in the case
|
39
39
|
# of an unhandled exception
|
40
40
|
b = e.backtrace
|
41
|
-
|
42
|
-
@stderr.puts
|
41
|
+
@stderr.puts "#{b.shift}: #{e.message} (#{e.class})"
|
42
|
+
@stderr.puts b.map { |s| "\tfrom #{s}" }.join("\n")
|
43
43
|
1
|
44
44
|
rescue SystemExit => e
|
45
45
|
e.status
|
data/lib/guard/cli.rb
CHANGED
@@ -3,7 +3,8 @@ require "thor"
|
|
3
3
|
require "guard"
|
4
4
|
require "guard/version"
|
5
5
|
require "guard/dsl_describer"
|
6
|
-
require "guard/guardfile"
|
6
|
+
require "guard/guardfile/evaluator"
|
7
|
+
require "guard/guardfile/generator"
|
7
8
|
|
8
9
|
module Guard
|
9
10
|
# Facade for the Guard command line interface managed by
|
@@ -70,12 +71,6 @@ module Guard
|
|
70
71
|
aliases: "-B",
|
71
72
|
banner: "Turn off warning when Bundler is not present"
|
72
73
|
|
73
|
-
# DEPRECATED
|
74
|
-
method_option :show_deprecations,
|
75
|
-
type: :boolean,
|
76
|
-
default: false,
|
77
|
-
banner: "DEPRECATED: it does nothing"
|
78
|
-
|
79
74
|
# Listen options
|
80
75
|
method_option :latency,
|
81
76
|
type: :numeric,
|
@@ -131,6 +126,7 @@ module Guard
|
|
131
126
|
# @see Guard::DslDescriber.notifiers
|
132
127
|
#
|
133
128
|
def notifiers
|
129
|
+
::Guard.reset_options(options)
|
134
130
|
::Guard::DslDescriber.new(options).notifiers
|
135
131
|
end
|
136
132
|
|
@@ -170,24 +166,23 @@ module Guard
|
|
170
166
|
_verify_bundler_presence unless options[:no_bundler_warning]
|
171
167
|
|
172
168
|
::Guard.reset_options(options) # Since UI.deprecated uses config
|
173
|
-
::Guard.reset_evaluator(options) # for initialize_all_templates
|
174
169
|
|
175
|
-
|
176
|
-
|
170
|
+
generator = Guardfile::Generator.new(abort_on_existence: options[:bare])
|
171
|
+
generator.create_guardfile
|
177
172
|
|
178
173
|
# Note: this reset "hack" will be fixed after refactoring
|
179
174
|
::Guard.reset_plugins
|
180
175
|
|
181
176
|
# Evaluate because it might have existed and creating was skipped
|
182
|
-
::Guard.
|
177
|
+
::Guard::Guardfile::Evaluator.new(Guard.options).evaluate_guardfile
|
183
178
|
|
184
179
|
return if options[:bare]
|
185
180
|
|
186
181
|
if plugin_names.empty?
|
187
|
-
|
182
|
+
generator.initialize_all_templates
|
188
183
|
else
|
189
184
|
plugin_names.each do |plugin_name|
|
190
|
-
|
185
|
+
generator.initialize_template(plugin_name)
|
191
186
|
end
|
192
187
|
end
|
193
188
|
end
|