• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

guard / guard / 1053

pending completion
1053

push

travis-ci

thibaudgg
Merge pull request #562 from docwhat/speeling

Speeling typo

1 of 1 new or added line in 1 file covered. (100.0%)

5247 of 5368 relevant lines covered (97.75%)

86.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.44
/lib/guard/interactor.rb
1
require 'pry'
5✔
2

3
require 'guard/commands/all'
5✔
4
require 'guard/commands/change'
5✔
5
require 'guard/commands/notification'
5✔
6
require 'guard/commands/pause'
5✔
7
require 'guard/commands/reload'
5✔
8
require 'guard/commands/scope'
5✔
9
require 'guard/commands/show'
5✔
10
require 'guard/ui'
5✔
11

12
module Guard
5✔
13

14
  # The Guard interactor is a Pry REPL with a Guard
15
  # specific command set.
16
  #
17
  class Interactor
5✔
18

19
    # The default Ruby script to configure Guard Pry if the option `:guard_rc` is not defined.
20
    GUARD_RC = '~/.guardrc'
5✔
21

22
    # The default Guard Pry history file if the option `:history_file` is not defined.
23
    HISTORY_FILE = '~/.guard_history'
5✔
24

25
    # List of shortcuts for each interactor command
26
    SHORTCUTS = {
5✔
27
      help:         'h',
1 only rbx ✔
28
      all:          'a',
1 only rbx ✔
29
      reload:       'r',
1 only rbx ✔
30
      change:       'c',
1 only rbx ✔
31
      show:         's',
1 only rbx ✔
32
      scope:        'o',
1 only rbx ✔
33
      notification: 'n',
1 only rbx ✔
34
      pause:        'p',
1 only rbx ✔
35
      exit:         'e',
1 only rbx ✔
36
      quit:         'q'
1 only rbx ✔
37
    }
1 only rbx ✔
38

39
    attr_accessor :thread
5✔
40

41
    # Get the interactor options
42
    #
43
    # @return [Hash] the options
44
    #
45
    def self.options
5✔
46
      @options ||= {}
20✔
47
    end
48

49
    # Set the interactor options
50
    #
51
    # @param [Hash] options the interactor options
52
    # @option options [String] :guard_rc the Ruby script to configure Guard Pry
53
    # @option options [String] :history_file the file to write the Pry history to
54
    #
55
    def self.options=(options)
5✔
56
      @options = options
20✔
57
    end
58

59
    # Is the interactor enabled?
60
    #
61
    # @return [Boolean] true if enabled
62
    #
63
    def self.enabled
5✔
64
      @enabled || @enabled.nil?
246✔
65
    end
66

67
    # Set the enabled status for the interactor
68
    #
69
    # @param [Boolean] status true if enabled
70
    #
71
    def self.enabled=(status)
5✔
72
      @enabled = status
130✔
73
    end
74

75
    # Converts and validates a plain text scope
76
    # to a valid plugin or group scope.
77
    #
78
    # @param [Array<String>] entries the text scope
79
    # @return [Hash, Array<String>] the plugin or group scope, the unknown entries
80
    #
81
    def self.convert_scope(entries)
5✔
82
      scopes  = { plugins: [], groups: [] }
115✔
83
      unknown = []
115✔
84

85
      entries.each do |entry|
115✔
86
        if plugin = ::Guard.plugin(entry)
125✔
87
          scopes[:plugins] << plugin
40✔
88
        elsif group = ::Guard.group(entry)
17 only rbx ✔
89
          scopes[:groups] << group
40✔
90
        else
91
          unknown << entry
45✔
92
        end
93
      end
94

95
      [scopes, unknown]
115✔
96
    end
97

98
    # Initializes the interactor. This configures
99
    # Pry and creates some custom commands and aliases
100
    # for Guard.
101
    #
102
    def initialize
5✔
103
      return if ENV['GUARD_ENV'] == 'test'
13✔
104

105
      Pry.config.should_load_rc       = false
5✔
106
      Pry.config.should_load_local_rc = false
5✔
107
      Pry.config.history.file         = File.expand_path(self.class.options[:history_file] || HISTORY_FILE)
5✔
108

109
      @stty_exists = nil
5✔
110
      _add_hooks
5✔
111

112
      _replace_reset_command
5✔
113
      _create_run_all_command
5✔
114
      _create_command_aliases
5✔
115
      _create_guard_commands
5✔
116
      _create_group_commands
5✔
117

118
      _configure_prompt
5✔
119
    end
120

121
    # Start the line reader in its own thread and
122
    # stop Guard on Ctrl-D.
123
    #
124
    def start
5✔
125
      return if ENV['GUARD_ENV'] == 'test'
13✔
126

127
      _store_terminal_settings if _stty_exists?
5✔
128

129
      unless @thread
5✔
130
        ::Guard::UI.debug 'Start interactor'
5✔
131

132
        @thread = Thread.new do
5✔
133
          Pry.start
5✔
134
          ::Guard.stop
×
135
        end
136
      end
137
    end
138

139
    # Kill interactor thread if not current
140
    #
141
    def stop
5✔
142
      return if !@thread || ENV['GUARD_ENV'] == 'test'
61✔
143

144
      unless Thread.current == @thread
5✔
145
        ::Guard::UI.reset_line
5✔
146
        ::Guard::UI.debug 'Stop interactor'
5✔
147
        @thread.kill
5✔
148
        @thread = nil
5✔
149
      end
150

151
      _restore_terminal_settings if _stty_exists?
5✔
152
    end
153

154
    private
5✔
155

156
    # Add Pry hooks:
157
    #
158
    # * Load `~/.guardrc` within each new Pry session.
159
    # * Load project's `.guardrc` within each new Pry session.
160
    # * Restore prompt after each evaluation.
161
    #
162
    def _add_hooks
5✔
163
      _add_load_guard_rc_hook
5✔
164
      _add_load_project_guard_rc_hook
5✔
165
      _add_restore_visibility_hook if _stty_exists?
5✔
166
    end
167

168
    # Add a `when_started` hook that loads a global .guardrc if it exists.
169
    #
170
    def _add_load_guard_rc_hook
5✔
171
      Pry.config.hooks.add_hook :when_started, :load_guard_rc do
5✔
172
        (self.class.options[:guard_rc] || GUARD_RC).tap do |p|
×
173
          load p if File.exist?(File.expand_path(p))
×
174
        end
175
      end
176
    end
177

178
    # Add a `when_started` hook that loads a project .guardrc if it exists.
179
    #
180
    def _add_load_project_guard_rc_hook
5✔
181
      Pry.config.hooks.add_hook :when_started, :load_project_guard_rc do
5✔
182
        project_guard_rc = Dir.pwd + '/.guardrc'
×
183
        load project_guard_rc if File.exist?(project_guard_rc)
×
184
      end
185
    end
186

187
    # Add a `after_eval` hook that restores visibility after a command is eval.
188
    #
189
    def _add_restore_visibility_hook
5✔
190
      Pry.config.hooks.add_hook :after_eval, :restore_visibility do
×
191
        system("stty echo 2>#{ DEV_NULL }")
×
192
      end
193
    end
194

195
    # Replaces reset defined inside of Pry with a reset that
196
    # instead restarts guard.
197
    #
198
    def _replace_reset_command
5✔
199
      Pry.commands.command "reset", "Reset the Guard to a clean state." do
5✔
200
        output.puts "Guard reset."
×
201
        exec "guard"
×
202
      end
203
    end
204

205
    # Creates a command that triggers the `:run_all` action
206
    # when the command is empty (just pressing enter on the
207
    # beginning of a line).
208
    #
209
    def _create_run_all_command
5✔
210
      Pry.commands.block_command /^$/, 'Hit enter to run all' do
5✔
211
        Pry.run_command 'all'
×
212
      end
213
    end
214

215
    # Creates command aliases for the commands
216
    # `help`, `reload`, `change`, `scope`, `notification`, `pause`, `exit` and `quit`,
217
    # which will be the first letter of the command.
218
    #
219
    def _create_command_aliases
5✔
220
      SHORTCUTS.each do |command, shortcut|
5✔
221
        Pry.commands.alias_command shortcut, command.to_s
50✔
222
      end
223
    end
224

225
    # Create a shorthand command to run the `:run_all`
226
    # action on a specific Guard plugin. For example,
227
    # when guard-rspec is available, then a command
228
    # `rspec` is created that runs `all rspec`.
229
    #
230
    def _create_guard_commands
5✔
231
      ::Guard.plugins.each do |guard_plugin|
5✔
232
        Pry.commands.create_command guard_plugin.name, "Run all #{ guard_plugin.title }" do
×
233
          group 'Guard'
×
234

235
          def process
×
236
            Pry.run_command "all #{ match }"
×
237
          end
238
        end
239
      end
240
    end
241

242
    # Create a shorthand command to run the `:run_all`
243
    # action on a specific Guard group. For example,
244
    # when you have a group `frontend`, then a command
245
    # `frontend` is created that runs `all frontend`.
246
    #
247
    def _create_group_commands
5✔
248
      ::Guard.groups.each do |group|
5✔
249
        next if group.name == :default
5✔
250

251
        Pry.commands.create_command group.name.to_s, "Run all #{ group.title }" do
×
252
          group 'Guard'
×
253

254
          def process
×
255
            Pry.run_command "all #{ match }"
×
256
          end
257
        end
258
      end
259
    end
260

261
    # Configures the pry prompt to see `guard` instead of
262
    # `pry`.
263
    #
264
    def _configure_prompt
5✔
265
      Pry.config.prompt = [_prompt('>'), _prompt('*')]
5✔
266
    end
267

268
    # Returns the plugins scope, or the groups scope ready for display in the
269
    # prompt.
270
    #
271
    def _scope_for_prompt
5✔
272
      [:plugins, :groups].each do |scope_name|
18✔
273
        return "#{_join_scope_for_prompt(scope_name)} " unless ::Guard.scope[scope_name].empty?
32✔
274
      end
275

276
      ''
10✔
277
    end
278

279
    # Joins the scope corresponding to the given scope name with commas.
280
    #
281
    def _join_scope_for_prompt(scope_name)
5✔
282
      ::Guard.scope[scope_name].map(&:title).join(',')
10✔
283
    end
284

285
    # Returns a proc that will return itself a string ending with the given
286
    # `ending_char` when called.
287
    #
288
    def _prompt(ending_char)
5✔
289
      proc do |target_self, nest_level, pry|
30✔
290
        history = pry.input_array.size
20✔
291
        process = ::Guard.listener.paused? ? 'pause' : 'guard'
20✔
292
        level   = ":#{ nest_level }" unless nest_level.zero?
20✔
293

294
        "[#{ history }] #{ _scope_for_prompt }#{ process }(#{ _clip_name(target_self) })#{ level }#{ ending_char } "
44✔
295
      end
296
    end
297

298
    def _clip_name(target)
5✔
299
      Pry.view_clip(target)
×
300
    end
301

302
    # Detects whether or not the stty command exists
303
    # on the user machine.
304
    #
305
    # @return [Boolean] the status of stty
306
    #
307
    def _stty_exists?
5✔
308
      @stty_exists ||= system('hash', 'stty') ? true : false if @stty_exists.nil?
14✔
309
      @stty_exists
15✔
310
    end
311

312
    # Stores the terminal settings so we can resore them
313
    # when stopping.
314
    #
315
    def _store_terminal_settings
5✔
316
      @stty_save = `stty -g 2>#{ DEV_NULL }`.chomp
×
317
    end
318

319
    # Restore terminal settings
320
    #
321
    def _restore_terminal_settings
5✔
322
      system("stty #{ @stty_save } 2>#{ DEV_NULL }") if @stty_save
×
323
    end
324

325
  end
326
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc