guard 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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