guard 1.5.4 → 1.6.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 +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.
|