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 CHANGED
@@ -1,16 +1,41 @@
1
+ ## 1.6.0 - 21 December, 2012
2
+
3
+ ### New features
4
+
5
+ - Allow the Guard scope to be defined from the `Guardfile` with the `scope` DSL method. ([@netzpirat][])
6
+ - [#378][] Scope plugins and groups from CLI and interactor. ([@netzpirat][])
7
+ - [#369][] Allow Guard plugins to specify their template location. ([@schmurfy][])
8
+ - [#364][] Add `ignore!` and `filter!` DSL methods. ([@tarsolya][])
9
+ - [#362][] Add interactor options `:history_file` and `:guard_rc`. ([@netzpirat][])
10
+
11
+ ### Improvements
12
+
13
+ - [#372][] Restore original TMux settings on stop. ([@rudicode][])
14
+ - [#376][] Delegate Ctrl-C to Pry to exit continuation. ([@netzpirat][])
15
+ - [#360][] Improve Guard/listen/interactor thread coordination. ([@netzpirat][])
16
+ - [#368][] Detecting duplicate definitions and then warning the user. ([@jfolkins][])
17
+ - [#367][] Change modeline's fgcolor when changing bgcolor in emacs notifier. ([@iljoo][])
18
+
19
+ ### Bug fixes
20
+
21
+ - [#377][] Add the 'a' alias for the 'all' Pry command. (reported by [@cknadler][], fixed by [@rymai][])
22
+ - [#365][] Fix terminal reset redirect to null devise on Windows. ([@cablegram][])
23
+ - [#365][] Fix Emacs notifier detection on Windows. ([@cablegram][])
24
+ - [#361][] Tmux notifier affects only the local session. ([@netzpirat][])
25
+
1
26
  ## 1.5.4 - 9 November, 2012
2
27
 
3
28
  ### Improvements
4
29
 
5
30
  - Thread handling improved and added thread debug mode. ([@netzpirat][])
6
31
 
7
- ## Bug fix
32
+ ### Bug fix
8
33
 
9
34
  - [#358][] Ignore `~/.pryrc` since it breaks Guard when loading the Rails env. ([@netzpirat][])
10
35
 
11
36
  ## 1.5.3 - 31 October, 2012
12
37
 
13
- ### Bug fix
38
+ ### Bug fixes
14
39
 
15
40
  - [#352][] Guard always reloading twice. ([@netzpirat][])
16
41
  - [#354][] Ignore `./.pryrc` since it breaks Guard when loading the Rails env. ([@netzpirat][])
@@ -657,6 +682,17 @@ The Listen integration has been supervised by [@thibaudgg][] and executed by [@M
657
682
  [#353]: https://github.com/guard/guard/issues/353
658
683
  [#354]: https://github.com/guard/guard/issues/354
659
684
  [#358]: https://github.com/guard/guard/issues/358
685
+ [#360]: https://github.com/guard/guard/issues/360
686
+ [#361]: https://github.com/guard/guard/issues/361
687
+ [#362]: https://github.com/guard/guard/issues/362
688
+ [#364]: https://github.com/guard/guard/issues/364
689
+ [#365]: https://github.com/guard/guard/issues/365
690
+ [#367]: https://github.com/guard/guard/issues/367
691
+ [#368]: https://github.com/guard/guard/issues/368
692
+ [#369]: https://github.com/guard/guard/issues/369
693
+ [#376]: https://github.com/guard/guard/issues/376
694
+ [#377]: https://github.com/guard/guard/issues/377
695
+ [#378]: https://github.com/guard/guard/issues/378
660
696
  [@Gazer]: https://github.com/Gazer
661
697
  [@Maher4Ever]: https://github.com/Maher4Ever
662
698
  [@alandipert]: https://github.com/alandipert
@@ -665,9 +701,11 @@ The Listen integration has been supervised by [@thibaudgg][] and executed by [@M
665
701
  [@benolee]: https://github.com/benolee
666
702
  [@brainopia]: https://github.com/brainopia
667
703
  [@bronson]: https://github.com/bronson
704
+ [@cablegram]: https://github.com/cablegram
668
705
  [@capotej]: https://github.com/capotej
669
706
  [@ches]: https://github.com/ches
670
707
  [@chrisberkhout]: https://github.com/chrisberkhout
708
+ [@cknadler]: https://github.com/cknadler
671
709
  [@d1]: https://github.com/d1
672
710
  [@dgutov]: https://github.com/dgutov
673
711
  [@dnagir]: https://github.com/dnagir
@@ -685,9 +723,11 @@ The Listen integration has been supervised by [@thibaudgg][] and executed by [@M
685
723
  [@hawx]: https://github.com/hawx
686
724
  [@hron]: https://github.com/hron
687
725
  [@ianwhite]: https://github.com/ianwhite
726
+ [@iljoo]: https://github.com/iljoo
688
727
  [@indirect]: https://github.com/indirect
689
728
  [@japgolly]: https://github.com/japgolly
690
729
  [@jeffutter]: https://github.com/jeffutter
730
+ [@jfolkins]: https://github.com/jfolkins
691
731
  [@johnbintz]: https://github.com/johnbintz
692
732
  [@jredville]: https://github.com/jredville
693
733
  [@jrsacks]: https://github.com/jrsacks
@@ -716,6 +756,7 @@ The Listen integration has been supervised by [@thibaudgg][] and executed by [@M
716
756
  [@royvandewater]: https://github.com/royvandewater
717
757
  [@rupert654]: https://github.com/rupert654
718
758
  [@rymai]: https://github.com/rymai
759
+ [@schmurfy]: https://github.com/schmurfy
719
760
  [@scottdavis]: https://github.com/scottdavis
720
761
  [@semperos]: https://github.com/semperos
721
762
  [@spadin]: https://github.com/spadin
@@ -724,6 +765,7 @@ The Listen integration has been supervised by [@thibaudgg][] and executed by [@M
724
765
  [@stouset]: https://github.com/stouset
725
766
  [@sunaku]: https://github.com/sunaku
726
767
  [@sutherland]: https://github.com/sutherland
768
+ [@tarsolya]: https://github.com/tarsolya
727
769
  [@thibaudgg]: https://github.com/thibaudgg
728
770
  [@thierryhenrio]: https://github.com/thierryhenrio
729
771
  [@tinogomes]: https://github.com/tinogomes
data/README.md CHANGED
@@ -101,7 +101,7 @@ group :development do
101
101
  end
102
102
  ```
103
103
 
104
- ### System notifications
104
+ ## System notifications
105
105
 
106
106
  You can configure Guard to make use of the following system notification libraries:
107
107
 
@@ -374,7 +374,7 @@ Notifications can also be disabled globally by setting a `GUARD_NOTIFY` environm
374
374
 
375
375
  #### `-g`/`--group` option
376
376
 
377
- Only certain plugin groups can be run:
377
+ Scope Guard to certain plugin groups on start:
378
378
 
379
379
  ```bash
380
380
  $ guard --group group_name another_group_name
@@ -383,6 +383,15 @@ $ guard -g group_name another_group_name # shortcut
383
383
 
384
384
  See the Guardfile DSL below for creating groups.
385
385
 
386
+ #### `-P`/`--plugins` option
387
+
388
+ Scope Guard to certain plugins on start:
389
+
390
+ ```bash
391
+ $ guard --plugins plugin_name another_plugin_name
392
+ $ guard -p plugin_name another_plugin_name # shortcut
393
+ ```
394
+
386
395
  #### `-d`/`--debug` option
387
396
 
388
397
  Guard can display debug information which can be very usefull for plugins
@@ -499,6 +508,7 @@ commands:
499
508
  * `n`, `notification`: Toggles the notifications.
500
509
  * `p`, `pause`: Toggles the file listener.
501
510
  * `r`, `reload`: Reload all plugins.
511
+ * `o`, `scope`: Scope Guard actions to plugins or groups.
502
512
  * `s`, `show`: Show all Guard plugins.
503
513
  * `e`, `exit`: Stop all plugins and quit Guard
504
514
 
@@ -636,6 +646,29 @@ $ guard -g specs
636
646
 
637
647
  Guard plugins that don't belong to a group are considered global and are always run.
638
648
 
649
+ ### scope
650
+
651
+ The `scope` method allows you to define the default plugin or group scope for Guard, if not
652
+ specified as command line option. Thus command line group and plugin scope takes precedence over
653
+ the DSL scope configuration.
654
+
655
+ You can define either a single plugin or group:
656
+
657
+ ```ruby
658
+ scope :plugin => :rspec
659
+ scope :group => :docs
660
+ ```
661
+
662
+ or specify multiple plugins or groups.
663
+
664
+ ```ruby
665
+ scope :plugins => [:test, :jasmine]
666
+ scope :groups => [:docs, :frontend]
667
+ ```
668
+
669
+ If you define both the plugin and group scope, the plugin scope has precedence. If you use both the
670
+ plural and the singular option, the plural has precedence.
671
+
639
672
  ### notification
640
673
 
641
674
  If you don't specify any notification configuration in your `Guardfile`, Guard goes through the list of available
@@ -675,6 +708,12 @@ notification :off
675
708
 
676
709
  ### interactor
677
710
 
711
+ You can customize the Pry interactor history and RC file like:
712
+
713
+ ```ruby
714
+ interactor :guard_rc => '~/.my_guard-rc', :history_file => '~/.my_guard_history_file'
715
+ ```
716
+
678
717
  If you do not need the Pry interactions with Guard at all, you can turn it off:
679
718
 
680
719
  ```ruby
@@ -711,10 +750,18 @@ are ignored.
711
750
  Please note that method only accept regexps. More on the
712
751
  [Listen README](https://github.com/guard/listen#the-patterns-for-filtering-and-ignoring-paths).
713
752
 
753
+ To append to the default ignored files and directories, use the `ignore` method:
754
+
714
755
  ```ruby
715
756
  ignore %r{^ignored/path/}, /public/
716
757
  ```
717
758
 
759
+ To _replace_ to default ignored files and directories, use the `ignore!` method:
760
+
761
+ ```ruby
762
+ ignore! /data/
763
+ ```
764
+
718
765
  ### filter
719
766
 
720
767
  The `filter` method allows you to focus by filtering files and directories without having to specify them by hand in the
@@ -728,6 +775,12 @@ Please note that method only accept regexps. More on the
728
775
  filter /\.txt$/, /.*\.zip/
729
776
  ```
730
777
 
778
+ To _replace_ any existing filter, use the `filter!` method:
779
+
780
+ ```ruby
781
+ filter /\.js$/
782
+ ```
783
+
731
784
  ### logger
732
785
 
733
786
  The `logger` method allows you to customize the Guard log output to your needs by specifying one or more options like:
data/lib/guard.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'rbconfig'
1
2
  require 'thread'
2
3
  require 'listen'
3
4
 
@@ -21,8 +22,11 @@ module Guard
21
22
  # The location of user defined templates
22
23
  HOME_TEMPLATES = File.expand_path('~/.guard/templates')
23
24
 
25
+ WINDOWS = RbConfig::CONFIG["host_os"] =~ %r!(msdos|mswin|djgpp|mingw)!
26
+ DEV_NULL = WINDOWS ? "NUL" : "/dev/null"
27
+
24
28
  class << self
25
- attr_accessor :options, :interactor, :runner, :listener, :lock
29
+ attr_accessor :options, :interactor, :runner, :listener, :lock, :scope, :running
26
30
 
27
31
  # Initialize the Guard singleton:
28
32
  #
@@ -40,12 +44,14 @@ module Guard
40
44
  # @deprecated @option options [Boolean] no_vendor ignore vendored dependencies
41
45
  #
42
46
  def setup(options = {})
47
+ @running = true
43
48
  @lock = Mutex.new
44
49
  @options = options.dup
45
50
  @watchdir = (options[:watchdir] && File.expand_path(options[:watchdir])) || Dir.pwd
46
51
  @runner = ::Guard::Runner.new
52
+ @scope = { :plugins => [], :groups => []}
47
53
 
48
- if @options[:debug]
54
+ if options[:debug]
49
55
  Thread.abort_on_exception = true
50
56
  ::Guard::UI.options[:level] = :debug
51
57
  debug_command_execution
@@ -62,6 +68,14 @@ module Guard
62
68
  ::Guard::Dsl.evaluate_guardfile(options)
63
69
  ::Guard::UI.error 'No guards found in Guardfile, please add at least one.' if @guards.empty?
64
70
 
71
+ if @options[:group]
72
+ @scope[:groups] = @options[:group].map { |g| ::Guard.groups(g) }
73
+ end
74
+
75
+ if @options[:plugin]
76
+ @scope[:plugins] = @options[:plugin].map { |p| ::Guard.guards(p) }
77
+ end
78
+
65
79
  runner.deprecation_warning if @options[:show_deprecations]
66
80
 
67
81
  setup_notifier
@@ -91,15 +105,26 @@ module Guard
91
105
  # Currently two signals are caught:
92
106
  # - `USR1` which pauses listening to changes.
93
107
  # - `USR2` which resumes listening to changes.
108
+ # - 'INT' which is delegated to Pry if active, otherwise stops Guard.
94
109
  #
95
110
  def setup_signal_traps
96
111
  unless defined?(JRUBY_VERSION)
97
112
  if Signal.list.keys.include?('USR1')
98
- Signal.trap('USR1') { ::Guard.pause unless @listener.paused? }
113
+ Signal.trap('USR1') { ::Guard.pause unless listener.paused? }
99
114
  end
100
115
 
101
116
  if Signal.list.keys.include?('USR2')
102
- Signal.trap('USR2') { ::Guard.pause if @listener.paused? }
117
+ Signal.trap('USR2') { ::Guard.pause if listener.paused? }
118
+ end
119
+
120
+ if Signal.list.keys.include?('INT')
121
+ Signal.trap('INT') do
122
+ if interactor
123
+ interactor.thread.raise(Interrupt)
124
+ else
125
+ ::Guard.stop
126
+ end
127
+ end
103
128
  end
104
129
  end
105
130
  end
@@ -132,7 +157,7 @@ module Guard
132
157
  # Initializes the interactor unless the user has specified not to.
133
158
  #
134
159
  def setup_interactor
135
- unless options[:no_interactions]
160
+ unless options[:no_interactions] || !::Guard::Interactor.enabled
136
161
  @interactor = ::Guard::Interactor.new
137
162
  end
138
163
  end
@@ -156,25 +181,25 @@ module Guard
156
181
  #
157
182
  def start(options = {})
158
183
  setup(options)
159
- ::Guard::UI.info "Guard is now watching at '#{ @watchdir }'"
160
184
 
161
185
  within_preserved_state do
186
+ ::Guard::UI.debug 'Guard starts all plugins'
162
187
  runner.run(:start)
188
+ ::Guard::UI.info "Guard is now watching at '#{ @watchdir }'"
189
+ listener.start(false)
163
190
  end
164
-
165
- # Blocks main thread
166
- listener.start
167
191
  end
168
192
 
169
193
  # Stop Guard listening to file changes
170
194
  #
171
195
  def stop
172
196
  within_preserved_state(false) do
197
+ ::Guard::UI.debug 'Guard stops all plugins'
173
198
  runner.run(:stop)
199
+ ::Guard::Notifier.turn_off
174
200
  ::Guard::UI.info 'Bye bye...', :reset => true
175
-
176
- # Unblocks main thread
177
201
  listener.stop
202
+ ::Guard.running = false
178
203
  end
179
204
  end
180
205
 
@@ -185,6 +210,8 @@ module Guard
185
210
  # @param [Hash] scopes hash with a Guard plugin or a group scope
186
211
  #
187
212
  def reload(scopes = {})
213
+ scopes = convert_scopes(scopes)
214
+
188
215
  within_preserved_state do
189
216
  ::Guard::UI.clear(:force => true)
190
217
  ::Guard::UI.action_with_scopes('Reload', scopes)
@@ -202,6 +229,8 @@ module Guard
202
229
  # @param [Hash] scopes hash with a Guard plugin or a group scope
203
230
  #
204
231
  def run_all(scopes = {})
232
+ scopes = convert_scopes(scopes)
233
+
205
234
  within_preserved_state do
206
235
  ::Guard::UI.clear(:force => true)
207
236
  ::Guard::UI.action_with_scopes('Run', scopes)
@@ -333,6 +362,7 @@ module Guard
333
362
  interactor.stop if interactor
334
363
  @result = yield
335
364
  rescue Interrupt
365
+ # Bring back Pry when the block is halted with Ctrl-C
336
366
  end
337
367
 
338
368
  interactor.start if interactor && restart_interactor
@@ -400,7 +430,14 @@ module Guard
400
430
  #
401
431
  def guard_gem_names
402
432
  if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
403
- Gem::Specification.find_all.select { |x| x.name =~ /^guard-/ }
433
+ Gem::Specification.find_all.select do |x|
434
+ if x.name =~ /^guard-/
435
+ true
436
+ elsif x.name != "guard"
437
+ guard_plugin_path = File.join(x.full_gem_path, "lib/guard/#{x.name}.rb")
438
+ File.exists?( guard_plugin_path )
439
+ end
440
+ end
404
441
  else
405
442
  Gem.source_index.find_name(/^guard-/)
406
443
  end.map { |x| x.name.sub(/^guard-/, '') }
@@ -444,5 +481,40 @@ module Guard
444
481
  ::Guard::UI.deprecation(NO_VENDOR_DEPRECATION) if options[:no_vendor]
445
482
  end
446
483
 
484
+ # Convert the old scope format to the new scope format.
485
+ #
486
+ # @example Convert old scopes
487
+ # convert_scopes({ :guard => :rspec, :group => :backend })
488
+ # => { :plugins => [:rspec], :groups => [:backend] }
489
+ #
490
+ def convert_scopes(scopes)
491
+ if scopes[:guard]
492
+ scopes[:plugins] = [scopes[:guard]]
493
+ scopes.delete(:guard)
494
+ end
495
+
496
+ if scopes[:group]
497
+ scopes[:groups] = [scopes[:group]]
498
+ scopes.delete(:group)
499
+ end
500
+
501
+ scopes
502
+ end
503
+
504
+ # Determine if Guard needs to quit. This
505
+ # checks for Ctrl-D pressed.
506
+ #
507
+ # @return [Boolean] whether to quit or not
508
+ #
509
+ def quit?
510
+ STDIN.read_nonblock(1)
511
+ false
512
+ rescue Errno::EINTR
513
+ false
514
+ rescue Errno::EAGAIN
515
+ false
516
+ rescue EOFError
517
+ true
518
+ end
447
519
  end
448
520
  end
data/lib/guard/cli.rb CHANGED
@@ -41,6 +41,12 @@ module Guard
41
41
  :aliases => '-g',
42
42
  :banner => 'Run only the passed groups'
43
43
 
44
+ method_option :plugin,
45
+ :type => :array,
46
+ :default => [],
47
+ :aliases => '-P',
48
+ :banner => 'Run only the passed plugins'
49
+
44
50
  method_option :watchdir,
45
51
  :type => :string,
46
52
  :aliases => '-w',
@@ -102,9 +108,12 @@ module Guard
102
108
  def start
103
109
  verify_bundler_presence unless options[:no_bundler_warning]
104
110
  ::Guard.start(options)
105
- rescue Interrupt
106
- ::Guard.stop
107
- abort
111
+
112
+ return if ENV['GUARD_ENV'] == 'test'
113
+
114
+ while ::Guard.running do
115
+ sleep 0.5
116
+ end
108
117
  end
109
118
 
110
119
  desc 'list', 'Lists guards that can be used with init'
@@ -0,0 +1,32 @@
1
+ module Guard
2
+ class Interactor
3
+
4
+ SCOPE = Pry::CommandSet.new do
5
+ create_command 'scope' do
6
+
7
+ group 'Guard'
8
+ description 'Scope Guard actions to groups and plugins.'
9
+
10
+ banner <<-BANNER
11
+ Usage: scope <scope>
12
+
13
+ Set the global Guard scope.
14
+ BANNER
15
+
16
+ def process(*entries)
17
+ scope, rest = ::Guard::Interactor.convert_scope(entries)
18
+
19
+ if rest.length == 0
20
+ ::Guard.scope = scope
21
+ else
22
+ output.puts "Unkown scope #{ rest.join(', ') }"
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+
32
+ Pry.commands.import ::Guard::Interactor::SCOPE