• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

guard / guard / 1455

pending completion
1455

push

travis-ci

e2
Release 2.11.1

5188 of 5439 relevant lines covered (95.39%)

14.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.17
/lib/guard/dsl.rb
1
require "guard/guardfile/evaluator"
5✔
2
require "guard/interactor"
5✔
3
require "guard/notifier"
5✔
4
require "guard/ui"
5✔
5
require "guard/watcher"
5✔
6

7
require "guard/deprecated/dsl" unless Guard::Config.new.strict?
5✔
8
require "guard"
5✔
9

10
module Guard
5✔
11
  # The Dsl class provides the methods that are used in each `Guardfile` to
12
  # describe the behaviour of Guard.
13
  #
14
  # The main keywords of the DSL are {#guard} and {#watch}. These are necessary
15
  # to define the used Guard plugins and the file changes they are watching.
16
  #
17
  # You can optionally group the Guard plugins with the {#group} keyword and
18
  # ignore and filter certain paths with the {#ignore} and {#filter} keywords.
19
  #
20
  # You can set your preferred system notification library with {#notification}
21
  # and pass some optional configuration options for the library. If you don't
22
  # configure a library, Guard will automatically pick one with default options
23
  # (if you don't want notifications, specify `:off` as library). Please see
24
  # {Notifier} for more information about the supported libraries.
25
  #
26
  # A more advanced DSL use is the {#callback} keyword that allows you to
27
  # execute arbitrary code before or after any of the {Plugin#start},
28
  # {Plugin#stop}, {Plugin#reload}, {Plugin#run_all},
29
  # {Plugin#run_on_changes}, {Plugin#run_on_additions},
30
  # {Plugin#run_on_modifications} and {Plugin#run_on_removals}
31
  # Guard plugins method.
32
  # You can even insert more hooks inside these methods. Please [checkout the
33
  # Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for
34
  # more details.
35
  #
36
  # The DSL will also evaluate normal Ruby code.
37
  #
38
  # There are two possible locations for the `Guardfile`:
39
  #
40
  # * The `Guardfile` in the current directory where Guard has been started
41
  # * The `.Guardfile` in your home directory.
42
  #
43
  # In addition, if a user configuration `.guard.rb` in your home directory is
44
  # found, it will be appended to the current project `Guardfile`.
45
  #
46
  # @see https://github.com/guard/guard/wiki/Guardfile-examples
47
  #
48
  class Dsl
5✔
49
    Deprecated::Dsl.add_deprecated(self) unless Config.new.strict?
5✔
50

51
    # Wrap exceptions during parsing Guardfile
52
    class Error < RuntimeError
5✔
53
    end
54

55
    WARN_INVALID_LOG_LEVEL = "Invalid log level `%s` ignored. "\
5✔
56
      "Please use either :debug, :info, :warn or :error."
57

58
    WARN_INVALID_LOG_OPTIONS = "You cannot specify the logger options"\
5✔
59
      " :only and :except at the same time."
60

61
    # Set notification options for the system notifications.
62
    # You can set multiple notifications, which allows you to show local
63
    # system notifications and remote notifications with separate libraries.
64
    # You can also pass `:off` as library to turn off notifications.
65
    #
66
    # @example Define multiple notifications
67
    #   notification :ruby_gntp
68
    #   notification :ruby_gntp, host: '192.168.1.5'
69
    #
70
    # @param [Symbol, String] notifier the name of the notifier to use
71
    # @param [Hash] options the notification library options
72
    #
73
    # @see Guard::Notifier for available notifier and its options.
74
    #
75
    def notification(notifier, opts = {})
5✔
76
      Guard.state.session.guardfile_notification = { notifier.to_sym => opts }
12 all except jruby and rbx ✔
77
    end
78

79
    # Sets the interactor options or disable the interactor.
80
    #
81
    # @example Pass options to the interactor
82
    #   interactor option1: 'value1', option2: 'value2'
83
    #
84
    # @example Turn off interactions
85
    #   interactor :off
86
    #
87
    # @param [Symbol, Hash] options either `:off` or a Hash with interactor
88
    #   options
89
    #
90
    def interactor(options)
5✔
91
      # TODO: remove dependency on Interactor (let session handle this)
92
      case options
6 all except jruby and rbx ✔
93
      when :off
94
        Interactor.enabled = false
3 all except jruby and rbx ✔
95
      when Hash
96
        Interactor.options = options
3 all except jruby and rbx ✔
97
      end
98
    end
99

100
    # Declares a group of Guard plugins to be run with `guard start --group
101
    #   group_name`.
102
    #
103
    # @example Declare two groups of Guard plugins
104
    #   group :backend do
105
    #     guard :spork
106
    #     guard :rspec
107
    #   end
108
    #
109
    #   group :frontend do
110
    #     guard :passenger
111
    #     guard :livereload
112
    #   end
113
    #
114
    # @param [Symbol, String, Array<Symbol, String>] name the group name called
115
    #   from the CLI
116
    # @param [Hash] options the options accepted by the group
117
    # @yield a block where you can declare several Guard plugins
118
    #
119
    # @see Group
120
    # @see Guard.add_group
121
    # @see #guard
122
    #
123
    def group(*args)
5✔
124
      options = args.last.is_a?(Hash) ? args.pop : {}
33 all except jruby and rbx ✔
125
      groups = args
33 all except jruby and rbx ✔
126

127
      groups.each do |group|
33 all except jruby and rbx ✔
128
        next unless group.to_sym == :all
36 all except jruby and rbx ✔
129
        fail ArgumentError, "'all' is not an allowed group name!"
6 all except jruby and rbx ✔
130
      end
131

132
      if block_given?
27 all except jruby and rbx ✔
133
        groups.each do |group|
24 all except jruby and rbx ✔
134
          # TODO: let groups be added *after* evaluation
135
          Guard.state.session.groups.add(group, options)
27 all except jruby and rbx ✔
136
        end
137

138
        @current_groups ||= []
24 all except jruby and rbx ✔
139
        @current_groups.push(groups)
24 all except jruby and rbx ✔
140

141
        yield
24 all except jruby and rbx ✔
142

143
        @current_groups.pop
24 all except jruby and rbx ✔
144
      else
145
        UI.error \
3 all except jruby and rbx ✔
146
          "No Guard plugins found in the group '#{ groups.join(", ") }',"\
×
147
          " please add at least one."
148
      end
149
    end
150

151
    # Declares a Guard plugin to be used when running `guard start`.
152
    #
153
    # The name parameter is usually the name of the gem without
154
    # the 'guard-' prefix.
155
    #
156
    # The available options are different for each Guard implementation.
157
    #
158
    # @example Declare a Guard without `watch` patterns
159
    #   guard :rspec
160
    #
161
    # @example Declare a Guard with a `watch` pattern
162
    #   guard :rspec do
163
    #     watch %r{.*_spec.rb}
164
    #   end
165
    #
166
    # @param [String] name the Guard plugin name
167
    # @param [Hash] options the options accepted by the Guard plugin
168
    # @yield a block where you can declare several watch patterns and actions
169
    #
170
    # @see Plugin
171
    # @see Guard.add_plugin
172
    # @see #watch
173
    # @see #group
174
    #
175
    def guard(name, options = {})
5✔
176
      @plugin_options = options.merge(watchers: [], callbacks: [])
48 all except jruby and rbx ✔
177

178
      yield if block_given?
48 all except jruby and rbx ✔
179

180
      @current_groups ||= []
48 all except jruby and rbx ✔
181
      groups = @current_groups && @current_groups.last || [:default]
48 all except jruby and rbx ✔
182
      groups.each do |group|
48 all except jruby and rbx ✔
183
        opts = @plugin_options.merge(group: group)
48 all except jruby and rbx ✔
184
        # TODO: let plugins be added *after* evaluation
185
        Guard.state.session.plugins.add(name, opts)
48 all except jruby and rbx ✔
186
      end
187

188
      @plugin_options = nil
48 all except jruby and rbx ✔
189
    end
190

191
    # Defines a pattern to be watched in order to run actions on file
192
    # modification.
193
    #
194
    # @example Declare watchers for a Guard
195
    #   guard :rspec do
196
    #     watch('spec/spec_helper.rb')
197
    #     watch(%r{^.+_spec.rb})
198
    #     watch(%r{^app/controllers/(.+).rb}) do |m|
199
    #       'spec/acceptance/#{m[1]}s_spec.rb'
200
    #     end
201
    #   end
202
    #
203
    # @example Declare global watchers outside of a Guard
204
    #   watch(%r{^(.+)$}) { |m| puts "#{m[1]} changed." }
205
    #
206
    # @param [String, Regexp] pattern the pattern that Guard must watch for
207
    # modification
208
    #
209
    # @yield a block to be run when the pattern is matched
210
    # @yieldparam [MatchData] m matches of the pattern
211
    # @yieldreturn a directory, a filename, an array of
212
    #   directories / filenames, or nothing (can be an arbitrary command)
213
    #
214
    # @see Guard::Watcher
215
    # @see #guard
216
    #
217
    def watch(pattern, &action)
5✔
218
      # Allow watches in the global scope (to execute arbitrary commands) by
219
      # building a generic Guard::Plugin.
220
      @plugin_options ||= nil
12 all except jruby and rbx ✔
221
      return guard(:plugin) { watch(pattern, &action) } unless @plugin_options
15 all except jruby and rbx ✔
222

223
      @plugin_options[:watchers] << Watcher.new(pattern, action)
9 all except jruby and rbx ✔
224
    end
225

226
    # Defines a callback to execute arbitrary code before or after any of
227
    # the `start`, `stop`, `reload`, `run_all`, `run_on_changes`,
228
    # `run_on_additions`, `run_on_modifications` and `run_on_removals` plugin
229
    # method.
230
    #
231
    # @example Add callback before the `reload` action.
232
    #   callback(:reload_begin) { puts "Let's reload!" }
233
    #
234
    # @example Add callback before the `start` and `stop` actions.
235
    #
236
    #   my_lambda = lambda do |plugin, event, *args|
237
    #     puts "Let's #{event} #{plugin} with #{args}!"
238
    #   end
239
    #
240
    #   callback(my_lambda, [:start_begin, :start_end])
241
    #
242
    # @param [Array] args the callback arguments
243
    # @yield a callback block
244
    #
245
    def callback(*args, &block)
5✔
246
      @plugin_options ||= nil
9 all except jruby and rbx ✔
247
      fail "callback must be called within a guard block" unless @plugin_options
9 all except jruby and rbx ✔
248

249
      block, events = if args.size > 1
6 all except jruby and rbx ✔
250
                        # block must be the first argument in that case, the
251
                        # yielded block is ignored
252
                        args
3 all except jruby and rbx ✔
253
                      else
254
                        [block, args[0]]
3 all except jruby and rbx ✔
255
                      end
256
      @plugin_options[:callbacks] << { events: events, listener: block }
6 all except jruby and rbx ✔
257
    end
258

259
    # Ignores certain paths globally.
260
    #
261
    # @example Ignore some paths
262
    #   ignore %r{^ignored/path/}, /man/
263
    #
264
    # @param [Regexp] regexps a pattern (or list of patterns) for ignoring paths
265
    #
266
    def ignore(*regexps)
5✔
267
      # TODO: use guardfile results class
268
      Guard.state.session.guardfile_ignore = regexps
9 all except jruby and rbx ✔
269
    end
270

271
    # TODO: deprecate
272
    alias filter ignore
5✔
273

274
    # Replaces ignored paths globally
275
    #
276
    # @example Ignore only these paths
277
    #   ignore! %r{^ignored/path/}, /man/
278
    #
279
    # @param [Regexp] regexps a pattern (or list of patterns) for ignoring paths
280
    #
281
    def ignore!(*regexps)
5✔
282
      @ignore_regexps ||= []
9 all except jruby and rbx ✔
283
      @ignore_regexps << regexps
9 all except jruby and rbx ✔
284
      # TODO: use guardfile results class
285
      Guard.state.session.guardfile_ignore_bang = @ignore_regexps
9 all except jruby and rbx ✔
286
    end
287

288
    # TODO: deprecate
289
    alias filter! ignore!
5✔
290

291
    # Configures the Guard logger.
292
    #
293
    # * Log level must be either `:debug`, `:info`, `:warn` or `:error`.
294
    # * Template supports the following placeholders: `:time`, `:severity`,
295
    #   `:progname`, `:pid`, `:unit_of_work_id` and `:message`.
296
    # * Time format directives are the same as `Time#strftime` or
297
    #   `:milliseconds`.
298
    # * The `:only` and `:except` options must be a `RegExp`.
299
    #
300
    # @example Set the log level
301
    #   logger level: :warn
302
    #
303
    # @example Set a custom log template
304
    #   logger template: '[Guard - :severity - :progname - :time] :message'
305
    #
306
    # @example Set a custom time format
307
    #   logger time_format: '%h'
308
    #
309
    # @example Limit logging to a Guard plugin
310
    #   logger only: :jasmine
311
    #
312
    # @example Log all but not the messages from a specific Guard plugin
313
    #   logger except: :jasmine
314
    #
315
    # @param [Hash] options the log options
316
    # @option options [String, Symbol] level the log level
317
    # @option options [String] template the logger template
318
    # @option options [String, Symbol] time_format the time format
319
    # @option options [Regexp] only show only messages from the matching Guard
320
    #   plugin
321
    # @option options [Regexp] except does not show messages from the matching
322
    #   Guard plugin
323
    #
324
    def logger(options)
5✔
325
      if options[:level]
42 all except jruby and rbx ✔
326
        options[:level] = options[:level].to_sym
12 all except jruby and rbx ✔
327

328
        unless [:debug, :info, :warn, :error].include? options[:level]
12 all except jruby and rbx ✔
329
          UI.warning WARN_INVALID_LOG_LEVEL % [options[:level]]
6 all except jruby and rbx ✔
330
          options.delete :level
6 all except jruby and rbx ✔
331
        end
332
      end
333

334
      if options[:only] && options[:except]
42 all except jruby and rbx ✔
335
        UI.warning WARN_INVALID_LOG_OPTIONS
6 all except jruby and rbx ✔
336

337
        options.delete :only
6 all except jruby and rbx ✔
338
        options.delete :except
6 all except jruby and rbx ✔
339
      end
340

341
      # Convert the :only and :except options to a regular expression
342
      [:only, :except].each do |name|
42 all except jruby and rbx ✔
343
        next unless options[name]
84 all except jruby and rbx ✔
344

345
        list = [].push(options[name]).flatten.map do |plugin|
18 all except jruby and rbx ✔
346
          Regexp.escape(plugin.to_s)
27 all except jruby and rbx ✔
347
        end
348

349
        options[name] = Regexp.new(list.join("|"), Regexp::IGNORECASE)
18 all except jruby and rbx ✔
350
      end
351

352
      UI.options.merge!(options)
42 all except jruby and rbx ✔
353
    end
354

355
    # Sets the default scope on startup
356
    #
357
    # @example Scope Guard to a single group
358
    #   scope group: :frontend
359
    #
360
    # @example Scope Guard to multiple groups
361
    #   scope groups: [:specs, :docs]
362
    #
363
    # @example Scope Guard to a single plugin
364
    #   scope plugin: :test
365
    #
366
    # @example Scope Guard to multiple plugins
367
    #   scope plugins: [:jasmine, :rspec]
368
    #
369
    # @param [Hash] scopes the scope for the groups and plugins
370
    #
371
    def scope(scope = {})
5✔
372
      # TODO: use a Guardfile::Results class
373
      Guard.state.session.guardfile_scope(scope)
3 all except jruby and rbx ✔
374
    end
375

376
    def evaluate(contents, filename, lineno) # :nodoc
5✔
377
      instance_eval(contents, filename.to_s, lineno)
132 all except jruby and rbx ✔
378
    rescue StandardError, ScriptError => e
379
      prefix = "\n\t(dsl)> "
12 all except jruby and rbx ✔
380
      cleaned_backtrace = _cleanup_backtrace(e.backtrace)
12 all except jruby and rbx ✔
381
      backtrace = "#{prefix}#{cleaned_backtrace.join(prefix)}"
12 all except jruby and rbx ✔
382
      msg = "Invalid Guardfile, original error is: \n\n%s, \nbacktrace: %s"
12 all except jruby and rbx ✔
383
      raise Error, format(msg, e, backtrace)
12 all except jruby and rbx ✔
384
    end
385

386
    # Sets the directories to pass to Listen
387
    #
388
    # @example watch only given directories
389
    #   directories %w(lib specs)
390
    #
391
    # @param [Array] directories directories for Listen to watch
392
    #
393
    def directories(directories)
5✔
394
      directories.each do |dir|
9 all except jruby and rbx ✔
395
        fail "Directory #{dir.inspect} does not exist!" unless Dir.exist?(dir)
9 all except jruby and rbx ✔
396
      end
397
      Guard.state.session.watchdirs = directories
6 all except jruby and rbx ✔
398
    end
399

400
    # Sets Guard to clear the screen before every task is run
401
    #
402
    # @example switching clearing the screen on
403
    #   clearing(:on)
404
    #
405
    # @param [Symbol] on ':on' to turn on, ':off' (default) to turn off
406
    #
407
    def clearing(on)
5✔
408
      Guard.state.session.clearing(on == :on)
6 all except jruby and rbx ✔
409
    end
410

411
    private
5✔
412

413
    def _cleanup_backtrace(backtrace)
5✔
414
      dirs = { File.realpath(Dir.pwd) => ".", }
12 all except jruby and rbx ✔
415

416
      gem_env = ENV["GEM_HOME"] || ""
12 all except jruby and rbx ✔
417
      dirs[gem_env] = "$GEM_HOME" unless gem_env.empty?
12 all except jruby and rbx ✔
418

419
      gem_paths = (ENV["GEM_PATH"] || "").split(File::PATH_SEPARATOR)
12 all except jruby and rbx ✔
420
      gem_paths.each_with_index do |path, index|
12 all except jruby and rbx ✔
421
        dirs[path] = "$GEM_PATH[#{index}]"
24 all except jruby and rbx ✔
422
      end
423

424
      backtrace.dup.map do |raw_line|
12 all except jruby and rbx ✔
425
        path = nil
450 all except jruby and rbx ✔
426
        symlinked_path = raw_line.split(":").first
450 all except jruby and rbx ✔
427
        begin
450 all except jruby and rbx ✔
428
          path = raw_line.sub(symlinked_path, File.realpath(symlinked_path))
450 all except jruby and rbx ✔
429
          dirs.detect { |dir, name| path.sub!(File.realpath(dir), name) }
1,236 all except jruby and rbx ✔
430
          path
450 all except jruby and rbx ✔
431
        rescue Errno::ENOENT
432
          path || symlinked_path
×
433
        end
434
      end
435
    end
436
  end
437
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2023 Coveralls, Inc