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/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
- turn the Pry interactor off. All other usages are deprecated.
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
- def interactor(interactor)
331
- if interactor == :off
332
- ::Guard.options[:no_interactions] = true
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? && (@groups.empty? || @groups.map(&:to_sym).include?(name))
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 patterns paths globally.
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
- # Filter certain patterns paths globally.
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
- # ignore /\.txt$/, /.*\.zip/
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
@@ -37,7 +37,7 @@ module Guard
37
37
  # @return [String] the group name
38
38
  #
39
39
  def to_s
40
- "#{@name} group"
40
+ @name.to_s.capitalize
41
41
  end
42
42
 
43
43
  end
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 = File.read("#{ ::Guard.locate_guard(name) }/lib/guard/#{ name }/templates/Guardfile")
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
@@ -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))
@@ -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
- GUARD_RC = '~/.guardrc'
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 = false
88
+ Pry.config.should_load_rc = false
31
89
  Pry.config.should_load_local_rc = false
32
- Pry.config.history.file = 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? File.expand_path GUARD_RC
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`, `show`, `notification`, `pause`, `exit` and `quit`,
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
- %w(help reload change show notification pause exit quit).each do |command|
68
- Pry.commands.alias_command command[0].chr, 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
- "[#{ pry.input_array.size }] #{ ::Guard.listener.paused? ? 'pause' : 'guard' }(#{ Pry.view_clip(target_self) })#{":#{ nest_level }" unless nest_level.zero? }> "
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
- "[#{ pry.input_array.size }] #{ ::Guard.listener.paused? ? 'pause' : 'guard' }(#{ Pry.view_clip(target_self) })#{":#{ nest_level }" unless nest_level.zero? }* "
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>/dev/null`.chomp
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>/dev/null") if @stty_save
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 guard = ::Guard.guards(entry)
191
- scopes[:guard] ||= guard
272
+ if plugin = ::Guard.guards(entry)
273
+ scopes[:plugins] << plugin
192
274
  elsif group = ::Guard.groups(entry)
193
- scopes[:group] ||= group
275
+ scopes[:groups] << group
194
276
  else
195
277
  unknown << entry
196
278
  end
@@ -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 => 'emacsclient',
16
- :success => 'ForestGreen',
17
- :failed => 'Firebrick',
18
- :default => 'Black',
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> /dev/null || echo 'N/A'`
28
+ result = `#{ DEFAULTS[:client] } --eval '1' 2> #{DEV_NULL} || echo 'N/A'`
28
29
 
29
- if result.chomp! == 'N/A'
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 = DEFAULTS.merge options
52
- color = emacs_color type, options
53
- system(%(#{ options[:client] } --eval "(set-face-background 'modeline \\"#{ color }\\")"))
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.