guard 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Thibaud Guillaume-Gentil
1
+ Copyright (c) 2011 Thibaud Guillaume-Gentil
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -1,6 +1,6 @@
1
1
  = Guard
2
2
 
3
- Guard is a command line tool to easly handle events on files modifications.
3
+ Guard is a command line tool that easily handle events on files modifications.
4
4
 
5
5
  == Features
6
6
 
@@ -27,73 +27,90 @@ Generate an empty Guardfile with:
27
27
 
28
28
  guard init
29
29
 
30
- Add guard(s) you need (see available guards below)
30
+ Add the guards you need (see available guards below)
31
31
 
32
32
  === On Mac OS X
33
33
 
34
- Install rb-fsevent for {FSEvent}[http://en.wikipedia.org/wiki/FSEvents] support
34
+ Install rb-fsevent for {FSEvent}[http://en.wikipedia.org/wiki/FSEvents] support:
35
35
 
36
36
  gem install rb-fsevent
37
37
 
38
- Install growl for Growl notification support
38
+ Install growl for Growl notification support:
39
39
 
40
40
  gem install growl
41
41
 
42
- And add it to you Gemfile
42
+ And add it to you Gemfile:
43
43
 
44
44
  gem 'growl'
45
45
 
46
46
  === On Linux
47
47
 
48
- Install rb-inotify for {inotify}[http://en.wikipedia.org/wiki/Inotify] support
48
+ Install rb-inotify for {inotify}[http://en.wikipedia.org/wiki/Inotify] support:
49
49
 
50
50
  gem install rb-inotify
51
51
 
52
- Install libnotify for libonity notification support
52
+ Install libnotify for libonity notification support:
53
53
 
54
54
  gem install libnotify
55
55
 
56
- And add it to you Gemfile
56
+ And add it to you Gemfile:
57
57
 
58
58
  gem 'libnotify'
59
59
 
60
60
  == Usage
61
61
 
62
- Just launch Guard inside your ruby/rails project with:
62
+ Just launch Guard inside your Ruby / Rails project with:
63
63
 
64
64
  guard
65
65
 
66
+ or if you use Bundler, to run the Guard executable specific to your bundle:
67
+
68
+ bundle exec guard
69
+
70
+ == Command line options
71
+
66
72
  Shell can be cleared after each change with:
67
73
 
68
- guard -c
74
+ guard --clear
75
+ guard -c # shortcut
76
+
77
+ The guards to start can be specified by group (see Guardfile DSL below) specifying the `--group` (or `-g`) option:
78
+
79
+ guard --group group_name another_group_name
80
+ guard -g group_name another_group_name # shortcut
69
81
 
70
82
  Options list is available with:
71
83
 
72
84
  guard help [TASK]
73
85
 
86
+ == Signal handlers
87
+
74
88
  Signal handlers are used to interact with Guard:
75
89
 
76
- - Ctrl-C - Quit Guard (call stop guard(s) method before)
77
- - Ctrl-\ - Call run_all guard(s) method
78
- - Ctrl-Z - Call reload guard(s) method
90
+ - Ctrl-C - Quit Guard (call each guard's run_all method, in the same order they are declared in the Guarfile)
91
+ - Ctrl-\ - Call each guard's run_all method, in the same order they are declared in the Guarfile
92
+ - Ctrl-Z - Call each guard's reload method, in the same order they are declared in the Guarfile
79
93
 
80
94
  == Available Guards
81
95
 
82
- - {guard-rspec}[http://github.com/guard/guard-rspec]
83
- - {guard-test}[http://github.com/guard/guard-test]
84
- - {guard-minitest}[http://github.com/guard/guard-minitest]
85
- - {guard-livereload}[http://github.com/guard/guard-livereload]
86
- - {guard-sass}[http://github.com/guard/guard-sass]
87
- - {guard-compass}[http://github.com/guard/guard-compass]
88
- - {guard-shell}[http://github.com/guard/guard-shell]
89
- - {guard-bundler}[http://github.com/guard/guard-bundler]
90
- - {guard-passenger}[http://github.com/guard/guard-passenger]
91
-
92
- guard ideas:
93
-
94
- - guard-spork
95
- - guard-cucumber
96
- - others ideas?
96
+ - {guard-bundler}[https://github.com/guard/guard-bundler] by {Yann Lugrin}[https://github.com/yannlugrin]
97
+ - {guard-coffeescript}[https://github.com/guard/guard-coffeescript] by {Michael Kessler}[https://github.com/netzpirat]
98
+ - {guard-compass}[https://github.com/guard/guard-compass] by {Olivier Amblet}[https://github.com/oliamb]
99
+ - {guard-cucumber}[https://github.com/guard/guard-cucumber] by {Michael Kessler}[https://github.com/netzpirat]
100
+ - {guard-ego}[https://github.com/guard/guard-ego] by {Fabio Kuhn}[https://github.com/mordaroso]
101
+ - {guard-jammit}[https://github.com/guard/guard-jammit] by {Pelle Braendgaard}[https://github.com/pelle]
102
+ - {guard-less}[https://github.com/guard/guard-less] by {Brendan Erwin}[https://github.com/brendanjerwin]
103
+ - {guard-livereload}[https://github.com/guard/guard-livereload] by {Thibaud Guillaume-Gentil}[https://github.com/thibaudgg]
104
+ - {guard-minitest}[https://github.com/guard/guard-minitest] by {Yann Lugrin}[https://github.com/yannlugrin]
105
+ - {guard-nanoc}[https://github.com/guard/guard-nanoc] by {Yann Lugrin}[https://github.com/yannlugrin]
106
+ - {guard-passenger}[https://github.com/guard/guard-passenger] by {Fabio Kuhn}[https://github.com/mordaroso]
107
+ - {guard-rspec}[https://github.com/guard/guard-rspec] by {Thibaud Guillaume-Gentil}[https://github.com/thibaudgg]
108
+ - {guard-sass}[https://github.com/guard/guard-sass] by {Joshua Hawxwell}[https://github.com/hawx]
109
+ - {guard-shell}[https://github.com/guard/guard-shell] by {Joshua Hawxwell}[https://github.com/hawx]
110
+ - {guard-soca}[https://github.com/guard/guard-soca] by {Luke Amdor}[https://github.com/rubbish]
111
+ - {guard-spork}[https://github.com/guard/guard-spork] by {Thibaud Guillaume-Gentil}[https://github.com/thibaudgg]
112
+ - {guard-stendhal}[https://github.com/guard/guard-stendhal] by {Josep Mª Bach}[https://github.com/txus]
113
+ - {guard-test}[https://github.com/guard/guard-test] by {Rémy Coutable}[https://github.com/rymai]
97
114
 
98
115
  === Add a guard to your Guardfile
99
116
 
@@ -107,6 +124,46 @@ Add guard definition to your Guardfile by running this command:
107
124
 
108
125
  You are good to go!
109
126
 
127
+ == Guardfile DSL
128
+
129
+ The Guardfile DSL consists of just three simple main methods: `guard`, `watch` & `group`.
130
+
131
+ Required:
132
+ - The `guard` method allows you to add a guard with an optional options hash.
133
+ - The `watch` method allows you to define which files are supervised per this guard. A optional block can be added to overwrite path sent to run_on_change guard method or launch simple command.
134
+
135
+ Optional:
136
+ - The `group` method allows you to group several guards. Groups to run can be specified with the Guard DSL option `--group` (or `-g`). This comes in handy especially when you have a huge Guardfile and want to focus your development.
137
+
138
+ Example:
139
+
140
+ group 'backend' do
141
+ guard 'bundler' do
142
+ watch('Gemfile')
143
+ end
144
+
145
+ guard 'rspec' do
146
+ # Regexp watch patterns are matched with Regexp#match
147
+ watch(%r{^spec/(.+)_spec\.rb})
148
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
149
+ watch(%r{^spec/models/.+\.rb}) { ["spec/models", "spec/acceptance"] }
150
+ watch(%r{^spec/.+\.rb}) { `say hello` }
151
+
152
+ # String watch patterns are matched with simple '=='
153
+ watch('spec/spec_helper.rb') { "spec" }
154
+ end
155
+ end
156
+
157
+ group 'frontend' do
158
+ guard 'coffeescript', :output => 'public/javascripts/compiled' do
159
+ watch(%r{app/coffeescripts/.+\.coffee})
160
+ end
161
+
162
+ guard 'livereload' do
163
+ watch(%r{app/.+\.(erb|haml)})
164
+ end
165
+ end
166
+
110
167
  == Create a guard
111
168
 
112
169
  Create a new guard is very easy, just create a new gem with this basic structure:
@@ -122,72 +179,63 @@ lib/guard/guard-name.rb inherit from guard/guard and should overwrite at least o
122
179
 
123
180
  require 'guard'
124
181
  require 'guard/guard'
125
-
182
+
126
183
  module Guard
127
184
  class GuardName < Guard
128
-
185
+
186
+ def initialize(watchers = [], options = {})
187
+ super
188
+ # init stuff here, thx!
189
+ end
190
+
129
191
  # ================
130
192
  # = Guard method =
131
193
  # ================
132
-
194
+
195
+ # If one of those methods raise an exception, the Guard instance
196
+ # will be removed from the active guard.
197
+
133
198
  # Call once when guard starts
199
+ # Please override initialize method to init stuff
134
200
  def start
135
201
  true
136
202
  end
137
-
138
- # Call with Ctrl-C signal (when Guard quit).
139
- # This method must return a true value
140
- # if everything went well or guard will not stop.
203
+
204
+ # Call with Ctrl-C signal (when Guard quit)
141
205
  def stop
142
206
  true
143
207
  end
144
-
208
+
145
209
  # Call with Ctrl-Z signal
210
+ # This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
146
211
  def reload
147
212
  true
148
213
  end
149
-
214
+
150
215
  # Call with Ctrl-/ signal
216
+ # This method should be principally used for long action like running all specs/tests/...
151
217
  def run_all
152
218
  true
153
219
  end
154
-
220
+
155
221
  # Call on file(s) modifications
156
222
  def run_on_change(paths)
157
223
  true
158
224
  end
159
-
225
+
160
226
  end
161
227
  end
162
228
 
163
229
  Looks at available guards code for more concrete example.
164
230
 
165
- == Guardfile DSL
166
-
167
- Guardfile DSL consists of just two simple methods: guard & watch. Example:
168
-
169
- guard 'rspec', :version => 2 do
170
- watch('^spec/(.*)_spec.rb')
171
- watch('^lib/(.*)\.rb') { |m| "spec/lib/#{m[1]}_spec.rb" }
172
- watch('^spec/spec_helper.rb') { "spec" }
173
- watch('^spec/spec_helper.rb') { `say hello` }
174
- end
175
-
176
- - "guard" method allow to add a guard with an optional options hash
177
- - "watch" method allow to define which files are supervised per this guard. A optional block can be added to overwrite path sending to run_on_change guard method or launch simple command.
178
-
179
- == TODO
180
-
181
- - Add more specs, help are welcome because I'm not sure about how to test stuff like this :-)
182
-
183
231
  == Development
184
232
 
185
- - Source hosted at {GitHub}[http://github.com/guard/guard]
186
- - Report issues/Questions/Feature requests on {GitHub Issues}[http://github.com/guard/guard/issues]
233
+ - Source hosted at {GitHub}[https://github.com/guard/guard]
234
+ - Report issues/Questions/Feature requests on {GitHub Issues}[https://github.com/guard/guard/issues]
187
235
 
188
236
  Pull requests are very welcome! Make sure your patches are well tested. Please create a topic branch for every separate change
189
237
  you make.
190
238
 
191
239
  == Authors
192
240
 
193
- {Thibaud Guillaume-Gentil}[http://github.com/thibaudgg]
241
+ {Thibaud Guillaume-Gentil}[https://github.com/thibaudgg]
@@ -1,74 +1,101 @@
1
1
  require 'bundler'
2
2
 
3
3
  module Guard
4
-
4
+
5
5
  autoload :UI, 'guard/ui'
6
6
  autoload :Dsl, 'guard/dsl'
7
7
  autoload :Interactor, 'guard/interactor'
8
8
  autoload :Listener, 'guard/listener'
9
9
  autoload :Watcher, 'guard/watcher'
10
10
  autoload :Notifier, 'guard/notifier'
11
-
11
+
12
12
  class << self
13
13
  attr_accessor :options, :guards, :listener
14
-
15
- def start(options = {})
14
+
15
+ # initialize this singleton
16
+ def setup(options = {})
16
17
  @options = options
17
- @listener = Listener.init
18
+ @listener = Listener.select_and_init
18
19
  @guards = []
19
-
20
- Dsl.evaluate_guardfile
20
+ self
21
+ end
22
+
23
+ def start(options = {})
24
+ setup(options)
25
+
26
+ Interactor.init_signal_traps
27
+ Dsl.evaluate_guardfile(options)
28
+
21
29
  if guards.empty?
22
- UI.error "No guards found in Guardfile, please add it at least one."
30
+ UI.error "No guards found in Guardfile, please add at least one."
23
31
  else
24
- Interactor.init_signal_traps
25
-
26
32
  listener.on_change do |files|
27
- run do
28
- guards.each do |guard|
29
- paths = Watcher.match_files(guard, files)
30
- guard.run_on_change(paths) unless paths.empty?
31
- end
33
+ if Watcher.match_files?(guards, files)
34
+ run { run_on_change_for_all_guards(files) }
32
35
  end
33
36
  end
34
-
37
+
35
38
  UI.info "Guard is now watching at '#{Dir.pwd}'"
36
- guards.each { |g| g.start }
39
+ guards.each { |guard| supervised_task(guard, :start) }
37
40
  listener.start
38
41
  end
39
42
  end
40
-
43
+
44
+ def run_on_change_for_all_guards(files)
45
+ guards.each do |guard|
46
+ paths = Watcher.match_files(guard, files)
47
+ supervised_task(guard, :run_on_change, paths) unless paths.empty?
48
+ end
49
+ # Reparse the whole directory to catch new files modified during the guards run
50
+ new_modified_files = listener.modified_files([Dir.pwd + '/'], :all => true)
51
+ listener.update_last_event
52
+ unless new_modified_files.empty?
53
+ run_on_change_for_all_guards(new_modified_files)
54
+ end
55
+ end
56
+
57
+ # Let a guard execute its task but
58
+ # fire it if his work leads to a system failure
59
+ def supervised_task(guard, task_to_supervise, *args)
60
+ guard.send(task_to_supervise, *args)
61
+ rescue Exception
62
+ UI.error("#{guard.class.name} guard failed to achieve its <#{task_to_supervise.to_s}> command: #{$!}")
63
+ ::Guard.guards.delete guard
64
+ UI.info("Guard #{guard.class.name} has just been fired")
65
+ return $!
66
+ end
67
+
68
+ def run
69
+ listener.stop
70
+ UI.clear if options[:clear]
71
+ begin
72
+ yield
73
+ rescue Interrupt
74
+ end
75
+ listener.start
76
+ end
77
+
41
78
  def add_guard(name, watchers = [], options = {})
42
79
  guard_class = get_guard_class(name)
43
80
  @guards << guard_class.new(watchers, options)
44
81
  end
45
-
82
+
46
83
  def get_guard_class(name)
47
84
  require "guard/#{name.downcase}"
48
85
  klasses = []
49
86
  ObjectSpace.each_object(Class) do |klass|
50
- klasses << klass if klass.to_s.downcase.match "^guard::#{name.downcase}"
87
+ klasses << klass if klass.to_s.downcase.match(/^guard::#{name.downcase}/)
51
88
  end
52
89
  klasses.first
53
90
  rescue LoadError
54
- UI.error "Could not find gem 'guard-#{name}' in the current Gemfile."
91
+ UI.error "Could not find gem 'guard-#{name}', please add it in your Gemfile."
55
92
  end
56
-
93
+
57
94
  def locate_guard(name)
58
95
  `gem open guard-#{name} --latest --command echo`.chomp
59
96
  rescue
60
97
  UI.error "Could not find 'guard-#{name}' gem path."
61
98
  end
62
-
63
- def run
64
- listener.stop
65
- UI.clear if options[:clear]
66
- begin
67
- yield
68
- rescue Interrupt
69
- end
70
- listener.start
71
- end
72
-
99
+
73
100
  end
74
101
  end
@@ -5,20 +5,22 @@ module Guard
5
5
  class CLI < Thor
6
6
  default_task :start
7
7
 
8
- desc "start", "Starts guard"
9
8
  method_option :clear, :type => :boolean, :default => false, :aliases => '-c', :banner => "Auto clear shell before each change/run_all/reload"
10
9
  method_option :debug, :type => :boolean, :default => false, :aliases => '-d', :banner => "Print debug messages"
10
+ method_option :group, :type => :array, :default => [], :aliases => '-g', :banner => "Run only the passed groups"
11
+
12
+ desc "start", "Starts Guard"
11
13
  def start
12
14
  ::Guard.start(options)
13
15
  end
14
16
 
15
- desc "version", "Prints the guard's version information"
17
+ desc "version", "Prints Guard's version information"
16
18
  def version
17
19
  ::Guard::UI.info "Guard version #{Guard::VERSION}"
18
20
  end
19
21
  map %w(-v --version) => :version
20
22
 
21
- desc "init [GUARD]", "Generates a Guardfile into the current working directory, or add it given guard"
23
+ desc "init [GUARD]", "Generates a Guardfile into the current working directory, or insert the given GUARD"
22
24
  def init(guard_name = nil)
23
25
  if !File.exist?("Guardfile")
24
26
  puts "Writing new Guardfile to #{Dir.pwd}/Guardfile"
@@ -1,28 +1,45 @@
1
1
  module Guard
2
2
  class Dsl
3
-
4
- def self.evaluate_guardfile
5
- guardfile = "#{Dir.pwd}/Guardfile"
6
- dsl = new
7
- dsl.instance_eval(File.read(guardfile.to_s), guardfile.to_s, 1)
8
- rescue
9
- UI.error "Guardfile not found or invalid"
10
- exit 1
3
+
4
+ class << self
5
+ def evaluate_guardfile(options = {})
6
+ @@options = options
7
+
8
+ if File.exists?(guardfile_path)
9
+ begin
10
+ new.instance_eval(File.read(guardfile_path), guardfile_path, 1)
11
+ rescue
12
+ UI.error "Invalid Guardfile, original error is:\n#{$!}"
13
+ exit 1
14
+ end
15
+ else
16
+ UI.error "No Guardfile in current folder, please create one."
17
+ exit 1
18
+ end
19
+ end
20
+
21
+ def guardfile_include?(guard_name)
22
+ File.read(guardfile_path).match(/^guard\s*\(?\s*['":]#{guard_name}['"]?/)
23
+ end
24
+
25
+ def guardfile_path
26
+ File.join(Dir.pwd, 'Guardfile')
27
+ end
11
28
  end
12
-
13
- def self.guardfile_included?(guard_name)
14
- File.read('Guardfile').include?("guard '#{guard_name}'")
29
+
30
+ def group(name, &guard_definition)
31
+ guard_definition.call if guard_definition && (@@options[:group].empty? || @@options[:group].include?(name))
15
32
  end
16
-
17
- def guard(name, options = {}, &definition)
33
+
34
+ def guard(name, options = {}, &watch_definition)
18
35
  @watchers = []
19
- definition.call if definition
36
+ watch_definition.call if watch_definition
20
37
  ::Guard.add_guard(name, @watchers, options)
21
38
  end
22
-
39
+
23
40
  def watch(pattern, &action)
24
41
  @watchers << ::Guard::Watcher.new(pattern, action)
25
42
  end
26
-
43
+
27
44
  end
28
45
  end
@@ -1,51 +1,55 @@
1
1
  module Guard
2
2
  class Guard
3
3
  attr_accessor :watchers, :options
4
-
4
+
5
5
  def initialize(watchers = [], options = {})
6
6
  @watchers, @options = watchers, options
7
7
  end
8
-
8
+
9
9
  # Guardfile template needed inside guard gem
10
10
  def self.init(name)
11
- if ::Guard::Dsl.guardfile_included?(name)
11
+ if ::Guard::Dsl.guardfile_include?(name)
12
12
  ::Guard::UI.info "Guardfile already include #{name} guard"
13
13
  else
14
14
  content = File.read('Guardfile')
15
15
  guard = File.read("#{::Guard.locate_guard(name)}/lib/guard/#{name}/templates/Guardfile")
16
16
  File.open('Guardfile', 'wb') do |f|
17
- f.puts content
18
- f.puts ""
19
- f.puts guard
17
+ f.puts(content)
18
+ f.puts("")
19
+ f.puts(guard)
20
20
  end
21
21
  ::Guard::UI.info "#{name} guard added to Guardfile, feel free to edit it"
22
22
  end
23
23
  end
24
-
24
+
25
25
  # ================
26
26
  # = Guard method =
27
27
  # ================
28
-
28
+
29
+ # Call once when guard starts
30
+ # Please override initialize method to init stuff
29
31
  def start
30
32
  true
31
33
  end
32
-
33
- # Retrieve a true value if the instance successfuly stopped
34
+
35
+ # Call once when guard quit
34
36
  def stop
35
37
  true
36
38
  end
37
-
39
+
40
+ # Should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
38
41
  def reload
39
42
  true
40
43
  end
41
-
44
+
45
+ # Should be principally used for long action like running all specs/tests/...
42
46
  def run_all
43
47
  true
44
48
  end
45
-
49
+
46
50
  def run_on_change(paths)
47
51
  true
48
52
  end
49
-
53
+
50
54
  end
51
55
  end
@@ -5,25 +5,22 @@ module Guard
5
5
  # Run all (Ctrl-\)
6
6
  Signal.trap('QUIT') do
7
7
  ::Guard.run do
8
- ::Guard.guards.each { |g| g.run_all }
8
+ ::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :run_all) }
9
9
  end
10
10
  end
11
11
 
12
12
  # Stop (Ctrl-C)
13
13
  Signal.trap('INT') do
14
+ UI.info "Bye bye...", :reset => true
14
15
  ::Guard.listener.stop
15
- if ::Guard.guards.all? { |g| g.stop }
16
- UI.info "Bye bye...", :reset => true
17
- abort("\n")
18
- else
19
- ::Guard.listener.start
20
- end
16
+ ::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :stop) }
17
+ abort("\n")
21
18
  end
22
19
 
23
20
  # Reload (Ctrl-Z)
24
21
  Signal.trap('TSTP') do
25
22
  ::Guard.run do
26
- ::Guard.guards.each { |g| g.reload }
23
+ ::Guard.guards.each { |guard| ::Guard.supervised_task(guard, :reload) }
27
24
  end
28
25
  end
29
26
  end
@@ -1,15 +1,15 @@
1
1
  require 'rbconfig'
2
2
 
3
3
  module Guard
4
-
4
+
5
5
  autoload :Darwin, 'guard/listeners/darwin'
6
6
  autoload :Linux, 'guard/listeners/linux'
7
7
  autoload :Polling, 'guard/listeners/polling'
8
-
8
+
9
9
  class Listener
10
10
  attr_reader :last_event
11
-
12
- def self.init
11
+
12
+ def self.select_and_init
13
13
  if mac? && Darwin.usable?
14
14
  Darwin.new
15
15
  elsif linux? && Linux.usable?
@@ -19,40 +19,107 @@ module Guard
19
19
  Polling.new
20
20
  end
21
21
  end
22
-
22
+
23
23
  def initialize
24
24
  update_last_event
25
25
  end
26
-
27
- private
28
-
26
+
27
+ def update_last_event
28
+ @last_event = Time.now
29
+ end
30
+
29
31
  def modified_files(dirs, options = {})
30
32
  files = potentially_modified_files(dirs, options).select { |path| File.file?(path) && recent_file?(path) }
31
33
  files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
32
34
  end
33
-
35
+
36
+ private
37
+
34
38
  def potentially_modified_files(dirs, options = {})
35
39
  match = options[:all] ? "**/*" : "*"
36
40
  Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
37
41
  end
38
-
42
+
39
43
  def recent_file?(file)
40
44
  File.mtime(file) >= last_event
41
45
  rescue
42
46
  false
43
47
  end
44
-
45
- def update_last_event
46
- @last_event = Time.now
47
- end
48
-
48
+
49
+
49
50
  def self.mac?
50
51
  Config::CONFIG['target_os'] =~ /darwin/i
51
52
  end
52
-
53
+
53
54
  def self.linux?
54
55
  Config::CONFIG['target_os'] =~ /linux/i
55
56
  end
56
-
57
+
57
58
  end
58
- end
59
+ end
60
+
61
+ # require 'rbconfig'
62
+ #
63
+ # module Guard
64
+ #
65
+ # autoload :Darwin, 'guard/listeners/darwin'
66
+ # autoload :Linux, 'guard/listeners/linux'
67
+ # autoload :Polling, 'guard/listeners/polling'
68
+ #
69
+ # class Listener
70
+ # attr_accessor :last_event, :changed_files
71
+ #
72
+ # def self.select_and_init
73
+ # if mac? && Darwin.usable?
74
+ # Darwin.new
75
+ # elsif linux? && Linux.usable?
76
+ # Linux.new
77
+ # else
78
+ # UI.info "Using polling (Please help us to support your system better than that.)"
79
+ # Polling.new
80
+ # end
81
+ # end
82
+ #
83
+ # def initialize
84
+ # @changed_files = []
85
+ # update_last_event
86
+ # end
87
+ #
88
+ # def get_and_clear_changed_files
89
+ # files = changed_files.dup
90
+ # changed_files.clear
91
+ # files.uniq
92
+ # end
93
+ #
94
+ # private
95
+ #
96
+ # def find_changed_files(dirs, options = {})
97
+ # files = potentially_changed_files(dirs, options).select { |path| File.file?(path) && changed_file?(path) }
98
+ # files.map! { |file| file.gsub("#{Dir.pwd}/", '') }
99
+ # end
100
+ #
101
+ # def potentially_changed_files(dirs, options = {})
102
+ # match = options[:all] ? "**/*" : "*"
103
+ # Dir.glob(dirs.map { |dir| "#{dir}#{match}" })
104
+ # end
105
+ #
106
+ # def changed_file?(file)
107
+ # File.mtime(file) >= last_event
108
+ # rescue
109
+ # false
110
+ # end
111
+ #
112
+ # def update_last_event
113
+ # @last_event = Time.now
114
+ # end
115
+ #
116
+ # def self.mac?
117
+ # Config::CONFIG['target_os'] =~ /darwin/i
118
+ # end
119
+ #
120
+ # def self.linux?
121
+ # Config::CONFIG['target_os'] =~ /linux/i
122
+ # end
123
+ #
124
+ # end
125
+ # end
@@ -1,12 +1,12 @@
1
1
  module Guard
2
2
  class Darwin < Listener
3
3
  attr_reader :fsevent
4
-
4
+
5
5
  def initialize
6
6
  super
7
7
  @fsevent = FSEvent.new
8
8
  end
9
-
9
+
10
10
  def on_change(&callback)
11
11
  @fsevent.watch Dir.pwd do |modified_dirs|
12
12
  files = modified_files(modified_dirs)
@@ -14,19 +14,19 @@ module Guard
14
14
  callback.call(files)
15
15
  end
16
16
  end
17
-
17
+
18
18
  def start
19
19
  @fsevent.run
20
20
  end
21
-
21
+
22
22
  def stop
23
23
  @fsevent.stop
24
24
  end
25
-
25
+
26
26
  def self.usable?
27
27
  require 'rb-fsevent'
28
- if !defined?(FSEvent::VERSION) || Gem::Version.new(FSEvent::VERSION) < Gem::Version.new('0.3.5')
29
- UI.info "Please update rb-fsevent (>= 0.3.5)"
28
+ if !defined?(FSEvent::VERSION) || Gem::Version.new(FSEvent::VERSION) < Gem::Version.new('0.3.9')
29
+ UI.info "Please update rb-fsevent (>= 0.3.9)"
30
30
  false
31
31
  else
32
32
  true
@@ -35,6 +35,6 @@ module Guard
35
35
  UI.info "Please install rb-fsevent gem for Mac OSX FSEvents support"
36
36
  false
37
37
  end
38
-
38
+
39
39
  end
40
- end
40
+ end
@@ -70,4 +70,4 @@ module Guard
70
70
  end
71
71
 
72
72
  end
73
- end
73
+ end
@@ -1,27 +1,27 @@
1
1
  module Guard
2
2
  class Polling < Listener
3
3
  attr_reader :callback, :latency
4
-
4
+
5
5
  def initialize
6
6
  super
7
7
  @latency = 1.5
8
8
  end
9
-
9
+
10
10
  def on_change(&callback)
11
11
  @callback = callback
12
12
  end
13
-
13
+
14
14
  def start
15
15
  @stop = false
16
16
  watch_change
17
17
  end
18
-
18
+
19
19
  def stop
20
20
  @stop = true
21
21
  end
22
-
22
+
23
23
  private
24
-
24
+
25
25
  def watch_change
26
26
  while !@stop
27
27
  start = Time.now.to_f
@@ -32,6 +32,6 @@ module Guard
32
32
  sleep(nap_time) if nap_time > 0
33
33
  end
34
34
  end
35
-
35
+
36
36
  end
37
- end
37
+ end
@@ -1,2 +1,2 @@
1
1
  # A sample Guardfile
2
- # More info at http://github.com/guard/guard#readme
2
+ # More info at https://github.com/guard/guard#readme
@@ -9,12 +9,16 @@ module Guard
9
9
  end
10
10
  end
11
11
 
12
- def error(message)
13
- puts "ERROR: #{message}"
12
+ def error(message, options = {})
13
+ unless ENV["GUARD_ENV"] == "test"
14
+ reset_line if options[:reset]
15
+ puts "ERROR: #{message}"
16
+ end
14
17
  end
15
18
 
16
- def debug(message)
19
+ def debug(message, options = {})
17
20
  unless ENV["GUARD_ENV"] == "test"
21
+ reset_line if options[:reset]
18
22
  puts "DEBUG: #{message}" if ::Guard.options && ::Guard.options[:debug]
19
23
  end
20
24
  end
@@ -1,3 +1,3 @@
1
1
  module Guard
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -4,29 +4,57 @@ module Guard
4
4
 
5
5
  def initialize(pattern, action = nil)
6
6
  @pattern, @action = pattern, action
7
+ @@warning_printed ||= false
8
+
9
+ # deprecation warning
10
+ if @pattern.is_a?(String) && @pattern =~ /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/
11
+ unless @@warning_printed
12
+ UI.info "*"*20 + "\nDEPRECATION WARNING!\n" + "*"*20
13
+ UI.info "You have strings in your Guardfile's watch patterns that seem to represent regexps.\nGuard matchs String with == and Regexp with Regexp#match.\nYou should either use plain String (without Regexp special characters) or real Regexp.\n"
14
+ @@warning_printed = true
15
+ end
16
+ UI.info "\"#{@pattern}\" has been converted to #{Regexp.new(@pattern).inspect}\n"
17
+ @pattern = Regexp.new(@pattern)
18
+ end
7
19
  end
8
20
 
9
21
  def self.match_files(guard, files)
10
22
  guard.watchers.inject([]) do |paths, watcher|
11
23
  files.each do |file|
12
- if matches = file.match(watcher.pattern)
24
+ if matches = watcher.match_file?(file)
13
25
  if watcher.action
14
- begin
15
- if watcher.action.arity == 1
16
- result = watcher.action.call(matches)
17
- else
18
- result = watcher.action.call
19
- end
20
- rescue
21
- UI.info "Problem with watch action"
22
- end
23
- paths << result if result.is_a?(String) && result != ''
26
+ result = watcher.call_action(matches)
27
+ paths << Array(result) if result.respond_to?(:empty?) && !result.empty?
24
28
  else
25
29
  paths << matches[0]
26
30
  end
27
31
  end
28
32
  end
29
- paths
33
+ paths.flatten.map { |p| p.to_s }
34
+ end
35
+ end
36
+
37
+ def self.match_files?(guards, files)
38
+ guards.any? do |guard|
39
+ guard.watchers.any? do |watcher|
40
+ files.any? { |file| watcher.match_file?(file) }
41
+ end
42
+ end
43
+ end
44
+
45
+ def match_file?(file)
46
+ if @pattern.is_a?(Regexp)
47
+ file.match(@pattern)
48
+ else
49
+ file == @pattern ? [file] : nil
50
+ end
51
+ end
52
+
53
+ def call_action(matches)
54
+ begin
55
+ @action.arity > 0 ? @action.call(matches) : @action.call
56
+ rescue
57
+ UI.error "Problem with watch action!"
30
58
  end
31
59
  end
32
60
 
metadata CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 2
10
- version: 0.2.2
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Thibaud Guillaume-Gentil
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-26 00:00:00 +02:00
18
+ date: 2011-01-19 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -26,12 +26,12 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- hash: 17
29
+ hash: 25
30
30
  segments:
31
31
  - 1
32
32
  - 0
33
- - 3
34
- version: 1.0.3
33
+ - 7
34
+ version: 1.0.7
35
35
  type: :development
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency
@@ -42,12 +42,12 @@ dependencies:
42
42
  requirements:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
- hash: 13
45
+ hash: 31
46
46
  segments:
47
47
  - 2
48
+ - 4
48
49
  - 0
49
- - 1
50
- version: 2.0.1
50
+ version: 2.4.0
51
51
  type: :development
52
52
  version_requirements: *id002
53
53
  - !ruby/object:Gem::Dependency
@@ -58,12 +58,12 @@ dependencies:
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- hash: 19
61
+ hash: 9
62
62
  segments:
63
63
  - 0
64
64
  - 1
65
- - 4
66
- version: 0.1.4
65
+ - 9
66
+ version: 0.1.9
67
67
  type: :development
68
68
  version_requirements: *id003
69
69
  - !ruby/object:Gem::Dependency
@@ -74,12 +74,12 @@ dependencies:
74
74
  requirements:
75
75
  - - ~>
76
76
  - !ruby/object:Gem::Version
77
- hash: 33
77
+ hash: 43
78
78
  segments:
79
79
  - 0
80
80
  - 14
81
- - 3
82
- version: 0.14.3
81
+ - 6
82
+ version: 0.14.6
83
83
  type: :runtime
84
84
  version_requirements: *id004
85
85
  - !ruby/object:Gem::Dependency