guard 2.9.0 → 2.9.1
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/lib/guard.rb +43 -160
- data/lib/guard/cli.rb +49 -17
- data/lib/guard/commander.rb +18 -17
- data/lib/guard/commands/all.rb +1 -2
- data/lib/guard/commands/change.rb +1 -2
- data/lib/guard/commands/reload.rb +1 -2
- data/lib/guard/commands/scope.rb +1 -2
- data/lib/guard/deprecated/evaluator.rb +34 -0
- data/lib/guard/deprecated/guard.rb +15 -3
- data/lib/guard/deprecated/watcher.rb +27 -0
- data/lib/guard/dsl.rb +58 -17
- data/lib/guard/dsl_describer.rb +55 -73
- data/lib/guard/guardfile.rb +1 -1
- data/lib/guard/guardfile/evaluator.rb +96 -158
- data/lib/guard/guardfile/generator.rb +45 -41
- data/lib/guard/interactor.rb +4 -2
- data/lib/guard/internals/groups.rb +40 -0
- data/lib/guard/internals/plugins.rb +51 -0
- data/lib/guard/internals/queue.rb +50 -0
- data/lib/guard/internals/scope.rb +110 -0
- data/lib/guard/internals/session.rb +135 -0
- data/lib/guard/internals/state.rb +33 -0
- data/lib/guard/jobs/pry_wrapper.rb +33 -32
- data/lib/guard/jobs/sleep.rb +3 -4
- data/lib/guard/notifier.rb +2 -0
- data/lib/guard/notifier/detected.rb.orig +83 -0
- data/lib/guard/plugin.rb +242 -6
- data/lib/guard/plugin_util.rb +19 -18
- data/lib/guard/reevaluator.rb +50 -10
- data/lib/guard/runner.rb +30 -108
- data/lib/guard/ui.rb +6 -24
- data/lib/guard/version.rb +1 -1
- data/lib/guard/watcher.rb +4 -12
- metadata +11 -6
- data/lib/guard/metadata.rb +0 -190
- data/lib/guard/plugin/base.rb +0 -183
- data/lib/guard/plugin/hooker.rb +0 -108
- data/lib/guard/session.rb +0 -5
data/lib/guard/guardfile.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
require "guard/config"
|
2
|
+
|
2
3
|
require "guard/options"
|
3
4
|
require "guard/plugin"
|
4
5
|
|
5
|
-
# TODO: this class shouldn't use notify directly
|
6
|
-
require "guard/notifier"
|
7
|
-
|
8
6
|
require "guard/dsl"
|
9
|
-
|
7
|
+
|
8
|
+
require "guard/deprecated/evaluator" unless Guard::Config.new.strict?
|
10
9
|
|
11
10
|
module Guard
|
12
11
|
module Guardfile
|
@@ -15,9 +14,30 @@ module Guard
|
|
15
14
|
#
|
16
15
|
# @see Guard::Dsl
|
17
16
|
#
|
17
|
+
# TODO: rename this to a Locator or Loader or something
|
18
18
|
class Evaluator
|
19
|
+
Deprecated::Evaluator.add_deprecated(self) unless Config.new.strict?
|
20
|
+
|
21
|
+
ERROR_NO_GUARDFILE = "No Guardfile found,"\
|
22
|
+
" please create one with `guard init`."
|
23
|
+
|
19
24
|
attr_reader :options, :guardfile_path
|
20
25
|
|
26
|
+
ERROR_NO_PLUGINS = "No Guard plugins found in Guardfile,"\
|
27
|
+
" please add at least one."
|
28
|
+
|
29
|
+
class Error < RuntimeError
|
30
|
+
end
|
31
|
+
|
32
|
+
class NoGuardfileError < Error
|
33
|
+
end
|
34
|
+
|
35
|
+
class NoCustomGuardfile < Error
|
36
|
+
end
|
37
|
+
|
38
|
+
class NoPluginsError < Error
|
39
|
+
end
|
40
|
+
|
21
41
|
def guardfile_source
|
22
42
|
@source
|
23
43
|
end
|
@@ -25,53 +45,50 @@ module Guard
|
|
25
45
|
# Initializes a new Guard::Guardfile::Evaluator object.
|
26
46
|
#
|
27
47
|
# @option opts [String] guardfile the path to a valid Guardfile
|
28
|
-
# @option opts [String]
|
48
|
+
# @option opts [String] contents a string representing the
|
29
49
|
# content of a valid Guardfile
|
30
50
|
#
|
31
51
|
def initialize(opts = {})
|
32
|
-
@
|
33
|
-
@
|
34
|
-
@
|
52
|
+
@type = nil
|
53
|
+
@path = nil
|
54
|
+
@user_config = nil
|
35
55
|
|
36
|
-
|
37
|
-
[:guardfile, :guardfile_contents].include?(k.to_sym)
|
38
|
-
end
|
56
|
+
opts = _from_deprecated(opts)
|
39
57
|
|
40
|
-
|
58
|
+
if opts[:contents]
|
59
|
+
@type = :inline
|
60
|
+
@contents = opts[:contents]
|
61
|
+
else
|
62
|
+
if opts[:guardfile]
|
63
|
+
@type = :custom
|
64
|
+
@path = Pathname(opts[:guardfile]) # may be updated by _read
|
65
|
+
end
|
66
|
+
end
|
41
67
|
end
|
42
68
|
|
43
69
|
# Evaluates the DSL methods in the `Guardfile`.
|
44
70
|
#
|
45
71
|
# @example Programmatically evaluate a Guardfile
|
46
|
-
# Guard::Guardfile::Evaluator.new.
|
72
|
+
# Guard::Guardfile::Evaluator.new.evaluate
|
47
73
|
#
|
48
74
|
# @example Programmatically evaluate a Guardfile with a custom Guardfile
|
49
75
|
# path
|
50
76
|
#
|
51
77
|
# options = { guardfile: '/Users/guardfile/MyAwesomeGuardfile' }
|
52
|
-
# Guard::Guardfile::Evaluator.new(options).
|
78
|
+
# Guard::Guardfile::Evaluator.new(options).evaluate
|
53
79
|
#
|
54
80
|
# @example Programmatically evaluate a Guardfile with an inline Guardfile
|
55
81
|
#
|
56
|
-
# options = {
|
57
|
-
# Guard::Guardfile::Evaluator.new(options).
|
82
|
+
# options = { contents: 'guard :rspec' }
|
83
|
+
# Guard::Guardfile::Evaluator.new(options).evaluate
|
58
84
|
#
|
59
|
-
def
|
60
|
-
|
61
|
-
_instance_eval_guardfile(guardfile_contents)
|
62
|
-
Guard.add_builtin_plugins(guardfile_path)
|
63
|
-
end
|
85
|
+
def evaluate
|
86
|
+
inline? || _use_provided || _use_default!
|
64
87
|
|
65
|
-
|
66
|
-
|
67
|
-
#
|
68
|
-
def reevaluate_guardfile
|
69
|
-
# Don't re-evaluate inline Guardfile
|
70
|
-
return if @source == :inline
|
88
|
+
contents = _guardfile_contents
|
89
|
+
fail NoPluginsError, ERROR_NO_PLUGINS unless /guard/m =~ contents
|
71
90
|
|
72
|
-
|
73
|
-
evaluate_guardfile
|
74
|
-
_after_reevaluate_guardfile
|
91
|
+
Dsl.new.evaluate(contents, @path || "", 1)
|
75
92
|
end
|
76
93
|
|
77
94
|
# Tests if the current `Guardfile` contains a specific Guard plugin.
|
@@ -89,8 +106,13 @@ module Guard
|
|
89
106
|
# @return [Boolean] whether the Guard plugin has been declared
|
90
107
|
#
|
91
108
|
def guardfile_include?(plugin_name)
|
92
|
-
/^\s*guard\s*\(?\s*['":]#{
|
93
|
-
|
109
|
+
/^\s*guard\s*\(?\s*['":]#{plugin_name}['"]?/.match(@contents)
|
110
|
+
end
|
111
|
+
|
112
|
+
attr_reader :path
|
113
|
+
|
114
|
+
def custom?
|
115
|
+
@type == :custom
|
94
116
|
end
|
95
117
|
|
96
118
|
# Gets the content of the `Guardfile` concatenated with the global
|
@@ -107,41 +129,32 @@ module Guard
|
|
107
129
|
[_guardfile_contents_without_user_config, config].compact.join("\n")
|
108
130
|
end
|
109
131
|
|
132
|
+
def inline?
|
133
|
+
@type == :inline
|
134
|
+
end
|
135
|
+
|
110
136
|
private
|
111
137
|
|
112
|
-
# Gets the content of the `Guardfile`.
|
113
|
-
#
|
114
|
-
# @return [String] the Guardfile content
|
115
|
-
#
|
116
138
|
def _guardfile_contents_without_user_config
|
117
|
-
fail "BUG: no data - Guardfile wasn't evaluated" unless @evaluated
|
118
139
|
@guardfile_contents || ""
|
119
140
|
end
|
120
141
|
|
121
|
-
# Evaluates the content of the `Guardfile`.
|
122
|
-
#
|
123
|
-
# @param [String] contents the content to evaluate.
|
124
|
-
#
|
125
142
|
def _instance_eval_guardfile(contents)
|
126
|
-
|
143
|
+
Dsl.new.evaluate(contents, @guardfile_path || "", 1)
|
127
144
|
rescue => ex
|
128
145
|
::Guard::UI.error "Invalid Guardfile, original error is:\n#{ $! }"
|
129
146
|
raise ex
|
130
147
|
end
|
131
148
|
|
132
|
-
# Gets the content to evaluate and stores it into @guardfile_contents.
|
133
|
-
#
|
134
149
|
def _fetch_guardfile_contents
|
135
150
|
_use_inline || _use_provided || _use_default
|
136
151
|
@evaluated = true
|
137
152
|
|
138
153
|
return if _guardfile_contents_usable?
|
139
|
-
|
154
|
+
UI.error "No Guard plugins found in Guardfile,"\
|
140
155
|
" please add at least one."
|
141
156
|
end
|
142
157
|
|
143
|
-
# Use the provided inline Guardfile if provided.
|
144
|
-
#
|
145
158
|
def _use_inline
|
146
159
|
source_from_option = @source.nil? && options[:guardfile_contents]
|
147
160
|
inline = @source == :inline
|
@@ -155,130 +168,55 @@ module Guard
|
|
155
168
|
true
|
156
169
|
end
|
157
170
|
|
158
|
-
# Try to use the provided Guardfile. Exits Guard if the Guardfile cannot
|
159
|
-
# be found.
|
160
|
-
#
|
161
171
|
def _use_provided
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
@source = :custom
|
166
|
-
|
167
|
-
options[:guardfile] = File.expand_path(options[:guardfile])
|
168
|
-
if File.exist?(options[:guardfile])
|
169
|
-
_read_guardfile(options[:guardfile])
|
170
|
-
::Guard::UI.info "Using Guardfile at #{ options[:guardfile] }."
|
171
|
-
true
|
172
|
-
else
|
173
|
-
::Guard::UI.error "No Guardfile exists at #{ options[:guardfile] }."
|
174
|
-
exit 1
|
175
|
-
end
|
176
|
-
|
172
|
+
return unless custom?
|
173
|
+
@path, @contents = _read(@path)
|
177
174
|
true
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
175
|
+
rescue Errno::ENOENT
|
176
|
+
fail NoCustomGuardfile, "No Guardfile exists at #{ @path }."
|
177
|
+
end
|
178
|
+
|
179
|
+
def _use_default!
|
180
|
+
@path, @contents = _read("Guardfile")
|
181
|
+
@type = :default
|
182
|
+
rescue Errno::ENOENT
|
183
|
+
begin
|
184
|
+
@path, @contents = _read("~/.Guardfile")
|
185
|
+
@type = :default
|
186
|
+
rescue Errno::ENOENT
|
187
|
+
fail NoGuardfileError, ERROR_NO_GUARDFILE
|
191
188
|
end
|
192
189
|
end
|
193
190
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
#
|
205
|
-
# @param [String] guardfile_path the path to the Guardfile
|
206
|
-
#
|
207
|
-
def _read_guardfile(guardfile_path)
|
208
|
-
@guardfile_path = guardfile_path
|
209
|
-
@guardfile_contents = File.read(guardfile_path)
|
210
|
-
rescue => ex
|
211
|
-
::Guard::UI.error "Error reading file #{ guardfile_path }:"
|
212
|
-
::Guard::UI.error ex.inspect
|
213
|
-
::Guard::UI.error ex.backtrace
|
214
|
-
exit 1
|
215
|
-
end
|
216
|
-
|
217
|
-
# Stops Guard and clear internal state
|
218
|
-
# before the Guardfile will be re-evaluated.
|
219
|
-
#
|
220
|
-
def _before_reevaluate_guardfile
|
221
|
-
Guard::Runner.new.run(:stop)
|
222
|
-
::Guard.reset_groups
|
223
|
-
::Guard.reset_plugins
|
224
|
-
::Guard.reset_scope
|
225
|
-
::Guard::Notifier.disconnect
|
191
|
+
def _read(path)
|
192
|
+
full_path = Pathname(path).expand_path
|
193
|
+
[full_path, full_path.read]
|
194
|
+
rescue Errno::ENOENT
|
195
|
+
fail
|
196
|
+
rescue SystemCallError => e
|
197
|
+
::Guard::UI.error "Error reading file #{full_path}:"
|
198
|
+
::Guard::UI.error e.inspect
|
199
|
+
::Guard::UI.error e.backtrace
|
200
|
+
abort
|
226
201
|
end
|
227
202
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
if ::Guard.send(:_pluginless_guardfile?)
|
235
|
-
::Guard::Notifier.notify(
|
236
|
-
"No plugins found in Guardfile, please add at least one.",
|
237
|
-
title: "Guard re-evaluate",
|
238
|
-
image: :failed)
|
239
|
-
else
|
240
|
-
msg = "Guardfile has been re-evaluated."
|
241
|
-
::Guard::UI.info(msg)
|
242
|
-
::Guard::Notifier.notify(msg, title: "Guard re-evaluate")
|
243
|
-
|
244
|
-
::Guard.setup_scope
|
245
|
-
Guard::Runner.new.run(:start)
|
246
|
-
end
|
203
|
+
def _guardfile_contents
|
204
|
+
@user_config ||= Pathname("~/.guard.rb").expand_path.read
|
205
|
+
[@contents, @user_config].compact.join("\n")
|
206
|
+
rescue Errno::ENOENT
|
207
|
+
@contents || ""
|
247
208
|
end
|
248
209
|
|
249
|
-
# Tests if the current `Guardfile` content is usable.
|
250
|
-
#
|
251
|
-
# @return [Boolean] if the Guardfile is usable
|
252
|
-
#
|
253
210
|
def _guardfile_contents_usable?
|
254
211
|
guardfile_contents && guardfile_contents =~ /guard/m
|
255
212
|
end
|
256
213
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
File.expand_path(File.join(Dir.pwd, "Guardfile"))
|
264
|
-
end
|
265
|
-
|
266
|
-
# The path to the `.Guardfile` that is located at
|
267
|
-
# the users home directory.
|
268
|
-
#
|
269
|
-
# @return [String] the path to `~/.Guardfile`
|
270
|
-
#
|
271
|
-
def _home_guardfile_path
|
272
|
-
File.expand_path(File.join("~", ".Guardfile"))
|
273
|
-
end
|
274
|
-
|
275
|
-
# The path to the user configuration `.guard.rb`
|
276
|
-
# that is located at the users home directory.
|
277
|
-
#
|
278
|
-
# @return [String] the path to `~/.guard.rb`
|
279
|
-
#
|
280
|
-
def _user_config_path
|
281
|
-
File.expand_path(File.join("~", ".guard.rb"))
|
214
|
+
def _from_deprecated(opts)
|
215
|
+
res = opts.dup
|
216
|
+
if opts.key?(:guardfile_contents)
|
217
|
+
res[:contents] = opts[:guardfile_contents]
|
218
|
+
end
|
219
|
+
res
|
282
220
|
end
|
283
221
|
end
|
284
222
|
end
|
@@ -1,6 +1,15 @@
|
|
1
1
|
require "guard/ui"
|
2
2
|
require "guard/plugin_util"
|
3
3
|
|
4
|
+
# Add Pathname#binwrite to 1.9.3
|
5
|
+
unless Pathname.instance_methods.include?(:binwrite)
|
6
|
+
class Pathname
|
7
|
+
def binwrite(*args)
|
8
|
+
IO.binwrite(to_s, *args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
4
13
|
module Guard
|
5
14
|
module Guardfile
|
6
15
|
# This class is responsible for generating the Guardfile and adding Guard'
|
@@ -9,7 +18,11 @@ module Guard
|
|
9
18
|
# @see Guard::CLI
|
10
19
|
#
|
11
20
|
class Generator
|
12
|
-
|
21
|
+
require "guard"
|
22
|
+
require "guard/ui"
|
23
|
+
|
24
|
+
INFO_TEMPLATE_ADDED =
|
25
|
+
"%s template added to Guardfile, feel free to edit it"
|
13
26
|
|
14
27
|
# The Guardfile template for `guard init`
|
15
28
|
GUARDFILE_TEMPLATE = File.expand_path(
|
@@ -18,21 +31,11 @@ module Guard
|
|
18
31
|
|
19
32
|
# The location of user defined templates
|
20
33
|
begin
|
21
|
-
HOME_TEMPLATES =
|
34
|
+
HOME_TEMPLATES = Pathname("~/.guard/templates").expand_path
|
22
35
|
rescue ArgumentError
|
23
36
|
# home isn't defined. Set to the root of the drive. Trust that there
|
24
37
|
# won't be user defined templates there
|
25
|
-
HOME_TEMPLATES =
|
26
|
-
end
|
27
|
-
|
28
|
-
# Initialize a new `Guard::Guardfile::Generator` object.
|
29
|
-
#
|
30
|
-
# @param [Hash] options The options for creating a Guardfile
|
31
|
-
# @option options [Boolean] :abort_on_existence Whether to abort or not
|
32
|
-
# when a Guardfile already exists
|
33
|
-
#
|
34
|
-
def initialize(options = {})
|
35
|
-
@options = options
|
38
|
+
HOME_TEMPLATES = Pathname("/").expand_path
|
36
39
|
end
|
37
40
|
|
38
41
|
# Creates the initial Guardfile template when it does not
|
@@ -41,13 +44,14 @@ module Guard
|
|
41
44
|
# @see Guard::CLI#init
|
42
45
|
#
|
43
46
|
def create_guardfile
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
elsif options[:abort_on_existence]
|
48
|
-
::Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
|
47
|
+
path = Pathname("Guardfile").expand_path
|
48
|
+
if path.exist?
|
49
|
+
_ui(:error, "Guardfile already exists at #{path}")
|
49
50
|
abort
|
50
51
|
end
|
52
|
+
|
53
|
+
_ui(:info, "Writing new Guardfile to #{path}")
|
54
|
+
FileUtils.cp(GUARDFILE_TEMPLATE, path.to_s)
|
51
55
|
end
|
52
56
|
|
53
57
|
# Adds the Guardfile template of a Guard plugin to an existing Guardfile.
|
@@ -58,33 +62,27 @@ module Guard
|
|
58
62
|
# initialize
|
59
63
|
#
|
60
64
|
def initialize_template(plugin_name)
|
65
|
+
guardfile = Pathname("Guardfile")
|
66
|
+
|
61
67
|
plugin_util = ::Guard::PluginUtil.new(plugin_name)
|
62
|
-
# TODO: change to "
|
68
|
+
# TODO: change to "valid?" method
|
63
69
|
if plugin_util.plugin_class(fail_gracefully: true)
|
64
70
|
plugin_util.add_to_guardfile
|
65
|
-
|
66
|
-
begin
|
67
|
-
@options[:guardfile] = IO.read("Guardfile")
|
68
|
-
rescue Errno::ENOENT
|
69
|
-
end
|
70
|
-
|
71
|
-
elsif File.exist?(File.join(HOME_TEMPLATES, plugin_name))
|
72
|
-
content = File.read("Guardfile")
|
73
|
-
|
74
|
-
File.open("Guardfile", "wb") do |f|
|
75
|
-
f.puts(content)
|
76
|
-
f.puts("")
|
77
|
-
f.puts(File.read(File.join(HOME_TEMPLATES, plugin_name)))
|
78
|
-
end
|
79
|
-
|
80
|
-
::Guard::UI.info \
|
81
|
-
"#{ plugin_name } template added to Guardfile, feel free to edit it"
|
82
|
-
else
|
83
|
-
const_name = plugin_name.downcase.gsub("-", "")
|
84
|
-
UI.error "Could not load 'guard/#{ plugin_name.downcase }'"\
|
85
|
-
" or '~/.guard/templates/#{ plugin_name.downcase }'"\
|
86
|
-
" or find class Guard::#{ const_name.capitalize }"
|
71
|
+
return
|
87
72
|
end
|
73
|
+
|
74
|
+
template_code = (HOME_TEMPLATES + plugin_name).read
|
75
|
+
guardfile.binwrite(format("\n%s\n", template_code), open_args: ["a"])
|
76
|
+
|
77
|
+
_ui(:info, format(INFO_TEMPLATE_ADDED, plugin_name))
|
78
|
+
|
79
|
+
rescue Errno::ENOENT
|
80
|
+
|
81
|
+
name = plugin_name.downcase
|
82
|
+
class_name = name.gsub("-", "").capitalize
|
83
|
+
_ui(:error, "Could not load 'guard/#{name}'"\
|
84
|
+
" or '~/.guard/templates/#{name}'"\
|
85
|
+
" or find class Guard::#{class_name}")
|
88
86
|
end
|
89
87
|
|
90
88
|
# Adds the templates of all installed Guard implementations to an
|
@@ -95,6 +93,12 @@ module Guard
|
|
95
93
|
def initialize_all_templates
|
96
94
|
::Guard::PluginUtil.plugin_names.each { |g| initialize_template(g) }
|
97
95
|
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def _ui(*args)
|
100
|
+
::Guard::UI.send(*args)
|
101
|
+
end
|
98
102
|
end
|
99
103
|
end
|
100
104
|
end
|