guard 2.8.2 → 2.9.0

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