guard 1.8.3 → 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -10
  3. data/README.md +54 -33
  4. data/lib/guard.rb +133 -483
  5. data/lib/guard/cli.rb +78 -82
  6. data/lib/guard/commander.rb +121 -0
  7. data/lib/guard/commands/all.rb +1 -1
  8. data/lib/guard/commands/reload.rb +1 -1
  9. data/lib/guard/deprecated_methods.rb +59 -0
  10. data/lib/guard/deprecator.rb +107 -0
  11. data/lib/guard/dsl.rb +143 -329
  12. data/lib/guard/dsl_describer.rb +101 -57
  13. data/lib/guard/group.rb +27 -8
  14. data/lib/guard/guard.rb +25 -150
  15. data/lib/guard/guardfile.rb +35 -85
  16. data/lib/guard/guardfile/evaluator.rb +245 -0
  17. data/lib/guard/guardfile/generator.rb +89 -0
  18. data/lib/guard/interactor.rb +147 -163
  19. data/lib/guard/notifier.rb +83 -137
  20. data/lib/guard/notifiers/base.rb +220 -0
  21. data/lib/guard/notifiers/emacs.rb +39 -37
  22. data/lib/guard/notifiers/file_notifier.rb +29 -25
  23. data/lib/guard/notifiers/gntp.rb +68 -75
  24. data/lib/guard/notifiers/growl.rb +49 -52
  25. data/lib/guard/notifiers/growl_notify.rb +51 -56
  26. data/lib/guard/notifiers/libnotify.rb +41 -48
  27. data/lib/guard/notifiers/notifysend.rb +58 -38
  28. data/lib/guard/notifiers/rb_notifu.rb +54 -54
  29. data/lib/guard/notifiers/terminal_notifier.rb +48 -36
  30. data/lib/guard/notifiers/terminal_title.rb +23 -19
  31. data/lib/guard/notifiers/tmux.rb +110 -93
  32. data/lib/guard/options.rb +21 -0
  33. data/lib/guard/plugin.rb +66 -0
  34. data/lib/guard/plugin/base.rb +178 -0
  35. data/lib/guard/plugin/hooker.rb +123 -0
  36. data/lib/guard/plugin_util.rb +158 -0
  37. data/lib/guard/rake_task.rb +47 -0
  38. data/lib/guard/runner.rb +62 -82
  39. data/lib/guard/setuper.rb +248 -0
  40. data/lib/guard/ui.rb +24 -80
  41. data/lib/guard/ui/colors.rb +60 -0
  42. data/lib/guard/version.rb +1 -2
  43. data/lib/guard/watcher.rb +30 -30
  44. data/man/guard.1 +4 -4
  45. data/man/guard.1.html +6 -4
  46. metadata +25 -11
  47. data/lib/guard/hook.rb +0 -120
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake'
4
+ require 'rake/tasklib'
5
+
6
+ require 'guard/cli'
7
+
8
+ module Guard
9
+
10
+ # Provides a method to define a Rake task that
11
+ # runs the Guard plugins.
12
+ #
13
+ class RakeTask < ::Rake::TaskLib
14
+
15
+ # Name of the main, top level task
16
+ attr_accessor :name
17
+
18
+ # CLI options
19
+ attr_accessor :options
20
+
21
+ # Initialize the Rake task
22
+ #
23
+ # @param [Symbol] name the name of the Rake task
24
+ # @param [String] options the CLI options
25
+ # @yield [Guard::RakeTask] the task
26
+ #
27
+ def initialize(name = :guard, options = '')
28
+ @name = name
29
+ @options = options
30
+
31
+ yield self if block_given?
32
+
33
+ desc "Starts Guard with options: '#{options}'"
34
+ task name => ["#{name}:start"]
35
+
36
+ namespace(name) do
37
+
38
+ desc "Starts Guard with options: '#{options}'"
39
+ task(:start) do
40
+ ::Guard::CLI.start(options.split)
41
+ end
42
+
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -1,56 +1,24 @@
1
+ require 'lumberjack'
2
+
3
+ require 'guard/ui'
4
+ require 'guard/watcher'
5
+
1
6
  module Guard
2
7
 
3
- # The runner is responsible for running all methods defined on each guards.
8
+ # The runner is responsible for running all methods defined on each plugin.
4
9
  #
5
10
  class Runner
6
11
 
7
- require 'guard'
8
- require 'guard/ui'
9
- require 'guard/watcher'
10
-
11
- require 'lumberjack'
12
-
13
- # Deprecation message for the `run_on_change` method
14
- RUN_ON_CHANGE_DEPRECATION = <<-EOS.gsub(/^\s*/, '')
15
- Starting with Guard v1.1 the use of the 'run_on_change' method in the '%s' guard is deprecated.
16
-
17
- Please consider replacing that method-call with 'run_on_changes' if the type of change
18
- is not important for your usecase or using either 'run_on_modifications' or 'run_on_additions'
19
- based on the type of the changes you want to handle.
20
-
21
- For more information on how to update existing guards, please head over to:
22
- https://github.com/guard/guard/wiki/Upgrade-guide-for-existing-guards-to-Guard-v1.1
23
- EOS
24
-
25
- # Deprecation message for the `run_on_deletion` method
26
- RUN_ON_DELETION_DEPRECATION = <<-EOS.gsub(/^\s*/, '')
27
- Starting with Guard v1.1 the use of the 'run_on_deletion' method in the '%s' guard is deprecated.
28
-
29
- Please consider replacing that method-call with 'run_on_removals' for future proofing your code.
30
-
31
- For more information on how to update existing guards, please head over to:
32
- https://github.com/guard/guard/wiki/Upgrade-guide-for-existing-guards-to-Guard-v1.1
33
- EOS
34
-
35
- # Displays a warning for each deprecated-method used is any registered guard.
36
- #
37
- def deprecation_warning
38
- ::Guard.guards.each do |guard|
39
- ::Guard::UI.deprecation(RUN_ON_CHANGE_DEPRECATION % guard.class.name) if guard.respond_to?(:run_on_change)
40
- ::Guard::UI.deprecation(RUN_ON_DELETION_DEPRECATION % guard.class.name) if guard.respond_to?(:run_on_deletion)
41
- end
42
- end
43
-
44
- # Runs a Guard-task on all registered guards.
12
+ # Runs a Guard-task on all registered plugins.
45
13
  #
46
14
  # @param [Symbol] task the task to run
47
15
  # @param [Hash] scopes either the Guard plugin or the group to run the task on
48
16
  #
49
17
  # @see self.run_supervised_task
50
18
  #
51
- def run(task, scopes = {})
19
+ def run(task, scope = {})
52
20
  Lumberjack.unit_of_work do
53
- scoped_guards(scopes) do |guard|
21
+ _scoped_plugins(scope) do |guard|
54
22
  run_supervised_task(guard, task) if guard.respond_to?(task)
55
23
  end
56
24
  end
@@ -60,7 +28,7 @@ module Guard
60
28
  ADDITION_TASKS = [:run_on_additions, :run_on_changes, :run_on_change]
61
29
  REMOVAL_TASKS = [:run_on_removals, :run_on_changes, :run_on_deletion]
62
30
 
63
- # Runs the appropriate tasks on all registered guards
31
+ # Runs the appropriate tasks on all registered plugins
64
32
  # based on the passed changes.
65
33
  #
66
34
  # @param [Array<String>] modified the modified paths.
@@ -69,16 +37,16 @@ module Guard
69
37
  #
70
38
  def run_on_changes(modified, added, removed)
71
39
  ::Guard::UI.clearable
72
- scoped_guards do |guard|
40
+ _scoped_plugins do |guard|
73
41
  modified_paths = ::Guard::Watcher.match_files(guard, modified)
74
42
  added_paths = ::Guard::Watcher.match_files(guard, added)
75
43
  removed_paths = ::Guard::Watcher.match_files(guard, removed)
76
44
 
77
- ::Guard::UI.clear if clearable?(guard, modified_paths, added_paths, removed_paths)
45
+ ::Guard::UI.clear if _clearable?(guard, modified_paths, added_paths, removed_paths)
78
46
 
79
- run_first_task_found(guard, MODIFICATION_TASKS, modified_paths) unless modified_paths.empty?
80
- run_first_task_found(guard, ADDITION_TASKS, added_paths) unless added_paths.empty?
81
- run_first_task_found(guard, REMOVAL_TASKS, removed_paths) unless removed_paths.empty?
47
+ _run_first_task_found(guard, MODIFICATION_TASKS, modified_paths) unless modified_paths.empty?
48
+ _run_first_task_found(guard, ADDITION_TASKS, added_paths) unless added_paths.empty?
49
+ _run_first_task_found(guard, REMOVAL_TASKS, removed_paths) unless removed_paths.empty?
82
50
  end
83
51
  end
84
52
 
@@ -87,14 +55,14 @@ module Guard
87
55
  # When the Group has `:halt_on_fail` disabled, we've to catch `:task_has_failed`
88
56
  # here in order to avoid an uncaught throw error.
89
57
  #
90
- # @param [Guard::Guard] guard the Guard to execute
58
+ # @param [Guard::Plugin] guard the Guard to execute
91
59
  # @param [Symbol] task the task to run
92
60
  # @param [Array] args the arguments for the task
93
61
  # @raise [:task_has_failed] when task has failed
94
62
  #
95
63
  def run_supervised_task(guard, task, *args)
96
64
  begin
97
- catch Runner.stopping_symbol_for(guard) do
65
+ catch self.class.stopping_symbol_for(guard) do
98
66
  guard.hook("#{ task }_begin", *args)
99
67
  result = guard.send(task, *args)
100
68
  guard.hook("#{ task }_end", result)
@@ -105,7 +73,7 @@ module Guard
105
73
  ::Guard::UI.error("#{ guard.class.name } failed to achieve its <#{ task.to_s }>, exception was:" +
106
74
  "\n#{ ex.class }: #{ ex.message }\n#{ ex.backtrace.join("\n") }")
107
75
 
108
- ::Guard.guards.delete guard
76
+ ::Guard.plugins.delete guard
109
77
  ::Guard::UI.info("\n#{ guard.class.name } has just been fired")
110
78
 
111
79
  ex
@@ -117,28 +85,25 @@ module Guard
117
85
  # @note If a Guard group is being run and it has the `:halt_on_fail`
118
86
  # option set, this method returns :no_catch as it will be caught at the
119
87
  # group level.
120
- # @see .scoped_guards
88
+ # @see ._scoped_plugins
121
89
  #
122
- # @param [Guard::Guard] guard the Guard plugin to execute
90
+ # @param [Guard::Plugin] guard the Guard plugin to execute
123
91
  # @return [Symbol] the symbol to catch
124
92
  #
125
93
  def self.stopping_symbol_for(guard)
126
- return :task_has_failed if guard.group.class != Symbol
127
-
128
- group = ::Guard.groups(guard.group)
129
- group.options.fetch(:halt_on_fail, false) ? :no_catch : :task_has_failed
94
+ guard.group.options[:halt_on_fail] ? :no_catch : :task_has_failed
130
95
  end
131
96
 
132
- private
97
+ private
133
98
 
134
99
  # Tries to run the first implemented task by a given guard
135
100
  # from a collection of tasks.
136
101
  #
137
- # @param [Guard::Guard] guard the Guard plugin to run the first found task on
102
+ # @param [Guard::Plugin] guard the Guard plugin to run the first found task on
138
103
  # @param [Array<Symbol>] tasks the tasks to run the first among
139
104
  # @param [Object] task_param the param to pass to each task
140
105
  #
141
- def run_first_task_found(guard, tasks, task_param)
106
+ def _run_first_task_found(guard, tasks, task_param)
142
107
  tasks.each do |task|
143
108
  if guard.respond_to?(task)
144
109
  run_supervised_task(guard, task, task_param)
@@ -161,16 +126,16 @@ module Guard
161
126
  # @param [Hash] scopes hash with plugins or a groups scope
162
127
  # @yield the task to run
163
128
  #
164
- def scoped_guards(scopes = {})
165
- if guards = current_plugins_scope(scopes)
166
- guards.each do |guard|
129
+ def _scoped_plugins(scopes = {})
130
+ if plugins = _current_plugins_scope(scopes)
131
+ plugins.each do |guard|
167
132
  yield(guard)
168
133
  end
169
134
  else
170
- current_groups_scope(scopes).each do |group|
135
+ _current_groups_scope(scopes).each do |group|
171
136
  current_plugin = nil
172
137
  block_return = catch :task_has_failed do
173
- ::Guard.guards(:group => group.name).each do |guard|
138
+ ::Guard.plugins(group: group.name).each do |guard|
174
139
  current_plugin = guard
175
140
  yield(guard)
176
141
  end
@@ -186,12 +151,12 @@ module Guard
186
151
  # Logic to know if the UI can be cleared or not in the run_on_changes method
187
152
  # based on the guard and the changes.
188
153
  #
189
- # @param [Guard::Guard] guard the Guard plugin where run_on_changes is called
154
+ # @param [Guard::Plugin] guard the Guard plugin where run_on_changes is called
190
155
  # @param [Array<String>] modified_paths the modified paths.
191
156
  # @param [Array<String>] added_paths the added paths.
192
157
  # @param [Array<String>] removed_paths the removed paths.
193
158
  #
194
- def clearable?(guard, modified_paths, added_paths, removed_paths)
159
+ def _clearable?(guard, modified_paths, added_paths, removed_paths)
195
160
  (MODIFICATION_TASKS.any? { |task| guard.respond_to?(task) } && !modified_paths.empty?) ||
196
161
  (ADDITION_TASKS.any? { |task| guard.respond_to?(task) } && !added_paths.empty?) ||
197
162
  (REMOVAL_TASKS.any? { |task| guard.respond_to?(task) } && !removed_paths.empty?)
@@ -202,15 +167,13 @@ module Guard
202
167
  # If no plugins scope is found, then NO plugins are returned.
203
168
  #
204
169
  # @param [Hash] scopes hash with a local plugins or a groups scope
205
- # @return [Array<Guard::Guard>] the plugins to scope to
170
+ # @return [Array<Guard::Plugin>] the plugins to scope to
206
171
  #
207
- def current_plugins_scope(scopes)
208
- if scopes[:plugins] && !scopes[:plugins].empty?
209
- scopes[:plugins]
210
-
211
- elsif !::Guard.scope[:plugins].empty?
212
- ::Guard.scope[:plugins]
213
-
172
+ def _current_plugins_scope(scope)
173
+ if plugins = _find_non_empty_plugins_scope(scope)
174
+ Array(plugins).map do |plugin|
175
+ plugin.is_a?(Symbol) ? ::Guard.plugin(plugin) : plugin
176
+ end
214
177
  else
215
178
  nil
216
179
  end
@@ -223,16 +186,33 @@ module Guard
223
186
  # @param [Hash] scopes hash with a local plugins or a groups scope
224
187
  # @return [Array<Guard::Group>] the groups to scope to
225
188
  #
226
- def current_groups_scope(scopes)
227
- if scopes[:groups] && !scopes[:groups].empty?
228
- scopes[:groups]
189
+ def _current_groups_scope(scope)
190
+ Array(_find_non_empty_groups_scope(scope)).map do |group|
191
+ group.is_a?(Symbol) ? ::Guard.group(group) : group
192
+ end
193
+ end
194
+
195
+ # Find the first non empty element in the given possibilities
196
+ #
197
+ def _find_non_empty_scope(type, local_scope, *additional_possibilities)
198
+ [
199
+ local_scope[:"#{type}s"],
200
+ local_scope[type.to_sym],
201
+ ::Guard.scope[:"#{type}s"],
202
+ additional_possibilities.flatten
203
+ ].compact.find { |a| !Array(a).empty? }
204
+ end
229
205
 
230
- elsif !::Guard.scope[:groups].empty?
231
- ::Guard.scope[:groups]
206
+ # Find the first non empty plugins scope
207
+ #
208
+ def _find_non_empty_plugins_scope(scope)
209
+ _find_non_empty_scope(:plugin, scope)
210
+ end
232
211
 
233
- else
234
- ::Guard.groups
235
- end
212
+ # Find the first non empty groups scope
213
+ #
214
+ def _find_non_empty_groups_scope(scope)
215
+ _find_non_empty_scope(:group, scope, ::Guard.groups)
236
216
  end
237
217
  end
238
218
  end
@@ -0,0 +1,248 @@
1
+ require 'thread'
2
+ require 'listen'
3
+ require 'guard/options'
4
+
5
+ module Guard
6
+
7
+ module Setuper
8
+
9
+ DEFAULT_OPTIONS = {
10
+ clear: false,
11
+ notify: true,
12
+ debug: false,
13
+ group: [],
14
+ plugin: [],
15
+ watchdir: nil,
16
+ guardfile: nil,
17
+ no_interactions: false,
18
+ no_bundler_warning: false,
19
+ show_deprecations: false,
20
+ latency: nil,
21
+ force_polling: false
22
+ }
23
+ DEFAULT_GROUPS = [:default]
24
+
25
+ # Initializes the Guard singleton:
26
+ #
27
+ # * Initialize the internal Guard state;
28
+ # * Create the interactor when necessary for user interaction;
29
+ # * Select and initialize the file change listener.
30
+ #
31
+ # @option options [Boolean] clear if auto clear the UI should be done
32
+ # @option options [Boolean] notify if system notifications should be shown
33
+ # @option options [Boolean] debug if debug output should be shown
34
+ # @option options [Array<String>] group the list of groups to start
35
+ # @option options [Array<String>] watchdir the directories to watch
36
+ # @option options [String] guardfile the path to the Guardfile
37
+ #
38
+ # @return [Guard] the Guard singleton
39
+ #
40
+ def setup(opts = {})
41
+ @running = true
42
+ @lock = Mutex.new
43
+ @opts = opts
44
+ @watchdirs = [Dir.pwd]
45
+ @evaluator = ::Guard::Guardfile::Evaluator.new(opts)
46
+ @runner = ::Guard::Runner.new
47
+
48
+ if options.watchdir
49
+ # Ensure we have an array
50
+ @watchdirs = Array(options.watchdir).map { |dir| File.expand_path dir }
51
+ end
52
+
53
+ ::Guard::UI.clear(force: true)
54
+ _setup_debug if options.debug
55
+ _setup_listener
56
+ _setup_signal_traps
57
+
58
+ reset_groups
59
+ reset_plugins
60
+ reset_scope
61
+
62
+ evaluate_guardfile
63
+
64
+ setup_scope(groups: options.group, plugins: options.plugin)
65
+
66
+ _setup_notifier
67
+ _setup_interactor
68
+
69
+ self
70
+ end
71
+
72
+ # Lazy initializer for Guard's options hash
73
+ #
74
+ def options
75
+ @options ||= ::Guard::Options.new(@opts || {}, DEFAULT_OPTIONS)
76
+ end
77
+
78
+ # Clear Guard's options hash
79
+ #
80
+ def clear_options
81
+ @options = nil
82
+ end
83
+
84
+ # Initializes the groups array with the default group(s).
85
+ #
86
+ # @see DEFAULT_GROUPS
87
+ #
88
+ def reset_groups
89
+ @groups = DEFAULT_GROUPS.map { |name| Group.new(name) }
90
+ end
91
+
92
+ # Initializes the plugins array to an empty array.
93
+ #
94
+ # @see Guard.plugins
95
+ #
96
+ def reset_plugins
97
+ @plugins = []
98
+ end
99
+
100
+ # Initializes the scope hash to `{ groups: [], plugins: [] }`.
101
+ #
102
+ # @see Guard.setup_scope
103
+ #
104
+ def reset_scope
105
+ @scope = { groups: [], plugins: [] }
106
+ end
107
+
108
+ # Stores the scopes defined by the user via the `--group` / `-g` option (to run
109
+ # only a specific group) or the `--plugin` / `-P` option (to run only a
110
+ # specific plugin).
111
+ #
112
+ # @see CLI#start
113
+ # @see Dsl#scope
114
+ #
115
+ def setup_scope(new_scope)
116
+ if new_scope[:groups] && new_scope[:groups].any?
117
+ scope[:groups] = new_scope[:groups].map { |group| ::Guard.add_group(group) }
118
+ end
119
+
120
+ if new_scope[:plugins] && new_scope[:plugins].any?
121
+ scope[:plugins] = new_scope[:plugins].map { |plugin| ::Guard.plugin(plugin) }
122
+ end
123
+ end
124
+
125
+ # Evaluates the Guardfile content. It displays an error message if no
126
+ # Guard plugins are instantiated after the Guardfile evaluation.
127
+ #
128
+ # @see Guard::Guardfile::Evaluator#evaluate_guardfile
129
+ #
130
+ def evaluate_guardfile
131
+ evaluator.evaluate_guardfile
132
+ ::Guard::UI.error 'No plugins found in Guardfile, please add at least one.' if plugins.empty?
133
+ end
134
+
135
+ private
136
+
137
+ # Sets up various debug behaviors:
138
+ #
139
+ # * Abort threads on exception;
140
+ # * Set the logging level to `:debug`;
141
+ # * Modify the system and ` methods to log themselves before being executed
142
+ #
143
+ # @see #_debug_command_execution
144
+ #
145
+ def _setup_debug
146
+ Thread.abort_on_exception = true
147
+ ::Guard::UI.options.level = :debug
148
+ _debug_command_execution
149
+ end
150
+
151
+ # Initializes the listener and registers a callback for changes.
152
+ #
153
+ def _setup_listener
154
+ listener_callback = lambda do |modified, added, removed|
155
+ # Convert to relative paths (respective to the watchdir it came from)
156
+ @watchdirs.each do |watchdir|
157
+ [modified, added, removed].each do |paths|
158
+ paths.map! do |path|
159
+ if path.start_with? watchdir
160
+ path.sub "#{watchdir}#{File::SEPARATOR}", ''
161
+ else
162
+ path
163
+ end
164
+ end
165
+ end
166
+ end
167
+ evaluator.reevaluate_guardfile if ::Guard::Watcher.match_guardfile?(modified)
168
+
169
+ within_preserved_state do
170
+ runner.run_on_changes(modified, added, removed)
171
+ end
172
+ end
173
+
174
+ listener_options = {}
175
+ %w[latency force_polling].each do |option|
176
+ listener_options[option.to_sym] = options.send(option) if options.send(option)
177
+ end
178
+
179
+ listen_args = @watchdirs + [listener_options]
180
+ @listener = Listen.to(*listen_args).change(&listener_callback)
181
+ end
182
+
183
+ # Sets up traps to catch signals used to control Guard.
184
+ #
185
+ # Currently two signals are caught:
186
+ # - `USR1` which pauses listening to changes.
187
+ # - `USR2` which resumes listening to changes.
188
+ # - 'INT' which is delegated to Pry if active, otherwise stops Guard.
189
+ #
190
+ def _setup_signal_traps
191
+ unless defined?(JRUBY_VERSION)
192
+ if Signal.list.keys.include?('USR1')
193
+ Signal.trap('USR1') { ::Guard.pause unless listener.paused? }
194
+ end
195
+
196
+ if Signal.list.keys.include?('USR2')
197
+ Signal.trap('USR2') { ::Guard.pause if listener.paused? }
198
+ end
199
+
200
+ if Signal.list.keys.include?('INT')
201
+ Signal.trap('INT') do
202
+ if interactor && interactor.thread
203
+ interactor.thread.raise(Interrupt)
204
+ else
205
+ ::Guard.stop
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ # Enables or disables the notifier based on user's configurations.
213
+ #
214
+ def _setup_notifier
215
+ if options.notify && ENV['GUARD_NOTIFY'] != 'false'
216
+ ::Guard::Notifier.turn_on
217
+ else
218
+ ::Guard::Notifier.turn_off
219
+ end
220
+ end
221
+
222
+ # Initializes the interactor unless the user has specified not to.
223
+ #
224
+ def _setup_interactor
225
+ unless options.no_interactions || !::Guard::Interactor.enabled
226
+ @interactor = ::Guard::Interactor.new
227
+ end
228
+ end
229
+
230
+ # Adds a command logger in debug mode. This wraps common command
231
+ # execution functions and logs the executed command before execution.
232
+ #
233
+ def _debug_command_execution
234
+ Kernel.send(:alias_method, :original_system, :system)
235
+ Kernel.send(:define_method, :system) do |command, *args|
236
+ ::Guard::UI.debug "Command execution: #{ command } #{ args.join(' ') }"
237
+ Kernel.send :original_system, command, *args
238
+ end
239
+
240
+ Kernel.send(:alias_method, :original_backtick, :'`')
241
+ Kernel.send(:define_method, :'`') do |command|
242
+ ::Guard::UI.debug "Command execution: #{ command }"
243
+ Kernel.send :original_backtick, command
244
+ end
245
+ end
246
+
247
+ end
248
+ end