guard 2.9.0 → 2.9.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2438b30b1e270d58f79c7f2ae9b8e2dce82fd6ad
4
- data.tar.gz: 50e9fa2441d8eefc8aa4adb62405765086eaeda3
3
+ metadata.gz: d5b1ccadd885e53f1a8063e6f6d78038874115e7
4
+ data.tar.gz: ed83d150185583201d3d6a76ebadc54172c94573
5
5
  SHA512:
6
- metadata.gz: d87720ec63302aaeeaacd21db3b76bc76768649af94ead543aba9cba5422f3a934db2e3d6172f7e5f3ec5d8a0ac3d7d3a7d4ba275c16adc52acdf51b2f510f4b
7
- data.tar.gz: 09427674cac325111ae666ec7043e559c5fd4ec7f96125d9ac929e6858aa962ca40a021541b3de6c67f8eb1e49b29c4a5aede1f98daa42617c73e257381833dd
6
+ metadata.gz: 30542f519409c98e17fe922651fb5d461ed771803b46833fd918ae74576bc0e2b5aefe23ec60686d55408fe1bcba687fa587a2f569ae29ede158d915dbbfd0d7
7
+ data.tar.gz: 8e352a2a2b4ec695b4ed2dd029bb5a1b46e38fcd308c2dddbcfcc007395b29498c7e0bbc28caec0c579482f1e3bd7326526f04a413f4a3ea972465cc7770383f
@@ -8,11 +8,13 @@ require "guard/internals/debugging"
8
8
  require "guard/internals/traps"
9
9
  require "guard/internals/helpers"
10
10
 
11
- require "guard/metadata"
12
- require "guard/options"
11
+ require "guard/internals/queue"
12
+ require "guard/internals/state"
13
13
 
14
+ require "guard/options"
14
15
  require "guard/commander"
15
16
  require "guard/dsl"
17
+ require "guard/group"
16
18
  require "guard/interactor"
17
19
  require "guard/notifier"
18
20
  require "guard/plugin_util"
@@ -20,6 +22,7 @@ require "guard/runner"
20
22
  require "guard/sheller"
21
23
  require "guard/ui"
22
24
  require "guard/watcher"
25
+ require "guard/guardfile/evaluator"
23
26
 
24
27
  # Guard is the main module for all Guard related modules and classes.
25
28
  # Also Guard plugins should use this namespace.
@@ -30,6 +33,9 @@ module Guard
30
33
  class << self
31
34
  attr_reader :listener
32
35
 
36
+ # @private api
37
+ attr_reader :queue
38
+
33
39
  include Internals::Helpers
34
40
 
35
41
  # Initializes the Guard singleton:
@@ -46,103 +52,37 @@ module Guard
46
52
  # @option options [String] guardfile the path to the Guardfile
47
53
  #
48
54
  # @return [Guard] the Guard singleton
49
- #
50
-
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)
58
-
59
- # NOTE: must be set before anything calls Guard::UI.debug
60
- ::Guard::Internals::Debugging.start if options[:debug]
55
+ def setup(cmdline_options = {})
56
+ init(cmdline_options)
61
57
 
62
- @queue = Queue.new
63
- self.watchdirs = Array(options[:watchdir])
58
+ @queue = Internals::Queue.new(Guard)
64
59
 
65
- ::Guard::UI.reset_and_clear
60
+ UI.reset_and_clear
66
61
 
67
- _reset_all
68
- evaluate_guardfile
69
- setup_scope
62
+ _evaluate(state.session.evaluator_options)
70
63
 
71
- @listener = _setup_listener
64
+ # NOTE: this should be *after* evaluate so :directories can work
65
+ @listener = Listen.send(*state.session.listener_args, &_listener_callback)
72
66
 
73
- ::Guard::Notifier.connect(notify: options[:notify])
67
+ Notifier.connect(state.session.notify_options)
74
68
 
75
69
  traps = Internals::Traps
76
70
  traps.handle("USR1") { async_queue_add([:guard_pause, :paused]) }
77
71
  traps.handle("USR2") { async_queue_add([:guard_pause, :unpaused]) }
78
72
 
79
- @interactor = ::Guard::Interactor.new(options[:no_interactions])
73
+ @interactor = Interactor.new(state.session.interactor_name == :sleep)
80
74
  traps.handle("INT") { @interactor.handle_interrupt }
81
75
 
82
76
  self
83
77
  end
84
78
 
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).
93
- #
94
- # @see DEFAULT_GROUPS
95
- #
96
- def reset_groups
97
- @groups = DEFAULT_GROUPS.map { |name| Group.new(name) }
98
- end
99
-
100
- # Initializes the plugins array to an empty array.
101
- #
102
- # @see Guard.plugins
103
- #
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
116
- #
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
- }
79
+ def init(cmdline_options)
80
+ @state = Internals::State.new(cmdline_options)
125
81
  end
126
82
 
127
- # Evaluates the Guardfile content. It displays an error message if no
128
- # Guard plugins are instantiated after the Guardfile evaluation.
129
- #
130
- # @see Guard::Guardfile::Evaluator#evaluate_guardfile
131
- #
132
- def evaluate_guardfile
133
- evaluator = Guard::Guardfile::Evaluator.new(options)
134
- evaluator.evaluate_guardfile
83
+ attr_reader :state
135
84
 
136
- # FIXME: temporary hack while due to upcoming refactorings
137
- options[:guardfile] = evaluator.guardfile_path
138
-
139
- msg = "No plugins found in Guardfile, please add at least one."
140
- ::Guard::UI.error msg if _pluginless_guardfile?
141
- end
142
-
143
- # @private api
144
- # used solely for match_guardfile?
145
- attr_reader :guardfile_path
85
+ attr_reader :interactor
146
86
 
147
87
  # Asynchronously trigger changes
148
88
  #
@@ -160,60 +100,17 @@ module Guard
160
100
  Thread.new { interactor.background }
161
101
  end
162
102
 
163
- def pending_changes?
164
- ! @queue.empty?
165
- end
166
-
167
- def watchdirs=(dirs)
168
- dirs = [Dir.pwd] if dirs.empty?
169
- @watchdirs = dirs.map { |dir| File.expand_path dir }
170
- end
171
-
172
103
  private
173
104
 
174
- # Initializes the listener and registers a callback for changes.
175
- #
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
105
  # Check if any of the changes are actually watched for
106
+ # TODO: why iterate twice? reuse this info when running tasks
214
107
  def _relevant_changes?(changes)
108
+ # TODO: no coverage!
215
109
  files = changes.values.flatten(1)
216
- watchers = _scoped_watchers
110
+ scope = Guard.state.scope
111
+ watchers = scope.grouped_plugins.map do |_group, plugins|
112
+ plugins.map(&:watchers).flatten
113
+ end.flatten
217
114
  watchers.any? { |watcher| files.any? { |file| watcher.match(file) } }
218
115
  end
219
116
 
@@ -221,19 +118,6 @@ module Guard
221
118
  paths.map { |path| _relative_pathname(path) }
222
119
  end
223
120
 
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
234
- end
235
- end
236
-
237
121
  def _listener_callback
238
122
  lambda do |modified, added, removed|
239
123
  relative_paths = {
@@ -246,12 +130,7 @@ module Guard
246
130
  end
247
131
  end
248
132
 
249
- def _reset_all
250
- reset_groups
251
- reset_plugins
252
- reset_scope
253
- end
254
-
133
+ # TODO: obsoleted? (move to Dsl?)
255
134
  def _pluginless_guardfile?
256
135
  # no Reevaluator means there was no Guardfile configured that could be
257
136
  # reevaluated, so we don't have a pluginless guardfile, because we don't
@@ -260,19 +139,23 @@ module Guard
260
139
  # But, if we have a Guardfile, we'll at least have the built-in
261
140
  # Reevaluator, so the following will work:
262
141
 
263
- # TODO: this is a workaround for tests
264
- return true if plugins.empty?
265
-
266
- plugins.map(&:name) == ["reevaluator"]
142
+ plugins = state.session.plugins.all
143
+ plugins.empty? || plugins.map(&:name) == ["reevaluator"]
267
144
  end
268
145
 
269
- def _reset_for_tests
270
- @options = nil
271
- @queue = nil
272
- @watchdirs = nil
273
- @listener = nil
274
- @interactor = nil
275
- @scope = nil
146
+ def _evaluate(options)
147
+ evaluator = Guardfile::Evaluator.new(options)
148
+ evaluator.evaluate
149
+ msg = "No plugins found in Guardfile, please add at least one."
150
+ UI.error msg if _pluginless_guardfile?
151
+
152
+ if evaluator.inline?
153
+ UI.info("Using inline Guardfile.")
154
+ elsif evaluator.custom?
155
+ UI.info("Using Guardfile at #{ evaluator.path }.")
156
+ end
157
+ rescue Guardfile::Evaluator::NoPluginsError => e
158
+ UI.error(e.message)
276
159
  end
277
160
  end
278
161
  end
@@ -2,9 +2,8 @@ require "thor"
2
2
 
3
3
  require "guard"
4
4
  require "guard/version"
5
- require "guard/dsl_describer"
6
- require "guard/guardfile/evaluator"
7
5
  require "guard/guardfile/generator"
6
+ require "guard/dsl_describer"
8
7
 
9
8
  module Guard
10
9
  # Facade for the Guard command line interface managed by
@@ -49,6 +48,7 @@ module Guard
49
48
  aliases: "-P",
50
49
  banner: "Run only the passed plugins"
51
50
 
51
+ # TODO: make it plural
52
52
  method_option :watchdir,
53
53
  type: :array,
54
54
  aliases: "-w",
@@ -105,7 +105,7 @@ module Guard
105
105
  #
106
106
  def start
107
107
  _verify_bundler_presence unless options[:no_bundler_warning]
108
- ::Guard.start(options)
108
+ _start(options)
109
109
  end
110
110
 
111
111
  desc "list", "Lists Guard plugins that can be used with init"
@@ -116,7 +116,8 @@ module Guard
116
116
  # @see Guard::DslDescriber.list
117
117
  #
118
118
  def list
119
- ::Guard::DslDescriber.new(options).list
119
+ _require_guardfile(options) # just to show which plugins are actually used
120
+ DslDescriber.new.list
120
121
  end
121
122
 
122
123
  desc "notifiers", "Lists notifiers and its options"
@@ -126,8 +127,8 @@ module Guard
126
127
  # @see Guard::DslDescriber.notifiers
127
128
  #
128
129
  def notifiers
129
- ::Guard.reset_options(options)
130
- ::Guard::DslDescriber.new(options).notifiers
130
+ _require_guardfile(options)
131
+ DslDescriber.new.notifiers
131
132
  end
132
133
 
133
134
  desc "version", "Show the Guard version"
@@ -138,7 +139,7 @@ module Guard
138
139
  # @see Guard::VERSION
139
140
  #
140
141
  def version
141
- $stdout.puts "Guard version #{ ::Guard::VERSION }"
142
+ $stdout.puts "Guard version #{ VERSION }"
142
143
  end
143
144
 
144
145
  desc "init [GUARDS]", "Generates a Guardfile at the current directory"\
@@ -164,19 +165,23 @@ module Guard
164
165
  #
165
166
  def init(*plugin_names)
166
167
  _verify_bundler_presence unless options[:no_bundler_warning]
168
+ bare = options[:bare]
167
169
 
168
- ::Guard.reset_options(options) # Since UI.deprecated uses config
170
+ generator = Guardfile::Generator.new
171
+ Guard.init(options)
172
+ session = Guard.state.session
169
173
 
170
- generator = Guardfile::Generator.new(abort_on_existence: options[:bare])
171
- generator.create_guardfile
174
+ begin
175
+ Guardfile::Evaluator.new(session.evaluator_options).evaluate
176
+ rescue Guardfile::Evaluator::NoGuardfileError
177
+ generator.create_guardfile
178
+ end
172
179
 
173
- # Note: this reset "hack" will be fixed after refactoring
174
- ::Guard.reset_plugins
180
+ return if bare
175
181
 
176
182
  # Evaluate because it might have existed and creating was skipped
177
- ::Guard::Guardfile::Evaluator.new(Guard.options).evaluate_guardfile
178
-
179
- return if options[:bare]
183
+ # FIXME: still, I don't know why this is needed
184
+ Guardfile::Evaluator.new(session.evaluator_options).evaluate
180
185
 
181
186
  if plugin_names.empty?
182
187
  generator.initialize_all_templates
@@ -196,7 +201,9 @@ module Guard
196
201
  # @see Guard::DslDescriber.show
197
202
  #
198
203
  def show
199
- ::Guard::DslDescriber.new(options).show
204
+ _require_guardfile(options)
205
+ # TODO: use Metadata class
206
+ DslDescriber.new.show
200
207
  end
201
208
 
202
209
  private
@@ -204,11 +211,12 @@ module Guard
204
211
  # Verifies if Guard is run with `bundle exec` and
205
212
  # shows a hint to do so if not.
206
213
  #
214
+ # TODO: move this elsewhere!!! (because of complex specs)
207
215
  def _verify_bundler_presence
208
216
  return unless File.exist?("Gemfile")
209
217
  return if ENV["BUNDLE_GEMFILE"] || ENV["RUBYGEMS_GEMDEPS"]
210
218
 
211
- ::Guard::UI.info <<EOF
219
+ UI.info <<EOF
212
220
 
213
221
  Guard here! It looks like your project has a Gemfile, yet you are running
214
222
  `guard` outside of Bundler. If this is your intent, feel free to ignore this
@@ -217,5 +225,29 @@ dependencies are loaded correctly.
217
225
  (You can run `guard` with --no-bundler-warning to get rid of this message.)
218
226
  EOF
219
227
  end
228
+
229
+ def _require_guardfile(options)
230
+ Guard.init(options) # to setup metadata
231
+ session = Guard.state.session
232
+ Guardfile::Evaluator.new(session.evaluator_options).evaluate
233
+ rescue Dsl::Error,
234
+ Guardfile::Evaluator::NoPluginsError,
235
+ Guardfile::Evaluator::NoGuardfileError,
236
+ Guardfile::Evaluator::NoCustomGuardfile => e
237
+ # catch to throw message instead of call stack
238
+ UI.error(e.message)
239
+ abort
240
+ end
241
+
242
+ def _start(options)
243
+ Guard.start(options)
244
+ rescue Dsl::Error,
245
+ Guardfile::Evaluator::NoPluginsError,
246
+ Guardfile::Evaluator::NoGuardfileError,
247
+ Guardfile::Evaluator::NoCustomGuardfile => e
248
+ # catch to throw message instead of call stack
249
+ UI.error(e.message)
250
+ abort
251
+ end
220
252
  end
221
253
  end
@@ -3,6 +3,8 @@ require "listen"
3
3
  require "guard/notifier"
4
4
  require "guard/interactor"
5
5
  require "guard/runner"
6
+ require "guard/reevaluator"
7
+ require "guard/dsl_describer"
6
8
  require "guard"
7
9
 
8
10
  module Guard
@@ -28,16 +30,16 @@ module Guard
28
30
  #
29
31
  def start(options = {})
30
32
  setup(options)
31
- ::Guard::UI.debug "Guard starts all plugins"
32
- Guard::Runner.new.run(:start)
33
+ UI.debug "Guard starts all plugins"
34
+ Runner.new.run(:start)
33
35
  listener.start
34
36
 
35
- watched = ::Guard.watchdirs.join("', '")
36
- ::Guard::UI.info "Guard is now watching at '#{ watched }'"
37
+ watched = Guard.state.session.watchdirs.join("', '")
38
+ UI.info "Guard is now watching at '#{ watched }'"
37
39
 
38
40
  begin
39
41
  while interactor.foreground != :exit
40
- _process_queue while pending_changes?
42
+ Guard.queue.process while Guard.queue.pending?
41
43
  end
42
44
  rescue Interrupt
43
45
  end
@@ -45,14 +47,13 @@ module Guard
45
47
  stop
46
48
  end
47
49
 
48
- # TODO: refactor (left to avoid breaking too many specs)
49
50
  def stop
50
51
  listener.stop
51
52
  interactor.background
52
- ::Guard::UI.debug "Guard stops all plugins"
53
- Guard::Runner.new.run(:stop)
54
- ::Guard::Notifier.disconnect
55
- ::Guard::UI.info "Bye bye...", reset: true
53
+ UI.debug "Guard stops all plugins"
54
+ Runner.new.run(:stop)
55
+ Notifier.disconnect
56
+ UI.info "Bye bye...", reset: true
56
57
  end
57
58
 
58
59
  # Reload Guardfile and all Guard plugins currently enabled.
@@ -63,13 +64,13 @@ module Guard
63
64
  #
64
65
  def reload(scopes = {})
65
66
  # TODO: guard reevaluator should probably handle all this
66
- ::Guard::UI.clear(force: true)
67
- ::Guard::UI.action_with_scopes("Reload", scopes)
67
+ UI.clear(force: true)
68
+ UI.action_with_scopes("Reload", scopes)
68
69
 
69
70
  if scopes.empty?
70
- Guard::Guardfile::Evaluator.new(Guard.options).reevaluate_guardfile
71
+ Reevaluator.new.reevaluate
71
72
  else
72
- Guard::Runner.new.run(:reload, scopes)
73
+ Runner.new.run(:reload, scopes)
73
74
  end
74
75
  end
75
76
 
@@ -78,8 +79,8 @@ module Guard
78
79
  # @param [Hash] scopes hash with a Guard plugin or a group scope
79
80
  #
80
81
  def run_all(scopes = {})
81
- ::Guard::UI.clear(force: true)
82
- ::Guard::UI.action_with_scopes("Run", scopes)
82
+ UI.clear(force: true)
83
+ UI.action_with_scopes("Run", scopes)
83
84
  Guard::Runner.new.run(:run_all, scopes)
84
85
  end
85
86
 
@@ -97,7 +98,7 @@ module Guard
97
98
  end
98
99
 
99
100
  def show
100
- ::Guard::DslDescriber.new(::Guard.options).show
101
+ DslDescriber.new.show
101
102
  end
102
103
  end
103
104
  extend Commander