guard 0.8.4 → 0.8.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +348 -330
- data/LICENSE +19 -19
- data/README.md +464 -431
- data/bin/guard +6 -6
- data/lib/guard.rb +452 -414
- data/lib/guard/cli.rb +119 -178
- data/lib/guard/dsl.rb +370 -370
- data/lib/guard/dsl_describer.rb +150 -60
- data/lib/guard/group.rb +37 -37
- data/lib/guard/guard.rb +129 -113
- data/lib/guard/hook.rb +118 -118
- data/lib/guard/interactor.rb +110 -44
- data/lib/guard/listener.rb +350 -350
- data/lib/guard/listeners/darwin.rb +66 -66
- data/lib/guard/listeners/linux.rb +97 -97
- data/lib/guard/listeners/polling.rb +55 -55
- data/lib/guard/listeners/windows.rb +61 -61
- data/lib/guard/notifier.rb +290 -211
- data/lib/guard/templates/Guardfile +2 -2
- data/lib/guard/ui.rb +193 -188
- data/lib/guard/version.rb +6 -6
- data/lib/guard/watcher.rb +114 -110
- data/man/guard.1 +93 -93
- data/man/guard.1.html +176 -176
- metadata +107 -59
data/lib/guard/dsl_describer.rb
CHANGED
@@ -1,60 +1,150 @@
|
|
1
|
-
require 'guard/dsl'
|
2
|
-
|
3
|
-
module Guard
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
class << self
|
17
|
-
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# @
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
CHANGED
@@ -1,37 +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
|
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
CHANGED
@@ -1,113 +1,129 @@
|
|
1
|
-
module Guard
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# Guard will trigger the `start`, `stop`, `reload`, `run_all`, `run_on_change` and
|
6
|
-
# `run_on_deletion` methods depending on user interaction and file modification.
|
7
|
-
#
|
8
|
-
# In each of these Guard methods you have to implement some work when you want to
|
9
|
-
# support this kind of task. The return value of each Guard method is
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# @
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
#
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
#
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
#
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
#
|
96
|
-
#
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|