guard 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ module Guard
2
+ class Interactor
3
+
4
+ RELOAD = Pry::CommandSet.new do
5
+ create_command 'reload' do
6
+
7
+ group 'Guard'
8
+ description 'Reload all plugins.'
9
+
10
+ banner <<-BANNER
11
+ Usage: reload <scope>
12
+
13
+ Run the Guard plugin `reload` action.
14
+
15
+ You may want to specify an optional scope to the action,
16
+ either the name of a Guard plugin or a plugin group.
17
+ BANNER
18
+
19
+ def process(*entries)
20
+ scopes, rest = ::Guard::Interactor.convert_scope(entries)
21
+
22
+ if rest.length == 0
23
+ ::Guard.reload scopes
24
+ else
25
+ output.puts "Unkown scope #{ rest.join(', ') }"
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+
35
+ Pry.commands.import ::Guard::Interactor::RELOAD
@@ -0,0 +1,27 @@
1
+ require 'guard/dsl_describer'
2
+
3
+ module Guard
4
+ class Interactor
5
+
6
+ SHOW = Pry::CommandSet.new do
7
+ create_command 'show' do
8
+
9
+ group 'Guard'
10
+ description 'Show all Guard plugins.'
11
+
12
+ banner <<-BANNER
13
+ Usage: show <scope>
14
+
15
+ Show all defined Guard plugins and their options.
16
+ BANNER
17
+
18
+ def process
19
+ ::Guard::DslDescriber.show(::Guard.options)
20
+ end
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+
27
+ Pry.commands.import ::Guard::Interactor::SHOW
data/lib/guard/dsl.rb CHANGED
@@ -16,7 +16,7 @@ module Guard
16
16
  #
17
17
  # A more advanced DSL use is the `callback` keyword that allows you to execute arbitrary
18
18
  # code before or after any of the `start`, `stop`, `reload`, `run_all`, `run_on_changes`,
19
- # `run_on_additions`, `run_on_modifications` and `run_on_removals` Guard plugins method.
19
+ # `run_on_additions`, `run_on_modifications` and `run_on_removals` Guard plugins method.
20
20
  # You can even insert more hooks inside these methods.
21
21
  # Please [checkout the Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for more details.
22
22
  #
@@ -93,12 +93,21 @@ module Guard
93
93
 
94
94
  # Deprecation message for the `ignore_paths` method
95
95
  IGNORE_PATHS_DEPRECATION = <<-EOS.gsub(/^\s*/, '')
96
- Starting with Guard v1.1 the use of the 'ignore_paths' Guardfile dsl method is deprecated.
96
+ Starting with Guard v1.1 the use of the 'ignore_paths' Guardfile DSL method is deprecated.
97
97
 
98
98
  Please replace that method with the better 'ignore' or/and 'filter' methods.
99
99
  Documentation on the README: https://github.com/guard/guard#guardfile-dsl-ignore
100
100
  EOS
101
101
 
102
+ # Deprecation message for the `ignore_paths` method
103
+ INTERACTOR_DEPRECATION = <<-EOS.gsub(/^\s*/, '')
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`.
109
+ EOS
110
+
102
111
  class << self
103
112
 
104
113
  @@options = nil
@@ -315,17 +324,15 @@ module Guard
315
324
 
316
325
  # Sets the interactor to use.
317
326
  #
318
- # @example Use the readline interactor
319
- # interactor :readline
320
- #
321
- # @example Use the gets interactor
322
- # interactor :gets
323
- #
324
327
  # @example Turn off interactions
325
328
  # interactor :off
326
329
  #
327
330
  def interactor(interactor)
328
- ::Guard::Interactor.interactor = interactor.to_sym
331
+ if interactor == :off
332
+ ::Guard.options[:no_interactions] = true
333
+ else
334
+ ::Guard::UI.deprecation(INTERACTOR_DEPRECATION)
335
+ end
329
336
  end
330
337
 
331
338
  # Declares a group of Guard plugins to be run with `guard start --group group_name`.
@@ -465,5 +472,62 @@ module Guard
465
472
  ::Guard.listener = ::Guard.listener.filter(*regexps)
466
473
  end
467
474
 
475
+ # Configure the Guard logger.
476
+ #
477
+ # * Log level must be either `:debug`, `:info`, `:warn` or `:error`.
478
+ # * Template supports the following placeholders: `:time`, `:severity`, `:progname`, `:pid`, `:unit_of_work_id` and `:message`
479
+ # * Time format directives are the same as Time#strftime or :milliseconds
480
+ # * The `:only` and `:except` options must be a RegExp.
481
+ #
482
+ # @example Set the log level
483
+ # logger :level => :warn
484
+ #
485
+ # @example Set a custom log template
486
+ # logger :template => '[Guard - :severity - :progname - :time] :message'
487
+ #
488
+ # @example Set a custom time format
489
+ # logger :time_format => '%h'
490
+ #
491
+ # @example Limit logging to a Guard plugin
492
+ # logger :only => :jasmine
493
+ #
494
+ # @example Log all but not the messages from a specific Guard plugin
495
+ # logger :except => :jasmine
496
+ #
497
+ # @param [Hash] options the log options
498
+ # @option options [String, Symbol] level the log level
499
+ # @option options [String] template the logger template
500
+ # @option options [String, Symbol] time_format the time format
501
+ # @option options [RegExp] only show only messages from the matching Guard plugin
502
+ # @option options [RegExp] except does not show messages from the matching Guard plugin
503
+ #
504
+ def logger(options)
505
+ if options[:level]
506
+ options[:level] = options[:level].to_sym
507
+
508
+ unless [:debug, :info, :warn, :error].include? options[:level]
509
+ ::Guard::UI.warning "Invalid log level `#{ options[:level] }` ignored. Please use either :debug, :info, :warn or :error."
510
+ options.delete :level
511
+ end
512
+ end
513
+
514
+ if options[:only] && options[:except]
515
+ ::Guard::UI.warning "You cannot specify the logger options :only and :except at the same time."
516
+
517
+ options.delete :only
518
+ options.delete :except
519
+ end
520
+
521
+ # Convert the :only and :except options to a regular expression
522
+ [:only, :except].each do |name|
523
+ if options[name]
524
+ list = [].push(options[name]).flatten.map { |plugin| Regexp.escape(plugin.to_s) }.join('|')
525
+ options[name] = Regexp.new(list, Regexp::IGNORECASE)
526
+ end
527
+ end
528
+
529
+ ::Guard::UI.options = ::Guard::UI.options.merge options
530
+ end
531
+
468
532
  end
469
533
  end
@@ -1,110 +1,123 @@
1
1
  module Guard
2
2
 
3
- # The interactor triggers specific action from input
4
- # read by a interactor implementation.
5
- #
6
- # Currently the following actions are implemented:
7
- #
8
- # - h, help => Show help
9
- # - e, exit,
10
- # q. quit => Exit Guard
11
- # - r, reload => Reload Guard
12
- # - p, pause => Toggle file modification listener
13
- # - n, notification => Toggle notifications
14
- # - s, show => Show Guard plugin configuration
15
- # - c, change => Trigger a file change
16
- # - <enter> => Run all
17
- #
18
- # It's also possible to scope `reload` and `run all` actions to only a specified group or a guard.
19
- #
20
- # @example Reload backend group
21
- # backend reload
22
- # reload backend
23
- #
24
- # @example Reload rspec guard
25
- # spork reload
26
- # reload spork
27
- #
28
- # @example Run all jasmine specs
29
- # jasmine
30
- #
31
- # @abstract
3
+ # The Guard interactor is a Pry REPL with a Guard
4
+ # specific command set.
32
5
  #
33
6
  class Interactor
34
7
 
8
+ require 'pry'
9
+
35
10
  require 'guard'
36
11
  require 'guard/ui'
37
- require 'guard/dsl_describer'
38
- require 'guard/notifier'
39
- require 'guard/interactors/readline'
40
- require 'guard/interactors/coolline'
41
- require 'guard/interactors/simple'
42
-
43
- ACTIONS = {
44
- :help => %w[help h],
45
- :reload => %w[reload r],
46
- :stop => %w[exit e quit q],
47
- :pause => %w[pause p],
48
- :notification => %w[notification n],
49
- :show => %w[show s],
50
- :change => %w[change c]
51
- }
52
-
53
- # Set the interactor implementation
54
- #
55
- # @param [Symbol] interactor the name of the interactor
12
+
13
+ require 'guard/commands/all'
14
+ require 'guard/commands/change'
15
+ require 'guard/commands/notification'
16
+ require 'guard/commands/pause'
17
+ require 'guard/commands/reload'
18
+ require 'guard/commands/show'
19
+
20
+ GUARD_RC = '~/.guardrc'
21
+ HISTORY_FILE = '~/.guard_history'
22
+
23
+ # Initialize the interactor. This configures
24
+ # Pry and creates some custom commands and aliases
25
+ # for Guard.
56
26
  #
57
- def self.interactor=(interactor)
58
- @interactor = interactor
27
+ def initialize
28
+ return if ENV['GUARD_ENV'] == 'test'
29
+
30
+ Pry.config.history.file = HISTORY_FILE
31
+
32
+ load_guard_rc
33
+
34
+ create_run_all_command
35
+ create_command_aliases
36
+ create_guard_commands
37
+ create_group_commands
38
+
39
+ configure_prompt
59
40
  end
60
41
 
61
- # Get an instance of the currently configured
62
- # interactor implementation.
42
+ # Loads the `~/.guardrc` file when pry has started.
63
43
  #
64
- # @return [Interactor] an interactor implementation
65
- #
66
- def self.fabricate
67
- case @interactor
68
- when :coolline
69
- ::Guard::CoollineInteractor.new if ::Guard::CoollineInteractor.available?
70
- when :readline
71
- ::Guard::ReadlineInteractor.new if ::Guard::ReadlineInteractor.available?
72
- when :simple
73
- ::Guard::SimpleInteractor.new
74
- when :off
75
- nil
76
- else
77
- auto_detect
44
+ def load_guard_rc
45
+ Pry.config.hooks.add_hook :when_started, :load_guard_rc do
46
+ load GUARD_RC if File.exist? File.expand_path GUARD_RC
78
47
  end
79
48
  end
80
49
 
81
- # Tries to detect an optimal interactor for the
82
- # current environment.
83
- #
84
- # It returns the Readline implementation when:
85
- #
86
- # * rb-readline is installed
87
- # * The Ruby implementation is JRuby
88
- # * The current OS is not Mac OS X
50
+ # Creates a command that triggers the `:run_all` action
51
+ # when the command is empty (just pressing enter on the
52
+ # beginning of a line).
89
53
  #
90
- # Otherwise the plain gets interactor is returned.
54
+ def create_run_all_command
55
+ Pry.commands.block_command /^$/, 'Hit enter to run all' do
56
+ Pry.run_command 'all'
57
+ end
58
+ end
59
+
60
+ # Creates command aliases for the commands
61
+ # `help`, `reload`, `change`, `show`, `notification`, `pause`, `exit` and `quit`,
62
+ # which will be the first letter of the command.
91
63
  #
92
- # @return [Interactor] an interactor implementation
64
+ def create_command_aliases
65
+ %w(help reload change show notification pause exit quit).each do |command|
66
+ Pry.commands.alias_command command[0].chr, command
67
+ end
68
+ end
69
+
70
+ # Create a shorthand command to run the `:run_all`
71
+ # action on a specific Guard plugin. For example,
72
+ # when guard-rspec is available, then a command
73
+ # `rspec` is created that runs `all rspec`.
93
74
  #
94
- def self.auto_detect
95
- [::Guard::CoollineInteractor, ::Guard::ReadlineInteractor, ::Guard::SimpleInteractor].detect do |interactor|
96
- interactor.available?(true)
97
- end.new
75
+ def create_guard_commands
76
+ ::Guard.guards.each do |guard|
77
+ name = guard.class.to_s.downcase.sub('guard::', '')
78
+
79
+ Pry.commands.create_command name, "Run all #{ name }" do
80
+ group 'Guard'
81
+
82
+ def process
83
+ Pry.run_command "all #{ match }"
84
+ end
85
+ end
86
+ end
98
87
  end
99
88
 
100
- # Template method for checking if the Interactor is
101
- # available in the current environment?
89
+ # Create a shorthand command to run the `:run_all`
90
+ # action on a specific Guard group. For example,
91
+ # when you have a group `frontend`, then a command
92
+ # `frontend` is created that runs `all frontend`.
102
93
  #
103
- # @param [Boolean] silent true if no error messages should be shown
104
- # @return [Boolean] the availability status
94
+ def create_group_commands
95
+ ::Guard.groups.each do |group|
96
+ name = group.name.to_s
97
+ next if name == 'default'
98
+
99
+ Pry.commands.create_command name, "Run all #{ name }" do
100
+ group 'Guard'
101
+
102
+ def process
103
+ Pry.run_command "all #{ match }"
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ # Configure the pry prompt to see `guard` instead of
110
+ # `pry`.
105
111
  #
106
- def self.available?(silent = false)
107
- true
112
+ def configure_prompt
113
+ Pry.config.prompt = [
114
+ proc do |target_self, nest_level, pry|
115
+ "[#{ pry.input_array.size }] #{ ::Guard.listener.paused? ? 'pause' : 'guard' }(#{ Pry.view_clip(target_self) })#{":#{ nest_level }" unless nest_level.zero? }> "
116
+ end,
117
+ proc do |target_self, nest_level, pry|
118
+ "[#{ pry.input_array.size }] #{ ::Guard.listener.paused? ? 'pause' : 'guard' }(#{ Pry.view_clip(target_self) })#{":#{ nest_level }" unless nest_level.zero? }* "
119
+ end
120
+ ]
108
121
  end
109
122
 
110
123
  # Start the line reader in its own thread.
@@ -112,8 +125,17 @@ module Guard
112
125
  def start
113
126
  return if ENV['GUARD_ENV'] == 'test'
114
127
 
115
- ::Guard::UI.debug 'Start interactor'
116
- @thread = Thread.new { read_line } if !@thread || !@thread.alive?
128
+ store_terminal_settings if stty_exists?
129
+
130
+ if !@thread || !@thread.alive?
131
+ ::Guard::UI.debug 'Start interactor'
132
+
133
+ @thread = Thread.new do
134
+ Pry.start
135
+ ::Guard.stop
136
+ exit
137
+ end
138
+ end
117
139
  end
118
140
 
119
141
  # Kill interactor thread if not current
@@ -121,157 +143,57 @@ module Guard
121
143
  def stop
122
144
  return if !@thread || ENV['GUARD_ENV'] == 'test'
123
145
 
124
- ::Guard::UI.debug 'Stop interactor'
125
146
  unless Thread.current == @thread
147
+ ::Guard::UI.debug 'Stop interactor'
126
148
  @thread.kill
127
149
  end
128
- end
129
150
 
130
- # Read the user input. This method must be implemented
131
- # by each interactor implementation.
132
- #
133
- # @abstract
134
- #
135
- def read_line
136
- raise NotImplementedError
151
+ restore_terminal_settings if stty_exists?
137
152
  end
138
153
 
139
- # Process the input from readline.
140
- #
141
- # @param [String] line the input line
154
+ # Detects whether or not the stty command exists
155
+ # on the user machine.
142
156
  #
143
- def process_input(line)
144
- scopes, action, rest = extract_scopes_and_action(line)
145
-
146
- case action
147
- when :help
148
- help
149
- when :show
150
- ::Guard::DslDescriber.show(::Guard.options)
151
- when :stop
152
- ::Guard.stop
153
- exit
154
- when :pause
155
- ::Guard.pause
156
- when :reload
157
- ::Guard.reload(scopes)
158
- when :change
159
- ::Guard.within_preserved_state do
160
- ::Guard.runner.run_on_changes(rest, [], [])
161
- end
162
- when :run_all
163
- ::Guard.run_all(scopes)
164
- when :notification
165
- toggle_notification
166
- else
167
- ::Guard::UI.error "Unknown command #{ line }"
168
- end
169
- end
170
-
171
- # Toggle the system notifications on/off
157
+ # @return [Boolean] the status of stty
172
158
  #
173
- def toggle_notification
174
- if ENV['GUARD_NOTIFY'] == 'true'
175
- ::Guard::UI.info 'Turn off notifications'
176
- ::Guard::Notifier.turn_off
177
- else
178
- ::Guard::Notifier.turn_on
179
- end
159
+ def stty_exists?
160
+ @stty_exists ||= system('hash', 'stty')
180
161
  end
181
162
 
182
- # Show the help.
163
+ # Stores the terminal settings so we can resore them
164
+ # when stopping.
183
165
  #
184
- def help
185
- puts ''
186
- puts '[e]xit, [q]uit Exit Guard'
187
- puts '[p]ause Toggle file modification listener'
188
- puts '[r]eload Reload Guard'
189
- puts '[n]otification Toggle notifications'
190
- puts '[s]how Show available Guard plugins'
191
- puts '[c]hange <file> Trigger a file change'
192
- puts '<enter> Run all Guard plugins'
193
- puts ''
194
- puts 'You can scope the reload action to a specific guard or group:'
195
- puts ''
196
- puts 'rspec reload Reload the RSpec Guard'
197
- puts 'backend reload Reload the backend group'
198
- puts ''
199
- puts 'You can also run only a specific Guard or all Guard plugins in a specific group:'
200
- puts ''
201
- puts 'jasmine Run the jasmine Guard'
202
- puts 'frontend Run all Guard plugins in the frontend group'
203
- puts ''
166
+ def store_terminal_settings
167
+ @stty_save = `stty -g 2>/dev/null`.chomp
204
168
  end
205
169
 
206
- # Extract the Guard or group scope and action from the
207
- # input line. There's no strict order for scopes and
208
- # actions.
209
- #
210
- # @example `spork reload` will only reload rspec
211
- # @example `jasmine` will only run all jasmine specs
170
+ # Restore terminal settings
212
171
  #
213
- # @param [String] line the readline input
214
- # @return [Array] the group or guard scope, the action and the rest
215
- #
216
- def extract_scopes_and_action(line)
217
- entries = line.split(' ')
218
-
219
- scopes = extract_scopes(entries)
220
- action = extract_action(entries)
221
-
222
- action = :run_all if !action && (!scopes.empty? || entries.empty?)
223
-
224
- [scopes, action, entries]
172
+ def restore_terminal_settings
173
+ system("stty #{ @stty_save } 2>/dev/null") if @stty_save
225
174
  end
226
175
 
227
- private
228
-
229
- # Extract a guard or group scope from entry if valid.
230
- # Any entry found will be removed from the entries.
176
+ # Converts and validates a plain text scope
177
+ # to a valid plugin or group scope.
231
178
  #
232
- # @param [Array<String>] entries the user entries
233
- # @return [Hash] a hash with a Guard or a group scope
179
+ # @param [Array<String>] entries the text scope
180
+ # @return [Hash, Array<String>] the plugin or group scope, the unknown entries
234
181
  #
235
- def extract_scopes(entries)
236
- scopes = { }
182
+ def self.convert_scope(entries)
183
+ scopes = { }
184
+ unknown = []
237
185
 
238
- entries.delete_if do |entry|
186
+ entries.each do |entry|
239
187
  if guard = ::Guard.guards(entry)
240
188
  scopes[:guard] ||= guard
241
- true
242
-
243
189
  elsif group = ::Guard.groups(entry)
244
190
  scopes[:group] ||= group
245
- true
246
-
247
191
  else
248
- false
192
+ unknown << entry
249
193
  end
250
194
  end
251
195
 
252
- scopes
196
+ [scopes, unknown]
253
197
  end
254
-
255
- # Find the action for the given input entry.
256
- # Any action found will be removed from the entries.
257
- #
258
- # @param [Array<String>] entries the user entries
259
- # @return [Symbol] a Guard action
260
- #
261
- def extract_action(entries)
262
- action = nil
263
-
264
- entries.delete_if do |entry|
265
- if command = ACTIONS.detect { |k, list| list.include?(entry) }
266
- action ||= command.first
267
- true
268
- else
269
- false
270
- end
271
- end
272
-
273
- action
274
- end
275
-
276
198
  end
277
199
  end