module Guard autoload :ReadlineInteractor, 'guard/interactors/readline' autoload :SimpleInteractor, 'guard/interactors/simple' # The interactor triggers specific action from input # read by a interactor implementation. # # Currently the following actions are implemented: # # - h, help => Show help # - e, exit, # q. quit => Exit Guard # - r, reload => Reload Guard # - p, pause => Toggle file modification listener # - n, notification => Toggle notifications # - => Run all # # It's also possible to scope `reload` and `run all` actions to only a specified group or a guard. # # @example Reload backend group # backend reload # # @example Reload rspec guard # spork reload # # @example Run all jasmine specs # jasmine # # @abstract # class Interactor HELP_ENTRIES = %w[help h] RELOAD_ENTRIES = %w[reload r] STOP_ENTRIES = %w[exit e quit q] PAUSE_ENTRIES = %w[pause p] NOTIFICATION_ENTRIES = %w[notification n] # Set the interactor implementation # # @param [Symbol] interactor the name of the interactor # def self.interactor=(interactor) @interactor = interactor end # Get an instance of the currently configured # interactor implementation. # # @return [Interactor] an interactor implementation # def self.fabricate case @interactor when :readline ReadlineInteractor.new when :simple SimpleInteractor.new when :off nil else auto_detect end end # Tries to detect an optimal interactor for the # current environment. # # It returns the Readline implementation when: # # * rb-readline is installed # * The Ruby implementation is JRuby # * The current OS is not Mac OS X # # Otherwise the plain gets interactor is returned. # # @return [Interactor] an interactor implementation # def self.auto_detect require 'readline' if defined?(RbReadline) || defined?(JRUBY_VERSION) || !RbConfig::CONFIG['target_os'] =~ /darwin/i ReadlineInteractor.new else SimpleInteractor.new end end # Start the line reader in its own thread. # def start return if ENV['GUARD_ENV'] == 'test' @thread = Thread.new { read_line } if !@thread || !@thread.alive? end # Kill interactor thread if not current # def stop unless Thread.current == @thread @thread.kill end end # Read the user input. This method must be implemented # by each interactor implementation. # # @abstract # def read_line raise NotImplementedError end # Process the input from readline. # # @param [String] line the input line # def process_input(line) scopes, action = extract_scopes_and_action(line) case action when :help help when :stop ::Guard.stop when :pause ::Guard.pause when :reload reload(scopes) when :run_all ::Guard.run_all(scopes) when :notification toggle_notification else ::Guard::UI.error "Unknown command #{ line }" end end # Execute the reload action. # # @param [Hash] scopes the reload scopes # def reload(scopes) ::Guard::UI.info 'Reload' ::Guard::Dsl.reevaluate_guardfile if scopes.empty? ::Guard.reload(scopes) end # Toggle the system notifications on/off # def toggle_notification if ENV['GUARD_NOTIFY'] == 'true' ::Guard::UI.info 'Turn off notifications' ::Guard::Notifier.turn_off else ::Guard::Notifier.turn_on end end # Show the help. # def help puts '' puts '[e]xit, [q]uit Exit Guard' puts '[p]ause Toggle file modification listener' puts '[r]eload Reload Guard' puts '[n]otification Toggle notifications' puts ' Run all Guards' puts '' puts 'You can scope the reload action to a specific guard or group:' puts '' puts 'rspec reload Reload the RSpec Guard' puts 'backend reload Reload the backend group' puts '' puts 'You can also run only a specific Guard or all Guards in a specific group:' puts '' puts 'jasmine Run the jasmine Guard' puts 'frontend Run all Guards in the frontend group' puts '' end # Extract the Guard or group scope and action from the # input line. # # @example `spork reload` will only reload rspec # @example `jasmine` will only run all jasmine specs # # @param [String] line the readline input # @return [Array] the group or guard scope and the action # def extract_scopes_and_action(line) scopes = { } entries = line.split(' ') case entries.length when 1 unless action = action_from_entry(entries[0]) scopes = scopes_from_entry(entries[0]) end when 2 scopes = scopes_from_entry(entries[0]) action = action_from_entry(entries[1]) end action = :run_all if !action && (!scopes.empty? || entries.empty?) [scopes, action] end private # Extract guard or group scope from entry if valid # # @param [String] entry the possible scope entry # @return [Hash] a hash with a Guard or a group scope # def scopes_from_entry(entry) scopes = { } if guard = ::Guard.guards(entry) scopes[:guard] = guard end if group = ::Guard.groups(entry) scopes[:group] = group end scopes end # Find the action for the given input entry. # # @param [String] entry the possible action entry # @return [Symbol] a Guard action # def action_from_entry(entry) if STOP_ENTRIES.include?(entry) :stop elsif RELOAD_ENTRIES.include?(entry) :reload elsif PAUSE_ENTRIES.include?(entry) :pause elsif HELP_ENTRIES.include?(entry) :help elsif NOTIFICATION_ENTRIES.include?(entry) :notification end end end end