guard 2.8.2 → 2.9.0

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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -7
  3. data/lib/guard.rb +220 -152
  4. data/lib/guard.rb.orig +213 -155
  5. data/lib/guard/aruba_adapter.rb +2 -2
  6. data/lib/guard/cli.rb +8 -13
  7. data/lib/guard/cli.rb.orig +12 -10
  8. data/lib/guard/commander.rb +15 -7
  9. data/lib/guard/commands/all.rb +3 -0
  10. data/lib/guard/commands/change.rb +3 -0
  11. data/lib/guard/commands/pause.rb +2 -0
  12. data/lib/guard/commands/reload.rb +4 -0
  13. data/lib/guard/commands/scope.rb +3 -0
  14. data/lib/guard/config.rb +24 -0
  15. data/lib/guard/deprecated/dsl.rb +45 -0
  16. data/lib/guard/deprecated/guard.rb +166 -0
  17. data/lib/guard/deprecated/guardfile.rb +84 -0
  18. data/lib/guard/dsl.rb +24 -13
  19. data/lib/guard/dsl.rb.orig +378 -0
  20. data/lib/guard/dsl_describer.rb +8 -2
  21. data/lib/guard/dsl_describer.rb.orig +11 -3
  22. data/lib/guard/guardfile.rb +32 -44
  23. data/lib/guard/guardfile/evaluator.rb +13 -6
  24. data/lib/guard/guardfile/generator.rb +4 -3
  25. data/lib/guard/interactor.rb +7 -3
  26. data/lib/guard/internals/debugging.rb +1 -0
  27. data/lib/guard/internals/environment.rb +93 -0
  28. data/lib/guard/internals/helpers.rb +13 -0
  29. data/lib/guard/internals/traps.rb +10 -0
  30. data/lib/guard/jobs/pry_wrapper.rb +4 -3
  31. data/lib/guard/jobs/sleep.rb +2 -0
  32. data/lib/guard/metadata.rb +190 -0
  33. data/lib/guard/notifier.rb +124 -99
  34. data/lib/guard/notifier.rb.orig +124 -99
  35. data/lib/guard/notifier/detected.rb +83 -0
  36. data/lib/guard/notifiers/emacs.rb +2 -1
  37. data/lib/guard/notifiers/tmux.rb +173 -177
  38. data/lib/guard/plugin/base.rb +2 -0
  39. data/lib/guard/plugin_util.rb +26 -32
  40. data/lib/guard/reevaluator.rb +3 -3
  41. data/lib/guard/reevaluator.rb.orig +22 -0
  42. data/lib/guard/runner.rb +1 -0
  43. data/lib/guard/session.rb +5 -0
  44. data/lib/guard/sheller.rb +2 -2
  45. data/lib/guard/templates/Guardfile +4 -0
  46. data/lib/guard/templates/Guardfile.orig +2 -0
  47. data/lib/guard/terminal.rb +1 -0
  48. data/lib/guard/ui.rb +4 -1
  49. data/lib/guard/version.rb +1 -1
  50. data/lib/guard/version.rb.orig +1 -1
  51. data/lib/guard/watcher.rb +3 -1
  52. data/lib/guard/watcher.rb.orig +122 -0
  53. data/man/guard.1 +1 -4
  54. data/man/guard.1.html +1 -4
  55. metadata +17 -25
  56. data/lib/guard/commander.rb.orig +0 -103
  57. data/lib/guard/commands/all.rb.orig +0 -36
  58. data/lib/guard/commands/reload.rb.orig +0 -34
  59. data/lib/guard/commands/scope.rb.orig +0 -36
  60. data/lib/guard/deprecated_methods.rb +0 -72
  61. data/lib/guard/deprecated_methods.rb.orig +0 -71
  62. data/lib/guard/deprecator.rb +0 -133
  63. data/lib/guard/deprecator.rb.orig +0 -206
  64. data/lib/guard/guard.rb +0 -100
  65. data/lib/guard/guard.rb.orig +0 -42
  66. data/lib/guard/guardfile.rb.orig +0 -43
  67. data/lib/guard/guardfile/evaluator.rb.orig +0 -275
  68. data/lib/guard/internals/debugging.rb.orig +0 -0
  69. data/lib/guard/internals/environment.rb.orig +0 -0
  70. data/lib/guard/internals/tracing.rb.orig +0 -0
  71. data/lib/guard/notifiers/base.rb.orig +0 -221
  72. data/lib/guard/notifiers/tmux.rb.orig +0 -339
  73. data/lib/guard/plugin_util.rb.orig +0 -186
  74. data/lib/guard/runner.rb.orig +0 -210
  75. data/lib/guard/setuper.rb +0 -359
  76. data/lib/guard/setuper.rb.orig +0 -395
  77. data/lib/guard/ui.rb.orig +0 -278
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5623a43a66b46a5d8137215f562d99c05a3235a4
4
- data.tar.gz: 5d49353627874560143c223d57e2d426bc8f8639
3
+ metadata.gz: 2438b30b1e270d58f79c7f2ae9b8e2dce82fd6ad
4
+ data.tar.gz: 50e9fa2441d8eefc8aa4adb62405765086eaeda3
5
5
  SHA512:
6
- metadata.gz: 9364e851605d579463afe517ccbc56c6bde7c7b0353a5a6199f93520e23fc55ac029a923ef679af561510b4c38ddf3c947bd7d8bd2330aaf273f35573065a05a
7
- data.tar.gz: c01ce4150b9fd551263893f9ce80e768a1694afc182ded540089781041dce167f9452819768f9f621f54969a9e62d3d7710a491321663eaed2d1ab8eb01a4421
6
+ metadata.gz: d87720ec63302aaeeaacd21db3b76bc76768649af94ead543aba9cba5422f3a934db2e3d6172f7e5f3ec5d8a0ac3d7d3a7d4ba275c16adc52acdf51b2f510f4b
7
+ data.tar.gz: 09427674cac325111ae666ec7043e559c5fd4ec7f96125d9ac929e6858aa962ca40a021541b3de6c67f8eb1e49b29c4a5aede1f98daa42617c73e257381833dd
data/README.md CHANGED
@@ -291,13 +291,6 @@ $ bundle exec guard start -B
291
291
  $ bundle exec guard start --no-bundler-warning
292
292
  ```
293
293
 
294
- #### `--show-deprecations`
295
-
296
- This option is deprecated. No, seriously! Deprecations are now always shown.
297
-
298
- (To *really* hide them, set the environment variable `GUARD_GEM_SILENCE_DEPRECATIONS=1`)
299
-
300
-
301
294
  #### `-l`/`--latency` option
302
295
 
303
296
  Overwrite Listen's default latency, useful when your hard-drive / system is slow.
data/lib/guard.rb CHANGED
@@ -1,210 +1,278 @@
1
- require "rbconfig"
1
+ require "thread"
2
+ require "listen"
3
+
4
+ require "guard/config"
5
+ require "guard/deprecated/guard" unless Guard::Config.new.strict?
6
+
7
+ require "guard/internals/debugging"
8
+ require "guard/internals/traps"
9
+ require "guard/internals/helpers"
10
+
11
+ require "guard/metadata"
12
+ require "guard/options"
2
13
 
3
14
  require "guard/commander"
4
- require "guard/deprecated_methods"
5
- require "guard/deprecator"
6
15
  require "guard/dsl"
7
- require "guard/group"
8
- require "guard/guardfile"
9
16
  require "guard/interactor"
10
17
  require "guard/notifier"
11
18
  require "guard/plugin_util"
12
19
  require "guard/runner"
13
- require "guard/setuper"
14
20
  require "guard/sheller"
15
21
  require "guard/ui"
16
22
  require "guard/watcher"
17
- require "guard/reevaluator"
18
23
 
19
24
  # Guard is the main module for all Guard related modules and classes.
20
25
  # Also Guard plugins should use this namespace.
21
26
  #
22
27
  module Guard
23
- DEV_NULL = Gem.win_platform? ? "NUL" : "/dev/null"
24
-
25
- extend Commander
26
- extend DeprecatedMethods
27
- extend Setuper
28
+ Deprecated::Guard.add_deprecated(self) unless Config.new.strict?
28
29
 
29
30
  class << self
30
- # Called by Pry scope command
31
+ attr_reader :listener
31
32
 
32
- def scope=(new_scope)
33
- @scope = new_scope
34
- @scope.dup.freeze
35
- end
33
+ include Internals::Helpers
36
34
 
37
- def scope
38
- fail "::Guard.setup() not called" if @scope.nil?
39
- @scope.dup.freeze
40
- end
41
- attr_reader :runner, :listener
42
-
43
- # Smart accessor for retrieving specific plugins at once.
44
- #
45
- # @see Guard.plugin
46
- # @see Guard.group
47
- # @see Guard.groups
48
- #
49
- # @example Filter plugins by String or Symbol
50
- # Guard.plugins('rspec')
51
- # Guard.plugins(:rspec)
35
+ # Initializes the Guard singleton:
52
36
  #
53
- # @example Filter plugins by Regexp
54
- # Guard.plugins(/rsp.+/)
37
+ # * Initialize the internal Guard state;
38
+ # * Create the interactor
39
+ # * Select and initialize the file change listener.
55
40
  #
56
- # @example Filter plugins by Hash
57
- # Guard.plugins(name: 'rspec', group: 'backend')
41
+ # @option options [Boolean] clear if auto clear the UI should be done
42
+ # @option options [Boolean] notify if system notifications should be shown
43
+ # @option options [Boolean] debug if debug output should be shown
44
+ # @option options [Array<String>] group the list of groups to start
45
+ # @option options [Array<String>] watchdir the directories to watch
46
+ # @option options [String] guardfile the path to the Guardfile
58
47
  #
59
- # @param [String, Symbol, Regexp, Hash] filter the filter to apply to the
60
- # plugins
61
- # @return [Plugin, Array<Plugin>] the filtered plugin(s)
48
+ # @return [Guard] the Guard singleton
62
49
  #
63
- def plugins(filter = nil)
64
- @plugins ||= []
65
50
 
66
- return @plugins if filter.nil?
51
+ # TODO: this method has too many instance variables
52
+ # and some are mock and leak between tests,
53
+ # so ideally there should be a guard "instance"
54
+ # object that can be created anew between tests
55
+ def setup(opts = {})
56
+ # NOTE: must be set before anything calls Guard.options
57
+ reset_options(opts)
67
58
 
68
- filtered_plugins = case filter
69
- when String, Symbol
70
- @plugins.select do |plugin|
71
- plugin.name == filter.to_s.downcase.gsub("-", "")
72
- end
73
- when Regexp
74
- @plugins.select do |plugin|
75
- plugin.name =~ filter
76
- end
77
- when Hash
78
- @plugins.select do |plugin|
79
- filter.all? do |k, v|
80
- case k
81
- when :name
82
- plugin.name == v.to_s.downcase.gsub("-", "")
83
- when :group
84
- plugin.group.name == v.to_sym
85
- end
86
- end
87
- end
88
- end
59
+ # NOTE: must be set before anything calls Guard::UI.debug
60
+ ::Guard::Internals::Debugging.start if options[:debug]
89
61
 
90
- filtered_plugins
62
+ @queue = Queue.new
63
+ self.watchdirs = Array(options[:watchdir])
64
+
65
+ ::Guard::UI.reset_and_clear
66
+
67
+ _reset_all
68
+ evaluate_guardfile
69
+ setup_scope
70
+
71
+ @listener = _setup_listener
72
+
73
+ ::Guard::Notifier.connect(notify: options[:notify])
74
+
75
+ traps = Internals::Traps
76
+ traps.handle("USR1") { async_queue_add([:guard_pause, :paused]) }
77
+ traps.handle("USR2") { async_queue_add([:guard_pause, :unpaused]) }
78
+
79
+ @interactor = ::Guard::Interactor.new(options[:no_interactions])
80
+ traps.handle("INT") { @interactor.handle_interrupt }
81
+
82
+ self
91
83
  end
92
84
 
93
- # Smart accessor for retrieving a specific plugin.
94
- #
95
- # @see Guard.plugins
96
- # @see Guard.group
97
- # @see Guard.groups
98
- #
99
- # @example Find a plugin by String or Symbol
100
- # Guard.plugin('rspec')
101
- # Guard.plugin(:rspec)
102
- #
103
- # @example Find a plugin by Regexp
104
- # Guard.plugin(/rsp.+/)
105
- #
106
- # @example Find a plugin by Hash
107
- # Guard.plugin(name: 'rspec', group: 'backend')
85
+ attr_reader :interactor
86
+
87
+ # Used only by tests (for all I know...)
88
+ def clear_options
89
+ @options = nil
90
+ end
91
+
92
+ # Initializes the groups array with the default group(s).
108
93
  #
109
- # @param [String, Symbol, Regexp, Hash] filter the filter for finding the
110
- # plugin the Guard plugin
111
- # @return [Plugin, nil] the plugin found, nil otherwise
94
+ # @see DEFAULT_GROUPS
112
95
  #
113
- def plugin(filter)
114
- plugins(filter).first
96
+ def reset_groups
97
+ @groups = DEFAULT_GROUPS.map { |name| Group.new(name) }
115
98
  end
116
99
 
117
- # Smart accessor for retrieving specific groups at once.
100
+ # Initializes the plugins array to an empty array.
118
101
  #
119
- # @see Guard.plugin
120
102
  # @see Guard.plugins
121
- # @see Guard.group
122
103
  #
123
- # @example Filter groups by String or Symbol
124
- # Guard.groups('backend')
125
- # Guard.groups(:backend)
104
+ def reset_plugins
105
+ @plugins = []
106
+ end
107
+
108
+ attr_reader :watchdirs
109
+
110
+ # Stores the scopes defined by the user via the `--group` / `-g` option (to
111
+ # run only a specific group) or the `--plugin` / `-P` option (to run only a
112
+ # specific plugin).
113
+ #
114
+ # @see CLI#start
115
+ # @see Dsl#scope
126
116
  #
127
- # @example Filter groups by Regexp
128
- # Guard.groups(/(back|front)end/)
117
+ def setup_scope(scope = {})
118
+ # TODO: there should be a special Scope class instead
119
+ scope = _prepare_scope(scope)
120
+
121
+ ::Guard.scope = {
122
+ groups: scope[:groups].map { |item| ::Guard.add_group(item) },
123
+ plugins: scope[:plugins].map { |item| ::Guard.plugin(item) },
124
+ }
125
+ end
126
+
127
+ # Evaluates the Guardfile content. It displays an error message if no
128
+ # Guard plugins are instantiated after the Guardfile evaluation.
129
129
  #
130
- # @param [String, Symbol, Regexp] filter the filter to apply to the Groups
131
- # @return [Array<Group>] the filtered group(s)
130
+ # @see Guard::Guardfile::Evaluator#evaluate_guardfile
132
131
  #
133
- def groups(filter = nil)
134
- @groups ||= []
132
+ def evaluate_guardfile
133
+ evaluator = Guard::Guardfile::Evaluator.new(options)
134
+ evaluator.evaluate_guardfile
135
135
 
136
- return @groups if filter.nil?
136
+ # FIXME: temporary hack while due to upcoming refactorings
137
+ options[:guardfile] = evaluator.guardfile_path
137
138
 
138
- case filter
139
- when String, Symbol
140
- @groups.select { |group| group.name == filter.to_sym }
141
- when Regexp
142
- @groups.select { |group| group.name.to_s =~ filter }
143
- else
144
- fail "Invalid filter: #{filter.inspect}"
145
- end
139
+ msg = "No plugins found in Guardfile, please add at least one."
140
+ ::Guard::UI.error msg if _pluginless_guardfile?
146
141
  end
147
142
 
148
- # Smart accessor for retrieving a specific group.
149
- #
150
- # @see Guard.plugin
151
- # @see Guard.plugins
152
- # @see Guard.groups
143
+ # @private api
144
+ # used solely for match_guardfile?
145
+ attr_reader :guardfile_path
146
+
147
+ # Asynchronously trigger changes
153
148
  #
154
- # @example Find a group by String or Symbol
155
- # Guard.group('backend')
156
- # Guard.group(:backend)
149
+ # Currently supported args:
157
150
  #
158
- # @example Find a group by Regexp
159
- # Guard.group(/(back|front)end/)
151
+ # old style hash: {modified: ['foo'], added: ['bar'], removed: []}
160
152
  #
161
- # @param [String, Symbol, Regexp] filter the filter for finding the group
162
- # @return [Group] the group found, nil otherwise
153
+ # new style signals with args: [:guard_pause, :unpaused ]
163
154
  #
164
- def group(filter)
165
- groups(filter).first
155
+ def async_queue_add(changes)
156
+ @queue << changes
157
+
158
+ # Putting interactor in background puts guard into foreground
159
+ # so it can handle change notifications
160
+ Thread.new { interactor.background }
166
161
  end
167
162
 
168
- # Add a Guard plugin to use.
169
- #
170
- # @param [String] name the Guard name
171
- # @param [Hash] options the plugin options (see Plugin documentation)
172
- # @option options [String] group the group to which the plugin belongs
173
- # @option options [Array<Watcher>] watchers the list of declared watchers
174
- # @option options [Array<Hash>] callbacks the list of callbacks
175
- # @return [Plugin] the added Guard plugin
176
- # @see Plugin
177
- #
178
- def add_plugin(name, options = {})
179
- instance = ::Guard::PluginUtil.new(name).initialize_plugin(options)
180
- @plugins << instance
181
- instance
163
+ def pending_changes?
164
+ ! @queue.empty?
182
165
  end
183
166
 
184
- # Used by runner to remove a failed plugin
185
- def remove_plugin(plugin)
186
- # TODO: coverage/aruba
187
- @plugins.delete(plugin)
167
+ def watchdirs=(dirs)
168
+ dirs = [Dir.pwd] if dirs.empty?
169
+ @watchdirs = dirs.map { |dir| File.expand_path dir }
188
170
  end
189
171
 
190
- # Add a Guard plugin group.
191
- #
192
- # @param [String] name the group name
193
- # @option options [Boolean] halt_on_fail if a task execution
194
- # should be halted for all Guard plugins in this group if
195
- # one Guard throws `:task_has_failed`
196
- # @return [Group] the group added (or retrieved from the `@groups`
197
- # variable if already present)
198
- #
199
- # @see Group
172
+ private
173
+
174
+ # Initializes the listener and registers a callback for changes.
200
175
  #
201
- def add_group(name, options = {})
202
- unless (group = group(name))
203
- group = ::Guard::Group.new(name, options)
204
- @groups << group
176
+ def _setup_listener
177
+ if options[:listen_on]
178
+ Listen.on(options[:listen_on], &_listener_callback)
179
+ else
180
+ listener_options = {}
181
+ [:latency, :force_polling, :wait_for_delay].each do |option|
182
+ listener_options[option] = options[option] if options[option]
183
+ end
184
+ listen_args = watchdirs + [listener_options]
185
+ Listen.to(*listen_args, &_listener_callback)
186
+ end
187
+ end
188
+
189
+ # Process the change queue, running tasks within the main Guard thread
190
+ def _process_queue
191
+ actions, changes = [], { modified: [], added: [], removed: [] }
192
+
193
+ while pending_changes?
194
+ if (item = @queue.pop).first.is_a?(Symbol)
195
+ actions << item
196
+ else
197
+ item.each { |key, value| changes[key] += value }
198
+ end
199
+ end
200
+
201
+ _run_actions(actions)
202
+ return if changes.values.all?(&:empty?)
203
+ Runner.new.run_on_changes(*changes.values)
204
+ end
205
+
206
+ # TODO: Guard::Watch or Guard::Scope should provide this
207
+ def _scoped_watchers
208
+ watchers = []
209
+ Runner.new.send(:_scoped_plugins) { |guard| watchers += guard.watchers }
210
+ watchers
211
+ end
212
+
213
+ # Check if any of the changes are actually watched for
214
+ def _relevant_changes?(changes)
215
+ files = changes.values.flatten(1)
216
+ watchers = _scoped_watchers
217
+ watchers.any? { |watcher| files.any? { |file| watcher.match(file) } }
218
+ end
219
+
220
+ def _relative_pathnames(paths)
221
+ paths.map { |path| _relative_pathname(path) }
222
+ end
223
+
224
+ def _run_actions(actions)
225
+ actions.each do |action_args|
226
+ args = action_args.dup
227
+ namespaced_action = args.shift
228
+ action = namespaced_action.to_s.sub(/^guard_/, "")
229
+ if ::Guard.respond_to?(action)
230
+ ::Guard.send(action, *args)
231
+ else
232
+ fail "Unknown action: #{action.inspect}"
233
+ end
205
234
  end
235
+ end
236
+
237
+ def _listener_callback
238
+ lambda do |modified, added, removed|
239
+ relative_paths = {
240
+ modified: _relative_pathnames(modified),
241
+ added: _relative_pathnames(added),
242
+ removed: _relative_pathnames(removed)
243
+ }
244
+
245
+ async_queue_add(relative_paths) if _relevant_changes?(relative_paths)
246
+ end
247
+ end
248
+
249
+ def _reset_all
250
+ reset_groups
251
+ reset_plugins
252
+ reset_scope
253
+ end
254
+
255
+ def _pluginless_guardfile?
256
+ # no Reevaluator means there was no Guardfile configured that could be
257
+ # reevaluated, so we don't have a pluginless guardfile, because we don't
258
+ # have a Guardfile to begin with...
259
+ #
260
+ # But, if we have a Guardfile, we'll at least have the built-in
261
+ # Reevaluator, so the following will work:
262
+
263
+ # TODO: this is a workaround for tests
264
+ return true if plugins.empty?
265
+
266
+ plugins.map(&:name) == ["reevaluator"]
267
+ end
206
268
 
207
- group
269
+ def _reset_for_tests
270
+ @options = nil
271
+ @queue = nil
272
+ @watchdirs = nil
273
+ @listener = nil
274
+ @interactor = nil
275
+ @scope = nil
208
276
  end
209
277
  end
210
278
  end