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/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.