guard 2.6.1 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +73 -58
  3. data/bin/guard +2 -2
  4. data/lib/guard.rb +64 -59
  5. data/lib/guard/cli.rb +66 -60
  6. data/lib/guard/cli.rb.orig +215 -0
  7. data/lib/guard/commander.rb +45 -69
  8. data/lib/guard/commands/all.rb +21 -19
  9. data/lib/guard/commands/change.rb +17 -22
  10. data/lib/guard/commands/notification.rb +15 -16
  11. data/lib/guard/commands/pause.rb +14 -15
  12. data/lib/guard/commands/reload.rb +19 -20
  13. data/lib/guard/commands/scope.rb +23 -19
  14. data/lib/guard/commands/show.rb +13 -16
  15. data/lib/guard/deprecated_methods.rb +6 -10
  16. data/lib/guard/deprecator.rb +52 -37
  17. data/lib/guard/dsl.rb +55 -33
  18. data/lib/guard/dsl_describer.rb +83 -31
  19. data/lib/guard/dsl_describer.rb.orig +184 -0
  20. data/lib/guard/group.rb +7 -6
  21. data/lib/guard/guard.rb +4 -4
  22. data/lib/guard/guard.rb.orig +42 -0
  23. data/lib/guard/guardfile.rb +12 -13
  24. data/lib/guard/guardfile/evaluator.rb +77 -55
  25. data/lib/guard/guardfile/evaluator.rb.orig +275 -0
  26. data/lib/guard/guardfile/generator.rb +25 -20
  27. data/lib/guard/interactor.rb +52 -293
  28. data/lib/guard/interactor.rb.orig +85 -0
  29. data/lib/guard/jobs/base.rb +21 -0
  30. data/lib/guard/jobs/pry_wrapper.rb +290 -0
  31. data/lib/guard/jobs/pry_wrapper.rb.orig +293 -0
  32. data/lib/guard/jobs/sleep.rb +25 -0
  33. data/lib/guard/notifier.rb +42 -39
  34. data/lib/guard/notifiers/base.rb +25 -24
  35. data/lib/guard/notifiers/emacs.rb +30 -24
  36. data/lib/guard/notifiers/file_notifier.rb +3 -7
  37. data/lib/guard/notifiers/gntp.rb +22 -22
  38. data/lib/guard/notifiers/growl.rb +16 -15
  39. data/lib/guard/notifiers/libnotify.rb +7 -10
  40. data/lib/guard/notifiers/notifysend.rb +15 -14
  41. data/lib/guard/notifiers/rb_notifu.rb +8 -10
  42. data/lib/guard/notifiers/terminal_notifier.rb +15 -11
  43. data/lib/guard/notifiers/terminal_title.rb +4 -8
  44. data/lib/guard/notifiers/tmux.rb +104 -71
  45. data/lib/guard/options.rb +1 -5
  46. data/lib/guard/plugin.rb +1 -3
  47. data/lib/guard/plugin/base.rb +12 -9
  48. data/lib/guard/plugin/hooker.rb +1 -5
  49. data/lib/guard/plugin_util.rb +46 -25
  50. data/lib/guard/plugin_util.rb.orig +178 -0
  51. data/lib/guard/rake_task.rb +4 -7
  52. data/lib/guard/reevaluator.rb +13 -0
  53. data/lib/guard/runner.rb +50 -78
  54. data/lib/guard/runner.rb.orig +200 -0
  55. data/lib/guard/setuper.rb +199 -130
  56. data/lib/guard/setuper.rb.orig +348 -0
  57. data/lib/guard/sheller.rb +107 -0
  58. data/lib/guard/tags +367 -0
  59. data/lib/guard/ui.rb +50 -38
  60. data/lib/guard/ui.rb.orig +254 -0
  61. data/lib/guard/ui/colors.rb +17 -21
  62. data/lib/guard/version.rb +1 -1
  63. data/lib/guard/version.rb.orig +3 -0
  64. data/lib/guard/watcher.rb +49 -62
  65. metadata +21 -4
  66. data/lib/guard/notifiers/growl_notify.rb +0 -93
@@ -0,0 +1,215 @@
1
+ require "thor"
2
+
3
+ require "guard"
4
+ require "guard/version"
5
+ require "guard/dsl_describer"
6
+ require "guard/guardfile"
7
+
8
+ module Guard
9
+ # Facade for the Guard command line interface managed by
10
+ # [Thor](https://github.com/wycats/thor).
11
+ #
12
+ # This is the main interface to Guard that is called by the Guard binary
13
+ # `bin/guard`. Do not put any logic in here, create a class and delegate
14
+ # instead.
15
+ #
16
+ class CLI < Thor
17
+ default_task :start
18
+
19
+ desc "start", "Starts Guard"
20
+
21
+ method_option :clear,
22
+ type: :boolean,
23
+ default: false,
24
+ aliases: "-c",
25
+ banner: "Auto clear shell before each action"
26
+
27
+ method_option :notify,
28
+ type: :boolean,
29
+ default: true,
30
+ aliases: "-n",
31
+ banner: "Notifications feature"
32
+
33
+ method_option :debug,
34
+ type: :boolean,
35
+ default: false,
36
+ aliases: "-d",
37
+ banner: "Show debug information"
38
+
39
+ method_option :group,
40
+ type: :array,
41
+ default: [],
42
+ aliases: "-g",
43
+ banner: "Run only the passed groups"
44
+
45
+ method_option :plugin,
46
+ type: :array,
47
+ default: [],
48
+ aliases: "-P",
49
+ banner: "Run only the passed plugins"
50
+
51
+ method_option :watchdir,
52
+ type: :array,
53
+ aliases: "-w",
54
+ banner: "Specify the directories to watch"
55
+
56
+ method_option :guardfile,
57
+ type: :string,
58
+ aliases: "-G",
59
+ banner: "Specify a Guardfile"
60
+
61
+ method_option :no_interactions,
62
+ type: :boolean,
63
+ default: false,
64
+ aliases: "-i",
65
+ banner: "Turn off completely any Guard terminal interactions"
66
+
67
+ method_option :no_bundler_warning,
68
+ type: :boolean,
69
+ default: false,
70
+ aliases: "-B",
71
+ banner: "Turn off warning when Bundler is not present"
72
+
73
+ method_option :show_deprecations,
74
+ type: :boolean,
75
+ default: false,
76
+ banner: "Turn on deprecation warnings"
77
+
78
+ # Listen options
79
+ method_option :latency,
80
+ type: :numeric,
81
+ aliases: "-l",
82
+ banner: 'Overwrite Listen\'s default latency'
83
+
84
+ method_option :force_polling,
85
+ type: :boolean,
86
+ default: false,
87
+ aliases: "-p",
88
+ banner: "Force usage of the Listen polling listener"
89
+
90
+ method_option :wait_for_delay,
91
+ type: :numeric,
92
+ aliases: "-y",
93
+ banner: 'Overwrite Listen\'s default wait_for_delay'
94
+
95
+ method_option :listen_on,
96
+ type: :string,
97
+ aliases: "-o",
98
+ default: false,
99
+ banner: "Specify a network address to Listen on for "\
100
+ "file change events (e.g. for use in VMs)"
101
+
102
+ # Start Guard by initializing the defined Guard plugins and watch the file
103
+ # system.
104
+ #
105
+ # This is the default task, so calling `guard` is the same as calling
106
+ # `guard start`.
107
+ #
108
+ # @see Guard.start
109
+ #
110
+ def start
111
+ _verify_bundler_presence unless options[:no_bundler_warning]
112
+ ::Guard.start(options)
113
+ end
114
+
115
+ desc "list", "Lists Guard plugins that can be used with init"
116
+
117
+ # List the Guard plugins that are available for use in your system and
118
+ # marks those that are currently used in your `Guardfile`.
119
+ #
120
+ # @see Guard::DslDescriber.list
121
+ #
122
+ def list
123
+ ::Guard::DslDescriber.new(options).list
124
+ end
125
+
126
+ desc "notifiers", "Lists notifiers and its options"
127
+
128
+ # List the Notifiers for use in your system.
129
+ #
130
+ # @see Guard::DslDescriber.notifiers
131
+ #
132
+ def notifiers
133
+ ::Guard::DslDescriber.new(options).notifiers
134
+ end
135
+
136
+ desc "version", "Show the Guard version"
137
+ map %w(-v --version) => :version
138
+
139
+ # Shows the current version of Guard.
140
+ #
141
+ # @see Guard::VERSION
142
+ #
143
+ def version
144
+ STDOUT.puts "Guard version #{ ::Guard::VERSION }"
145
+ end
146
+
147
+ desc "init [GUARDS]", "Generates a Guardfile at the current directory"\
148
+ " (if it is not already there) and adds all installed Guard plugins"\
149
+ " or the given GUARDS into it"
150
+
151
+ method_option :bare,
152
+ type: :boolean,
153
+ default: false,
154
+ aliases: "-b",
155
+ banner: "Generate a bare Guardfile without adding any"\
156
+ " installed plugin into it"
157
+
158
+ # Initializes the templates of all installed Guard plugins and adds them
159
+ # to the `Guardfile` when no Guard name is passed. When passing
160
+ # Guard plugin names it does the same but only for those Guard plugins.
161
+ #
162
+ # @see Guard::Guardfile.initialize_template
163
+ # @see Guard::Guardfile.initialize_all_templates
164
+ #
165
+ # @param [Array<String>] plugin_names the name of the Guard plugins to
166
+ # initialize
167
+ #
168
+ def init(*plugin_names)
169
+ _verify_bundler_presence unless options[:no_bundler_warning]
170
+
171
+ ::Guard::Guardfile.create_guardfile(abort_on_existence: options[:bare])
172
+
173
+ return if options[:bare]
174
+
175
+ if plugin_names.empty?
176
+ ::Guard::Guardfile.initialize_all_templates
177
+ else
178
+ plugin_names.each do |plugin_name|
179
+ ::Guard::Guardfile.initialize_template(plugin_name)
180
+ end
181
+ end
182
+ end
183
+
184
+ desc "show", "Show all defined Guard plugins and their options"
185
+ map %w(-T) => :show
186
+
187
+ # Shows all Guard plugins and their options that are defined in
188
+ # the `Guardfile`
189
+ #
190
+ # @see Guard::DslDescriber.show
191
+ #
192
+ def show
193
+ ::Guard::DslDescriber.new(options).show
194
+ end
195
+
196
+ private
197
+
198
+ # Verifies if Guard is run with `bundle exec` and
199
+ # shows a hint to do so if not.
200
+ #
201
+ def _verify_bundler_presence
202
+ return unless File.exist?("Gemfile")
203
+ return if ENV["BUNDLE_GEMFILE"] || ENV["RUBYGEMS_GEMDEPS"]
204
+
205
+ ::Guard::UI.info <<EOF
206
+
207
+ Guard here! It looks like your project has a Gemfile, yet you are running
208
+ `guard` outside of Bundler. If this is your intent, feel free to ignore this
209
+ message. Otherwise, consider using `bundle exec guard` to ensure your
210
+ dependencies are loaded correctly.
211
+ (You can run `guard` with --no-bundler-warning to get rid of this message.)
212
+ EOF
213
+ end
214
+ end
215
+ end
@@ -1,9 +1,8 @@
1
1
  module Guard
2
-
2
+ # Commands supported by guard
3
3
  module Commander
4
-
5
- # Start Guard by evaluating the `Guardfile`, initializing declared Guard plugins
6
- # and starting the available file change listener.
4
+ # Start Guard by evaluating the `Guardfile`, initializing declared Guard
5
+ # plugins and starting the available file change listener.
7
6
  # Main method for Guard that is called from the CLI when Guard starts.
8
7
  #
9
8
  # - Setup Guard internals
@@ -21,29 +20,23 @@ module Guard
21
20
  # @see CLI#start
22
21
  #
23
22
  def start(options = {})
24
- setup(options) unless running
23
+ setup(options)
24
+ ::Guard::UI.debug "Guard starts all plugins"
25
+ runner.run(:start)
26
+ listener.start
27
+ ::Guard::UI.info "Guard is now watching at '#{ @watchdirs.join "', '" }'"
25
28
 
26
- within_preserved_state do
27
- ::Guard::UI.debug 'Guard starts all plugins'
28
- runner.run(:start)
29
- ::Guard::UI.info "Guard is now watching at '#{ @watchdirs.join "', '" }'"
30
- listener.start
31
- end
29
+ _interactor_loop
32
30
  end
33
31
 
34
- # Stop Guard listening to file changes.
35
- #
32
+ # TODO: refactor (left to avoid breaking too many specs)
36
33
  def stop
37
- setup unless running
38
-
39
- within_preserved_state do
40
- ::Guard::UI.debug 'Guard stops all plugins'
41
- runner.run(:stop)
42
- ::Guard::Notifier.turn_off
43
- ::Guard::UI.info 'Bye bye...', reset: true
44
- listener.stop
45
- @running = false
46
- end
34
+ listener.stop
35
+ interactor.background
36
+ ::Guard::UI.debug "Guard stops all plugins"
37
+ runner.run(:stop)
38
+ ::Guard::Notifier.turn_off
39
+ ::Guard::UI.info "Bye bye...", reset: true
47
40
  end
48
41
 
49
42
  # Reload Guardfile and all Guard plugins currently enabled.
@@ -53,17 +46,13 @@ module Guard
53
46
  # @param [Hash] scopes hash with a Guard plugin or a group scope
54
47
  #
55
48
  def reload(scopes = {})
56
- setup unless running
57
-
58
- within_preserved_state do
59
- ::Guard::UI.clear(force: true)
60
- ::Guard::UI.action_with_scopes('Reload', scopes)
49
+ ::Guard::UI.clear(force: true)
50
+ ::Guard::UI.action_with_scopes("Reload", scopes)
61
51
 
62
- if scopes.empty?
63
- evaluator.reevaluate_guardfile
64
- else
65
- runner.run(:reload, scopes)
66
- end
52
+ if scopes.empty?
53
+ evaluator.reevaluate_guardfile
54
+ else
55
+ runner.run(:reload, scopes)
67
56
  end
68
57
  end
69
58
 
@@ -72,50 +61,37 @@ module Guard
72
61
  # @param [Hash] scopes hash with a Guard plugin or a group scope
73
62
  #
74
63
  def run_all(scopes = {})
75
- setup unless running
76
-
77
- within_preserved_state do
78
- ::Guard::UI.clear(force: true)
79
- ::Guard::UI.action_with_scopes('Run', scopes)
80
- runner.run(:run_all, scopes)
81
- end
64
+ ::Guard::UI.clear(force: true)
65
+ ::Guard::UI.action_with_scopes("Run", scopes)
66
+ runner.run(:run_all, scopes)
82
67
  end
83
68
 
84
69
  # Pause Guard listening to file changes.
85
70
  #
86
- def pause
87
- if listener.paused?
88
- ::Guard::UI.info 'Un-paused files modification listening', reset: true
89
- listener.unpause
90
- else
91
- ::Guard::UI.info 'Paused files modification listening', reset: true
92
- listener.pause
93
- end
71
+ def pause(expected = nil)
72
+ paused = listener.paused?
73
+ states = { paused: true, unpaused: false, toggle: !paused }
74
+ pause = states[expected || :toggle]
75
+ fail ArgumentError, "invalid mode: #{expected.inspect}" if pause.nil?
76
+ return if pause == paused
77
+
78
+ listener.send(pause ? :pause : :unpause)
79
+ UI.info "File modification listening is now #{pause.to_s.upcase}"
94
80
  end
95
81
 
96
- # Runs a block where the interactor is
97
- # blocked and execution is synchronized
98
- # to avoid state inconsistency.
99
- #
100
- # @param [Boolean] restart_interactor whether to restart the interactor or
101
- # not
102
- # @yield the block to run
103
- #
104
- def within_preserved_state
105
- lock.synchronize do
106
- begin
107
- interactor.stop if interactor
108
- @result = yield
109
- rescue Interrupt
110
- # Bring back Pry when the block is halted with Ctrl-C
111
- end
82
+ def show
83
+ ::Guard::DslDescriber.new(::Guard.options).show
84
+ end
112
85
 
113
- interactor.start if interactor && running
114
- end
86
+ private
115
87
 
116
- @result
88
+ # TODO: remove (left to avoid breaking too many specs)
89
+ def _interactor_loop
90
+ while interactor.foreground != :exit
91
+ _process_queue while pending_changes?
92
+ end
93
+ rescue Interrupt
94
+ stop
117
95
  end
118
-
119
96
  end
120
-
121
97
  end
@@ -1,34 +1,36 @@
1
- module Guard
2
- class Interactor
3
-
4
- ALL = Pry::CommandSet.new do
5
- create_command 'all' do
1
+ # required for async_queue_add
2
+ require "pry"
3
+ require "guard"
6
4
 
7
- group 'Guard'
8
- description 'Run all plugins.'
9
-
10
- banner <<-BANNER
5
+ module Guard
6
+ module Commands
7
+ class All
8
+ def self.import
9
+ Pry::Commands.create_command "all" do
10
+ group "Guard"
11
+ description "Run all plugins."
12
+
13
+ banner <<-BANNER
11
14
  Usage: all <scope>
12
15
 
13
16
  Run the Guard plugin `run_all` action.
14
17
 
15
18
  You may want to specify an optional scope to the action,
16
19
  either the name of a Guard plugin or a plugin group.
17
- BANNER
20
+ BANNER
18
21
 
19
- def process(*entries)
20
- scopes, rest = ::Guard::Interactor.convert_scope(entries)
22
+ def process(*entries)
23
+ scopes, unknown = ::Guard::Interactor.convert_scope(entries)
21
24
 
22
- if rest.empty?
23
- ::Guard.run_all scopes
24
- else
25
- output.puts "Unknown scope #{ rest.join(', ') }"
25
+ unless unknown.empty?
26
+ output.puts "Unknown scopes: #{ unknown.join(", ") }"
27
+ return
28
+ end
29
+
30
+ ::Guard.async_queue_add([:guard_run_all, scopes])
26
31
  end
27
32
  end
28
33
  end
29
34
  end
30
-
31
35
  end
32
36
  end
33
-
34
- Pry.commands.import ::Guard::Interactor::ALL
@@ -1,34 +1,29 @@
1
- module Guard
2
- class Interactor
3
-
4
- CHANGE = Pry::CommandSet.new do
5
- create_command 'change' do
6
-
7
- group 'Guard'
8
- description 'Trigger a file change.'
1
+ require "pry"
9
2
 
10
- banner <<-BANNER
3
+ module Guard
4
+ module Commands
5
+ class Change
6
+ def self.import
7
+ Pry::Commands.create_command "change" do
8
+ group "Guard"
9
+ description "Trigger a file change."
10
+
11
+ banner <<-BANNER
11
12
  Usage: change <file> <other_file>
12
13
 
13
14
  Pass the given files to the Guard plugin `run_on_changes` action.
14
- BANNER
15
-
16
- def process(*entries)
17
- scopes, files = ::Guard::Interactor.convert_scope(entries)
15
+ BANNER
18
16
 
19
- if files.empty?
20
- output.puts 'Please specify a file.'
21
- else
22
- ::Guard.within_preserved_state do
23
- ::Guard.runner.run_on_changes(files, [], [])
17
+ def process(*files)
18
+ if files.empty?
19
+ output.puts "Please specify a file."
20
+ return
24
21
  end
22
+
23
+ ::Guard.async_queue_add(modified: files, added: [], removed: [])
25
24
  end
26
25
  end
27
-
28
26
  end
29
27
  end
30
-
31
28
  end
32
29
  end
33
-
34
- Pry.commands.import ::Guard::Interactor::CHANGE