guard 1.8.3 → 2.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +68 -10
- data/README.md +54 -33
- data/lib/guard.rb +133 -483
- data/lib/guard/cli.rb +78 -82
- data/lib/guard/commander.rb +121 -0
- data/lib/guard/commands/all.rb +1 -1
- data/lib/guard/commands/reload.rb +1 -1
- data/lib/guard/deprecated_methods.rb +59 -0
- data/lib/guard/deprecator.rb +107 -0
- data/lib/guard/dsl.rb +143 -329
- data/lib/guard/dsl_describer.rb +101 -57
- data/lib/guard/group.rb +27 -8
- data/lib/guard/guard.rb +25 -150
- data/lib/guard/guardfile.rb +35 -85
- data/lib/guard/guardfile/evaluator.rb +245 -0
- data/lib/guard/guardfile/generator.rb +89 -0
- data/lib/guard/interactor.rb +147 -163
- data/lib/guard/notifier.rb +83 -137
- data/lib/guard/notifiers/base.rb +220 -0
- data/lib/guard/notifiers/emacs.rb +39 -37
- data/lib/guard/notifiers/file_notifier.rb +29 -25
- data/lib/guard/notifiers/gntp.rb +68 -75
- data/lib/guard/notifiers/growl.rb +49 -52
- data/lib/guard/notifiers/growl_notify.rb +51 -56
- data/lib/guard/notifiers/libnotify.rb +41 -48
- data/lib/guard/notifiers/notifysend.rb +58 -38
- data/lib/guard/notifiers/rb_notifu.rb +54 -54
- data/lib/guard/notifiers/terminal_notifier.rb +48 -36
- data/lib/guard/notifiers/terminal_title.rb +23 -19
- data/lib/guard/notifiers/tmux.rb +110 -93
- data/lib/guard/options.rb +21 -0
- data/lib/guard/plugin.rb +66 -0
- data/lib/guard/plugin/base.rb +178 -0
- data/lib/guard/plugin/hooker.rb +123 -0
- data/lib/guard/plugin_util.rb +158 -0
- data/lib/guard/rake_task.rb +47 -0
- data/lib/guard/runner.rb +62 -82
- data/lib/guard/setuper.rb +248 -0
- data/lib/guard/ui.rb +24 -80
- data/lib/guard/ui/colors.rb +60 -0
- data/lib/guard/version.rb +1 -2
- data/lib/guard/watcher.rb +30 -30
- data/man/guard.1 +4 -4
- data/man/guard.1.html +6 -4
- metadata +25 -11
- data/lib/guard/hook.rb +0 -120
@@ -0,0 +1,245 @@
|
|
1
|
+
module Guard
|
2
|
+
module Guardfile
|
3
|
+
|
4
|
+
# This class is responsible for evaluating the Guardfile. It delegates
|
5
|
+
# to Guard::Dsl for the actual objects generation from the Guardfile content.
|
6
|
+
#
|
7
|
+
# @see Guard::Dsl
|
8
|
+
#
|
9
|
+
class Evaluator
|
10
|
+
|
11
|
+
attr_reader :options
|
12
|
+
|
13
|
+
# Initializes a new Guard::Guardfile::Evaluator object.
|
14
|
+
#
|
15
|
+
# @option opts [String] guardfile the path to a valid Guardfile
|
16
|
+
# @option opts [String] guardfile_contents a string representing the content of a valid Guardfile
|
17
|
+
#
|
18
|
+
def initialize(opts = {})
|
19
|
+
@options = ::Guard::Options.new([:guardfile, :guardfile_contents].reduce({}) { |h, key| h[key] = opts[key]; h })
|
20
|
+
end
|
21
|
+
|
22
|
+
# Evaluates the DSL methods in the `Guardfile`.
|
23
|
+
#
|
24
|
+
# @example Programmatically evaluate a Guardfile
|
25
|
+
# Guard::Guardfile::Evaluator.new.evaluate_guardfile
|
26
|
+
#
|
27
|
+
# @example Programmatically evaluate a Guardfile with a custom Guardfile path
|
28
|
+
# Guard::Guardfile::Evaluator.new(guardfile: '/Users/guardfile/MyAwesomeGuardfile').evaluate_guardfile
|
29
|
+
#
|
30
|
+
# @example Programmatically evaluate a Guardfile with an inline Guardfile
|
31
|
+
# Guard::Guardfile::Evaluator.new(guardfile_contents: 'guard :rspec').evaluate_guardfile
|
32
|
+
#
|
33
|
+
def evaluate_guardfile
|
34
|
+
_fetch_guardfile_contents
|
35
|
+
_instance_eval_guardfile(guardfile_contents)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Re-evaluates the `Guardfile` to update
|
39
|
+
# the current Guard configuration.
|
40
|
+
#
|
41
|
+
def reevaluate_guardfile
|
42
|
+
_before_reevaluate_guardfile
|
43
|
+
evaluate_guardfile
|
44
|
+
_after_reevaluate_guardfile
|
45
|
+
end
|
46
|
+
|
47
|
+
# Tests if the current `Guardfile` contains a specific Guard plugin.
|
48
|
+
#
|
49
|
+
# @example Programmatically test if a Guardfile contains a specific Guard plugin
|
50
|
+
# File.read('Guardfile')
|
51
|
+
# #=> "guard :rspec"
|
52
|
+
#
|
53
|
+
# Guard::Guardfile::Evaluator.new.guardfile_include?('rspec)
|
54
|
+
# #=> true
|
55
|
+
#
|
56
|
+
# @param [String] plugin_name the name of the Guard
|
57
|
+
# @return [Boolean] whether the Guard plugin has been declared
|
58
|
+
#
|
59
|
+
def guardfile_include?(plugin_name)
|
60
|
+
_guardfile_contents_without_user_config.match(/^guard\s*\(?\s*['":]#{ plugin_name }['"]?/)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Gets the file path to the project `Guardfile`.
|
64
|
+
#
|
65
|
+
# @example Gets the path of the currently evaluated Guardfile
|
66
|
+
# Dir.pwd
|
67
|
+
# #=> "/Users/remy/Code/github/guard"
|
68
|
+
#
|
69
|
+
# evaluator = Guard::Guardfile::Evaluator.new
|
70
|
+
# evaluator.evaluate_guardfile
|
71
|
+
# #=> nil
|
72
|
+
#
|
73
|
+
# evaluator.guardfile_path
|
74
|
+
# #=> "/Users/remy/Code/github/guard/Guardfile"
|
75
|
+
#
|
76
|
+
# @example Gets the "path" of an inline Guardfile
|
77
|
+
# > Guard::Guardfile::Evaluator.new(guardfile_contents: 'guard :rspec').evaluate_guardfile
|
78
|
+
# => nil
|
79
|
+
#
|
80
|
+
# > Guard::Guardfile::Evaluator.new.guardfile_path
|
81
|
+
# => "Inline Guardfile"
|
82
|
+
#
|
83
|
+
# @return [String] the path to the Guardfile or 'Inline Guardfile' if
|
84
|
+
# the Guardfile has been specified via the `:guardfile_contents` option.
|
85
|
+
#
|
86
|
+
def guardfile_path
|
87
|
+
options.guardfile_path || ''
|
88
|
+
end
|
89
|
+
|
90
|
+
# Gets the content of the `Guardfile` concatenated with the global
|
91
|
+
# user configuration file.
|
92
|
+
#
|
93
|
+
# @example Programmatically get the content of the current Guardfile
|
94
|
+
# Guard::Guardfile::Evaluator.new.guardfile_contents
|
95
|
+
# #=> "guard :rspec"
|
96
|
+
#
|
97
|
+
# @return [String] the Guardfile content
|
98
|
+
#
|
99
|
+
def guardfile_contents
|
100
|
+
config = File.read(_user_config_path) if File.exist?(_user_config_path)
|
101
|
+
[_guardfile_contents_without_user_config, config].compact.join("\n")
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
# Gets the default path of the `Guardfile`. This returns the `Guardfile`
|
107
|
+
# from the current directory when existing, or the global `~/.Guardfile`.
|
108
|
+
#
|
109
|
+
# @return [String] the path to the Guardfile
|
110
|
+
#
|
111
|
+
def _guardfile_default_path
|
112
|
+
File.exist?(_local_guardfile_path) ? _local_guardfile_path : _home_guardfile_path
|
113
|
+
end
|
114
|
+
|
115
|
+
# Gets the content of the `Guardfile`.
|
116
|
+
#
|
117
|
+
# @return [String] the Guardfile content
|
118
|
+
#
|
119
|
+
def _guardfile_contents_without_user_config
|
120
|
+
options.guardfile_contents || ''
|
121
|
+
end
|
122
|
+
|
123
|
+
# Evaluates the content of the `Guardfile`.
|
124
|
+
#
|
125
|
+
# @param [String] contents the content to evaluate.
|
126
|
+
#
|
127
|
+
def _instance_eval_guardfile(contents)
|
128
|
+
::Guard::Dsl.new.instance_eval(contents, options.guardfile_path, 1)
|
129
|
+
rescue => ex
|
130
|
+
::Guard::UI.error "Invalid Guardfile, original error is:\n#{ $! }"
|
131
|
+
raise ex
|
132
|
+
end
|
133
|
+
|
134
|
+
# Gets the content to evaluate and stores it into
|
135
|
+
# the options as `:guardfile_contents`.
|
136
|
+
#
|
137
|
+
def _fetch_guardfile_contents
|
138
|
+
if options.guardfile_contents
|
139
|
+
::Guard::UI.info 'Using inline Guardfile.'
|
140
|
+
options.guardfile_path = 'Inline Guardfile'
|
141
|
+
|
142
|
+
elsif options.guardfile
|
143
|
+
if File.exist?(options.guardfile)
|
144
|
+
_read_guardfile(options.guardfile)
|
145
|
+
::Guard::UI.info "Using Guardfile at #{ options.guardfile }."
|
146
|
+
else
|
147
|
+
::Guard::UI.error "No Guardfile exists at #{ options.guardfile }."
|
148
|
+
exit 1
|
149
|
+
end
|
150
|
+
|
151
|
+
else
|
152
|
+
if File.exist?(_guardfile_default_path)
|
153
|
+
_read_guardfile(_guardfile_default_path)
|
154
|
+
else
|
155
|
+
::Guard::UI.error 'No Guardfile found, please create one with `guard init`.'
|
156
|
+
exit 1
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
unless _guardfile_contents_usable?
|
161
|
+
::Guard::UI.error 'No Guard plugins found in Guardfile, please add at least one.'
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Reads the current `Guardfile` content.
|
166
|
+
#
|
167
|
+
# @param [String] guardfile_path the path to the Guardfile
|
168
|
+
#
|
169
|
+
def _read_guardfile(guardfile_path)
|
170
|
+
options.guardfile_path = guardfile_path
|
171
|
+
options.guardfile_contents = File.read(guardfile_path)
|
172
|
+
rescue => ex
|
173
|
+
::Guard::UI.error ex.inspect
|
174
|
+
::Guard::UI.error("Error reading file #{ guardfile_path }")
|
175
|
+
exit 1
|
176
|
+
end
|
177
|
+
|
178
|
+
# Stops Guard and clear internal state
|
179
|
+
# before the Guardfile will be re-evaluated.
|
180
|
+
#
|
181
|
+
def _before_reevaluate_guardfile
|
182
|
+
::Guard.runner.run(:stop)
|
183
|
+
::Guard.reset_groups
|
184
|
+
::Guard.reset_plugins
|
185
|
+
::Guard::Notifier.clear_notifiers
|
186
|
+
|
187
|
+
options.guardfile_contents
|
188
|
+
end
|
189
|
+
|
190
|
+
# Starts Guard and notification and show a message
|
191
|
+
# after the Guardfile has been re-evaluated.
|
192
|
+
#
|
193
|
+
def _after_reevaluate_guardfile
|
194
|
+
::Guard::Notifier.turn_on if ::Guard::Notifier.enabled?
|
195
|
+
|
196
|
+
if ::Guard.plugins.empty?
|
197
|
+
::Guard::Notifier.notify('No plugins found in Guardfile, please add at least one.', title: 'Guard re-evaluate', image: :failed)
|
198
|
+
else
|
199
|
+
msg = 'Guardfile has been re-evaluated.'
|
200
|
+
::Guard::UI.info(msg)
|
201
|
+
::Guard::Notifier.notify(msg, title: 'Guard re-evaluate')
|
202
|
+
|
203
|
+
::Guard.runner.run(:start)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Tests if the current `Guardfile` content is usable.
|
208
|
+
#
|
209
|
+
# @return [Boolean] if the Guardfile is usable
|
210
|
+
#
|
211
|
+
def _guardfile_contents_usable?
|
212
|
+
guardfile_contents && guardfile_contents =~ /guard/m
|
213
|
+
end
|
214
|
+
|
215
|
+
# The path to the `Guardfile` that is located at
|
216
|
+
# the directory, where Guard has been started from.
|
217
|
+
#
|
218
|
+
# @return [String] the path to the local Guardfile
|
219
|
+
#
|
220
|
+
def _local_guardfile_path
|
221
|
+
File.join(Dir.pwd, 'Guardfile')
|
222
|
+
end
|
223
|
+
|
224
|
+
# The path to the `.Guardfile` that is located at
|
225
|
+
# the users home directory.
|
226
|
+
#
|
227
|
+
# @return [String] the path to `~/.Guardfile`
|
228
|
+
#
|
229
|
+
def _home_guardfile_path
|
230
|
+
File.expand_path(File.join('~', '.Guardfile'))
|
231
|
+
end
|
232
|
+
|
233
|
+
# The path to the user configuration `.guard.rb`
|
234
|
+
# that is located at the users home directory.
|
235
|
+
#
|
236
|
+
# @return [String] the path to `~/.guard.rb`
|
237
|
+
#
|
238
|
+
def _user_config_path
|
239
|
+
File.expand_path(File.join('~', '.guard.rb'))
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Guard
|
2
|
+
module Guardfile
|
3
|
+
|
4
|
+
# This class is responsible for generating the Guardfile and adding Guard'
|
5
|
+
# plugins' templates into it.
|
6
|
+
#
|
7
|
+
# @see Guard::CLI
|
8
|
+
#
|
9
|
+
class Generator
|
10
|
+
|
11
|
+
require 'guard'
|
12
|
+
require 'guard/ui'
|
13
|
+
|
14
|
+
attr_reader :options
|
15
|
+
|
16
|
+
# The Guardfile template for `guard init`
|
17
|
+
GUARDFILE_TEMPLATE = File.expand_path('../../../guard/templates/Guardfile', __FILE__)
|
18
|
+
|
19
|
+
# The location of user defined templates
|
20
|
+
HOME_TEMPLATES = File.expand_path('~/.guard/templates')
|
21
|
+
|
22
|
+
# Initialize a new `Guard::Guardfile::Generator` object.
|
23
|
+
#
|
24
|
+
# @param [Hash] options The options for creating a Guardfile
|
25
|
+
# @option options [Boolean] :abort_on_existence Whether to abort or not
|
26
|
+
# when a Guardfile already exists
|
27
|
+
#
|
28
|
+
def initialize(options = {})
|
29
|
+
@options = options
|
30
|
+
end
|
31
|
+
|
32
|
+
# Creates the initial Guardfile template when it does not
|
33
|
+
# already exist.
|
34
|
+
#
|
35
|
+
# @see Guard::CLI#init
|
36
|
+
#
|
37
|
+
def create_guardfile
|
38
|
+
if !File.exist?('Guardfile')
|
39
|
+
::Guard::UI.info "Writing new Guardfile to #{ Dir.pwd }/Guardfile"
|
40
|
+
FileUtils.cp(GUARDFILE_TEMPLATE, 'Guardfile')
|
41
|
+
elsif options[:abort_on_existence]
|
42
|
+
::Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
|
43
|
+
abort
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Adds the Guardfile template of a Guard plugin to an existing Guardfile.
|
48
|
+
#
|
49
|
+
# @see Guard::CLI#init
|
50
|
+
#
|
51
|
+
# @param [String] plugin_name the name of the Guard plugin or template to
|
52
|
+
# initialize
|
53
|
+
#
|
54
|
+
def initialize_template(plugin_name)
|
55
|
+
plugin_util = ::Guard::PluginUtil.new(plugin_name)
|
56
|
+
if plugin_util.plugin_class(fail_gracefully: true)
|
57
|
+
plugin_util.add_to_guardfile
|
58
|
+
|
59
|
+
@options[:guardfile] = File.read('Guardfile') if File.exists?('Guardfile')
|
60
|
+
|
61
|
+
elsif File.exist?(File.join(HOME_TEMPLATES, plugin_name))
|
62
|
+
content = File.read('Guardfile')
|
63
|
+
|
64
|
+
File.open('Guardfile', 'wb') do |f|
|
65
|
+
f.puts(content)
|
66
|
+
f.puts('')
|
67
|
+
f.puts(File.read(File.join(HOME_TEMPLATES, plugin_name)))
|
68
|
+
end
|
69
|
+
|
70
|
+
::Guard::UI.info "#{ plugin_name } template added to Guardfile, feel free to edit it"
|
71
|
+
else
|
72
|
+
const_name = plugin_name.downcase.gsub('-', '')
|
73
|
+
UI.error "Could not load 'guard/#{ plugin_name.downcase }' or '~/.guard/templates/#{ plugin_name.downcase }' or find class Guard::#{ const_name.capitalize }"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Adds the templates of all installed Guard implementations to an
|
78
|
+
# existing Guardfile.
|
79
|
+
#
|
80
|
+
# @see Guard::CLI#init
|
81
|
+
#
|
82
|
+
def initialize_all_templates
|
83
|
+
::Guard::PluginUtil.plugin_names.each { |g| initialize_template(g) }
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
data/lib/guard/interactor.rb
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
3
|
+
require 'guard/commands/all'
|
4
|
+
require 'guard/commands/change'
|
5
|
+
require 'guard/commands/notification'
|
6
|
+
require 'guard/commands/pause'
|
7
|
+
require 'guard/commands/reload'
|
8
|
+
require 'guard/commands/scope'
|
9
|
+
require 'guard/commands/show'
|
10
|
+
require 'guard/ui'
|
11
|
+
|
1
12
|
module Guard
|
2
13
|
|
3
14
|
# The Guard interactor is a Pry REPL with a Guard
|
@@ -5,80 +16,86 @@ module Guard
|
|
5
16
|
#
|
6
17
|
class Interactor
|
7
18
|
|
8
|
-
require 'pry'
|
9
|
-
|
10
|
-
require 'guard'
|
11
|
-
require 'guard/ui'
|
12
|
-
|
13
|
-
require 'guard/commands/all'
|
14
|
-
require 'guard/commands/change'
|
15
|
-
require 'guard/commands/notification'
|
16
|
-
require 'guard/commands/pause'
|
17
|
-
require 'guard/commands/reload'
|
18
|
-
require 'guard/commands/scope'
|
19
|
-
require 'guard/commands/show'
|
20
|
-
|
21
19
|
# The default Ruby script to configure Guard Pry if the option `:guard_rc` is not defined.
|
22
|
-
GUARD_RC
|
20
|
+
GUARD_RC = '~/.guardrc'
|
23
21
|
|
24
22
|
# The default Guard Pry history file if the option `:history_file` is not defined.
|
25
23
|
HISTORY_FILE = '~/.guard_history'
|
26
24
|
|
27
25
|
# List of shortcuts for each interactor command
|
28
26
|
SHORTCUTS = {
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
27
|
+
help: 'h',
|
28
|
+
all: 'a',
|
29
|
+
reload: 'r',
|
30
|
+
change: 'c',
|
31
|
+
show: 's',
|
32
|
+
scope: 'o',
|
33
|
+
notification: 'n',
|
34
|
+
pause: 'p',
|
35
|
+
exit: 'e',
|
36
|
+
quit: 'q'
|
39
37
|
}
|
40
38
|
|
41
39
|
attr_accessor :thread
|
42
40
|
|
43
|
-
|
41
|
+
# Get the interactor options
|
42
|
+
#
|
43
|
+
# @return [Hash] the options
|
44
|
+
#
|
45
|
+
def self.options
|
46
|
+
@options ||= {}
|
47
|
+
end
|
44
48
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
# Set the interactor options
|
50
|
+
#
|
51
|
+
# @param [Hash] options the interactor options
|
52
|
+
# @option options [String] :guard_rc the Ruby script to configure Guard Pry
|
53
|
+
# @option options [String] :history_file the file to write the Pry history to
|
54
|
+
#
|
55
|
+
def self.options=(options)
|
56
|
+
@options = options
|
57
|
+
end
|
52
58
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
@options = options
|
61
|
-
end
|
59
|
+
# Is the interactor enabled?
|
60
|
+
#
|
61
|
+
# @return [Boolean] true if enabled
|
62
|
+
#
|
63
|
+
def self.enabled
|
64
|
+
@enabled || @enabled.nil?
|
65
|
+
end
|
62
66
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
# Set the enabled status for the interactor
|
68
|
+
#
|
69
|
+
# @param [Boolean] status true if enabled
|
70
|
+
#
|
71
|
+
def self.enabled=(status)
|
72
|
+
@enabled = status
|
73
|
+
end
|
74
|
+
|
75
|
+
# Converts and validates a plain text scope
|
76
|
+
# to a valid plugin or group scope.
|
77
|
+
#
|
78
|
+
# @param [Array<String>] entries the text scope
|
79
|
+
# @return [Hash, Array<String>] the plugin or group scope, the unknown entries
|
80
|
+
#
|
81
|
+
def self.convert_scope(entries)
|
82
|
+
scopes = { plugins: [], groups: [] }
|
83
|
+
unknown = []
|
70
84
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
85
|
+
entries.each do |entry|
|
86
|
+
if plugin = ::Guard.plugin(entry)
|
87
|
+
scopes[:plugins] << plugin
|
88
|
+
elsif group = ::Guard.group(entry)
|
89
|
+
scopes[:groups] << group
|
90
|
+
else
|
91
|
+
unknown << entry
|
92
|
+
end
|
77
93
|
end
|
78
94
|
|
95
|
+
[scopes, unknown]
|
79
96
|
end
|
80
97
|
|
81
|
-
#
|
98
|
+
# Initializes the interactor. This configures
|
82
99
|
# Pry and creates some custom commands and aliases
|
83
100
|
# for Guard.
|
84
101
|
#
|
@@ -90,24 +107,58 @@ module Guard
|
|
90
107
|
Pry.config.history.file = File.expand_path(self.class.options[:history_file] || HISTORY_FILE)
|
91
108
|
|
92
109
|
@stty_exists = nil
|
93
|
-
|
110
|
+
_add_hooks
|
111
|
+
|
112
|
+
_replace_reset_command
|
113
|
+
_create_run_all_command
|
114
|
+
_create_command_aliases
|
115
|
+
_create_guard_commands
|
116
|
+
_create_group_commands
|
117
|
+
|
118
|
+
_configure_prompt
|
119
|
+
end
|
120
|
+
|
121
|
+
# Start the line reader in its own thread and
|
122
|
+
# stop Guard on Ctrl-D.
|
123
|
+
#
|
124
|
+
def start
|
125
|
+
return if ENV['GUARD_ENV'] == 'test'
|
126
|
+
|
127
|
+
_store_terminal_settings if _stty_exists?
|
128
|
+
|
129
|
+
if !@thread || !['sleep', 'run'].include?(@thread.status)
|
130
|
+
::Guard::UI.debug 'Start interactor'
|
131
|
+
|
132
|
+
@thread = Thread.new do
|
133
|
+
Pry.start
|
134
|
+
::Guard.stop
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Kill interactor thread if not current
|
140
|
+
#
|
141
|
+
def stop
|
142
|
+
return if !@thread || ENV['GUARD_ENV'] == 'test'
|
94
143
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
144
|
+
unless Thread.current == @thread
|
145
|
+
::Guard::UI.reset_line
|
146
|
+
::Guard::UI.debug 'Stop interactor'
|
147
|
+
@thread.kill
|
148
|
+
end
|
100
149
|
|
101
|
-
|
150
|
+
_restore_terminal_settings if _stty_exists?
|
102
151
|
end
|
103
152
|
|
153
|
+
private
|
154
|
+
|
104
155
|
# Add Pry hooks:
|
105
156
|
#
|
106
157
|
# * Load `~/.guardrc` within each new Pry session.
|
107
158
|
# * Load project's `.guardrc` within each new Pry session.
|
108
159
|
# * Restore prompt after each evaluation.
|
109
160
|
#
|
110
|
-
def
|
161
|
+
def _add_hooks
|
111
162
|
Pry.config.hooks.add_hook :when_started, :load_guard_rc do
|
112
163
|
(self.class.options[:guard_rc] || GUARD_RC).tap do |p|
|
113
164
|
load p if File.exist?(File.expand_path(p))
|
@@ -119,7 +170,7 @@ module Guard
|
|
119
170
|
load project_guard_rc if File.exist?(project_guard_rc)
|
120
171
|
end
|
121
172
|
|
122
|
-
if
|
173
|
+
if _stty_exists?
|
123
174
|
Pry.config.hooks.add_hook :after_eval, :restore_visibility do
|
124
175
|
system("stty echo 2>#{ DEV_NULL }")
|
125
176
|
end
|
@@ -128,8 +179,8 @@ module Guard
|
|
128
179
|
|
129
180
|
# Replaces reset defined inside of Pry with a reset that
|
130
181
|
# instead restarts guard.
|
131
|
-
|
132
|
-
def
|
182
|
+
#
|
183
|
+
def _replace_reset_command
|
133
184
|
Pry.commands.command "reset", "Reset the Guard to a clean state." do
|
134
185
|
output.puts "Guard reset."
|
135
186
|
exec "guard"
|
@@ -140,7 +191,7 @@ module Guard
|
|
140
191
|
# when the command is empty (just pressing enter on the
|
141
192
|
# beginning of a line).
|
142
193
|
#
|
143
|
-
def
|
194
|
+
def _create_run_all_command
|
144
195
|
Pry.commands.block_command /^$/, 'Hit enter to run all' do
|
145
196
|
Pry.run_command 'all'
|
146
197
|
end
|
@@ -150,7 +201,7 @@ module Guard
|
|
150
201
|
# `help`, `reload`, `change`, `scope`, `notification`, `pause`, `exit` and `quit`,
|
151
202
|
# which will be the first letter of the command.
|
152
203
|
#
|
153
|
-
def
|
204
|
+
def _create_command_aliases
|
154
205
|
SHORTCUTS.each do |command, shortcut|
|
155
206
|
Pry.commands.alias_command shortcut, command.to_s
|
156
207
|
end
|
@@ -161,11 +212,9 @@ module Guard
|
|
161
212
|
# when guard-rspec is available, then a command
|
162
213
|
# `rspec` is created that runs `all rspec`.
|
163
214
|
#
|
164
|
-
def
|
165
|
-
::Guard.
|
166
|
-
|
167
|
-
|
168
|
-
Pry.commands.create_command name, "Run all #{ name }" do
|
215
|
+
def _create_guard_commands
|
216
|
+
::Guard.plugins.each do |guard_plugin|
|
217
|
+
Pry.commands.create_command guard_plugin.name, "Run all #{ guard_plugin.title }" do
|
169
218
|
group 'Guard'
|
170
219
|
|
171
220
|
def process
|
@@ -180,12 +229,11 @@ module Guard
|
|
180
229
|
# when you have a group `frontend`, then a command
|
181
230
|
# `frontend` is created that runs `all frontend`.
|
182
231
|
#
|
183
|
-
def
|
232
|
+
def _create_group_commands
|
184
233
|
::Guard.groups.each do |group|
|
185
|
-
|
186
|
-
next if name == 'default'
|
234
|
+
next if group.name == :default
|
187
235
|
|
188
|
-
Pry.commands.create_command name, "Run all #{
|
236
|
+
Pry.commands.create_command group.name.to_s, "Run all #{ group.title }" do
|
189
237
|
group 'Guard'
|
190
238
|
|
191
239
|
def process
|
@@ -198,71 +246,29 @@ module Guard
|
|
198
246
|
# Configure the pry prompt to see `guard` instead of
|
199
247
|
# `pry`.
|
200
248
|
#
|
201
|
-
def
|
202
|
-
Pry.config.prompt = [
|
203
|
-
proc do |target_self, nest_level, pry|
|
204
|
-
history = pry.input_array.size
|
205
|
-
process = ::Guard.listener.paused? ? 'pause' : 'guard'
|
206
|
-
clip = Pry.view_clip(target_self)
|
207
|
-
level = ":#{ nest_level }" unless nest_level.zero?
|
208
|
-
scope = if !::Guard.scope[:plugins].empty?
|
209
|
-
"{#{ ::Guard.scope[:plugins].join(',') }} "
|
210
|
-
elsif !::Guard.scope[:groups].empty?
|
211
|
-
"{#{ ::Guard.scope[:groups].join(',') }} "
|
212
|
-
else
|
213
|
-
''
|
214
|
-
end
|
215
|
-
|
216
|
-
"[#{ history }] #{ scope }#{ process }(#{ clip })#{ level }> "
|
217
|
-
end,
|
218
|
-
proc do |target_self, nest_level, pry|
|
219
|
-
history = pry.input_array.size
|
220
|
-
process = ::Guard.listener.paused? ? 'pause' : 'guard'
|
221
|
-
clip = Pry.view_clip(target_self)
|
222
|
-
level = ":#{ nest_level }" unless nest_level.zero?
|
223
|
-
scope = if !::Guard.scope[:plugins].empty?
|
224
|
-
"{#{ ::Guard.scope[:plugins].join }} "
|
225
|
-
elsif !::Guard.scope[:groups].empty?
|
226
|
-
"{#{ ::Guard.scope[:groups].join }} "
|
227
|
-
else
|
228
|
-
''
|
229
|
-
end
|
230
|
-
|
231
|
-
"[#{ history }] #{ scope }#{ process }(#{ clip })#{ level }* "
|
232
|
-
end
|
233
|
-
]
|
234
|
-
end
|
235
|
-
|
236
|
-
# Start the line reader in its own thread and
|
237
|
-
# stop Guard on Ctrl-D.
|
238
|
-
#
|
239
|
-
def start
|
240
|
-
return if ENV['GUARD_ENV'] == 'test'
|
241
|
-
|
242
|
-
store_terminal_settings if stty_exists?
|
243
|
-
|
244
|
-
if !@thread || !['sleep', 'run'].include?(@thread.status)
|
245
|
-
::Guard::UI.debug 'Start interactor'
|
246
|
-
|
247
|
-
@thread = Thread.new do
|
248
|
-
Pry.start
|
249
|
-
::Guard.stop
|
250
|
-
end
|
251
|
-
end
|
249
|
+
def _configure_prompt
|
250
|
+
Pry.config.prompt = [_prompt('>'), _prompt('*')]
|
252
251
|
end
|
253
252
|
|
254
|
-
#
|
253
|
+
# Returns a proc that will return itself a string ending with the given
|
254
|
+
# `ending_char` when called.
|
255
255
|
#
|
256
|
-
def
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
256
|
+
def _prompt(ending_char)
|
257
|
+
proc do |target_self, nest_level, pry|
|
258
|
+
history = pry.input_array.size
|
259
|
+
process = ::Guard.listener.paused? ? 'pause' : 'guard'
|
260
|
+
clip = Pry.view_clip(target_self)
|
261
|
+
level = ":#{ nest_level }" unless nest_level.zero?
|
262
|
+
scope = if !::Guard.scope[:plugins].empty?
|
263
|
+
"{#{ ::Guard.scope[:plugins].map(&:title).join(',') }} "
|
264
|
+
elsif !::Guard.scope[:groups].empty?
|
265
|
+
"{#{ ::Guard.scope[:groups].map(&:title).join(',') }} "
|
266
|
+
else
|
267
|
+
''
|
268
|
+
end
|
269
|
+
|
270
|
+
"[#{ history }] #{ scope }#{ process }(#{ clip })#{ level }#{ending_char} "
|
263
271
|
end
|
264
|
-
|
265
|
-
restore_terminal_settings if stty_exists?
|
266
272
|
end
|
267
273
|
|
268
274
|
# Detects whether or not the stty command exists
|
@@ -270,7 +276,7 @@ module Guard
|
|
270
276
|
#
|
271
277
|
# @return [Boolean] the status of stty
|
272
278
|
#
|
273
|
-
def
|
279
|
+
def _stty_exists?
|
274
280
|
@stty_exists ||= system('hash', 'stty') ? true : false if @stty_exists.nil?
|
275
281
|
@stty_exists
|
276
282
|
end
|
@@ -278,37 +284,15 @@ module Guard
|
|
278
284
|
# Stores the terminal settings so we can resore them
|
279
285
|
# when stopping.
|
280
286
|
#
|
281
|
-
def
|
287
|
+
def _store_terminal_settings
|
282
288
|
@stty_save = `stty -g 2>#{ DEV_NULL }`.chomp
|
283
289
|
end
|
284
290
|
|
285
291
|
# Restore terminal settings
|
286
292
|
#
|
287
|
-
def
|
293
|
+
def _restore_terminal_settings
|
288
294
|
system("stty #{ @stty_save } 2>#{ DEV_NULL }") if @stty_save
|
289
295
|
end
|
290
296
|
|
291
|
-
# Converts and validates a plain text scope
|
292
|
-
# to a valid plugin or group scope.
|
293
|
-
#
|
294
|
-
# @param [Array<String>] entries the text scope
|
295
|
-
# @return [Hash, Array<String>] the plugin or group scope, the unknown entries
|
296
|
-
#
|
297
|
-
def self.convert_scope(entries)
|
298
|
-
scopes = { :plugins => [], :groups => [] }
|
299
|
-
unknown = []
|
300
|
-
|
301
|
-
entries.each do |entry|
|
302
|
-
if plugin = ::Guard.guards(entry)
|
303
|
-
scopes[:plugins] << plugin
|
304
|
-
elsif group = ::Guard.groups(entry)
|
305
|
-
scopes[:groups] << group
|
306
|
-
else
|
307
|
-
unknown << entry
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
[scopes, unknown]
|
312
|
-
end
|
313
297
|
end
|
314
298
|
end
|