guard 1.5.4 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +44 -2
- data/README.md +55 -2
- data/lib/guard.rb +84 -12
- data/lib/guard/cli.rb +12 -3
- data/lib/guard/commands/scope.rb +32 -0
- data/lib/guard/dsl.rb +64 -12
- data/lib/guard/group.rb +1 -1
- data/lib/guard/guard.rb +14 -4
- data/lib/guard/guardfile.rb +21 -0
- data/lib/guard/interactor.rb +99 -17
- data/lib/guard/notifier.rb +7 -0
- data/lib/guard/notifiers/emacs.rb +11 -9
- data/lib/guard/notifiers/gntp.rb +1 -1
- data/lib/guard/notifiers/tmux.rb +60 -4
- data/lib/guard/runner.rb +48 -6
- data/lib/guard/ui.rb +16 -8
- data/lib/guard/version.rb +1 -1
- data/man/guard.1 +5 -2
- data/man/guard.1.html +8 -4
- metadata +10 -9
data/lib/guard/dsl.rb
CHANGED
@@ -102,10 +102,7 @@ module Guard
|
|
102
102
|
# Deprecation message for the `ignore_paths` method
|
103
103
|
INTERACTOR_DEPRECATION = <<-EOS.gsub(/^\s*/, '')
|
104
104
|
Starting with Guard v1.4 the use of the 'interactor' Guardfile DSL method is only used to
|
105
|
-
|
106
|
-
|
107
|
-
Please make use of the Pry plugin architecture to customize the interactions and place them
|
108
|
-
either in your `~/.guardrc` or the `Guardfile`.
|
105
|
+
disable or pass options to the Pry interactor. All other usages are deprecated.
|
109
106
|
EOS
|
110
107
|
|
111
108
|
class << self
|
@@ -327,9 +324,15 @@ module Guard
|
|
327
324
|
# @example Turn off interactions
|
328
325
|
# interactor :off
|
329
326
|
#
|
330
|
-
|
331
|
-
|
332
|
-
|
327
|
+
# @param [Symbol,Hash] options either `:off` or a Hash with interactor options
|
328
|
+
#
|
329
|
+
def interactor(options)
|
330
|
+
if options == :off
|
331
|
+
::Guard::Interactor.enabled = false
|
332
|
+
|
333
|
+
elsif options.is_a?(Hash)
|
334
|
+
::Guard::Interactor.options = options
|
335
|
+
|
333
336
|
else
|
334
337
|
::Guard::UI.deprecation(INTERACTOR_DEPRECATION)
|
335
338
|
end
|
@@ -358,10 +361,9 @@ module Guard
|
|
358
361
|
# @see Guard::DslDescriber
|
359
362
|
#
|
360
363
|
def group(name, options = {})
|
361
|
-
@groups = @@options[:group] || []
|
362
364
|
name = name.to_sym
|
363
365
|
|
364
|
-
if block_given?
|
366
|
+
if block_given?
|
365
367
|
::Guard.add_group(name.to_s.downcase, options)
|
366
368
|
@current_group = name
|
367
369
|
|
@@ -450,7 +452,7 @@ module Guard
|
|
450
452
|
::Guard::UI.deprecation(IGNORE_PATHS_DEPRECATION)
|
451
453
|
end
|
452
454
|
|
453
|
-
# Ignore certain
|
455
|
+
# Ignore certain paths globally.
|
454
456
|
#
|
455
457
|
# @example Ignore some paths
|
456
458
|
# ignore %r{^ignored/path/}, /man/
|
@@ -461,10 +463,21 @@ module Guard
|
|
461
463
|
::Guard.listener = ::Guard.listener.ignore(*regexps)
|
462
464
|
end
|
463
465
|
|
464
|
-
#
|
466
|
+
# Replace ignored paths globally
|
467
|
+
#
|
468
|
+
# @example Ignore only these paths
|
469
|
+
# ignore! %r{^ignored/path/}, /man/
|
470
|
+
#
|
471
|
+
# @param [Regexp] regexps a pattern for ignoring paths
|
472
|
+
#
|
473
|
+
def ignore!(*regexps)
|
474
|
+
::Guard.listener = ::Guard.listener.ignore!(*regexps)
|
475
|
+
end
|
476
|
+
|
477
|
+
# Filter certain paths globally.
|
465
478
|
#
|
466
479
|
# @example Filter some files
|
467
|
-
#
|
480
|
+
# filter /\.txt$/, /.*\.zip/
|
468
481
|
#
|
469
482
|
# @param [Regexp] regexps a pattern for filtering paths
|
470
483
|
#
|
@@ -472,6 +485,17 @@ module Guard
|
|
472
485
|
::Guard.listener = ::Guard.listener.filter(*regexps)
|
473
486
|
end
|
474
487
|
|
488
|
+
# Replace filtered paths globally.
|
489
|
+
#
|
490
|
+
# @example Filter only these files
|
491
|
+
# filter! /\.txt$/, /.*\.zip/
|
492
|
+
#
|
493
|
+
# @param [Regexp] regexps a pattern for filtering paths
|
494
|
+
#
|
495
|
+
def filter!(*regexps)
|
496
|
+
::Guard.listener = ::Guard.listener.filter!(*regexps)
|
497
|
+
end
|
498
|
+
|
475
499
|
# Configure the Guard logger.
|
476
500
|
#
|
477
501
|
# * Log level must be either `:debug`, `:info`, `:warn` or `:error`.
|
@@ -529,5 +553,33 @@ module Guard
|
|
529
553
|
::Guard::UI.options = ::Guard::UI.options.merge options
|
530
554
|
end
|
531
555
|
|
556
|
+
# Sets the default scope on startup
|
557
|
+
#
|
558
|
+
# @example Scope Guard to a single group
|
559
|
+
# scope :group => :frontend
|
560
|
+
#
|
561
|
+
# @example Scope Guard to multiple groups
|
562
|
+
# scope :groups => [:specs, :docs]
|
563
|
+
#
|
564
|
+
# @example Scope Guard to a single plugin
|
565
|
+
# scope :plugin => :test
|
566
|
+
#
|
567
|
+
# @example Scope Guard to multiple plugins
|
568
|
+
# scope :plugins => [:jasmine, :rspec]
|
569
|
+
#
|
570
|
+
# @param [Hash] scopes the scope for the groups and plugins
|
571
|
+
#
|
572
|
+
def scope(scopes = {})
|
573
|
+
if ::Guard.options[:plugin].empty?
|
574
|
+
::Guard.options[:plugin] = [scopes[:plugin]] if scopes[:plugin]
|
575
|
+
::Guard.options[:plugin] = scopes[:plugins] if scopes[:plugins]
|
576
|
+
end
|
577
|
+
|
578
|
+
if ::Guard.options[:group].empty?
|
579
|
+
::Guard.options[:group] = [scopes[:group]] if scopes[:group]
|
580
|
+
::Guard.options[:group] = scopes[:groups] if scopes[:groups]
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
532
584
|
end
|
533
585
|
end
|
data/lib/guard/group.rb
CHANGED
data/lib/guard/guard.rb
CHANGED
@@ -58,6 +58,15 @@ module Guard
|
|
58
58
|
@watchers, @options = watchers, options
|
59
59
|
end
|
60
60
|
|
61
|
+
# Specify the source for the Guardfile template.
|
62
|
+
# Each Guard plugin can redefine this method to add its own logic.
|
63
|
+
#
|
64
|
+
# @param [String] The plugin name
|
65
|
+
#
|
66
|
+
def self.template(name)
|
67
|
+
File.read("#{ ::Guard.locate_guard(name) }/lib/guard/#{ name }/templates/Guardfile")
|
68
|
+
end
|
69
|
+
|
61
70
|
# Initialize the Guard plugin. This will copy the Guardfile template inside the Guard plugin gem.
|
62
71
|
# The template Guardfile must be located within the Gem at `lib/guard/guard-name/templates/Guardfile`.
|
63
72
|
#
|
@@ -68,7 +77,7 @@ module Guard
|
|
68
77
|
::Guard::UI.info "Guardfile already includes #{ name } guard"
|
69
78
|
else
|
70
79
|
content = File.read('Guardfile')
|
71
|
-
guard
|
80
|
+
guard = template(name)
|
72
81
|
|
73
82
|
File.open('Guardfile', 'wb') do |f|
|
74
83
|
f.puts(content)
|
@@ -143,12 +152,13 @@ module Guard
|
|
143
152
|
# @!method run_on_removals(paths)
|
144
153
|
|
145
154
|
# Convert plugin to string representation. The
|
146
|
-
# default just uses the plugin class name
|
147
|
-
#
|
155
|
+
# default just uses the plugin class name and
|
156
|
+
# removes the Guard module name.
|
157
|
+
#
|
148
158
|
# @return [String] the string representation
|
149
159
|
#
|
150
160
|
def to_s
|
151
|
-
self.class.to_s
|
161
|
+
self.class.to_s.downcase.sub('guard::', '').capitalize
|
152
162
|
end
|
153
163
|
|
154
164
|
end
|
data/lib/guard/guardfile.rb
CHANGED
@@ -30,6 +30,24 @@ module Guard
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
# Opens an existing guardfile and searches for redundant definitions
|
34
|
+
# if extraneous defintions are found, it warns the user
|
35
|
+
#
|
36
|
+
# @see Guard::CLI.init
|
37
|
+
#
|
38
|
+
# @param [String] class name of gem definition that you would like to search for in the Guardfile
|
39
|
+
# @param [String] contents of existing guardfile
|
40
|
+
#
|
41
|
+
def duplicate_definitions?(guard_class, guard_file)
|
42
|
+
matches = guard_file.to_s.scan(/guard\s[\'|\"]#{guard_class}[\'|\"]\sdo/)
|
43
|
+
if matches.count > 1
|
44
|
+
::Guard::UI.info "There are #{matches.count.to_s} definitions in your Guardfile for '#{guard_class}', you may want to clean up your Guardfile as this could cause issues."
|
45
|
+
return true
|
46
|
+
else
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
33
51
|
# Adds the Guardfile template of a Guard implementation
|
34
52
|
# to an existing Guardfile.
|
35
53
|
#
|
@@ -42,6 +60,9 @@ module Guard
|
|
42
60
|
|
43
61
|
if guard_class
|
44
62
|
guard_class.init(guard_name)
|
63
|
+
guardfile_name = 'Guardfile'
|
64
|
+
guard_file = File.read(guardfile_name) if File.exists?(guardfile_name)
|
65
|
+
duplicate_definitions?(guard_name, guard_file)
|
45
66
|
elsif File.exist?(File.join(HOME_TEMPLATES, guard_name))
|
46
67
|
content = File.read('Guardfile')
|
47
68
|
template = File.read(File.join(HOME_TEMPLATES, guard_name))
|
data/lib/guard/interactor.rb
CHANGED
@@ -15,11 +15,69 @@ module Guard
|
|
15
15
|
require 'guard/commands/notification'
|
16
16
|
require 'guard/commands/pause'
|
17
17
|
require 'guard/commands/reload'
|
18
|
+
require 'guard/commands/scope'
|
18
19
|
require 'guard/commands/show'
|
19
20
|
|
20
|
-
|
21
|
+
# The default Ruby script to configure Guard Pry if the option `:guard_rc` is not defined.
|
22
|
+
GUARD_RC = '~/.guardrc'
|
23
|
+
|
24
|
+
# The default Guard Pry history file if the option `:history_file` is not defined.
|
21
25
|
HISTORY_FILE = '~/.guard_history'
|
22
26
|
|
27
|
+
# List of shortcuts for each interactor command
|
28
|
+
SHORTCUTS = {
|
29
|
+
:help => 'h',
|
30
|
+
:all => 'a',
|
31
|
+
:reload => 'r',
|
32
|
+
:change => 'c',
|
33
|
+
:show => 's',
|
34
|
+
:scope => 'o',
|
35
|
+
:notification => 'n',
|
36
|
+
:pause => 'p',
|
37
|
+
:exit => 'e',
|
38
|
+
:quit => 'q'
|
39
|
+
}
|
40
|
+
|
41
|
+
attr_accessor :thread
|
42
|
+
|
43
|
+
class << self
|
44
|
+
|
45
|
+
# Get the interactor options
|
46
|
+
#
|
47
|
+
# @return [Hash] the options
|
48
|
+
#
|
49
|
+
def options
|
50
|
+
@options ||= { }
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set the interactor options
|
54
|
+
#
|
55
|
+
# @param [Hash] options the interactor options
|
56
|
+
# @option options [String] :guard_rc the Ruby script to configure Guard Pry
|
57
|
+
# @option options [String] :history_file the file to write the Pry history to
|
58
|
+
#
|
59
|
+
def options=(options)
|
60
|
+
@options = options
|
61
|
+
end
|
62
|
+
|
63
|
+
# Is the interactor enabled?
|
64
|
+
#
|
65
|
+
# @return [Boolean] true if enabled
|
66
|
+
#
|
67
|
+
def enabled
|
68
|
+
@enabled.nil? ? true : @enabled
|
69
|
+
end
|
70
|
+
|
71
|
+
# Set the enabled status for the interactor
|
72
|
+
#
|
73
|
+
# @param [Boolean] status true if enabled
|
74
|
+
#
|
75
|
+
def enabled=(status)
|
76
|
+
@enabled = status
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
23
81
|
# Initialize the interactor. This configures
|
24
82
|
# Pry and creates some custom commands and aliases
|
25
83
|
# for Guard.
|
@@ -27,9 +85,9 @@ module Guard
|
|
27
85
|
def initialize
|
28
86
|
return if ENV['GUARD_ENV'] == 'test'
|
29
87
|
|
30
|
-
Pry.config.should_load_rc
|
88
|
+
Pry.config.should_load_rc = false
|
31
89
|
Pry.config.should_load_local_rc = false
|
32
|
-
Pry.config.history.file
|
90
|
+
Pry.config.history.file = self.class.options[:history_file] || HISTORY_FILE
|
33
91
|
|
34
92
|
load_guard_rc
|
35
93
|
|
@@ -45,7 +103,7 @@ module Guard
|
|
45
103
|
#
|
46
104
|
def load_guard_rc
|
47
105
|
Pry.config.hooks.add_hook :when_started, :load_guard_rc do
|
48
|
-
load GUARD_RC if File.exist?
|
106
|
+
load GUARD_RC if File.exist?(File.expand_path(self.class.options[:guard_rc] || GUARD_RC))
|
49
107
|
end
|
50
108
|
end
|
51
109
|
|
@@ -60,12 +118,12 @@ module Guard
|
|
60
118
|
end
|
61
119
|
|
62
120
|
# Creates command aliases for the commands
|
63
|
-
# `help`, `reload`, `change`, `
|
121
|
+
# `help`, `reload`, `change`, `scope`, `notification`, `pause`, `exit` and `quit`,
|
64
122
|
# which will be the first letter of the command.
|
65
123
|
#
|
66
124
|
def create_command_aliases
|
67
|
-
|
68
|
-
Pry.commands.alias_command
|
125
|
+
SHORTCUTS.each do |command, shortcut|
|
126
|
+
Pry.commands.alias_command shortcut, command.to_s
|
69
127
|
end
|
70
128
|
end
|
71
129
|
|
@@ -114,15 +172,40 @@ module Guard
|
|
114
172
|
def configure_prompt
|
115
173
|
Pry.config.prompt = [
|
116
174
|
proc do |target_self, nest_level, pry|
|
117
|
-
|
175
|
+
history = pry.input_array.size
|
176
|
+
process = ::Guard.listener.paused? ? 'pause' : 'guard'
|
177
|
+
clip = Pry.view_clip(target_self)
|
178
|
+
level = ":#{ nest_level }" unless nest_level.zero?
|
179
|
+
scope = if !::Guard.scope[:plugins].empty?
|
180
|
+
"{#{ ::Guard.scope[:plugins].join }} "
|
181
|
+
elsif !::Guard.scope[:groups].empty?
|
182
|
+
"{#{ ::Guard.scope[:groups].join }} "
|
183
|
+
else
|
184
|
+
''
|
185
|
+
end
|
186
|
+
|
187
|
+
"[#{ history }] #{ scope }#{ process }(#{ clip })#{ level }> "
|
118
188
|
end,
|
119
189
|
proc do |target_self, nest_level, pry|
|
120
|
-
|
190
|
+
history = pry.input_array.size
|
191
|
+
process = ::Guard.listener.paused? ? 'pause' : 'guard'
|
192
|
+
clip = Pry.view_clip(target_self)
|
193
|
+
level = ":#{ nest_level }" unless nest_level.zero?
|
194
|
+
scope = if !::Guard.scope[:plugins].empty?
|
195
|
+
"{#{ ::Guard.scope[:plugins].join }} "
|
196
|
+
elsif !::Guard.scope[:groups].empty?
|
197
|
+
"{#{ ::Guard.scope[:groups].join }} "
|
198
|
+
else
|
199
|
+
''
|
200
|
+
end
|
201
|
+
|
202
|
+
"[#{ history }] #{ scope }#{ process }(#{ clip })#{ level }* "
|
121
203
|
end
|
122
204
|
]
|
123
205
|
end
|
124
206
|
|
125
|
-
# Start the line reader in its own thread
|
207
|
+
# Start the line reader in its own thread and
|
208
|
+
# stop Guard on Ctrl-D.
|
126
209
|
#
|
127
210
|
def start
|
128
211
|
return if ENV['GUARD_ENV'] == 'test'
|
@@ -135,7 +218,6 @@ module Guard
|
|
135
218
|
@thread = Thread.new do
|
136
219
|
Pry.start
|
137
220
|
::Guard.stop
|
138
|
-
exit
|
139
221
|
end
|
140
222
|
end
|
141
223
|
end
|
@@ -167,13 +249,13 @@ module Guard
|
|
167
249
|
# when stopping.
|
168
250
|
#
|
169
251
|
def store_terminal_settings
|
170
|
-
@stty_save = `stty -g 2
|
252
|
+
@stty_save = `stty -g 2>#{ DEV_NULL }`.chomp
|
171
253
|
end
|
172
254
|
|
173
255
|
# Restore terminal settings
|
174
256
|
#
|
175
257
|
def restore_terminal_settings
|
176
|
-
system("stty #{ @stty_save } 2
|
258
|
+
system("stty #{ @stty_save } 2>#{ DEV_NULL }") if @stty_save
|
177
259
|
end
|
178
260
|
|
179
261
|
# Converts and validates a plain text scope
|
@@ -183,14 +265,14 @@ module Guard
|
|
183
265
|
# @return [Hash, Array<String>] the plugin or group scope, the unknown entries
|
184
266
|
#
|
185
267
|
def self.convert_scope(entries)
|
186
|
-
scopes = { }
|
268
|
+
scopes = { :plugins => [], :groups => [] }
|
187
269
|
unknown = []
|
188
270
|
|
189
271
|
entries.each do |entry|
|
190
|
-
if
|
191
|
-
scopes[:
|
272
|
+
if plugin = ::Guard.guards(entry)
|
273
|
+
scopes[:plugins] << plugin
|
192
274
|
elsif group = ::Guard.groups(entry)
|
193
|
-
scopes[:
|
275
|
+
scopes[:groups] << group
|
194
276
|
else
|
195
277
|
unknown << entry
|
196
278
|
end
|
data/lib/guard/notifier.rb
CHANGED
@@ -103,6 +103,8 @@ module Guard
|
|
103
103
|
else
|
104
104
|
notifications.each do |notification|
|
105
105
|
::Guard::UI.info "Guard uses #{ get_notifier_module(notification[:name]).to_s.split('::').last } to send notifications."
|
106
|
+
notifier = get_notifier_module(notification[:name])
|
107
|
+
notifier.turn_on(notification[:options]) if notifier.respond_to?(:turn_on)
|
106
108
|
end
|
107
109
|
|
108
110
|
ENV['GUARD_NOTIFY'] = 'true'
|
@@ -112,6 +114,11 @@ module Guard
|
|
112
114
|
# Turn notifications off.
|
113
115
|
#
|
114
116
|
def turn_off
|
117
|
+
notifications.each do |notification|
|
118
|
+
notifier = get_notifier_module(notification[:name])
|
119
|
+
notifier.turn_off(notification[:options]) if notifier.respond_to?(:turn_off)
|
120
|
+
end
|
121
|
+
|
115
122
|
ENV['GUARD_NOTIFY'] = 'false'
|
116
123
|
end
|
117
124
|
|
@@ -12,10 +12,11 @@ module Guard
|
|
12
12
|
extend self
|
13
13
|
|
14
14
|
DEFAULTS = {
|
15
|
-
:client
|
16
|
-
:success
|
17
|
-
:failed
|
18
|
-
:default
|
15
|
+
:client => 'emacsclient',
|
16
|
+
:success => 'ForestGreen',
|
17
|
+
:failed => 'Firebrick',
|
18
|
+
:default => 'Black',
|
19
|
+
:fontcolor => 'White',
|
19
20
|
}
|
20
21
|
|
21
22
|
# Test if Emacs with running server is available.
|
@@ -24,9 +25,9 @@ module Guard
|
|
24
25
|
# @return [Boolean] the availability status
|
25
26
|
#
|
26
27
|
def available?(silent = false)
|
27
|
-
result = `#{ DEFAULTS[:client] } --eval '1' 2>
|
28
|
+
result = `#{ DEFAULTS[:client] } --eval '1' 2> #{DEV_NULL} || echo 'N/A'`
|
28
29
|
|
29
|
-
if
|
30
|
+
if %w(N/A 'N/A').include?(result.chomp!)
|
30
31
|
false
|
31
32
|
else
|
32
33
|
true
|
@@ -48,9 +49,10 @@ module Guard
|
|
48
49
|
# @option options [String, Integer] priority specify an int or named key (default is 0)
|
49
50
|
#
|
50
51
|
def notify(type, title, message, image, options = { })
|
51
|
-
options
|
52
|
-
color
|
53
|
-
|
52
|
+
options = DEFAULTS.merge options
|
53
|
+
color = emacs_color type, options
|
54
|
+
fontcolor = emacs_color :fontcolor, options
|
55
|
+
system(%(#{ options[:client] } --eval "(set-face-attribute 'mode-line nil :background \\"#{ color }\\" :foreground \\"#{ fontcolor }\\")"))
|
54
56
|
end
|
55
57
|
|
56
58
|
# Get the Emacs color for the notification type.
|