guard 0.8.8 → 0.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.
- data/CHANGELOG.md +16 -1
- data/README.md +665 -293
- data/bin/fsevent_watch_guard +0 -0
- data/lib/guard.rb +66 -31
- data/lib/guard/cli.rb +9 -3
- data/lib/guard/dsl.rb +32 -5
- data/lib/guard/dsl_describer.rb +3 -2
- data/lib/guard/guard.rb +2 -2
- data/lib/guard/interactor.rb +179 -48
- data/lib/guard/listener.rb +32 -17
- data/lib/guard/listeners/darwin.rb +5 -9
- data/lib/guard/listeners/linux.rb +6 -10
- data/lib/guard/listeners/windows.rb +4 -2
- data/lib/guard/notifier.rb +171 -258
- data/lib/guard/notifiers/gntp.rb +114 -0
- data/lib/guard/notifiers/growl.rb +98 -0
- data/lib/guard/notifiers/growl_notify.rb +91 -0
- data/lib/guard/notifiers/libnotify.rb +96 -0
- data/lib/guard/notifiers/rb_notifu.rb +101 -0
- data/lib/guard/ui.rb +2 -2
- data/lib/guard/version.rb +1 -1
- data/lib/guard/watcher.rb +1 -1
- data/lib/vendor/darwin/Gemfile +6 -0
- data/lib/vendor/darwin/Guardfile +8 -0
- data/lib/vendor/darwin/LICENSE +20 -0
- data/lib/vendor/darwin/README.rdoc +254 -0
- data/lib/vendor/darwin/Rakefile +21 -0
- data/lib/vendor/darwin/ext/extconf.rb +61 -0
- data/lib/vendor/darwin/ext/fsevent/fsevent_watch.c +226 -0
- data/lib/vendor/darwin/lib/rb-fsevent.rb +2 -0
- data/lib/vendor/darwin/lib/rb-fsevent/fsevent.rb +105 -0
- data/lib/vendor/darwin/lib/rb-fsevent/version.rb +3 -0
- data/lib/vendor/darwin/rb-fsevent.gemspec +24 -0
- data/lib/vendor/darwin/spec/fixtures/folder1/file1.txt +0 -0
- data/lib/vendor/darwin/spec/fixtures/folder1/folder2/file2.txt +0 -0
- data/lib/vendor/darwin/spec/rb-fsevent/fsevent_spec.rb +75 -0
- data/lib/vendor/darwin/spec/spec_helper.rb +24 -0
- data/lib/vendor/linux/MIT-LICENSE +20 -0
- data/lib/vendor/linux/README.md +66 -0
- data/lib/vendor/linux/Rakefile +54 -0
- data/lib/vendor/linux/VERSION +1 -0
- data/lib/vendor/linux/lib/rb-inotify.rb +17 -0
- data/lib/vendor/linux/lib/rb-inotify/event.rb +139 -0
- data/lib/vendor/linux/lib/rb-inotify/native.rb +31 -0
- data/lib/vendor/linux/lib/rb-inotify/native/flags.rb +89 -0
- data/lib/vendor/linux/lib/rb-inotify/notifier.rb +308 -0
- data/lib/vendor/linux/lib/rb-inotify/watcher.rb +83 -0
- data/lib/vendor/linux/rb-inotify.gemspec +53 -0
- data/lib/vendor/windows/Gemfile +4 -0
- data/lib/vendor/windows/README.md +34 -0
- data/lib/vendor/windows/Rakefile +18 -0
- data/lib/vendor/windows/lib/rb-fchange.rb +14 -0
- data/lib/vendor/windows/lib/rb-fchange/event.rb +29 -0
- data/lib/vendor/windows/lib/rb-fchange/native.rb +45 -0
- data/lib/vendor/windows/lib/rb-fchange/native/flags.rb +78 -0
- data/lib/vendor/windows/lib/rb-fchange/notifier.rb +149 -0
- data/lib/vendor/windows/lib/rb-fchange/version.rb +3 -0
- data/lib/vendor/windows/lib/rb-fchange/watcher.rb +99 -0
- data/lib/vendor/windows/rb-fchange.gemspec +34 -0
- data/lib/vendor/windows/spec/fixtures/folder1/file1.txt +0 -0
- data/lib/vendor/windows/spec/fixtures/folder1/folder2/file2.txt +0 -0
- data/lib/vendor/windows/spec/rb-fchange/fchange_spec.rb +119 -0
- data/lib/vendor/windows/spec/spec_helper.rb +21 -0
- metadata +87 -22
- data/lib/guard/version.rbc +0 -180
Binary file
|
data/lib/guard.rb
CHANGED
@@ -21,7 +21,7 @@ module Guard
|
|
21
21
|
class << self
|
22
22
|
attr_accessor :options, :interactor, :listener, :lock
|
23
23
|
|
24
|
-
# Creates the initial Guardfile template or add a Guard implementation
|
24
|
+
# Creates the initial Guardfile template and/or add a Guard implementation
|
25
25
|
# Guardfile template to an existing Guardfile.
|
26
26
|
#
|
27
27
|
# @see Guard::Guard.init
|
@@ -43,45 +43,54 @@ module Guard
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
# Initialize the Guard singleton
|
46
|
+
# Initialize the Guard singleton:
|
47
|
+
#
|
48
|
+
# - Initialize the internal Guard state.
|
49
|
+
# - Create the interactor when necessary for user interaction.
|
50
|
+
# - Select and initialize the file change listener.
|
47
51
|
#
|
48
52
|
# @option options [Boolean] clear if auto clear the UI should be done
|
49
53
|
# @option options [Boolean] notify if system notifications should be shown
|
50
|
-
# @option options [Boolean]
|
54
|
+
# @option options [Boolean] verbose if verbose output should be shown
|
51
55
|
# @option options [Array<String>] group the list of groups to start
|
52
56
|
# @option options [String] watchdir the director to watch
|
53
57
|
# @option options [String] guardfile the path to the Guardfile
|
54
58
|
# @option options [Boolean] watch_all_modifications watches all file modifications if true
|
55
59
|
#
|
56
60
|
def setup(options = {})
|
57
|
-
@lock
|
58
|
-
|
61
|
+
@lock = Mutex.new
|
59
62
|
@options = options
|
60
63
|
@guards = []
|
61
64
|
self.reset_groups
|
62
|
-
@interactor = Interactor.new unless
|
63
|
-
@listener = Listener.select_and_init(
|
64
|
-
|
65
|
-
@options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off
|
65
|
+
@interactor = Interactor.new unless options[:no_interactions]
|
66
|
+
@listener = Listener.select_and_init(options[:watchdir] && File.expand_path(options[:watchdir]), options)
|
66
67
|
|
67
68
|
UI.clear if @options[:clear]
|
68
|
-
|
69
|
-
debug_command_execution if @options[:debug]
|
69
|
+
debug_command_execution if @options[:verbose]
|
70
70
|
|
71
71
|
self
|
72
72
|
end
|
73
73
|
|
74
74
|
# Smart accessor for retrieving a specific guard or several guards at once.
|
75
75
|
#
|
76
|
-
# @param [String, Symbol] filter return the guard with the given name, or nil if not found
|
77
|
-
# @param [Regexp] filter returns all guards matching the Regexp, or [] if no guard found
|
78
|
-
# @param [Hash] filter returns all guards matching the given Hash.
|
79
|
-
# Example: `{ :name => 'rspec', :group => 'backend' }`, or [] if no guard found
|
80
|
-
# @param [NilClass] filter returns all guards
|
81
|
-
#
|
82
76
|
# @see Guard.groups
|
83
77
|
#
|
78
|
+
# @example Filter Guards by String or Symbol
|
79
|
+
# Guard.guards('rspec')
|
80
|
+
# Guard.guards(:rspec)
|
81
|
+
#
|
82
|
+
# @example Filter Guards by Regexp
|
83
|
+
# Guard.guards(/rsp.+/)
|
84
|
+
#
|
85
|
+
# @example Filter Guards by Hash
|
86
|
+
# Guard.guards({ :name => 'rspec', :group => 'backend' })
|
87
|
+
#
|
88
|
+
# @param [String, Symbol, Regexp, Hash] filter the filter to apply to the Guards
|
89
|
+
# @return [Array<Guard>] the filtered Guards
|
90
|
+
#
|
84
91
|
def guards(filter = nil)
|
92
|
+
@guards ||= []
|
93
|
+
|
85
94
|
case filter
|
86
95
|
when String, Symbol
|
87
96
|
@guards.find { |guard| guard.class.to_s.downcase.sub('guard::', '') == filter.to_s.downcase.gsub('-', '') }
|
@@ -102,12 +111,18 @@ module Guard
|
|
102
111
|
|
103
112
|
# Smart accessor for retrieving a specific group or several groups at once.
|
104
113
|
#
|
105
|
-
# @param [NilClass] filter returns all groups
|
106
|
-
# @param [String, Symbol] filter return the group with the given name, or nil if not found
|
107
|
-
# @param [Regexp] filter returns all groups matching the Regexp, or [] if no group found
|
108
|
-
#
|
109
114
|
# @see Guard.guards
|
110
115
|
#
|
116
|
+
# @example Filter groups by String or Symbol
|
117
|
+
# Guard.groups('backend')
|
118
|
+
# Guard.groups(:backend)
|
119
|
+
#
|
120
|
+
# @example Filter groups by Regexp
|
121
|
+
# Guard.groups(/(back|front)end/)
|
122
|
+
#
|
123
|
+
# @param [String, Symbol, Regexp] filter the filter to apply to the Groups
|
124
|
+
# @return [Array<Group>] the filtered groups
|
125
|
+
#
|
111
126
|
def groups(filter = nil)
|
112
127
|
case filter
|
113
128
|
when String, Symbol
|
@@ -129,6 +144,13 @@ module Guard
|
|
129
144
|
|
130
145
|
# Start Guard by evaluate the `Guardfile`, initialize the declared Guards
|
131
146
|
# and start the available file change listener.
|
147
|
+
# Main method for Guard that is called from the CLI when guard starts.
|
148
|
+
#
|
149
|
+
# - Setup Guard internals
|
150
|
+
# - Evaluate the `Guardfile`
|
151
|
+
# - Configure Notifiers
|
152
|
+
# - Initialize the declared Guards
|
153
|
+
# - Start the available file change listener
|
132
154
|
#
|
133
155
|
# @option options [Boolean] clear if auto clear the UI should be done
|
134
156
|
# @option options [Boolean] notify if system notifications should be shown
|
@@ -142,6 +164,8 @@ module Guard
|
|
142
164
|
|
143
165
|
Dsl.evaluate_guardfile(options)
|
144
166
|
|
167
|
+
options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off
|
168
|
+
|
145
169
|
listener.on_change do |files|
|
146
170
|
Dsl.reevaluate_guardfile if Watcher.match_guardfile?(files)
|
147
171
|
listener.changed_files += files if Watcher.match_files?(guards, files)
|
@@ -172,7 +196,7 @@ module Guard
|
|
172
196
|
|
173
197
|
# Reload all Guards currently enabled.
|
174
198
|
#
|
175
|
-
# @param [Hash]
|
199
|
+
# @param [Hash] scopes an hash with a guard or a group scope
|
176
200
|
#
|
177
201
|
def reload(scopes)
|
178
202
|
run do
|
@@ -184,7 +208,7 @@ module Guard
|
|
184
208
|
|
185
209
|
# Trigger `run_all` on all Guards currently enabled.
|
186
210
|
#
|
187
|
-
# @param [Hash]
|
211
|
+
# @param [Hash] scopes an hash with a guard or a group scope
|
188
212
|
#
|
189
213
|
def run_all(scopes)
|
190
214
|
run do
|
@@ -236,16 +260,17 @@ module Guard
|
|
236
260
|
end
|
237
261
|
end
|
238
262
|
|
239
|
-
# Loop through all groups and run the given task
|
263
|
+
# Loop through all groups and run the given task for each Guard.
|
240
264
|
#
|
241
265
|
# Stop the task run for the all Guards within a group if one Guard
|
242
266
|
# throws `:task_has_failed`.
|
243
267
|
#
|
244
|
-
# @param [Hash]
|
268
|
+
# @param [Hash] scopes an hash with a guard or a group scope
|
269
|
+
# @yield the task to run
|
245
270
|
#
|
246
271
|
def run_on_guards(scopes = {})
|
247
|
-
if
|
248
|
-
yield(
|
272
|
+
if scope_guard = scopes[:guard]
|
273
|
+
yield(scope_guard)
|
249
274
|
else
|
250
275
|
groups = scopes[:group] ? [scopes[:group]] : @groups
|
251
276
|
groups.each do |group|
|
@@ -390,7 +415,17 @@ module Guard
|
|
390
415
|
group
|
391
416
|
end
|
392
417
|
|
393
|
-
# Tries to load the Guard main class.
|
418
|
+
# Tries to load the Guard main class. This transforms the supplied Guard
|
419
|
+
# name into a class name:
|
420
|
+
#
|
421
|
+
# * `guardname` will become `Guard::Guardname`
|
422
|
+
# * `dashed-guard-name` will become `Guard::DashedGuardName`
|
423
|
+
# * `underscore_guard_name` will become `Guard::UnderscoreGuardName`
|
424
|
+
#
|
425
|
+
# When no class is found with the strict case sensitive rules, another
|
426
|
+
# try is made to locate the class without matching case:
|
427
|
+
#
|
428
|
+
# * `rspec` will find a class `Guard::RSpec`
|
394
429
|
#
|
395
430
|
# @param [String] name the name of the Guard
|
396
431
|
# @return [Class, nil] the loaded class
|
@@ -398,10 +433,10 @@ module Guard
|
|
398
433
|
def get_guard_class(name)
|
399
434
|
name = name.to_s
|
400
435
|
try_require = false
|
401
|
-
const_name = name.
|
436
|
+
const_name = name.gsub(/\/(.?)/) { "::#{ $1.upcase }" }.gsub(/(?:^|[_-])(.)/) { $1.upcase }
|
402
437
|
begin
|
403
438
|
require "guard/#{ name.downcase }" if try_require
|
404
|
-
self.const_get(self.constants.find { |c| c.to_s.downcase == const_name })
|
439
|
+
self.const_get(self.constants.find { |c| c.to_s == const_name } || self.constants.find { |c| c.to_s.downcase == const_name.downcase })
|
405
440
|
rescue TypeError
|
406
441
|
unless try_require
|
407
442
|
try_require = true
|
@@ -439,7 +474,7 @@ module Guard
|
|
439
474
|
Gem::Specification.find_all.select { |x| x.name =~ /^guard-/ }
|
440
475
|
else
|
441
476
|
Gem.source_index.find_name(/^guard-/)
|
442
|
-
end.map { |x| x.name.sub
|
477
|
+
end.map { |x| x.name.sub(/^guard-/, '') }
|
443
478
|
end
|
444
479
|
|
445
480
|
# Adds a command logger in debug mode. This wraps common command
|
data/lib/guard/cli.rb
CHANGED
@@ -25,11 +25,11 @@ module Guard
|
|
25
25
|
:aliases => '-n',
|
26
26
|
:banner => 'Notifications feature (growl/libnotify)'
|
27
27
|
|
28
|
-
method_option :
|
28
|
+
method_option :verbose,
|
29
29
|
:type => :boolean,
|
30
30
|
:default => false,
|
31
|
-
:aliases => '-
|
32
|
-
:banner => '
|
31
|
+
:aliases => '-v',
|
32
|
+
:banner => 'Show verbose messages'
|
33
33
|
|
34
34
|
method_option :group,
|
35
35
|
:type => :array,
|
@@ -47,6 +47,12 @@ module Guard
|
|
47
47
|
:aliases => '-G',
|
48
48
|
:banner => 'Specify a Guardfile'
|
49
49
|
|
50
|
+
method_option :no_vendor,
|
51
|
+
:type => :boolean,
|
52
|
+
:default => false,
|
53
|
+
:aliases => '-I',
|
54
|
+
:banner => 'Ignore vendored dependencies'
|
55
|
+
|
50
56
|
method_option :watch_all_modifications,
|
51
57
|
:type => :boolean,
|
52
58
|
:default => false,
|
data/lib/guard/dsl.rb
CHANGED
@@ -9,6 +9,11 @@ module Guard
|
|
9
9
|
# You can optionally group the Guards with the `group` keyword and ignore certain paths
|
10
10
|
# with the `ignore_paths` keyword.
|
11
11
|
#
|
12
|
+
# You can set your preferred system notification library with `notification` and pass
|
13
|
+
# some optional configuration options for the library. If you don't configure a library,
|
14
|
+
# Guard will automatically pick one with default options (if you don't want notifications,
|
15
|
+
# specify `:off` as library). @see ::Guard::Notifier for more information about the supported libraries.
|
16
|
+
#
|
12
17
|
# A more advanced DSL use is the `callback` keyword that allows you to execute arbitrary
|
13
18
|
# code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change`
|
14
19
|
# Guards' method. You can even insert more hooks inside these methods.
|
@@ -25,6 +30,8 @@ module Guard
|
|
25
30
|
#
|
26
31
|
# @example A sample of a complex Guardfile
|
27
32
|
#
|
33
|
+
# notification :growl
|
34
|
+
#
|
28
35
|
# group 'frontend' do
|
29
36
|
# guard 'passenger', :ping => true do
|
30
37
|
# watch('config/application.rb')
|
@@ -102,6 +109,7 @@ module Guard
|
|
102
109
|
def reevaluate_guardfile
|
103
110
|
::Guard.guards.clear
|
104
111
|
::Guard.reset_groups
|
112
|
+
::Guard::Notifier.notifications.clear
|
105
113
|
@@options.delete(:guardfile_contents)
|
106
114
|
Dsl.evaluate_guardfile(@@options)
|
107
115
|
msg = 'Guardfile has been re-evaluated.'
|
@@ -131,7 +139,7 @@ module Guard
|
|
131
139
|
|
132
140
|
# Read the current `Guardfile` content.
|
133
141
|
#
|
134
|
-
# @param [String] the path to the Guardfile
|
142
|
+
# @param [String] guardfile_path the path to the Guardfile
|
135
143
|
#
|
136
144
|
def read_guardfile(guardfile_path)
|
137
145
|
@@options[:guardfile_path] = guardfile_path
|
@@ -224,7 +232,7 @@ module Guard
|
|
224
232
|
# The path to the `Guardfile` that is located at
|
225
233
|
# the directory, where Guard has been started from.
|
226
234
|
#
|
227
|
-
# @
|
235
|
+
# @return [String] the path to the local Guardfile
|
228
236
|
#
|
229
237
|
def local_guardfile_path
|
230
238
|
File.join(Dir.pwd, 'Guardfile')
|
@@ -233,7 +241,7 @@ module Guard
|
|
233
241
|
# The path to the `.Guardfile` that is located at
|
234
242
|
# the users home directory.
|
235
243
|
#
|
236
|
-
# @
|
244
|
+
# @return [String] the path to ~/.Guardfile
|
237
245
|
#
|
238
246
|
def home_guardfile_path
|
239
247
|
File.expand_path(File.join('~', '.Guardfile'))
|
@@ -242,7 +250,7 @@ module Guard
|
|
242
250
|
# The path to the user configuration `.guard.rb`
|
243
251
|
# that is located at the users home directory.
|
244
252
|
#
|
245
|
-
# @
|
253
|
+
# @return [String] the path to ~/.guard.rb
|
246
254
|
#
|
247
255
|
def user_config_path
|
248
256
|
File.expand_path(File.join('~', '.guard.rb'))
|
@@ -250,6 +258,24 @@ module Guard
|
|
250
258
|
|
251
259
|
end
|
252
260
|
|
261
|
+
# Set notification options for the system notifications.
|
262
|
+
# You can set multiple notification, which allows you to show local
|
263
|
+
# system notifications and remote notifications with separate libraries.
|
264
|
+
# You can also pass `:off` as library to turn off notifications.
|
265
|
+
#
|
266
|
+
# @example Define multiple notifications
|
267
|
+
# notification :growl_notify
|
268
|
+
# notification :ruby_gntp, :host => '192.168.1.5'
|
269
|
+
#
|
270
|
+
# @see Guard::Notifier for available notifier and its options.
|
271
|
+
#
|
272
|
+
# @param [Symbol, String] notifier the name of the notifier to use
|
273
|
+
# @param [Hash] options the notification library options
|
274
|
+
#
|
275
|
+
def notification(notifier, options = {})
|
276
|
+
::Guard::Notifier.add_notification(notifier.to_sym, options, false)
|
277
|
+
end
|
278
|
+
|
253
279
|
# Declares a group of guards to be run with `guard start --group group_name`.
|
254
280
|
#
|
255
281
|
# @example Declare two groups of Guards
|
@@ -310,10 +336,11 @@ module Guard
|
|
310
336
|
def guard(name, options = {})
|
311
337
|
@watchers = []
|
312
338
|
@callbacks = []
|
339
|
+
@current_group ||= :default
|
313
340
|
|
314
341
|
yield if block_given?
|
315
342
|
|
316
|
-
options.update(:group =>
|
343
|
+
options.update(:group => @current_group)
|
317
344
|
::Guard.add_guard(name.to_s.downcase, @watchers, @callbacks, options)
|
318
345
|
end
|
319
346
|
|
data/lib/guard/dsl_describer.rb
CHANGED
@@ -47,7 +47,7 @@ module Guard
|
|
47
47
|
def list(options)
|
48
48
|
evaluate_guardfile(options)
|
49
49
|
|
50
|
-
|
50
|
+
installed_guards = guardfile_structure.inject([]) do |installed, group|
|
51
51
|
group[:guards].each { |guard| installed << guard[:name] } if group[:guards]
|
52
52
|
installed
|
53
53
|
end
|
@@ -55,7 +55,7 @@ module Guard
|
|
55
55
|
UI.info 'Available guards:'
|
56
56
|
|
57
57
|
::Guard.guard_gem_names.sort.uniq.each do |name|
|
58
|
-
UI.info " #{ name }#{
|
58
|
+
UI.info " #{ name }#{ installed_guards.include?(name) ? '*' : '' }"
|
59
59
|
end
|
60
60
|
|
61
61
|
UI.info ''
|
@@ -141,6 +141,7 @@ module Guard
|
|
141
141
|
# @see Guard::Dsl#guard
|
142
142
|
#
|
143
143
|
def guard(name, options = { })
|
144
|
+
@group ||= false
|
144
145
|
node = (@group ? @@guardfile_structure.last : @@guardfile_structure.first)
|
145
146
|
|
146
147
|
node[:guards] << { :name => name, :options => options }
|
data/lib/guard/guard.rb
CHANGED
@@ -42,8 +42,8 @@ module Guard
|
|
42
42
|
#
|
43
43
|
# @param [Array<Guard::Watcher>] watchers the Guard file watchers
|
44
44
|
# @param [Hash] options the custom Guard options
|
45
|
-
# @options [Symbol] group the group this Guard belongs to
|
46
|
-
# @options [Boolean] any_return allow any object to be returned from a watcher
|
45
|
+
# @option options [Symbol] group the group this Guard belongs to
|
46
|
+
# @option options [Boolean] any_return allow any object to be returned from a watcher
|
47
47
|
#
|
48
48
|
def initialize(watchers = [], options = {})
|
49
49
|
@group = options[:group] ? options.delete(:group).to_sym : :default
|
data/lib/guard/interactor.rb
CHANGED
@@ -1,52 +1,66 @@
|
|
1
|
+
require 'readline'
|
2
|
+
|
1
3
|
module Guard
|
2
4
|
|
3
5
|
# The interactor reads user input and triggers
|
4
6
|
# specific action upon them unless its locked.
|
5
7
|
#
|
8
|
+
# It used the readline library for history and
|
9
|
+
# completion support.
|
10
|
+
#
|
6
11
|
# Currently the following actions are implemented:
|
7
12
|
#
|
8
|
-
# -
|
9
|
-
# -
|
10
|
-
# -
|
11
|
-
# -
|
13
|
+
# - h, help => Show help
|
14
|
+
# - e, exit => Exit Guard
|
15
|
+
# - r, reload => Reload Guard
|
16
|
+
# - p, pause => Toggle file modification listener
|
17
|
+
# - n, notification => Toggle notifications
|
18
|
+
# - <enter> => Run all
|
12
19
|
#
|
13
20
|
# It's also possible to scope `reload` and `run all` actions to only a specified group or a guard.
|
14
21
|
#
|
15
|
-
# @example
|
16
|
-
#
|
17
|
-
#
|
22
|
+
# @example Reload backend group
|
23
|
+
# backend reload
|
24
|
+
#
|
25
|
+
# @example Reload rspec guard
|
26
|
+
# spork reload
|
27
|
+
#
|
28
|
+
# @example Run all jasmine specs
|
29
|
+
# jasmine
|
18
30
|
#
|
19
31
|
class Interactor
|
20
32
|
|
21
|
-
|
22
|
-
|
23
|
-
|
33
|
+
HELP_ENTRIES = %w[help h]
|
34
|
+
RELOAD_ENTRIES = %w[reload r]
|
35
|
+
STOP_ENTRIES = %w[exit e]
|
36
|
+
PAUSE_ENTRIES = %w[pause p]
|
37
|
+
NOTIFICATION_ENTRIES = %w[notification n]
|
24
38
|
|
25
|
-
|
39
|
+
COMPLETION_ACTIONS = %w[help reload exit pause notification]
|
40
|
+
|
41
|
+
# Initialize the interactor.
|
26
42
|
#
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
::Guard.pause
|
39
|
-
when :reload
|
40
|
-
::Guard::Dsl.reevaluate_guardfile if scopes.empty?
|
41
|
-
::Guard.reload(scopes)
|
42
|
-
when :run_all
|
43
|
-
::Guard.run_all(scopes)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
43
|
+
def initialize
|
44
|
+
unless defined?(RbReadline) || defined?(JRUBY_VERSION)
|
45
|
+
::Guard::UI.info 'Please add rb-readline for proper Readline support.'
|
46
|
+
end
|
47
|
+
|
48
|
+
Readline.completion_proc = proc { |word| auto_complete(word) }
|
49
|
+
|
50
|
+
begin
|
51
|
+
Readline.completion_append_character = ' '
|
52
|
+
rescue NotImplementedError
|
53
|
+
# Ignore, we just don't support it then
|
47
54
|
end
|
48
55
|
end
|
49
56
|
|
57
|
+
# Start the line reader in its own thread.
|
58
|
+
#
|
59
|
+
def start
|
60
|
+
return if ENV['GUARD_ENV'] == 'test'
|
61
|
+
@thread = Thread.new { read_line } if !@thread || !@thread.alive?
|
62
|
+
end
|
63
|
+
|
50
64
|
# Kill interactor thread if not current
|
51
65
|
#
|
52
66
|
def stop
|
@@ -54,18 +68,128 @@ module Guard
|
|
54
68
|
@thread.kill
|
55
69
|
end
|
56
70
|
end
|
57
|
-
|
58
|
-
#
|
71
|
+
|
72
|
+
# Read a line from stdin with Readline.
|
73
|
+
#
|
74
|
+
def read_line
|
75
|
+
while line = Readline.readline(prompt, true)
|
76
|
+
process_input(line)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Auto complete the given word.
|
81
|
+
#
|
82
|
+
# @param [String] word the partial word
|
83
|
+
# @return [Array<String>] the matching words
|
84
|
+
#
|
85
|
+
def auto_complete(word)
|
86
|
+
completion_list.grep(/^#{ Regexp.escape(word) }/)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Get the auto completion list.
|
90
|
+
#
|
91
|
+
# @return [Array<String>] the list of words
|
92
|
+
#
|
93
|
+
def completion_list
|
94
|
+
groups = ::Guard.groups.map { |group| group.name.to_s }
|
95
|
+
guards = ::Guard.guards.map { |guard| guard.class.to_s.downcase.sub('guard::', '') }
|
96
|
+
|
97
|
+
COMPLETION_ACTIONS + groups + guards - ['default']
|
98
|
+
end
|
99
|
+
|
100
|
+
# The current interactor prompt
|
101
|
+
#
|
102
|
+
# @return [String] the prompt to show
|
103
|
+
#
|
104
|
+
def prompt
|
105
|
+
::Guard.listener.paused? ? 'p> ' : '> '
|
106
|
+
end
|
107
|
+
|
108
|
+
# Process the input from readline.
|
109
|
+
#
|
110
|
+
# @param [String] line the input line
|
111
|
+
#
|
112
|
+
def process_input(line)
|
113
|
+
if line =~ /^\s*$/ or Readline::HISTORY.to_a[-2] == line
|
114
|
+
Readline::HISTORY.pop
|
115
|
+
end
|
116
|
+
|
117
|
+
scopes, action = extract_scopes_and_action(line)
|
118
|
+
|
119
|
+
case action
|
120
|
+
when :help
|
121
|
+
help
|
122
|
+
when :stop
|
123
|
+
::Guard.stop
|
124
|
+
when :pause
|
125
|
+
::Guard.pause
|
126
|
+
when :reload
|
127
|
+
reload(scopes)
|
128
|
+
when :run_all
|
129
|
+
::Guard.run_all(scopes)
|
130
|
+
when :notification
|
131
|
+
toggle_notification
|
132
|
+
else
|
133
|
+
::Guard::UI.error "Unknown command #{ line }"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Execute the reload action.
|
138
|
+
#
|
139
|
+
# @param [Hash] scopes the reload scopes
|
140
|
+
#
|
141
|
+
def reload(scopes)
|
142
|
+
::Guard::UI.info 'Reload'
|
143
|
+
::Guard::Dsl.reevaluate_guardfile if scopes.empty?
|
144
|
+
::Guard.reload(scopes)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Toggle the system notifications on/off
|
148
|
+
#
|
149
|
+
def toggle_notification
|
150
|
+
if ENV['GUARD_NOTIFY'] == 'true'
|
151
|
+
::Guard::UI.info 'Turn off notifications'
|
152
|
+
::Guard::Notifier.turn_off
|
153
|
+
else
|
154
|
+
::Guard::Notifier.turn_on
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Show the help.
|
159
|
+
#
|
160
|
+
def help
|
161
|
+
puts ''
|
162
|
+
puts 'e, exit Exit Guard'
|
163
|
+
puts 'p, pause Toggle file modification listener'
|
164
|
+
puts 'r, reload Reload Guard'
|
165
|
+
puts 'n, notification Toggle notifications'
|
166
|
+
puts '<enter> Run all Guards'
|
167
|
+
puts ''
|
168
|
+
puts 'You can scope the reload action to a specific guard or group:'
|
169
|
+
puts ''
|
170
|
+
puts 'rspec reload Reload the RSpec Guard'
|
171
|
+
puts 'backend reload Reload the backend group'
|
172
|
+
puts ''
|
173
|
+
puts 'You can also run only a specific Guard or all Guards in a specific group:'
|
174
|
+
puts ''
|
175
|
+
puts 'jasmine Run the jasmine Guard'
|
176
|
+
puts 'frontend Run all Guards in the frontend group'
|
177
|
+
puts ''
|
178
|
+
end
|
179
|
+
|
180
|
+
# Extract the Guard or group scope and action from the
|
181
|
+
# input line.
|
59
182
|
#
|
60
183
|
# @example `spork reload` will only reload rspec
|
61
184
|
# @example `jasmine` will only run all jasmine specs
|
62
185
|
#
|
63
|
-
# @param [String]
|
64
|
-
# @return [Array]
|
186
|
+
# @param [String] line the readline input
|
187
|
+
# @return [Array] the group or guard scope and the action
|
65
188
|
#
|
66
|
-
def extract_scopes_and_action(
|
67
|
-
scopes = {}
|
68
|
-
entries =
|
189
|
+
def extract_scopes_and_action(line)
|
190
|
+
scopes = { }
|
191
|
+
entries = line.split(' ')
|
192
|
+
|
69
193
|
case entries.length
|
70
194
|
when 1
|
71
195
|
unless action = action_from_entry(entries[0])
|
@@ -75,18 +199,21 @@ module Guard
|
|
75
199
|
scopes = scopes_from_entry(entries[0])
|
76
200
|
action = action_from_entry(entries[1])
|
77
201
|
end
|
78
|
-
|
202
|
+
|
203
|
+
action = :run_all if !action && (!scopes.empty? || entries.empty?)
|
79
204
|
|
80
205
|
[scopes, action]
|
81
206
|
end
|
82
207
|
|
208
|
+
private
|
209
|
+
|
83
210
|
# Extract guard or group scope from entry if valid
|
84
211
|
#
|
85
|
-
# @param [String]
|
86
|
-
# @return [Hash]
|
212
|
+
# @param [String] entry the possible scope entry
|
213
|
+
# @return [Hash] a hash with a Guard or a group scope
|
87
214
|
#
|
88
215
|
def scopes_from_entry(entry)
|
89
|
-
scopes = {}
|
216
|
+
scopes = { }
|
90
217
|
if guard = ::Guard.guards(entry)
|
91
218
|
scopes[:guard] = guard
|
92
219
|
end
|
@@ -97,18 +224,22 @@ module Guard
|
|
97
224
|
scopes
|
98
225
|
end
|
99
226
|
|
100
|
-
#
|
227
|
+
# Find the action for the given input entry.
|
101
228
|
#
|
102
|
-
# @param [String]
|
103
|
-
# @return [Symbol]
|
229
|
+
# @param [String] entry the possible action entry
|
230
|
+
# @return [Symbol] a Guard action
|
104
231
|
#
|
105
232
|
def action_from_entry(entry)
|
106
|
-
if
|
233
|
+
if STOP_ENTRIES.include?(entry)
|
107
234
|
:stop
|
108
|
-
elsif
|
235
|
+
elsif RELOAD_ENTRIES.include?(entry)
|
109
236
|
:reload
|
110
|
-
elsif
|
237
|
+
elsif PAUSE_ENTRIES.include?(entry)
|
111
238
|
:pause
|
239
|
+
elsif HELP_ENTRIES.include?(entry)
|
240
|
+
:help
|
241
|
+
elsif NOTIFICATION_ENTRIES.include?(entry)
|
242
|
+
:notification
|
112
243
|
end
|
113
244
|
end
|
114
245
|
|