guard 1.4.0 → 2.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -677
  3. data/LICENSE +4 -2
  4. data/README.md +91 -753
  5. data/bin/_guard-core +11 -0
  6. data/bin/guard +108 -3
  7. data/lib/guard/aruba_adapter.rb +59 -0
  8. data/lib/guard/cli/environments/bundler.rb +22 -0
  9. data/lib/guard/cli/environments/evaluate_only.rb +35 -0
  10. data/lib/guard/cli/environments/valid.rb +69 -0
  11. data/lib/guard/cli.rb +129 -128
  12. data/lib/guard/commander.rb +104 -0
  13. data/lib/guard/commands/all.rb +37 -0
  14. data/lib/guard/commands/change.rb +31 -0
  15. data/lib/guard/commands/notification.rb +26 -0
  16. data/lib/guard/commands/pause.rb +29 -0
  17. data/lib/guard/commands/reload.rb +36 -0
  18. data/lib/guard/commands/scope.rb +38 -0
  19. data/lib/guard/commands/show.rb +24 -0
  20. data/lib/guard/config.rb +18 -0
  21. data/lib/guard/deprecated/dsl.rb +45 -0
  22. data/lib/guard/deprecated/evaluator.rb +39 -0
  23. data/lib/guard/deprecated/guard.rb +328 -0
  24. data/lib/guard/deprecated/guardfile.rb +84 -0
  25. data/lib/guard/deprecated/watcher.rb +27 -0
  26. data/lib/guard/dsl.rb +332 -363
  27. data/lib/guard/dsl_describer.rb +132 -122
  28. data/lib/guard/dsl_reader.rb +51 -0
  29. data/lib/guard/group.rb +34 -14
  30. data/lib/guard/guardfile/evaluator.rb +232 -0
  31. data/lib/guard/guardfile/generator.rb +128 -0
  32. data/lib/guard/guardfile.rb +24 -60
  33. data/lib/guard/interactor.rb +31 -255
  34. data/lib/guard/internals/debugging.rb +68 -0
  35. data/lib/guard/internals/groups.rb +40 -0
  36. data/lib/guard/internals/helpers.rb +13 -0
  37. data/lib/guard/internals/plugins.rb +53 -0
  38. data/lib/guard/internals/queue.rb +51 -0
  39. data/lib/guard/internals/scope.rb +121 -0
  40. data/lib/guard/internals/session.rb +180 -0
  41. data/lib/guard/internals/state.rb +25 -0
  42. data/lib/guard/internals/tracing.rb +33 -0
  43. data/lib/guard/internals/traps.rb +10 -0
  44. data/lib/guard/jobs/base.rb +21 -0
  45. data/lib/guard/jobs/pry_wrapper.rb +336 -0
  46. data/lib/guard/jobs/sleep.rb +26 -0
  47. data/lib/guard/notifier.rb +46 -212
  48. data/lib/guard/options.rb +22 -0
  49. data/lib/guard/plugin.rb +303 -0
  50. data/lib/guard/plugin_util.rb +191 -0
  51. data/lib/guard/rake_task.rb +42 -0
  52. data/lib/guard/runner.rb +80 -140
  53. data/lib/guard/templates/Guardfile +14 -0
  54. data/lib/guard/terminal.rb +13 -0
  55. data/lib/guard/ui/colors.rb +56 -0
  56. data/lib/guard/ui/config.rb +70 -0
  57. data/lib/guard/ui/logger.rb +30 -0
  58. data/lib/guard/ui.rb +163 -128
  59. data/lib/guard/version.rb +1 -2
  60. data/lib/guard/watcher/pattern/deprecated_regexp.rb +45 -0
  61. data/lib/guard/watcher/pattern/match_result.rb +18 -0
  62. data/lib/guard/watcher/pattern/matcher.rb +33 -0
  63. data/lib/guard/watcher/pattern/pathname_path.rb +15 -0
  64. data/lib/guard/watcher/pattern/simple_path.rb +23 -0
  65. data/lib/guard/watcher/pattern.rb +24 -0
  66. data/lib/guard/watcher.rb +52 -95
  67. data/lib/guard.rb +108 -376
  68. data/lib/tasks/releaser.rb +116 -0
  69. data/man/guard.1 +12 -9
  70. data/man/guard.1.html +18 -12
  71. metadata +148 -77
  72. data/images/guard.png +0 -0
  73. data/lib/guard/guard.rb +0 -156
  74. data/lib/guard/hook.rb +0 -120
  75. data/lib/guard/interactors/coolline.rb +0 -64
  76. data/lib/guard/interactors/helpers/completion.rb +0 -32
  77. data/lib/guard/interactors/helpers/terminal.rb +0 -46
  78. data/lib/guard/interactors/readline.rb +0 -94
  79. data/lib/guard/interactors/simple.rb +0 -19
  80. data/lib/guard/notifiers/emacs.rb +0 -69
  81. data/lib/guard/notifiers/gntp.rb +0 -118
  82. data/lib/guard/notifiers/growl.rb +0 -99
  83. data/lib/guard/notifiers/growl_notify.rb +0 -92
  84. data/lib/guard/notifiers/libnotify.rb +0 -96
  85. data/lib/guard/notifiers/notifysend.rb +0 -84
  86. data/lib/guard/notifiers/rb_notifu.rb +0 -102
  87. data/lib/guard/notifiers/terminal_notifier.rb +0 -66
  88. data/lib/guard/notifiers/tmux.rb +0 -69
  89. data/lib/guard/version.rbc +0 -130
@@ -0,0 +1,128 @@
1
+ require "guard/ui"
2
+ require "guard/plugin_util"
3
+
4
+ # Add Pathname#binwrite to 1.9.3
5
+ unless Pathname.instance_methods.include?(:binwrite)
6
+ class Pathname
7
+ def binwrite(*args)
8
+ IO.binwrite(to_s, *args)
9
+ end
10
+ end
11
+ end
12
+
13
+ module Guard
14
+ module Guardfile
15
+ # This class is responsible for generating the Guardfile and adding Guard'
16
+ # plugins' templates into it.
17
+ #
18
+ # @see Guard::CLI
19
+ #
20
+ class Generator
21
+ require "guard"
22
+ require "guard/ui"
23
+
24
+ INFO_TEMPLATE_ADDED =
25
+ "%s template added to Guardfile, feel free to edit it"
26
+
27
+ # The Guardfile template for `guard init`
28
+ GUARDFILE_TEMPLATE = File.expand_path(
29
+ "../../../guard/templates/Guardfile",
30
+ __FILE__
31
+ )
32
+
33
+ # The location of user defined templates
34
+ begin
35
+ HOME_TEMPLATES = Pathname.new("~/.guard/templates").expand_path
36
+ rescue ArgumentError
37
+ # home isn't defined. Set to the root of the drive. Trust that there
38
+ # won't be user defined templates there
39
+ HOME_TEMPLATES = Pathname.new("/").expand_path
40
+ end
41
+
42
+ class Error < RuntimeError
43
+ end
44
+
45
+ class NoSuchPlugin < Error
46
+ attr_reader :plugin_name, :class_name
47
+
48
+ def initialize(plugin_name)
49
+ @plugin_name = plugin_name
50
+ @class_name = plugin_name.delete("-").capitalize
51
+ end
52
+
53
+ def message
54
+ "Could not load 'guard/#{plugin_name}'"\
55
+ " or '~/.guard/templates/#{plugin_name}'"\
56
+ " or find class Guard::#{class_name}\n"
57
+ end
58
+ end
59
+
60
+ # Creates the initial Guardfile template when it does not
61
+ # already exist.
62
+ #
63
+ # @see Guard::CLI#init
64
+ #
65
+ def create_guardfile
66
+ path = Pathname.new("Guardfile").expand_path
67
+ if path.exist?
68
+ _ui(:error, "Guardfile already exists at #{path}")
69
+ abort
70
+ end
71
+
72
+ _ui(:info, "Writing new Guardfile to #{path}")
73
+ FileUtils.cp(GUARDFILE_TEMPLATE, path.to_s)
74
+ end
75
+
76
+ # Adds the Guardfile template of a Guard plugin to an existing Guardfile.
77
+ #
78
+ # @see Guard::CLI#init
79
+ #
80
+ # @param [String] plugin_name the name of the Guard plugin or template to
81
+ # initialize
82
+ #
83
+ def initialize_template(plugin_name)
84
+ guardfile = Pathname.new("Guardfile")
85
+
86
+ plugin_util = PluginUtil.new(plugin_name)
87
+ # TODO: change to "valid?" method
88
+ plugin_class = plugin_util.plugin_class(fail_gracefully: true)
89
+ if plugin_class
90
+ begin
91
+ plugin_util.add_to_guardfile
92
+ rescue Errno::ENOENT => error
93
+ # TODO: refactor
94
+ template = plugin_class.template(plugin_util.plugin_location)
95
+ _ui(:error, "Found class #{plugin_class} but loading it's template"\
96
+ "failed: '#{template}'")
97
+ _ui(:error, "Error is: #{error}")
98
+ return
99
+ end
100
+ return
101
+ end
102
+
103
+ template_code = (HOME_TEMPLATES + plugin_name).read
104
+ guardfile.binwrite(format("\n%s\n", template_code), open_args: ["a"])
105
+
106
+ _ui(:info, format(INFO_TEMPLATE_ADDED, plugin_name))
107
+
108
+ rescue Errno::ENOENT
109
+ fail NoSuchPlugin, plugin_name.downcase
110
+ end
111
+
112
+ # Adds the templates of all installed Guard implementations to an
113
+ # existing Guardfile.
114
+ #
115
+ # @see Guard::CLI#init
116
+ #
117
+ def initialize_all_templates
118
+ PluginUtil.plugin_names.each { |g| initialize_template(g) }
119
+ end
120
+
121
+ private
122
+
123
+ def _ui(*args)
124
+ UI.send(*args)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -1,73 +1,37 @@
1
- module Guard
1
+ require "guard/config"
2
2
 
3
- # The Guardfile is responsible for generating the Guardfile
4
- # and adding guards' template into it.
5
- #
6
- # @see Guard::CLI
7
- #
8
- class Guardfile
3
+ if Guard::Config.new.strict?
4
+ abort "Error: Deprecated file #{__FILE__} is being used"
5
+ else
6
+ require "guard/deprecated/guardfile"
9
7
 
10
- require 'guard'
11
- require 'guard/ui'
8
+ # TODO: remove this file in next major version
12
9
 
13
- class << self
10
+ module Guard
11
+ unless Guard::Config.new.silence_deprecations?
12
+ UPGRADE_WIKI_URL =
13
+ "https://github.com/guard/guard/wiki/Upgrading-to-Guard-2.0"
14
14
 
15
- # Creates the initial Guardfile template when it does not
16
- # already exist.
17
- #
18
- # @see Guard::CLI.init
19
- #
20
- # @param [Hash] options The options for creating a Guardfile
21
- # @option options [Boolean] :abort_on_existence Whether to abort or not when a Guardfile already exists
22
- #
23
- def create_guardfile(options = {})
24
- if !File.exist?('Guardfile')
25
- ::Guard::UI.info "Writing new Guardfile to #{ Dir.pwd }/Guardfile"
26
- FileUtils.cp(GUARDFILE_TEMPLATE, 'Guardfile')
27
- elsif options[:abort_on_existence]
28
- ::Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
29
- abort
30
- end
31
- end
15
+ STDERR.puts <<-EOS
16
+ (guard/guardfile.rb message)
32
17
 
33
- # Adds the Guardfile template of a Guard implementation
34
- # to an existing Guardfile.
35
- #
36
- # @see Guard::CLI.init
37
- #
38
- # @param [String] guard_name the name of the Guard or template to initialize
39
- #
40
- def initialize_template(guard_name)
41
- guard_class = ::Guard.get_guard_class(guard_name, true)
18
+ You are including "guard/guardfile.rb", which has been deprecated
19
+ since 2013 ... and will be removed.
42
20
 
43
- if guard_class
44
- guard_class.init(guard_name)
45
- elsif File.exist?(File.join(HOME_TEMPLATES, guard_name))
46
- content = File.read('Guardfile')
47
- template = File.read(File.join(HOME_TEMPLATES, guard_name))
21
+ Migration is easy, see: #{UPGRADE_WIKI_URL}
48
22
 
49
- File.open('Guardfile', 'wb') do |f|
50
- f.puts(content)
51
- f.puts('')
52
- f.puts(template)
53
- end
23
+ This file was included from:
24
+ #{caller[0..10] * "\n >"}
54
25
 
55
- ::Guard::UI.info "#{ guard_name } template added to Guardfile, feel free to edit it"
56
- else
57
- const_name = guard_name.downcase.gsub('-', '')
58
- UI.error "Could not load 'guard/#{ guard_name.downcase }' or '~/.guard/templates/#{ guard_name.downcase }' or find class Guard::#{ const_name.capitalize }"
59
- end
60
- end
26
+ Sorry for the inconvenience and have a nice day!
61
27
 
62
- # Adds the templates of all installed Guard implementations
63
- # to an existing Guardfile.
64
- #
65
- # @see Guard::CLI.init
66
- #
67
- def initialize_all_templates
68
- ::Guard.guard_gem_names.each { |g| initialize_template(g) }
69
- end
28
+ (end of guard/guardfile.rb message)
70
29
 
30
+
31
+ EOS
32
+ end
33
+ module Guardfile
34
+ extend Deprecated::Guardfile::ClassMethods
71
35
  end
72
36
  end
73
37
  end
@@ -1,277 +1,53 @@
1
- module Guard
1
+ require "forwardable"
2
2
 
3
- # The interactor triggers specific action from input
4
- # read by a interactor implementation.
5
- #
6
- # Currently the following actions are implemented:
7
- #
8
- # - h, help => Show help
9
- # - e, exit,
10
- # q. quit => Exit Guard
11
- # - r, reload => Reload Guard
12
- # - p, pause => Toggle file modification listener
13
- # - n, notification => Toggle notifications
14
- # - s, show => Show Guard plugin configuration
15
- # - c, change => Trigger a file change
16
- # - <enter> => Run all
17
- #
18
- # It's also possible to scope `reload` and `run all` actions to only a specified group or a guard.
19
- #
20
- # @example Reload backend group
21
- # backend reload
22
- # reload backend
23
- #
24
- # @example Reload rspec guard
25
- # spork reload
26
- # reload spork
27
- #
28
- # @example Run all jasmine specs
29
- # jasmine
30
- #
31
- # @abstract
32
- #
3
+ module Guard
33
4
  class Interactor
34
-
35
- require 'guard'
36
- require 'guard/ui'
37
- require 'guard/dsl_describer'
38
- require 'guard/notifier'
39
- require 'guard/interactors/readline'
40
- require 'guard/interactors/coolline'
41
- require 'guard/interactors/simple'
42
-
43
- ACTIONS = {
44
- :help => %w[help h],
45
- :reload => %w[reload r],
46
- :stop => %w[exit e quit q],
47
- :pause => %w[pause p],
48
- :notification => %w[notification n],
49
- :show => %w[show s],
50
- :change => %w[change c]
51
- }
52
-
53
- # Set the interactor implementation
54
- #
55
- # @param [Symbol] interactor the name of the interactor
5
+ # Initializes the interactor. This configures
6
+ # Pry and creates some custom commands and aliases
7
+ # for Guard.
56
8
  #
57
- def self.interactor=(interactor)
58
- @interactor = interactor
59
- end
9
+ def initialize(no_interaction = false)
10
+ @interactive = !no_interaction && self.class.enabled?
60
11
 
61
- # Get an instance of the currently configured
62
- # interactor implementation.
63
- #
64
- # @return [Interactor] an interactor implementation
65
- #
66
- def self.fabricate
67
- case @interactor
68
- when :coolline
69
- ::Guard::CoollineInteractor.new if ::Guard::CoollineInteractor.available?
70
- when :readline
71
- ::Guard::ReadlineInteractor.new if ::Guard::ReadlineInteractor.available?
72
- when :simple
73
- ::Guard::SimpleInteractor.new
74
- when :off
75
- nil
76
- else
77
- auto_detect
78
- end
79
- end
80
-
81
- # Tries to detect an optimal interactor for the
82
- # current environment.
83
- #
84
- # It returns the Readline implementation when:
85
- #
86
- # * rb-readline is installed
87
- # * The Ruby implementation is JRuby
88
- # * The current OS is not Mac OS X
89
- #
90
- # Otherwise the plain gets interactor is returned.
91
- #
92
- # @return [Interactor] an interactor implementation
93
- #
94
- def self.auto_detect
95
- [::Guard::CoollineInteractor, ::Guard::ReadlineInteractor, ::Guard::SimpleInteractor].detect do |interactor|
96
- interactor.available?(true)
97
- end.new
98
- end
12
+ # TODO: only require the one used
13
+ require "guard/jobs/sleep"
14
+ require "guard/jobs/pry_wrapper"
99
15
 
100
- # Template method for checking if the Interactor is
101
- # available in the current environment?
102
- #
103
- # @param [Boolean] silent true if no error messages should be shown
104
- # @return [Boolean] the availability status
105
- #
106
- def self.available?(silent = false)
107
- true
16
+ job_klass = interactive? ? Jobs::PryWrapper : Jobs::Sleep
17
+ @idle_job = job_klass.new(self.class.options)
108
18
  end
109
19
 
110
- # Start the line reader in its own thread.
111
- #
112
- def start
113
- return if ENV['GUARD_ENV'] == 'test'
114
-
115
- ::Guard::UI.debug 'Start interactor'
116
- @thread = Thread.new { read_line } if !@thread || !@thread.alive?
20
+ def interactive?
21
+ @interactive
117
22
  end
118
23
 
119
- # Kill interactor thread if not current
120
- #
121
- def stop
122
- return if !@thread || ENV['GUARD_ENV'] == 'test'
24
+ extend Forwardable
25
+ delegate [:foreground, :background, :handle_interrupt] => :idle_job
123
26
 
124
- ::Guard::UI.debug 'Stop interactor'
125
- unless Thread.current == @thread
126
- @thread.kill
27
+ # TODO: everything below is just so the DSL can set options
28
+ # before setup() is called, which makes it useless for when
29
+ # Guardfile is reevaluated
30
+ class << self
31
+ def options
32
+ @options ||= {}
127
33
  end
128
- end
129
34
 
130
- # Read the user input. This method must be implemented
131
- # by each interactor implementation.
132
- #
133
- # @abstract
134
- #
135
- def read_line
136
- raise NotImplementedError
137
- end
138
-
139
- # Process the input from readline.
140
- #
141
- # @param [String] line the input line
142
- #
143
- def process_input(line)
144
- scopes, action, rest = extract_scopes_and_action(line)
35
+ # Pass options to interactor's job when it's created
36
+ attr_writer :options
145
37
 
146
- case action
147
- when :help
148
- help
149
- when :show
150
- ::Guard::DslDescriber.show(::Guard.options)
151
- when :stop
152
- ::Guard.stop
153
- exit
154
- when :pause
155
- ::Guard.pause
156
- when :reload
157
- ::Guard.reload(scopes)
158
- when :change
159
- ::Guard.within_preserved_state do
160
- ::Guard.runner.run_on_changes(rest, [], [])
161
- end
162
- when :run_all
163
- ::Guard.run_all(scopes)
164
- when :notification
165
- toggle_notification
166
- else
167
- ::Guard::UI.error "Unknown command #{ line }"
38
+ # TODO: allow custom user idle jobs, e.g. [:pry, :sleep, :exit, ...]
39
+ def enabled?
40
+ @enabled || @enabled.nil?
168
41
  end
169
- end
170
42
 
171
- # Toggle the system notifications on/off
172
- #
173
- def toggle_notification
174
- if ENV['GUARD_NOTIFY'] == 'true'
175
- ::Guard::UI.info 'Turn off notifications'
176
- ::Guard::Notifier.turn_off
177
- else
178
- ::Guard::Notifier.turn_on
179
- end
180
- end
43
+ alias_method :enabled, :enabled?
181
44
 
182
- # Show the help.
183
- #
184
- def help
185
- puts ''
186
- puts '[e]xit, [q]uit Exit Guard'
187
- puts '[p]ause Toggle file modification listener'
188
- puts '[r]eload Reload Guard'
189
- puts '[n]otification Toggle notifications'
190
- puts '[s]how Show available Guard plugins'
191
- puts '[c]hange <file> Trigger a file change'
192
- puts '<enter> Run all Guard plugins'
193
- puts ''
194
- puts 'You can scope the reload action to a specific guard or group:'
195
- puts ''
196
- puts 'rspec reload Reload the RSpec Guard'
197
- puts 'backend reload Reload the backend group'
198
- puts ''
199
- puts 'You can also run only a specific Guard or all Guard plugins in a specific group:'
200
- puts ''
201
- puts 'jasmine Run the jasmine Guard'
202
- puts 'frontend Run all Guard plugins in the frontend group'
203
- puts ''
204
- end
205
-
206
- # Extract the Guard or group scope and action from the
207
- # input line. There's no strict order for scopes and
208
- # actions.
209
- #
210
- # @example `spork reload` will only reload rspec
211
- # @example `jasmine` will only run all jasmine specs
212
- #
213
- # @param [String] line the readline input
214
- # @return [Array] the group or guard scope, the action and the rest
215
- #
216
- def extract_scopes_and_action(line)
217
- entries = line.split(' ')
218
-
219
- scopes = extract_scopes(entries)
220
- action = extract_action(entries)
221
-
222
- action = :run_all if !action && (!scopes.empty? || entries.empty?)
223
-
224
- [scopes, action, entries]
45
+ # TODO: handle switching interactors during runtime?
46
+ attr_writer :enabled
225
47
  end
226
48
 
227
49
  private
228
50
 
229
- # Extract a guard or group scope from entry if valid.
230
- # Any entry found will be removed from the entries.
231
- #
232
- # @param [Array<String>] entries the user entries
233
- # @return [Hash] a hash with a Guard or a group scope
234
- #
235
- def extract_scopes(entries)
236
- scopes = { }
237
-
238
- entries.delete_if do |entry|
239
- if guard = ::Guard.guards(entry)
240
- scopes[:guard] ||= guard
241
- true
242
-
243
- elsif group = ::Guard.groups(entry)
244
- scopes[:group] ||= group
245
- true
246
-
247
- else
248
- false
249
- end
250
- end
251
-
252
- scopes
253
- end
254
-
255
- # Find the action for the given input entry.
256
- # Any action found will be removed from the entries.
257
- #
258
- # @param [Array<String>] entries the user entries
259
- # @return [Symbol] a Guard action
260
- #
261
- def extract_action(entries)
262
- action = nil
263
-
264
- entries.delete_if do |entry|
265
- if command = ACTIONS.detect { |k, list| list.include?(entry) }
266
- action ||= command.first
267
- true
268
- else
269
- false
270
- end
271
- end
272
-
273
- action
274
- end
275
-
51
+ attr_reader :idle_job
276
52
  end
277
53
  end
@@ -0,0 +1,68 @@
1
+ # Because it's used by Sheller
2
+ require "open3"
3
+ require "logger"
4
+
5
+ require "guard/ui"
6
+
7
+ require "guard/internals/tracing"
8
+
9
+ module Guard
10
+ # @private api
11
+ module Internals
12
+ class Debugging
13
+ class << self
14
+ TRACES = [
15
+ [Kernel, :system],
16
+ [Kernel, :spawn],
17
+ [Kernel, :`],
18
+ [Open3, :popen3]
19
+ ]
20
+
21
+ # Sets up debugging:
22
+ #
23
+ # * aborts on thread exceptions
24
+ # * Set the logging level to `:debug`
25
+ # * traces execution of Kernel.system and backtick calls
26
+ def start
27
+ return if @started ||= false
28
+ @started = true
29
+
30
+ Thread.abort_on_exception = true
31
+
32
+ UI.level = Logger::DEBUG
33
+
34
+ TRACES.each { |mod, meth| _trace(mod, meth, &method(:_notify)) }
35
+ @traced = true
36
+ end
37
+
38
+ def stop
39
+ return unless @started ||= false
40
+ UI.level = Logger::INFO
41
+ _reset
42
+ end
43
+
44
+ private
45
+
46
+ def _notify(*args)
47
+ UI.debug "Command execution: #{args.join(' ')}"
48
+ end
49
+
50
+ # reset singleton - called by tests
51
+ def _reset
52
+ @started = false
53
+ return unless @traced
54
+ TRACES.each { |mod, meth| _untrace(mod, meth) }
55
+ @traced = false
56
+ end
57
+
58
+ def _trace(mod, meth, &block)
59
+ Tracing.trace(mod, meth, &block)
60
+ end
61
+
62
+ def _untrace(mod, meth)
63
+ Tracing.untrace(mod, meth)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,40 @@
1
+ require "guard/group"
2
+
3
+ module Guard
4
+ # @private api
5
+ module Internals
6
+ class Groups
7
+ DEFAULT_GROUPS = [:common, :default]
8
+
9
+ def initialize
10
+ @groups = DEFAULT_GROUPS.map { |name| Group.new(name) }
11
+ end
12
+
13
+ def all(filter = nil)
14
+ return @groups if filter.nil?
15
+ matcher = matcher_for(filter)
16
+ @groups.select { |group| matcher.call(group) }
17
+ end
18
+
19
+ def add(name, options = {})
20
+ all(name).first || Group.new(name, options).tap do |group|
21
+ fail if name == :specs && options.empty?
22
+ @groups << group
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def matcher_for(filter)
29
+ case filter
30
+ when String, Symbol
31
+ ->(group) { group.name == filter.to_sym }
32
+ when Regexp
33
+ ->(group) { group.name.to_s =~ filter }
34
+ else
35
+ fail "Invalid filter: #{filter.inspect}"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ module Guard
2
+ # @private api
3
+ module Internals
4
+ module Helpers
5
+ def _relative_pathname(path)
6
+ full_path = Pathname(path)
7
+ full_path.relative_path_from(Pathname.pwd)
8
+ rescue ArgumentError
9
+ full_path
10
+ end
11
+ end
12
+ end
13
+ end