guard 0.8.0 → 0.8.1

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/cli.rb CHANGED
@@ -1,179 +1,178 @@
1
- require 'thor'
2
- require 'guard/version'
3
-
4
- module Guard
5
-
6
- # Guard command line interface managed by [Thor](https://github.com/wycats/thor).
7
- # This is the main interface to Guard that is called by the Guard binary `bin/guard`.
8
- #
9
- class CLI < Thor
10
-
11
- default_task :start
12
-
13
- desc 'start', 'Starts Guard'
14
-
15
- method_option :clear,
16
- :type => :boolean,
17
- :default => false,
18
- :aliases => '-c',
19
- :banner => 'Auto clear shell before each change/run_all/reload'
20
-
21
- method_option :notify,
22
- :type => :boolean,
23
- :default => true,
24
- :aliases => '-n',
25
- :banner => 'Notifications feature (growl/libnotify)'
26
-
27
- method_option :debug,
28
- :type => :boolean,
29
- :default => false,
30
- :aliases => '-d',
31
- :banner => 'Print debug messages'
32
-
33
- method_option :group,
34
- :type => :array,
35
- :default => [],
36
- :aliases => '-g',
37
- :banner => 'Run only the passed groups'
38
-
39
- method_option :watchdir,
40
- :type => :string,
41
- :aliases => '-w',
42
- :banner => 'Specify the directory to watch'
43
-
44
- method_option :guardfile,
45
- :type => :string,
46
- :aliases => '-G',
47
- :banner => 'Specify a Guardfile'
48
-
49
- method_option :watch_all_modifications,
50
- :type => :boolean,
51
- :default => false,
52
- :aliases => '-A',
53
- :banner => "Watch for all file modifications including moves and deletions"
54
-
55
- # Start Guard by initialize the defined Guards and watch the file system.
56
- # This is the default task, so calling `guard` is the same as calling `guard start`.
57
- #
58
- # @see Guard.start
59
- #
60
- def start
61
- ::Guard.start(options)
62
- end
63
-
64
- desc 'list', 'Lists guards that can be used with init'
65
-
66
- # List the Guards that are available for use in your system and marks
67
- # those that are currently used in your `Guardfile`.
68
- #
69
- # @example Guard list output
70
- #
71
- # Available guards:
72
- # bundler *
73
- # livereload
74
- # ronn
75
- # rspec *
76
- # spork
77
- #
78
- # See also https://github.com/guard/guard/wiki/List-of-available-Guards
79
- # * denotes ones already in your Guardfile
80
- #
81
- # @see Guard::DslDescriber
82
- #
83
- def list
84
- Guard::DslDescriber.evaluate_guardfile(options)
85
-
86
- installed = Guard::DslDescriber.guardfile_structure.inject([]) do |installed, group|
87
- group[:guards].each { |guard| installed << guard[:name] } if group[:guards]
88
- installed
89
- end
90
-
91
- Guard::UI.info 'Available guards:'
92
-
93
- Guard::guard_gem_names.sort.uniq.each do |name|
94
- Guard::UI.info " #{ name } #{ installed.include?(name) ? '*' : '' }"
95
- end
96
-
97
- Guard::UI.info ' '
98
- Guard::UI.info 'See also https://github.com/guard/guard/wiki/List-of-available-Guards'
99
- Guard::UI.info '* denotes ones already in your Guardfile'
100
- end
101
-
102
- desc 'version', 'Show the Guard version'
103
- map %w(-v --version) => :version
104
-
105
- # Shows the current version of Guard.
106
- #
107
- # @see Guard::VERSION
108
- #
109
- def version
110
- Guard::UI.info "Guard version #{ Guard::VERSION }"
111
- end
112
-
113
- desc 'init [GUARD]', 'Generates a Guardfile at the current working directory, or insert the given GUARD to an existing Guardfile'
114
-
115
- # Appends the Guard template to the `Guardfile`, or creates an initial
116
- # `Guardfile` when no Guard name is passed.
117
- #
118
- # @param [String] guard_name the name of the Guard to initialize
119
- #
120
- def init(guard_name = nil)
121
- if guard_name
122
- guard_class = ::Guard.get_guard_class(guard_name)
123
- guard_class.init(guard_name)
124
-
125
- else
126
- if File.exist?('Guardfile')
127
- puts 'Writing new Guardfile to #{Dir.pwd}/Guardfile'
128
- FileUtils.cp(File.expand_path('../templates/Guardfile', __FILE__), 'Guardfile')
129
- else
130
- Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
131
- exit 1
132
- end
133
- end
134
- end
135
-
136
- desc 'show', 'Show all defined Guards and their options'
137
- map %w(-T) => :show
138
-
139
- # Shows all Guards and their options that are defined in
140
- # the `Guardfile`.
141
- #
142
- # @example guard show output
143
- #
144
- # (global):
145
- # bundler
146
- # coffeescript: input => "app/assets/javascripts", noop => true
147
- # jasmine
148
- # rspec: cli => "--fail-fast --format Fuubar
149
- #
150
- # @see Guard::DslDescriber
151
- #
152
- def show
153
- Guard::DslDescriber.evaluate_guardfile(options)
154
-
155
- Guard::DslDescriber.guardfile_structure.each do |group|
156
- unless group[:guards].empty?
157
- if group[:group]
158
- Guard::UI.info "Group #{ group[:group] }:"
159
- else
160
- Guard::UI.info '(global):'
161
- end
162
-
163
- group[:guards].each do |guard|
164
- line = " #{ guard[:name] }"
165
-
166
- unless guard[:options].empty?
167
- line += ": #{ guard[:options].collect { |k, v| "#{ k } => #{ v.inspect }" }.join(', ') }"
168
- end
169
-
170
- Guard::UI.info line
171
- end
172
- end
173
- end
174
-
175
- Guard::UI.info ''
176
- end
177
-
178
- end
179
- end
1
+ require 'thor'
2
+ require 'guard/version'
3
+
4
+ module Guard
5
+
6
+ # Guard command line interface managed by [Thor](https://github.com/wycats/thor).
7
+ # This is the main interface to Guard that is called by the Guard binary `bin/guard`.
8
+ #
9
+ class CLI < Thor
10
+
11
+ default_task :start
12
+
13
+ desc 'start', 'Starts Guard'
14
+
15
+ method_option :clear,
16
+ :type => :boolean,
17
+ :default => false,
18
+ :aliases => '-c',
19
+ :banner => 'Auto clear shell before each change/run_all/reload'
20
+
21
+ method_option :notify,
22
+ :type => :boolean,
23
+ :default => true,
24
+ :aliases => '-n',
25
+ :banner => 'Notifications feature (growl/libnotify)'
26
+
27
+ method_option :debug,
28
+ :type => :boolean,
29
+ :default => false,
30
+ :aliases => '-d',
31
+ :banner => 'Print debug messages'
32
+
33
+ method_option :group,
34
+ :type => :array,
35
+ :default => [],
36
+ :aliases => '-g',
37
+ :banner => 'Run only the passed groups'
38
+
39
+ method_option :watchdir,
40
+ :type => :string,
41
+ :aliases => '-w',
42
+ :banner => 'Specify the directory to watch'
43
+
44
+ method_option :guardfile,
45
+ :type => :string,
46
+ :aliases => '-G',
47
+ :banner => 'Specify a Guardfile'
48
+
49
+ method_option :watch_all_modifications,
50
+ :type => :boolean,
51
+ :default => false,
52
+ :aliases => '-A',
53
+ :banner => "Watch for all file modifications including moves and deletions"
54
+
55
+ # Start Guard by initialize the defined Guards and watch the file system.
56
+ # This is the default task, so calling `guard` is the same as calling `guard start`.
57
+ #
58
+ # @see Guard.start
59
+ #
60
+ def start
61
+ ::Guard.start(options)
62
+ end
63
+
64
+ desc 'list', 'Lists guards that can be used with init'
65
+
66
+ # List the Guards that are available for use in your system and marks
67
+ # those that are currently used in your `Guardfile`.
68
+ #
69
+ # @example Guard list output
70
+ #
71
+ # Available guards:
72
+ # bundler *
73
+ # livereload
74
+ # ronn
75
+ # rspec *
76
+ # spork
77
+ #
78
+ # See also https://github.com/guard/guard/wiki/List-of-available-Guards
79
+ # * denotes ones already in your Guardfile
80
+ #
81
+ # @see Guard::DslDescriber
82
+ #
83
+ def list
84
+ Guard::DslDescriber.evaluate_guardfile(options)
85
+
86
+ installed = Guard::DslDescriber.guardfile_structure.inject([]) do |installed, group|
87
+ group[:guards].each { |guard| installed << guard[:name] } if group[:guards]
88
+ installed
89
+ end
90
+
91
+ Guard::UI.info 'Available guards:'
92
+
93
+ Guard::guard_gem_names.sort.uniq.each do |name|
94
+ Guard::UI.info " #{ name } #{ installed.include?(name) ? '*' : '' }"
95
+ end
96
+
97
+ Guard::UI.info ' '
98
+ Guard::UI.info 'See also https://github.com/guard/guard/wiki/List-of-available-Guards'
99
+ Guard::UI.info '* denotes ones already in your Guardfile'
100
+ end
101
+
102
+ desc 'version', 'Show the Guard version'
103
+ map %w(-v --version) => :version
104
+
105
+ # Shows the current version of Guard.
106
+ #
107
+ # @see Guard::VERSION
108
+ #
109
+ def version
110
+ Guard::UI.info "Guard version #{ Guard::VERSION }"
111
+ end
112
+
113
+ desc 'init [GUARD]', 'Generates a Guardfile at the current working directory, or insert the given GUARD to an existing Guardfile'
114
+
115
+ # Appends the Guard template to the `Guardfile`, or creates an initial
116
+ # `Guardfile` when no Guard name is passed.
117
+ #
118
+ # @param [String] guard_name the name of the Guard to initialize
119
+ #
120
+ def init(guard_name = nil)
121
+ if !File.exist?('Guardfile')
122
+ puts 'Writing new Guardfile to #{Dir.pwd}/Guardfile'
123
+ FileUtils.cp(File.expand_path('../templates/Guardfile', __FILE__), 'Guardfile')
124
+ elsif guard_name.nil?
125
+ Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
126
+ exit 1
127
+ end
128
+
129
+ if guard_name
130
+ guard_class = ::Guard.get_guard_class(guard_name)
131
+ guard_class.init(guard_name)
132
+ end
133
+ end
134
+
135
+ desc 'show', 'Show all defined Guards and their options'
136
+ map %w(-T) => :show
137
+
138
+ # Shows all Guards and their options that are defined in
139
+ # the `Guardfile`.
140
+ #
141
+ # @example guard show output
142
+ #
143
+ # (global):
144
+ # bundler
145
+ # coffeescript: input => "app/assets/javascripts", noop => true
146
+ # jasmine
147
+ # rspec: cli => "--fail-fast --format Fuubar
148
+ #
149
+ # @see Guard::DslDescriber
150
+ #
151
+ def show
152
+ Guard::DslDescriber.evaluate_guardfile(options)
153
+
154
+ Guard::DslDescriber.guardfile_structure.each do |group|
155
+ unless group[:guards].empty?
156
+ if group[:group]
157
+ Guard::UI.info "Group #{ group[:group] }:"
158
+ else
159
+ Guard::UI.info '(global):'
160
+ end
161
+
162
+ group[:guards].each do |guard|
163
+ line = " #{ guard[:name] }"
164
+
165
+ unless guard[:options].empty?
166
+ line += ": #{ guard[:options].collect { |k, v| "#{ k } => #{ v.inspect }" }.join(', ') }"
167
+ end
168
+
169
+ Guard::UI.info line
170
+ end
171
+ end
172
+ end
173
+
174
+ Guard::UI.info ''
175
+ end
176
+
177
+ end
178
+ end
data/lib/guard/dsl.rb CHANGED
@@ -1,370 +1,370 @@
1
- module Guard
2
-
3
- # The DSL class provides the methods that are used in each `Guardfile` to describe
4
- # the behaviour of Guard.
5
- #
6
- # The main keywords of the DSL are `guard` and `watch`. These are necessary to define
7
- # the used Guards and the file changes they are watching.
8
- #
9
- # You can optionally group the Guards with the `group` keyword and ignore certain paths
10
- # with the `ignore_paths` keyword.
11
- #
12
- # A more advanced DSL use is the `callback` keyword that allows you to execute arbitrary
13
- # code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change`
14
- # Guards' method. You can even insert more hooks inside these methods.
15
- # Please [checkout the Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for more details.
16
- #
17
- # The DSL will also evaluate normal Ruby code.
18
- #
19
- # There are two possible locations for the `Guardfile`:
20
- # - The `Guardfile` in the current directory where Guard has been started
21
- # - The `.Guardfile` in your home directory.
22
- #
23
- # In addition, if a user configuration `.guard.rb` in your home directory is found, it will
24
- # be appended to the current project `Guardfile`.
25
- #
26
- # @example A sample of a complex Guardfile
27
- #
28
- # group 'frontend' do
29
- # guard 'passenger', :ping => true do
30
- # watch('config/application.rb')
31
- # watch('config/environment.rb')
32
- # watch(%r{^config/environments/.+\.rb})
33
- # watch(%r{^config/initializers/.+\.rb})
34
- # end
35
- #
36
- # guard 'livereload', :apply_js_live => false do
37
- # watch(%r{^app/.+\.(erb|haml)})
38
- # watch(%r{^app/helpers/.+\.rb})
39
- # watch(%r{^public/javascripts/.+\.js})
40
- # watch(%r{^public/stylesheets/.+\.css})
41
- # watch(%r{^public/.+\.html})
42
- # watch(%r{^config/locales/.+\.yml})
43
- # end
44
- # end
45
- #
46
- # group 'backend' do
47
- # # Reload the bundle when the Gemfile is modified
48
- # guard 'bundler' do
49
- # watch('Gemfile')
50
- # end
51
- #
52
- # # for big project you can fine tune the "timeout" before Spork's launch is considered failed
53
- # guard 'spork', :wait => 40 do
54
- # watch('Gemfile')
55
- # watch('config/application.rb')
56
- # watch('config/environment.rb')
57
- # watch(%r{^config/environments/.+\.rb})
58
- # watch(%r{^config/initializers/.+\.rb})
59
- # watch('spec/spec_helper.rb')
60
- # end
61
- #
62
- # # use RSpec 2, from the system's gem and with some direct RSpec CLI options
63
- # guard 'rspec', :version => 2, :cli => "--color --drb -f doc", :bundler => false do
64
- # watch('spec/spec_helper.rb') { "spec" }
65
- # watch('app/controllers/application_controller.rb') { "spec/controllers" }
66
- # watch('config/routes.rb') { "spec/routing" }
67
- # watch(%r{^spec/support/(controllers|acceptance)_helpers\.rb}) { |m| "spec/#{m[1]}" }
68
- # watch(%r{^spec/.+_spec\.rb})
69
- #
70
- # watch(%r{^app/controllers/(.+)_(controller)\.rb}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
71
- #
72
- # watch(%r{^app/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
73
- # watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
74
- # end
75
- # end
76
- #
77
- class Dsl
78
- class << self
79
-
80
- @@options = nil
81
-
82
- # Evaluate the DSL methods in the `Guardfile`.
83
- #
84
- # @option options [Array<Symbol,String>] groups the groups to evaluate
85
- # @option options [String] guardfile the path to a valid Guardfile
86
- # @option options [String] guardfile_contents a string representing the content of a valid Guardfile
87
- # @raise [ArgumentError] when options are not a Hash
88
- #
89
- def evaluate_guardfile(options = {})
90
- raise ArgumentError.new('No option hash passed to evaluate_guardfile!') unless options.is_a?(Hash)
91
-
92
- @@options = options.dup
93
-
94
- fetch_guardfile_contents
95
- instance_eval_guardfile(guardfile_contents_with_user_config)
96
-
97
- UI.error 'No guards found in Guardfile, please add at least one.' if !::Guard.guards.nil? && ::Guard.guards.empty?
98
- end
99
-
100
- # Re-evaluate the `Guardfile` to update the current Guard configuration.
101
- #
102
- def reevaluate_guardfile
103
- ::Guard.guards.clear
104
- ::Guard.groups.clear
105
- @@options.delete(:guardfile_contents)
106
- Dsl.evaluate_guardfile(@@options)
107
- msg = 'Guardfile has been re-evaluated.'
108
- UI.info(msg)
109
- Notifier.notify(msg)
110
- end
111
-
112
- # Evaluate the content of the `Guardfile`.
113
- #
114
- # @param [String] contents the content to evaluate.
115
- #
116
- def instance_eval_guardfile(contents)
117
- new.instance_eval(contents, @@options[:guardfile_path], 1)
118
- rescue
119
- UI.error "Invalid Guardfile, original error is:\n#{ $! }"
120
- exit 1
121
- end
122
-
123
- # Test if the current `Guardfile` contains a specific Guard.
124
- #
125
- # @param [String] guard_name the name of the Guard
126
- # @return [Boolean] whether the Guard has been declared
127
- #
128
- def guardfile_include?(guard_name)
129
- guardfile_contents.match(/^guard\s*\(?\s*['":]#{ guard_name }['"]?/)
130
- end
131
-
132
- # Read the current `Guardfile` content.
133
- #
134
- # @param [String] the path to the Guardfile
135
- #
136
- def read_guardfile(guardfile_path)
137
- @@options[:guardfile_path] = guardfile_path
138
- @@options[:guardfile_contents] = File.read(guardfile_path)
139
- rescue
140
- UI.error("Error reading file #{ guardfile_path }")
141
- exit 1
142
- end
143
-
144
- # Get the content to evaluate and stores it into
145
- # the options as `:guardfile_contents`.
146
- #
147
- def fetch_guardfile_contents
148
- if @@options[:guardfile_contents]
149
- UI.info 'Using inline Guardfile.'
150
- @@options[:guardfile_path] = 'Inline Guardfile'
151
-
152
- elsif @@options[:guardfile]
153
- if File.exist?(@@options[:guardfile])
154
- read_guardfile(@@options[:guardfile])
155
- UI.info "Using Guardfile at #{ @@options[:guardfile] }."
156
- else
157
- UI.error "No Guardfile exists at #{ @@options[:guardfile] }."
158
- exit 1
159
- end
160
-
161
- else
162
- if File.exist?(guardfile_default_path)
163
- read_guardfile(guardfile_default_path)
164
- else
165
- UI.error 'No Guardfile found, please create one with `guard init`.'
166
- exit 1
167
- end
168
- end
169
-
170
- unless guardfile_contents_usable?
171
- UI.error "The command file(#{ @@options[:guardfile] }) seems to be empty."
172
- exit 1
173
- end
174
- end
175
-
176
- # Get the content of the `Guardfile`.
177
- #
178
- # @return [String] the Guardfile content
179
- #
180
- def guardfile_contents
181
- @@options ? @@options[:guardfile_contents] : ''
182
- end
183
-
184
- # Get the content of the `Guardfile` and the global
185
- # user configuration file.
186
- #
187
- # @see #user_config_path
188
- #
189
- # @return [String] the Guardfile content
190
- #
191
- def guardfile_contents_with_user_config
192
- config = File.read(user_config_path) if File.exist?(user_config_path)
193
- [guardfile_contents, config].join("\n")
194
- end
195
-
196
- # Get the file path to the project `Guardfile`.
197
- #
198
- # @return [String] the path to the Guardfile
199
- #
200
- def guardfile_path
201
- @@options ? @@options[:guardfile_path] : ''
202
- end
203
-
204
- # Tests if the current `Guardfile` content is usable.
205
- #
206
- # @return [Boolean] if the Guardfile is usable
207
- #
208
- def guardfile_contents_usable?
209
- guardfile_contents && guardfile_contents.size >= 'guard :a'.size # Smallest Guard definition
210
- end
211
-
212
- # Gets the default path of the `Guardfile`. This returns the `Guardfile`
213
- # from the current directory when existing, or the global `.Guardfile`
214
- # at the home directory.
215
- #
216
- # @return [String] the path to the Guardfile
217
- #
218
- def guardfile_default_path
219
- File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
220
- end
221
-
222
- private
223
-
224
- # The path to the `Guardfile` that is located at
225
- # the directory, where Guard has been started from.
226
- #
227
- # @param [String] the path to the local Guardfile
228
- #
229
- def local_guardfile_path
230
- File.join(Dir.pwd, 'Guardfile')
231
- end
232
-
233
- # The path to the `.Guardfile` that is located at
234
- # the users home directory.
235
- #
236
- # @param [String] the path to ~/.Guardfile
237
- #
238
- def home_guardfile_path
239
- File.expand_path(File.join('~', '.Guardfile'))
240
- end
241
-
242
- # The path to the user configuration `.guard.rb`
243
- # that is located at the users home directory.
244
- #
245
- # @param [String] the path to ~/.guard.rb
246
- #
247
- def user_config_path
248
- File.expand_path(File.join('~', '.guard.rb'))
249
- end
250
-
251
- end
252
-
253
- # Declares a group of guards to be run with `guard start --group group_name`.
254
- #
255
- # @example Declare two groups of Guards
256
- #
257
- # group 'backend' do
258
- # guard 'spork'
259
- # guard 'rspec'
260
- # end
261
- #
262
- # group 'frontend' do
263
- # guard 'passenger'
264
- # guard 'livereload'
265
- # end
266
- #
267
- # @param [Symbol, String] name the group's name called from the CLI
268
- # @param [Hash] options the options accepted by the group
269
- # @yield a block where you can declare several guards
270
- #
271
- # @see Guard.add_group
272
- # @see Dsl#guard
273
- # @see Guard::DslDescriber
274
- #
275
- def group(name, options = {})
276
- @groups = @@options[:group] || []
277
- name = name.to_sym
278
-
279
- if block_given? && (@groups.empty? || @groups.map(&:to_sym).include?(name))
280
- ::Guard.add_group(name.to_s.downcase, options)
281
- @current_group = name
282
-
283
- yield if block_given?
284
-
285
- @current_group = nil
286
- end
287
- end
288
-
289
- # Declare a guard to be used when running `guard start`.
290
- #
291
- # The name parameter is usually the name of the gem without
292
- # the 'guard-' prefix.
293
- #
294
- # The available options are different for each Guard implementation.
295
- #
296
- # @example Declare a Guard
297
- #
298
- # guard 'rspec' do
299
- # end
300
- #
301
- # @param [String] name the Guard name
302
- # @param [Hash] options the options accepted by the Guard
303
- # @yield a block where you can declare several watch patterns and actions
304
- #
305
- # @see Guard.add_guard
306
- # @see Dsl#group
307
- # @see Dsl#watch
308
- # @see Guard::DslDescriber
309
- #
310
- def guard(name, options = {})
311
- @watchers = []
312
- @callbacks = []
313
-
314
- yield if block_given?
315
-
316
- options.update(:group => (@current_group || :default))
317
- ::Guard.add_guard(name.to_s.downcase, @watchers, @callbacks, options)
318
- end
319
-
320
- # Define a pattern to be watched in order to run actions on file modification.
321
- #
322
- # @example Declare watchers for a Guard
323
- #
324
- # guard 'rspec' do
325
- # watch('spec/spec_helper.rb')
326
- # watch(%r{^.+_spec.rb})
327
- # watch(%r{^app/controllers/(.+).rb}) { |m| 'spec/acceptance/#{m[1]}s_spec.rb' }
328
- # end
329
- #
330
- # @param [String, Regexp] pattern the pattern to be watched by the guard
331
- # @yield a block to be run when the pattern is matched
332
- # @yieldparam [MatchData] m matches of the pattern
333
- # @yieldreturn a directory, a filename, an array of directories / filenames, or nothing (can be an arbitrary command)
334
- #
335
- # @see Guard::Watcher
336
- # @see Dsl#guard
337
- #
338
- def watch(pattern, &action)
339
- @watchers << ::Guard::Watcher.new(pattern, action)
340
- end
341
-
342
- # Define a callback to execute arbitrary code before or after any of
343
- # the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method.
344
- #
345
- # @param [Array] args the callback arguments
346
- # @yield a block with listeners
347
- #
348
- # @see Guard::Hook
349
- #
350
- def callback(*args, &listener)
351
- listener, events = args.size > 1 ? args : [listener, args[0]]
352
- @callbacks << { :events => events, :listener => listener }
353
- end
354
-
355
- # Ignore certain paths globally.
356
- #
357
- # @example Ignore some paths
358
- # ignore_paths ".git", ".svn"
359
- #
360
- # @param [Array] paths the list of paths to ignore
361
- #
362
- # @see Guard::Listener
363
- #
364
- def ignore_paths(*paths)
365
- UI.info "Ignoring paths: #{ paths.join(', ') }"
366
- ::Guard.listener.ignore_paths.push(*paths)
367
- end
368
-
369
- end
370
- end
1
+ module Guard
2
+
3
+ # The DSL class provides the methods that are used in each `Guardfile` to describe
4
+ # the behaviour of Guard.
5
+ #
6
+ # The main keywords of the DSL are `guard` and `watch`. These are necessary to define
7
+ # the used Guards and the file changes they are watching.
8
+ #
9
+ # You can optionally group the Guards with the `group` keyword and ignore certain paths
10
+ # with the `ignore_paths` keyword.
11
+ #
12
+ # A more advanced DSL use is the `callback` keyword that allows you to execute arbitrary
13
+ # code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change`
14
+ # Guards' method. You can even insert more hooks inside these methods.
15
+ # Please [checkout the Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for more details.
16
+ #
17
+ # The DSL will also evaluate normal Ruby code.
18
+ #
19
+ # There are two possible locations for the `Guardfile`:
20
+ # - The `Guardfile` in the current directory where Guard has been started
21
+ # - The `.Guardfile` in your home directory.
22
+ #
23
+ # In addition, if a user configuration `.guard.rb` in your home directory is found, it will
24
+ # be appended to the current project `Guardfile`.
25
+ #
26
+ # @example A sample of a complex Guardfile
27
+ #
28
+ # group 'frontend' do
29
+ # guard 'passenger', :ping => true do
30
+ # watch('config/application.rb')
31
+ # watch('config/environment.rb')
32
+ # watch(%r{^config/environments/.+\.rb})
33
+ # watch(%r{^config/initializers/.+\.rb})
34
+ # end
35
+ #
36
+ # guard 'livereload', :apply_js_live => false do
37
+ # watch(%r{^app/.+\.(erb|haml)})
38
+ # watch(%r{^app/helpers/.+\.rb})
39
+ # watch(%r{^public/javascripts/.+\.js})
40
+ # watch(%r{^public/stylesheets/.+\.css})
41
+ # watch(%r{^public/.+\.html})
42
+ # watch(%r{^config/locales/.+\.yml})
43
+ # end
44
+ # end
45
+ #
46
+ # group 'backend' do
47
+ # # Reload the bundle when the Gemfile is modified
48
+ # guard 'bundler' do
49
+ # watch('Gemfile')
50
+ # end
51
+ #
52
+ # # for big project you can fine tune the "timeout" before Spork's launch is considered failed
53
+ # guard 'spork', :wait => 40 do
54
+ # watch('Gemfile')
55
+ # watch('config/application.rb')
56
+ # watch('config/environment.rb')
57
+ # watch(%r{^config/environments/.+\.rb})
58
+ # watch(%r{^config/initializers/.+\.rb})
59
+ # watch('spec/spec_helper.rb')
60
+ # end
61
+ #
62
+ # # use RSpec 2, from the system's gem and with some direct RSpec CLI options
63
+ # guard 'rspec', :version => 2, :cli => "--color --drb -f doc", :bundler => false do
64
+ # watch('spec/spec_helper.rb') { "spec" }
65
+ # watch('app/controllers/application_controller.rb') { "spec/controllers" }
66
+ # watch('config/routes.rb') { "spec/routing" }
67
+ # watch(%r{^spec/support/(controllers|acceptance)_helpers\.rb}) { |m| "spec/#{m[1]}" }
68
+ # watch(%r{^spec/.+_spec\.rb})
69
+ #
70
+ # watch(%r{^app/controllers/(.+)_(controller)\.rb}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
71
+ #
72
+ # watch(%r{^app/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
73
+ # watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
74
+ # end
75
+ # end
76
+ #
77
+ class Dsl
78
+ class << self
79
+
80
+ @@options = nil
81
+
82
+ # Evaluate the DSL methods in the `Guardfile`.
83
+ #
84
+ # @option options [Array<Symbol,String>] groups the groups to evaluate
85
+ # @option options [String] guardfile the path to a valid Guardfile
86
+ # @option options [String] guardfile_contents a string representing the content of a valid Guardfile
87
+ # @raise [ArgumentError] when options are not a Hash
88
+ #
89
+ def evaluate_guardfile(options = {})
90
+ raise ArgumentError.new('No option hash passed to evaluate_guardfile!') unless options.is_a?(Hash)
91
+
92
+ @@options = options.dup
93
+
94
+ fetch_guardfile_contents
95
+ instance_eval_guardfile(guardfile_contents_with_user_config)
96
+
97
+ UI.error 'No guards found in Guardfile, please add at least one.' if !::Guard.guards.nil? && ::Guard.guards.empty?
98
+ end
99
+
100
+ # Re-evaluate the `Guardfile` to update the current Guard configuration.
101
+ #
102
+ def reevaluate_guardfile
103
+ ::Guard.guards.clear
104
+ ::Guard.groups.clear
105
+ @@options.delete(:guardfile_contents)
106
+ Dsl.evaluate_guardfile(@@options)
107
+ msg = 'Guardfile has been re-evaluated.'
108
+ UI.info(msg)
109
+ Notifier.notify(msg)
110
+ end
111
+
112
+ # Evaluate the content of the `Guardfile`.
113
+ #
114
+ # @param [String] contents the content to evaluate.
115
+ #
116
+ def instance_eval_guardfile(contents)
117
+ new.instance_eval(contents, @@options[:guardfile_path], 1)
118
+ rescue
119
+ UI.error "Invalid Guardfile, original error is:\n#{ $! }"
120
+ exit 1
121
+ end
122
+
123
+ # Test if the current `Guardfile` contains a specific Guard.
124
+ #
125
+ # @param [String] guard_name the name of the Guard
126
+ # @return [Boolean] whether the Guard has been declared
127
+ #
128
+ def guardfile_include?(guard_name)
129
+ guardfile_contents.match(/^guard\s*\(?\s*['":]#{ guard_name }['"]?/)
130
+ end
131
+
132
+ # Read the current `Guardfile` content.
133
+ #
134
+ # @param [String] the path to the Guardfile
135
+ #
136
+ def read_guardfile(guardfile_path)
137
+ @@options[:guardfile_path] = guardfile_path
138
+ @@options[:guardfile_contents] = File.read(guardfile_path)
139
+ rescue
140
+ UI.error("Error reading file #{ guardfile_path }")
141
+ exit 1
142
+ end
143
+
144
+ # Get the content to evaluate and stores it into
145
+ # the options as `:guardfile_contents`.
146
+ #
147
+ def fetch_guardfile_contents
148
+ if @@options[:guardfile_contents]
149
+ UI.info 'Using inline Guardfile.'
150
+ @@options[:guardfile_path] = 'Inline Guardfile'
151
+
152
+ elsif @@options[:guardfile]
153
+ if File.exist?(@@options[:guardfile])
154
+ read_guardfile(@@options[:guardfile])
155
+ UI.info "Using Guardfile at #{ @@options[:guardfile] }."
156
+ else
157
+ UI.error "No Guardfile exists at #{ @@options[:guardfile] }."
158
+ exit 1
159
+ end
160
+
161
+ else
162
+ if File.exist?(guardfile_default_path)
163
+ read_guardfile(guardfile_default_path)
164
+ else
165
+ UI.error 'No Guardfile found, please create one with `guard init`.'
166
+ exit 1
167
+ end
168
+ end
169
+
170
+ unless guardfile_contents_usable?
171
+ UI.error "The command file(#{ @@options[:guardfile] }) seems to be empty."
172
+ exit 1
173
+ end
174
+ end
175
+
176
+ # Get the content of the `Guardfile`.
177
+ #
178
+ # @return [String] the Guardfile content
179
+ #
180
+ def guardfile_contents
181
+ @@options ? @@options[:guardfile_contents] : ''
182
+ end
183
+
184
+ # Get the content of the `Guardfile` and the global
185
+ # user configuration file.
186
+ #
187
+ # @see #user_config_path
188
+ #
189
+ # @return [String] the Guardfile content
190
+ #
191
+ def guardfile_contents_with_user_config
192
+ config = File.read(user_config_path) if File.exist?(user_config_path)
193
+ [guardfile_contents, config].join("\n")
194
+ end
195
+
196
+ # Get the file path to the project `Guardfile`.
197
+ #
198
+ # @return [String] the path to the Guardfile
199
+ #
200
+ def guardfile_path
201
+ @@options ? @@options[:guardfile_path] : ''
202
+ end
203
+
204
+ # Tests if the current `Guardfile` content is usable.
205
+ #
206
+ # @return [Boolean] if the Guardfile is usable
207
+ #
208
+ def guardfile_contents_usable?
209
+ guardfile_contents && guardfile_contents.size >= 'guard :a'.size # Smallest Guard definition
210
+ end
211
+
212
+ # Gets the default path of the `Guardfile`. This returns the `Guardfile`
213
+ # from the current directory when existing, or the global `.Guardfile`
214
+ # at the home directory.
215
+ #
216
+ # @return [String] the path to the Guardfile
217
+ #
218
+ def guardfile_default_path
219
+ File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
220
+ end
221
+
222
+ private
223
+
224
+ # The path to the `Guardfile` that is located at
225
+ # the directory, where Guard has been started from.
226
+ #
227
+ # @param [String] the path to the local Guardfile
228
+ #
229
+ def local_guardfile_path
230
+ File.join(Dir.pwd, 'Guardfile')
231
+ end
232
+
233
+ # The path to the `.Guardfile` that is located at
234
+ # the users home directory.
235
+ #
236
+ # @param [String] the path to ~/.Guardfile
237
+ #
238
+ def home_guardfile_path
239
+ File.expand_path(File.join('~', '.Guardfile'))
240
+ end
241
+
242
+ # The path to the user configuration `.guard.rb`
243
+ # that is located at the users home directory.
244
+ #
245
+ # @param [String] the path to ~/.guard.rb
246
+ #
247
+ def user_config_path
248
+ File.expand_path(File.join('~', '.guard.rb'))
249
+ end
250
+
251
+ end
252
+
253
+ # Declares a group of guards to be run with `guard start --group group_name`.
254
+ #
255
+ # @example Declare two groups of Guards
256
+ #
257
+ # group 'backend' do
258
+ # guard 'spork'
259
+ # guard 'rspec'
260
+ # end
261
+ #
262
+ # group 'frontend' do
263
+ # guard 'passenger'
264
+ # guard 'livereload'
265
+ # end
266
+ #
267
+ # @param [Symbol, String] name the group's name called from the CLI
268
+ # @param [Hash] options the options accepted by the group
269
+ # @yield a block where you can declare several guards
270
+ #
271
+ # @see Guard.add_group
272
+ # @see Dsl#guard
273
+ # @see Guard::DslDescriber
274
+ #
275
+ def group(name, options = {})
276
+ @groups = @@options[:group] || []
277
+ name = name.to_sym
278
+
279
+ if block_given? && (@groups.empty? || @groups.map(&:to_sym).include?(name))
280
+ ::Guard.add_group(name.to_s.downcase, options)
281
+ @current_group = name
282
+
283
+ yield if block_given?
284
+
285
+ @current_group = nil
286
+ end
287
+ end
288
+
289
+ # Declare a guard to be used when running `guard start`.
290
+ #
291
+ # The name parameter is usually the name of the gem without
292
+ # the 'guard-' prefix.
293
+ #
294
+ # The available options are different for each Guard implementation.
295
+ #
296
+ # @example Declare a Guard
297
+ #
298
+ # guard 'rspec' do
299
+ # end
300
+ #
301
+ # @param [String] name the Guard name
302
+ # @param [Hash] options the options accepted by the Guard
303
+ # @yield a block where you can declare several watch patterns and actions
304
+ #
305
+ # @see Guard.add_guard
306
+ # @see Dsl#group
307
+ # @see Dsl#watch
308
+ # @see Guard::DslDescriber
309
+ #
310
+ def guard(name, options = {})
311
+ @watchers = []
312
+ @callbacks = []
313
+
314
+ yield if block_given?
315
+
316
+ options.update(:group => (@current_group || :default))
317
+ ::Guard.add_guard(name.to_s.downcase, @watchers, @callbacks, options)
318
+ end
319
+
320
+ # Define a pattern to be watched in order to run actions on file modification.
321
+ #
322
+ # @example Declare watchers for a Guard
323
+ #
324
+ # guard 'rspec' do
325
+ # watch('spec/spec_helper.rb')
326
+ # watch(%r{^.+_spec.rb})
327
+ # watch(%r{^app/controllers/(.+).rb}) { |m| 'spec/acceptance/#{m[1]}s_spec.rb' }
328
+ # end
329
+ #
330
+ # @param [String, Regexp] pattern the pattern to be watched by the guard
331
+ # @yield a block to be run when the pattern is matched
332
+ # @yieldparam [MatchData] m matches of the pattern
333
+ # @yieldreturn a directory, a filename, an array of directories / filenames, or nothing (can be an arbitrary command)
334
+ #
335
+ # @see Guard::Watcher
336
+ # @see Dsl#guard
337
+ #
338
+ def watch(pattern, &action)
339
+ @watchers << ::Guard::Watcher.new(pattern, action)
340
+ end
341
+
342
+ # Define a callback to execute arbitrary code before or after any of
343
+ # the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method.
344
+ #
345
+ # @param [Array] args the callback arguments
346
+ # @yield a block with listeners
347
+ #
348
+ # @see Guard::Hook
349
+ #
350
+ def callback(*args, &listener)
351
+ listener, events = args.size > 1 ? args : [listener, args[0]]
352
+ @callbacks << { :events => events, :listener => listener }
353
+ end
354
+
355
+ # Ignore certain paths globally.
356
+ #
357
+ # @example Ignore some paths
358
+ # ignore_paths ".git", ".svn"
359
+ #
360
+ # @param [Array] paths the list of paths to ignore
361
+ #
362
+ # @see Guard::Listener
363
+ #
364
+ def ignore_paths(*paths)
365
+ UI.info "Ignoring paths: #{ paths.join(', ') }"
366
+ ::Guard.listener.ignore_paths.push(*paths)
367
+ end
368
+
369
+ end
370
+ end