joshbuddy-guard 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +370 -0
- data/LICENSE +20 -0
- data/README.md +470 -0
- data/bin/fsevent_watch_guard +0 -0
- data/bin/guard +6 -0
- data/images/failed.png +0 -0
- data/images/pending.png +0 -0
- data/images/success.png +0 -0
- data/lib/guard.rb +463 -0
- data/lib/guard/cli.rb +125 -0
- data/lib/guard/dsl.rb +370 -0
- data/lib/guard/dsl_describer.rb +150 -0
- data/lib/guard/group.rb +37 -0
- data/lib/guard/guard.rb +129 -0
- data/lib/guard/hook.rb +118 -0
- data/lib/guard/interactor.rb +116 -0
- data/lib/guard/listener.rb +351 -0
- data/lib/guard/listeners/darwin.rb +60 -0
- data/lib/guard/listeners/linux.rb +91 -0
- data/lib/guard/listeners/polling.rb +55 -0
- data/lib/guard/listeners/windows.rb +61 -0
- data/lib/guard/notifier.rb +290 -0
- data/lib/guard/templates/Guardfile +2 -0
- data/lib/guard/ui.rb +193 -0
- data/lib/guard/version.rb +6 -0
- data/lib/guard/watcher.rb +114 -0
- data/lib/vendor/darwin/Gemfile +6 -0
- data/lib/vendor/darwin/Guardfile +8 -0
- data/lib/vendor/darwin/LICENSE +20 -0
- data/lib/vendor/darwin/README.rdoc +254 -0
- data/lib/vendor/darwin/Rakefile +21 -0
- data/lib/vendor/darwin/ext/extconf.rb +61 -0
- data/lib/vendor/darwin/ext/fsevent/fsevent_watch.c +226 -0
- data/lib/vendor/darwin/lib/rb-fsevent.rb +2 -0
- data/lib/vendor/darwin/lib/rb-fsevent/fsevent.rb +105 -0
- data/lib/vendor/darwin/lib/rb-fsevent/version.rb +3 -0
- data/lib/vendor/darwin/rb-fsevent.gemspec +24 -0
- data/lib/vendor/darwin/spec/fixtures/folder1/file1.txt +0 -0
- data/lib/vendor/darwin/spec/fixtures/folder1/folder2/file2.txt +0 -0
- data/lib/vendor/darwin/spec/rb-fsevent/fsevent_spec.rb +75 -0
- data/lib/vendor/darwin/spec/spec_helper.rb +24 -0
- data/lib/vendor/linux/MIT-LICENSE +20 -0
- data/lib/vendor/linux/README.md +66 -0
- data/lib/vendor/linux/Rakefile +54 -0
- data/lib/vendor/linux/VERSION +1 -0
- data/lib/vendor/linux/lib/rb-inotify.rb +17 -0
- data/lib/vendor/linux/lib/rb-inotify/event.rb +139 -0
- data/lib/vendor/linux/lib/rb-inotify/native.rb +31 -0
- data/lib/vendor/linux/lib/rb-inotify/native/flags.rb +89 -0
- data/lib/vendor/linux/lib/rb-inotify/notifier.rb +308 -0
- data/lib/vendor/linux/lib/rb-inotify/watcher.rb +83 -0
- data/lib/vendor/linux/rb-inotify.gemspec +53 -0
- data/lib/vendor/windows/Gemfile +4 -0
- data/lib/vendor/windows/README.md +34 -0
- data/lib/vendor/windows/Rakefile +18 -0
- data/lib/vendor/windows/lib/rb-fchange.rb +14 -0
- data/lib/vendor/windows/lib/rb-fchange/event.rb +29 -0
- data/lib/vendor/windows/lib/rb-fchange/native.rb +45 -0
- data/lib/vendor/windows/lib/rb-fchange/native/flags.rb +78 -0
- data/lib/vendor/windows/lib/rb-fchange/notifier.rb +149 -0
- data/lib/vendor/windows/lib/rb-fchange/version.rb +3 -0
- data/lib/vendor/windows/lib/rb-fchange/watcher.rb +99 -0
- data/lib/vendor/windows/rb-fchange.gemspec +34 -0
- data/lib/vendor/windows/spec/fixtures/folder1/file1.txt +0 -0
- data/lib/vendor/windows/spec/fixtures/folder1/folder2/file2.txt +0 -0
- data/lib/vendor/windows/spec/rb-fchange/fchange_spec.rb +119 -0
- data/lib/vendor/windows/spec/spec_helper.rb +21 -0
- data/man/guard.1 +96 -0
- data/man/guard.1.html +181 -0
- metadata +193 -0
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'guard/dsl'
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
|
5
|
+
autoload :UI, 'guard/ui'
|
6
|
+
|
7
|
+
# The DslDescriber overrides methods to create an internal structure
|
8
|
+
# of the Guardfile that is used in some inspection utility methods
|
9
|
+
# like the CLI commands `show` and `list`.
|
10
|
+
#
|
11
|
+
# @see Guard::Dsl
|
12
|
+
# @see Guard::CLI
|
13
|
+
#
|
14
|
+
class DslDescriber < Dsl
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
# Evaluate the DSL methods in the `Guardfile`.
|
19
|
+
#
|
20
|
+
# @option options [Array<Symbol,String>] groups the groups to evaluate
|
21
|
+
# @option options [String] guardfile the path to a valid Guardfile
|
22
|
+
# @option options [String] guardfile_contents a string representing the content of a valid Guardfile
|
23
|
+
# @raise [ArgumentError] when options are not a Hash
|
24
|
+
#
|
25
|
+
def evaluate_guardfile(options = {})
|
26
|
+
@@guardfile_structure = [{ :guards => [] }]
|
27
|
+
super options
|
28
|
+
end
|
29
|
+
|
30
|
+
# List the Guards that are available for use in your system and marks
|
31
|
+
# those that are currently used in your `Guardfile`.
|
32
|
+
#
|
33
|
+
# @example Guard list output
|
34
|
+
#
|
35
|
+
# Available guards:
|
36
|
+
# bundler *
|
37
|
+
# livereload
|
38
|
+
# ronn
|
39
|
+
# rspec *
|
40
|
+
# spork
|
41
|
+
#
|
42
|
+
# See also https://github.com/guard/guard/wiki/List-of-available-Guards
|
43
|
+
# * denotes ones already in your Guardfile
|
44
|
+
#
|
45
|
+
# @param [Hash] options the Guard options
|
46
|
+
#
|
47
|
+
def list(options)
|
48
|
+
evaluate_guardfile(options)
|
49
|
+
|
50
|
+
installed = guardfile_structure.inject([]) do |installed, group|
|
51
|
+
group[:guards].each { |guard| installed << guard[:name] } if group[:guards]
|
52
|
+
installed
|
53
|
+
end
|
54
|
+
|
55
|
+
UI.info 'Available guards:'
|
56
|
+
|
57
|
+
::Guard.guard_gem_names.sort.uniq.each do |name|
|
58
|
+
UI.info " #{ name }#{ installed.include?(name) ? '*' : '' }"
|
59
|
+
end
|
60
|
+
|
61
|
+
UI.info ''
|
62
|
+
UI.info 'See also https://github.com/guard/guard/wiki/List-of-available-Guards'
|
63
|
+
UI.info '* denotes ones already in your Guardfile'
|
64
|
+
end
|
65
|
+
|
66
|
+
# Shows all Guards and their options that are defined in
|
67
|
+
# the `Guardfile`.
|
68
|
+
#
|
69
|
+
# @example guard show output
|
70
|
+
#
|
71
|
+
# (global):
|
72
|
+
# bundler
|
73
|
+
# coffeescript: input => "app/assets/javascripts", noop => true
|
74
|
+
# jasmine
|
75
|
+
# rspec: cli => "--fail-fast --format Fuubar
|
76
|
+
#
|
77
|
+
# @param [Hash] options the Guard options
|
78
|
+
#
|
79
|
+
def show(options)
|
80
|
+
evaluate_guardfile(options)
|
81
|
+
|
82
|
+
guardfile_structure.each do |group|
|
83
|
+
unless group[:guards].empty?
|
84
|
+
if group[:group]
|
85
|
+
UI.info "Group #{ group[:group] }:"
|
86
|
+
else
|
87
|
+
UI.info '(global):'
|
88
|
+
end
|
89
|
+
|
90
|
+
group[:guards].each do |guard|
|
91
|
+
line = " #{ guard[:name] }"
|
92
|
+
|
93
|
+
unless guard[:options].empty?
|
94
|
+
line += ": #{ guard[:options].sort.collect { |k, v| "#{ k } => #{ v.inspect }" }.join(', ') }"
|
95
|
+
end
|
96
|
+
|
97
|
+
UI.info line
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
UI.info ''
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Get the Guardfile structure.
|
108
|
+
#
|
109
|
+
# @return [Array<Hash>] the structure
|
110
|
+
#
|
111
|
+
def guardfile_structure
|
112
|
+
@@guardfile_structure
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# Declares a group of guards.
|
120
|
+
#
|
121
|
+
# @param [String] name the group's name called from the CLI
|
122
|
+
# @yield a block where you can declare several guards
|
123
|
+
#
|
124
|
+
# @see Guard::Dsl#group
|
125
|
+
#
|
126
|
+
def group(name)
|
127
|
+
@@guardfile_structure << { :group => name.to_sym, :guards => [] }
|
128
|
+
@group = true
|
129
|
+
|
130
|
+
yield if block_given?
|
131
|
+
|
132
|
+
@group = false
|
133
|
+
end
|
134
|
+
|
135
|
+
# Declares a Guard.
|
136
|
+
#
|
137
|
+
# @param [String] name the Guard name
|
138
|
+
# @param [Hash] options the options accepted by the Guard
|
139
|
+
# @yield a block where you can declare several watch patterns and actions
|
140
|
+
#
|
141
|
+
# @see Guard::Dsl#guard
|
142
|
+
#
|
143
|
+
def guard(name, options = { })
|
144
|
+
node = (@group ? @@guardfile_structure.last : @@guardfile_structure.first)
|
145
|
+
|
146
|
+
node[:guards] << { :name => name, :options => options }
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
data/lib/guard/group.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Guard
|
2
|
+
|
3
|
+
# A group of Guards. There are two reasons why you want to group your guards:
|
4
|
+
#
|
5
|
+
# - You can start only certain Groups from the command line by passing the `--group` option.
|
6
|
+
# - Abort task execution chain on failure within a group.
|
7
|
+
#
|
8
|
+
# @example Group that aborts on failure
|
9
|
+
#
|
10
|
+
# group :frontend, :halt_on_fail => true do
|
11
|
+
# guard 'coffeescript', :input => 'spec/coffeescripts', :output => 'spec/javascripts'
|
12
|
+
# guard 'jasmine-headless-webkit' do
|
13
|
+
# watch(%r{^spec/javascripts/(.*)\..*}) { |m| newest_js_file("spec/javascripts/#{m[1]}_spec") }
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @see Guard::CLI
|
18
|
+
#
|
19
|
+
class Group
|
20
|
+
|
21
|
+
attr_accessor :name, :options
|
22
|
+
|
23
|
+
# Initialize a Group.
|
24
|
+
#
|
25
|
+
# @param [String] name the name of the group
|
26
|
+
# @param [Hash] options the group options
|
27
|
+
# @option options [Boolean] halt_on_fail if a task execution
|
28
|
+
# should be halted for all Guards in this group if one Guard throws `:task_has_failed`
|
29
|
+
#
|
30
|
+
def initialize(name, options = {})
|
31
|
+
@name = name.to_sym
|
32
|
+
@options = options
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/guard/guard.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
module Guard
|
2
|
+
|
3
|
+
# Base class that every Guard implementation must inherit from.
|
4
|
+
#
|
5
|
+
# Guard will trigger the `start`, `stop`, `reload`, `run_all`, `run_on_change` and
|
6
|
+
# `run_on_deletion` task methods depending on user interaction and file modification.
|
7
|
+
#
|
8
|
+
# In each of these Guard task methods you have to implement some work when you want to
|
9
|
+
# support this kind of task. The return value of each Guard task method is not evaluated
|
10
|
+
# by Guard, but I'll be passed to the "_end" hook for further evaluation. You can
|
11
|
+
# throw `:task_has_failed` to indicate that your Guard method was not successful,
|
12
|
+
# and successive guard tasks will be aborted when the group has set the `:halt_on_fail`
|
13
|
+
# option.
|
14
|
+
#
|
15
|
+
# @see Guard::Hook
|
16
|
+
# @see Guard::Group
|
17
|
+
#
|
18
|
+
# @example Throw :task_has_failed
|
19
|
+
#
|
20
|
+
# def run_all
|
21
|
+
# if !runner.run(['all'])
|
22
|
+
# throw :task_has_failed
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# Each Guard should provide a template Guardfile located within the Gem
|
27
|
+
# at `lib/guard/guard-name/templates/Guardfile`.
|
28
|
+
#
|
29
|
+
# By default all watchers for a Guard are returning strings of paths to the
|
30
|
+
# Guard, but if your Guard want to allow any return value from a watcher,
|
31
|
+
# you can set the `any_return` option to true.
|
32
|
+
#
|
33
|
+
# If one of those methods raise an exception other than `:task_has_failed`,
|
34
|
+
# the Guard::GuardName instance will be removed from the active guards.
|
35
|
+
#
|
36
|
+
class Guard
|
37
|
+
include Hook
|
38
|
+
|
39
|
+
attr_accessor :watchers, :options, :group
|
40
|
+
|
41
|
+
# Initialize a Guard.
|
42
|
+
#
|
43
|
+
# @param [Array<Guard::Watcher>] watchers the Guard file watchers
|
44
|
+
# @param [Hash] options the custom Guard options
|
45
|
+
# @options [Symbol] group the group this Guard belongs to
|
46
|
+
# @options [Boolean] any_return allow any object to be returned from a watcher
|
47
|
+
#
|
48
|
+
def initialize(watchers = [], options = {})
|
49
|
+
@group = options[:group] ? options.delete(:group).to_sym : :default
|
50
|
+
@watchers, @options = watchers, options
|
51
|
+
end
|
52
|
+
|
53
|
+
# Initialize the Guard. This will copy the Guardfile template inside the Guard gem.
|
54
|
+
# The template Guardfile must be located within the Gem at `lib/guard/guard-name/templates/Guardfile`.
|
55
|
+
#
|
56
|
+
# @param [String] name the name of the Guard
|
57
|
+
#
|
58
|
+
def self.init(name)
|
59
|
+
if ::Guard::Dsl.guardfile_include?(name)
|
60
|
+
::Guard::UI.info "Guardfile already includes #{ name } guard"
|
61
|
+
else
|
62
|
+
content = File.read('Guardfile')
|
63
|
+
guard = File.read("#{ ::Guard.locate_guard(name) }/lib/guard/#{ name }/templates/Guardfile")
|
64
|
+
|
65
|
+
File.open('Guardfile', 'wb') do |f|
|
66
|
+
f.puts(content)
|
67
|
+
f.puts("")
|
68
|
+
f.puts(guard)
|
69
|
+
end
|
70
|
+
|
71
|
+
::Guard::UI.info "#{ name } guard added to Guardfile, feel free to edit it"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Call once when Guard starts. Please override initialize method to init stuff.
|
76
|
+
#
|
77
|
+
# @raise [:task_has_failed] when start has failed
|
78
|
+
# @return [Object] the task result
|
79
|
+
#
|
80
|
+
def start
|
81
|
+
end
|
82
|
+
|
83
|
+
# Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard quits).
|
84
|
+
#
|
85
|
+
# @raise [:task_has_failed] when stop has failed
|
86
|
+
# @return [Object] the task result
|
87
|
+
#
|
88
|
+
def stop
|
89
|
+
end
|
90
|
+
|
91
|
+
# Called when `reload|r|z + enter` is pressed.
|
92
|
+
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
93
|
+
#
|
94
|
+
# @raise [:task_has_failed] when reload has failed
|
95
|
+
# @return [Object] the task result
|
96
|
+
#
|
97
|
+
def reload
|
98
|
+
end
|
99
|
+
|
100
|
+
# Called when just `enter` is pressed
|
101
|
+
# This method should be principally used for long action like running all specs/tests/...
|
102
|
+
#
|
103
|
+
# @raise [:task_has_failed] when run_all has failed
|
104
|
+
# @return [Object] the task result
|
105
|
+
#
|
106
|
+
def run_all
|
107
|
+
end
|
108
|
+
|
109
|
+
# Called on file(s) modifications that the Guard watches.
|
110
|
+
#
|
111
|
+
# @param [Array<String>] paths the changes files or paths
|
112
|
+
# @raise [:task_has_failed] when run_on_change has failed
|
113
|
+
# @return [Object] the task result
|
114
|
+
#
|
115
|
+
def run_on_change(paths)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Called on file(s) deletions that the Guard watches.
|
119
|
+
#
|
120
|
+
# @param [Array<String>] paths the deleted files or paths
|
121
|
+
# @raise [:task_has_failed] when run_on_change has failed
|
122
|
+
# @return [Object] the task result
|
123
|
+
#
|
124
|
+
def run_on_deletion(paths)
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
data/lib/guard/hook.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
module Guard
|
2
|
+
|
3
|
+
# Guard has a hook mechanism that allows you to insert callbacks for individual Guards.
|
4
|
+
# By default, each of the Guard instance methods has a "_begin" and an "_end" hook.
|
5
|
+
# For example, the Guard::Guard#start method has a :start_begin hook that is runs immediately
|
6
|
+
# before Guard::Guard#start, and a :start_end hook that runs immediately after Guard::Guard#start.
|
7
|
+
#
|
8
|
+
# Read more about [hooks and callbacks on the wiki](https://github.com/guard/guard/wiki/Hooks-and-callbacks).
|
9
|
+
#
|
10
|
+
module Hook
|
11
|
+
|
12
|
+
# The Hook module gets included.
|
13
|
+
#
|
14
|
+
# @param [Class] base the class that includes the module
|
15
|
+
#
|
16
|
+
def self.included(base)
|
17
|
+
base.send :include, InstanceMethods
|
18
|
+
end
|
19
|
+
|
20
|
+
# Instance methods that gets included in the base class.
|
21
|
+
#
|
22
|
+
module InstanceMethods
|
23
|
+
|
24
|
+
# When event is a Symbol, {#hook} will generate a hook name
|
25
|
+
# by concatenating the method name from where {#hook} is called
|
26
|
+
# with the given Symbol.
|
27
|
+
#
|
28
|
+
# @example Add a hook with a Symbol
|
29
|
+
#
|
30
|
+
# def run_all
|
31
|
+
# hook :foo
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# Here, when {Guard::Guard#run_all} is called, {#hook} will notify callbacks
|
35
|
+
# registered for the "run_all_foo" event.
|
36
|
+
#
|
37
|
+
# When event is a String, {#hook} will directly turn the String
|
38
|
+
# into a Symbol.
|
39
|
+
#
|
40
|
+
# @example Add a hook with a String
|
41
|
+
#
|
42
|
+
# def run_all
|
43
|
+
# hook "foo_bar"
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# When {Guard::Guard#run_all} is called, {#hook} will notify callbacks
|
47
|
+
# registered for the "foo_bar" event.
|
48
|
+
#
|
49
|
+
# @param [Symbol, String] event the name of the Guard event
|
50
|
+
# @param [Array] args the parameters are passed as is to the callbacks registered for the given event.
|
51
|
+
#
|
52
|
+
def hook(event, *args)
|
53
|
+
hook_name = if event.is_a? Symbol
|
54
|
+
calling_method = caller[0][/`([^']*)'/, 1]
|
55
|
+
"#{ calling_method }_#{ event }"
|
56
|
+
else
|
57
|
+
event
|
58
|
+
end.to_sym
|
59
|
+
|
60
|
+
UI.debug "Hook :#{ hook_name } executed for #{ self.class }"
|
61
|
+
|
62
|
+
Hook.notify(self.class, hook_name, *args)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class << self
|
67
|
+
|
68
|
+
# Get all callbacks.
|
69
|
+
#
|
70
|
+
def callbacks
|
71
|
+
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
|
72
|
+
end
|
73
|
+
|
74
|
+
# Add a callback.
|
75
|
+
#
|
76
|
+
# @param [Block] listener the listener to notify
|
77
|
+
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
78
|
+
# @param [Array<Symbol>] events the events to register
|
79
|
+
#
|
80
|
+
def add_callback(listener, guard_class, events)
|
81
|
+
_events = events.is_a?(Array) ? events : [events]
|
82
|
+
_events.each do |event|
|
83
|
+
callbacks[[guard_class, event]] << listener
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Checks if a callback has been registered.
|
88
|
+
#
|
89
|
+
# @param [Block] listener the listener to notify
|
90
|
+
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
91
|
+
# @param [Symbol] event the event to look for
|
92
|
+
#
|
93
|
+
def has_callback?(listener, guard_class, event)
|
94
|
+
callbacks[[guard_class, event]].include?(listener)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Notify a callback.
|
98
|
+
#
|
99
|
+
# @param [Guard::Guard] guard_class the Guard class to add the callback
|
100
|
+
# @param [Symbol] event the event to trigger
|
101
|
+
# @param [Array] args the arguments for the listener
|
102
|
+
#
|
103
|
+
def notify(guard_class, event, *args)
|
104
|
+
callbacks[[guard_class, event]].each do |listener|
|
105
|
+
listener.call(guard_class, event, *args)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Reset all callbacks.
|
110
|
+
#
|
111
|
+
def reset_callbacks!
|
112
|
+
@callbacks = nil
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Guard
|
2
|
+
|
3
|
+
# The interactor reads user input and triggers
|
4
|
+
# specific action upon them unless its locked.
|
5
|
+
#
|
6
|
+
# Currently the following actions are implemented:
|
7
|
+
#
|
8
|
+
# - stop, quit, exit, s, q, e => Exit Guard
|
9
|
+
# - reload, r, z => Reload Guard
|
10
|
+
# - pause, p => Pause Guard
|
11
|
+
# - Everything else => Run all
|
12
|
+
#
|
13
|
+
# It's also possible to scope `reload` and `run all` actions to only a specified group or a guard.
|
14
|
+
#
|
15
|
+
# @example `backend reload` will only reload backend group
|
16
|
+
# @example `spork reload` will only reload rspec guard
|
17
|
+
# @example `jasmine` will only run all jasmine specs
|
18
|
+
#
|
19
|
+
class Interactor
|
20
|
+
|
21
|
+
STOP_ACTIONS = %w[stop quit exit s q e]
|
22
|
+
RELOAD_ACTIONS = %w[reload r z]
|
23
|
+
PAUSE_ACTIONS = %w[pause p]
|
24
|
+
|
25
|
+
# Start the interactor in its own thread.
|
26
|
+
#
|
27
|
+
def start
|
28
|
+
return if ENV["GUARD_ENV"] == 'test'
|
29
|
+
|
30
|
+
if !@thread || !@thread.alive?
|
31
|
+
@thread = Thread.new do
|
32
|
+
while entry = $stdin.gets.chomp
|
33
|
+
scopes, action = extract_scopes_and_action(entry)
|
34
|
+
case action
|
35
|
+
when :stop
|
36
|
+
::Guard.stop
|
37
|
+
when :pause
|
38
|
+
::Guard.pause
|
39
|
+
when :reload
|
40
|
+
::Guard::Dsl.reevaluate_guardfile if scopes.empty?
|
41
|
+
::Guard.reload(scopes)
|
42
|
+
when :run_all
|
43
|
+
::Guard.run_all(scopes)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Kill interactor thread if not current
|
51
|
+
#
|
52
|
+
def stop
|
53
|
+
unless Thread.current == @thread
|
54
|
+
@thread.kill
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Extract guard or group scope and action from Interactor entry
|
59
|
+
#
|
60
|
+
# @example `spork reload` will only reload rspec
|
61
|
+
# @example `jasmine` will only run all jasmine specs
|
62
|
+
#
|
63
|
+
# @param [String] Interactor entry gets from $stdin
|
64
|
+
# @return [Array] entry group or guard scope hash and action
|
65
|
+
#
|
66
|
+
def extract_scopes_and_action(entry)
|
67
|
+
scopes = {}
|
68
|
+
entries = entry.split(' ')
|
69
|
+
case entries.length
|
70
|
+
when 1
|
71
|
+
unless action = action_from_entry(entries[0])
|
72
|
+
scopes = scopes_from_entry(entries[0])
|
73
|
+
end
|
74
|
+
when 2
|
75
|
+
scopes = scopes_from_entry(entries[0])
|
76
|
+
action = action_from_entry(entries[1])
|
77
|
+
end
|
78
|
+
action ||= :run_all
|
79
|
+
|
80
|
+
[scopes, action]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Extract guard or group scope from entry if valid
|
84
|
+
#
|
85
|
+
# @param [String] Interactor entry gets from $stdin
|
86
|
+
# @return [Hash] An hash with a guard or a group scope
|
87
|
+
#
|
88
|
+
def scopes_from_entry(entry)
|
89
|
+
scopes = {}
|
90
|
+
if guard = ::Guard.guards(entry)
|
91
|
+
scopes[:guard] = guard
|
92
|
+
end
|
93
|
+
if group = ::Guard.groups(entry)
|
94
|
+
scopes[:group] = group
|
95
|
+
end
|
96
|
+
|
97
|
+
scopes
|
98
|
+
end
|
99
|
+
|
100
|
+
# Extract action from entry if an existing action is present
|
101
|
+
#
|
102
|
+
# @param [String] Interactor entry gets from $stdin
|
103
|
+
# @return [Symbol] A guard action
|
104
|
+
#
|
105
|
+
def action_from_entry(entry)
|
106
|
+
if STOP_ACTIONS.include?(entry)
|
107
|
+
:stop
|
108
|
+
elsif RELOAD_ACTIONS.include?(entry)
|
109
|
+
:reload
|
110
|
+
elsif PAUSE_ACTIONS.include?(entry)
|
111
|
+
:pause
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|