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/cli.rb
CHANGED
@@ -1,178 +1,119 @@
|
|
1
|
-
require 'thor'
|
2
|
-
require 'guard/version'
|
3
|
-
|
4
|
-
module Guard
|
5
|
-
|
6
|
-
# Guard command line interface managed by [Thor](https://github.com/wycats/thor).
|
7
|
-
# This is the main interface to Guard that is called by the Guard binary `bin/guard`.
|
8
|
-
#
|
9
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
def init(guard_name = nil)
|
121
|
-
if !File.exist?('Guardfile')
|
122
|
-
puts "Writing new Guardfile to #{Dir.pwd}/Guardfile"
|
123
|
-
FileUtils.cp(File.expand_path('../templates/Guardfile', __FILE__), 'Guardfile')
|
124
|
-
elsif guard_name.nil?
|
125
|
-
Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
|
126
|
-
exit 1
|
127
|
-
end
|
128
|
-
|
129
|
-
if guard_name
|
130
|
-
guard_class = ::Guard.get_guard_class(guard_name)
|
131
|
-
guard_class.init(guard_name)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
desc 'show', 'Show all defined Guards and their options'
|
136
|
-
map %w(-T) => :show
|
137
|
-
|
138
|
-
# Shows all Guards and their options that are defined in
|
139
|
-
# the `Guardfile`.
|
140
|
-
#
|
141
|
-
# @example guard show output
|
142
|
-
#
|
143
|
-
# (global):
|
144
|
-
# bundler
|
145
|
-
# coffeescript: input => "app/assets/javascripts", noop => true
|
146
|
-
# jasmine
|
147
|
-
# rspec: cli => "--fail-fast --format Fuubar
|
148
|
-
#
|
149
|
-
# @see Guard::DslDescriber
|
150
|
-
#
|
151
|
-
def show
|
152
|
-
Guard::DslDescriber.evaluate_guardfile(options)
|
153
|
-
|
154
|
-
Guard::DslDescriber.guardfile_structure.each do |group|
|
155
|
-
unless group[:guards].empty?
|
156
|
-
if group[:group]
|
157
|
-
Guard::UI.info "Group #{ group[:group] }:"
|
158
|
-
else
|
159
|
-
Guard::UI.info '(global):'
|
160
|
-
end
|
161
|
-
|
162
|
-
group[:guards].each do |guard|
|
163
|
-
line = " #{ guard[:name] }"
|
164
|
-
|
165
|
-
unless guard[:options].empty?
|
166
|
-
line += ": #{ guard[:options].collect { |k, v| "#{ k } => #{ v.inspect }" }.join(', ') }"
|
167
|
-
end
|
168
|
-
|
169
|
-
Guard::UI.info line
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
Guard::UI.info ''
|
175
|
-
end
|
176
|
-
|
177
|
-
end
|
178
|
-
end
|
1
|
+
require 'thor'
|
2
|
+
require 'guard/version'
|
3
|
+
|
4
|
+
module Guard
|
5
|
+
|
6
|
+
# Facade for the Guard command line interface managed by [Thor](https://github.com/wycats/thor).
|
7
|
+
# This is the main interface to Guard that is called by the Guard binary `bin/guard`.
|
8
|
+
# Do not put any logic in here, create a class and delegate instead.
|
9
|
+
#
|
10
|
+
class CLI < Thor
|
11
|
+
|
12
|
+
default_task :start
|
13
|
+
|
14
|
+
desc 'start', 'Starts Guard'
|
15
|
+
|
16
|
+
method_option :clear,
|
17
|
+
:type => :boolean,
|
18
|
+
:default => false,
|
19
|
+
:aliases => '-c',
|
20
|
+
:banner => 'Auto clear shell before each change/run_all/reload'
|
21
|
+
|
22
|
+
method_option :notify,
|
23
|
+
:type => :boolean,
|
24
|
+
:default => true,
|
25
|
+
:aliases => '-n',
|
26
|
+
:banner => 'Notifications feature (growl/libnotify)'
|
27
|
+
|
28
|
+
method_option :debug,
|
29
|
+
:type => :boolean,
|
30
|
+
:default => false,
|
31
|
+
:aliases => '-d',
|
32
|
+
:banner => 'Print debug messages'
|
33
|
+
|
34
|
+
method_option :group,
|
35
|
+
:type => :array,
|
36
|
+
:default => [],
|
37
|
+
:aliases => '-g',
|
38
|
+
:banner => 'Run only the passed groups'
|
39
|
+
|
40
|
+
method_option :watchdir,
|
41
|
+
:type => :string,
|
42
|
+
:aliases => '-w',
|
43
|
+
:banner => 'Specify the directory to watch'
|
44
|
+
|
45
|
+
method_option :guardfile,
|
46
|
+
:type => :string,
|
47
|
+
:aliases => '-G',
|
48
|
+
:banner => 'Specify a Guardfile'
|
49
|
+
|
50
|
+
method_option :watch_all_modifications,
|
51
|
+
:type => :boolean,
|
52
|
+
:default => false,
|
53
|
+
:aliases => '-A',
|
54
|
+
:banner => 'Watch for all file modifications including moves and deletions'
|
55
|
+
|
56
|
+
method_option :no_interactions,
|
57
|
+
:type => :boolean,
|
58
|
+
:default => false,
|
59
|
+
:aliases => '-i',
|
60
|
+
:banner => 'Turn off completely any guard terminal interactions'
|
61
|
+
|
62
|
+
# Start Guard by initialize the defined Guards and watch the file system.
|
63
|
+
# This is the default task, so calling `guard` is the same as calling `guard start`.
|
64
|
+
#
|
65
|
+
# @see Guard.start
|
66
|
+
#
|
67
|
+
def start
|
68
|
+
::Guard.start(options)
|
69
|
+
end
|
70
|
+
|
71
|
+
desc 'list', 'Lists guards that can be used with init'
|
72
|
+
|
73
|
+
# List the Guards that are available for use in your system and marks
|
74
|
+
# those that are currently used in your `Guardfile`.
|
75
|
+
#
|
76
|
+
# @see Guard::DslDescriber.list
|
77
|
+
#
|
78
|
+
def list
|
79
|
+
::Guard::DslDescriber.list(options)
|
80
|
+
end
|
81
|
+
|
82
|
+
desc 'version', 'Show the Guard version'
|
83
|
+
map %w(-v --version) => :version
|
84
|
+
|
85
|
+
# Shows the current version of Guard.
|
86
|
+
#
|
87
|
+
# @see Guard::VERSION
|
88
|
+
#
|
89
|
+
def version
|
90
|
+
::Guard::UI.info "Guard version #{ Guard::VERSION }"
|
91
|
+
end
|
92
|
+
|
93
|
+
desc 'init [GUARD]', 'Generates a Guardfile at the current working directory, or insert the given GUARD to an existing Guardfile'
|
94
|
+
|
95
|
+
# Appends the Guard template to the `Guardfile`, or creates an initial
|
96
|
+
# `Guardfile` when no Guard name is passed.
|
97
|
+
#
|
98
|
+
# @see Guard.initialize_template
|
99
|
+
#
|
100
|
+
# @param [String] guard_name the name of the Guard to initialize
|
101
|
+
#
|
102
|
+
def init(guard_name = nil)
|
103
|
+
::Guard.initialize_template(guard_name)
|
104
|
+
end
|
105
|
+
|
106
|
+
desc 'show', 'Show all defined Guards and their options'
|
107
|
+
map %w(-T) => :show
|
108
|
+
|
109
|
+
# Shows all Guards and their options that are defined in
|
110
|
+
# the `Guardfile`.
|
111
|
+
#
|
112
|
+
# @see Guard::DslDescriber.show
|
113
|
+
#
|
114
|
+
def show
|
115
|
+
::Guard::DslDescriber.show(options)
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
data/lib/guard/dsl.rb
CHANGED
@@ -1,370 +1,370 @@
|
|
1
|
-
module Guard
|
2
|
-
|
3
|
-
# The DSL class provides the methods that are used in each `Guardfile` to describe
|
4
|
-
# the behaviour of Guard.
|
5
|
-
#
|
6
|
-
# The main keywords of the DSL are `guard` and `watch`. These are necessary to define
|
7
|
-
# the used Guards and the file changes they are watching.
|
8
|
-
#
|
9
|
-
# You can optionally group the Guards with the `group` keyword and ignore certain paths
|
10
|
-
# with the `ignore_paths` keyword.
|
11
|
-
#
|
12
|
-
# A more advanced DSL use is the `callback` keyword that allows you to execute arbitrary
|
13
|
-
# code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change`
|
14
|
-
# Guards' method. You can even insert more hooks inside these methods.
|
15
|
-
# Please [checkout the Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for more details.
|
16
|
-
#
|
17
|
-
# The DSL will also evaluate normal Ruby code.
|
18
|
-
#
|
19
|
-
# There are two possible locations for the `Guardfile`:
|
20
|
-
# - The `Guardfile` in the current directory where Guard has been started
|
21
|
-
# - The `.Guardfile` in your home directory.
|
22
|
-
#
|
23
|
-
# In addition, if a user configuration `.guard.rb` in your home directory is found, it will
|
24
|
-
# be appended to the current project `Guardfile`.
|
25
|
-
#
|
26
|
-
# @example A sample of a complex Guardfile
|
27
|
-
#
|
28
|
-
# group 'frontend' do
|
29
|
-
# guard 'passenger', :ping => true do
|
30
|
-
# watch('config/application.rb')
|
31
|
-
# watch('config/environment.rb')
|
32
|
-
# watch(%r{^config/environments/.+\.rb})
|
33
|
-
# watch(%r{^config/initializers/.+\.rb})
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# guard 'livereload', :apply_js_live => false do
|
37
|
-
# watch(%r{^app/.+\.(erb|haml)})
|
38
|
-
# watch(%r{^app/helpers/.+\.rb})
|
39
|
-
# watch(%r{^public/javascripts/.+\.js})
|
40
|
-
# watch(%r{^public/stylesheets/.+\.css})
|
41
|
-
# watch(%r{^public/.+\.html})
|
42
|
-
# watch(%r{^config/locales/.+\.yml})
|
43
|
-
# end
|
44
|
-
# end
|
45
|
-
#
|
46
|
-
# group 'backend' do
|
47
|
-
# # Reload the bundle when the Gemfile is modified
|
48
|
-
# guard 'bundler' do
|
49
|
-
# watch('Gemfile')
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# # for big project you can fine tune the "timeout" before Spork's launch is considered failed
|
53
|
-
# guard 'spork', :wait => 40 do
|
54
|
-
# watch('Gemfile')
|
55
|
-
# watch('config/application.rb')
|
56
|
-
# watch('config/environment.rb')
|
57
|
-
# watch(%r{^config/environments/.+\.rb})
|
58
|
-
# watch(%r{^config/initializers/.+\.rb})
|
59
|
-
# watch('spec/spec_helper.rb')
|
60
|
-
# end
|
61
|
-
#
|
62
|
-
# # use RSpec 2, from the system's gem and with some direct RSpec CLI options
|
63
|
-
# guard 'rspec', :version => 2, :cli => "--color --drb -f doc", :bundler => false do
|
64
|
-
# watch('spec/spec_helper.rb') { "spec" }
|
65
|
-
# watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
66
|
-
# watch('config/routes.rb') { "spec/routing" }
|
67
|
-
# watch(%r{^spec/support/(controllers|acceptance)_helpers\.rb}) { |m| "spec/#{m[1]}" }
|
68
|
-
# watch(%r{^spec/.+_spec\.rb})
|
69
|
-
#
|
70
|
-
# watch(%r{^app/controllers/(.+)_(controller)\.rb}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
71
|
-
#
|
72
|
-
# watch(%r{^app/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
|
73
|
-
# watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
74
|
-
# end
|
75
|
-
# end
|
76
|
-
#
|
77
|
-
class Dsl
|
78
|
-
class << self
|
79
|
-
|
80
|
-
@@options = nil
|
81
|
-
|
82
|
-
# Evaluate the DSL methods in the `Guardfile`.
|
83
|
-
#
|
84
|
-
# @option options [Array<Symbol,String>] groups the groups to evaluate
|
85
|
-
# @option options [String] guardfile the path to a valid Guardfile
|
86
|
-
# @option options [String] guardfile_contents a string representing the content of a valid Guardfile
|
87
|
-
# @raise [ArgumentError] when options are not a Hash
|
88
|
-
#
|
89
|
-
def evaluate_guardfile(options = {})
|
90
|
-
raise ArgumentError.new('No option hash passed to evaluate_guardfile!') unless options.is_a?(Hash)
|
91
|
-
|
92
|
-
@@options = options.dup
|
93
|
-
|
94
|
-
fetch_guardfile_contents
|
95
|
-
instance_eval_guardfile(guardfile_contents_with_user_config)
|
96
|
-
|
97
|
-
UI.error 'No guards found in Guardfile, please add at least one.' if !::Guard.guards.nil? && ::Guard.guards.empty?
|
98
|
-
end
|
99
|
-
|
100
|
-
# Re-evaluate the `Guardfile` to update the current Guard configuration.
|
101
|
-
#
|
102
|
-
def reevaluate_guardfile
|
103
|
-
::Guard.guards.clear
|
104
|
-
::Guard.groups.clear
|
105
|
-
@@options.delete(:guardfile_contents)
|
106
|
-
Dsl.evaluate_guardfile(@@options)
|
107
|
-
msg = 'Guardfile has been re-evaluated.'
|
108
|
-
UI.info(msg)
|
109
|
-
Notifier.notify(msg)
|
110
|
-
end
|
111
|
-
|
112
|
-
# Evaluate the content of the `Guardfile`.
|
113
|
-
#
|
114
|
-
# @param [String] contents the content to evaluate.
|
115
|
-
#
|
116
|
-
def instance_eval_guardfile(contents)
|
117
|
-
new.instance_eval(contents, @@options[:guardfile_path], 1)
|
118
|
-
rescue
|
119
|
-
UI.error "Invalid Guardfile, original error is:\n#{ $! }"
|
120
|
-
exit 1
|
121
|
-
end
|
122
|
-
|
123
|
-
# Test if the current `Guardfile` contains a specific Guard.
|
124
|
-
#
|
125
|
-
# @param [String] guard_name the name of the Guard
|
126
|
-
# @return [Boolean] whether the Guard has been declared
|
127
|
-
#
|
128
|
-
def guardfile_include?(guard_name)
|
129
|
-
guardfile_contents.match(/^guard\s*\(?\s*['":]#{ guard_name }['"]?/)
|
130
|
-
end
|
131
|
-
|
132
|
-
# Read the current `Guardfile` content.
|
133
|
-
#
|
134
|
-
# @param [String] the path to the Guardfile
|
135
|
-
#
|
136
|
-
def read_guardfile(guardfile_path)
|
137
|
-
@@options[:guardfile_path] = guardfile_path
|
138
|
-
@@options[:guardfile_contents] = File.read(guardfile_path)
|
139
|
-
rescue
|
140
|
-
UI.error("Error reading file #{ guardfile_path }")
|
141
|
-
exit 1
|
142
|
-
end
|
143
|
-
|
144
|
-
# Get the content to evaluate and stores it into
|
145
|
-
# the options as `:guardfile_contents`.
|
146
|
-
#
|
147
|
-
def fetch_guardfile_contents
|
148
|
-
if @@options[:guardfile_contents]
|
149
|
-
UI.info 'Using inline Guardfile.'
|
150
|
-
@@options[:guardfile_path] = 'Inline Guardfile'
|
151
|
-
|
152
|
-
elsif @@options[:guardfile]
|
153
|
-
if File.exist?(@@options[:guardfile])
|
154
|
-
read_guardfile(@@options[:guardfile])
|
155
|
-
UI.info "Using Guardfile at #{ @@options[:guardfile] }."
|
156
|
-
else
|
157
|
-
UI.error "No Guardfile exists at #{ @@options[:guardfile] }."
|
158
|
-
exit 1
|
159
|
-
end
|
160
|
-
|
161
|
-
else
|
162
|
-
if File.exist?(guardfile_default_path)
|
163
|
-
read_guardfile(guardfile_default_path)
|
164
|
-
else
|
165
|
-
UI.error 'No Guardfile found, please create one with `guard init`.'
|
166
|
-
exit 1
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
unless guardfile_contents_usable?
|
171
|
-
UI.error "The command file(#{ @@options[:guardfile] }) seems to be empty."
|
172
|
-
exit 1
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
# Get the content of the `Guardfile`.
|
177
|
-
#
|
178
|
-
# @return [String] the Guardfile content
|
179
|
-
#
|
180
|
-
def guardfile_contents
|
181
|
-
@@options ? @@options[:guardfile_contents] : ''
|
182
|
-
end
|
183
|
-
|
184
|
-
# Get the content of the `Guardfile` and the global
|
185
|
-
# user configuration file.
|
186
|
-
#
|
187
|
-
# @see #user_config_path
|
188
|
-
#
|
189
|
-
# @return [String] the Guardfile content
|
190
|
-
#
|
191
|
-
def guardfile_contents_with_user_config
|
192
|
-
config = File.read(user_config_path) if File.exist?(user_config_path)
|
193
|
-
[guardfile_contents, config].join("\n")
|
194
|
-
end
|
195
|
-
|
196
|
-
# Get the file path to the project `Guardfile`.
|
197
|
-
#
|
198
|
-
# @return [String] the path to the Guardfile
|
199
|
-
#
|
200
|
-
def guardfile_path
|
201
|
-
@@options ? @@options[:guardfile_path] : ''
|
202
|
-
end
|
203
|
-
|
204
|
-
# Tests if the current `Guardfile` content is usable.
|
205
|
-
#
|
206
|
-
# @return [Boolean] if the Guardfile is usable
|
207
|
-
#
|
208
|
-
def guardfile_contents_usable?
|
209
|
-
guardfile_contents && guardfile_contents.size >= 'guard :a'.size # Smallest Guard definition
|
210
|
-
end
|
211
|
-
|
212
|
-
# Gets the default path of the `Guardfile`. This returns the `Guardfile`
|
213
|
-
# from the current directory when existing, or the global `.Guardfile`
|
214
|
-
# at the home directory.
|
215
|
-
#
|
216
|
-
# @return [String] the path to the Guardfile
|
217
|
-
#
|
218
|
-
def guardfile_default_path
|
219
|
-
File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
|
220
|
-
end
|
221
|
-
|
222
|
-
private
|
223
|
-
|
224
|
-
# The path to the `Guardfile` that is located at
|
225
|
-
# the directory, where Guard has been started from.
|
226
|
-
#
|
227
|
-
# @param [String] the path to the local Guardfile
|
228
|
-
#
|
229
|
-
def local_guardfile_path
|
230
|
-
File.join(Dir.pwd, 'Guardfile')
|
231
|
-
end
|
232
|
-
|
233
|
-
# The path to the `.Guardfile` that is located at
|
234
|
-
# the users home directory.
|
235
|
-
#
|
236
|
-
# @param [String] the path to ~/.Guardfile
|
237
|
-
#
|
238
|
-
def home_guardfile_path
|
239
|
-
File.expand_path(File.join('~', '.Guardfile'))
|
240
|
-
end
|
241
|
-
|
242
|
-
# The path to the user configuration `.guard.rb`
|
243
|
-
# that is located at the users home directory.
|
244
|
-
#
|
245
|
-
# @param [String] the path to ~/.guard.rb
|
246
|
-
#
|
247
|
-
def user_config_path
|
248
|
-
File.expand_path(File.join('~', '.guard.rb'))
|
249
|
-
end
|
250
|
-
|
251
|
-
end
|
252
|
-
|
253
|
-
# Declares a group of guards to be run with `guard start --group group_name`.
|
254
|
-
#
|
255
|
-
# @example Declare two groups of Guards
|
256
|
-
#
|
257
|
-
# group 'backend' do
|
258
|
-
# guard 'spork'
|
259
|
-
# guard 'rspec'
|
260
|
-
# end
|
261
|
-
#
|
262
|
-
# group 'frontend' do
|
263
|
-
# guard 'passenger'
|
264
|
-
# guard 'livereload'
|
265
|
-
# end
|
266
|
-
#
|
267
|
-
# @param [Symbol, String] name the group's name called from the CLI
|
268
|
-
# @param [Hash] options the options accepted by the group
|
269
|
-
# @yield a block where you can declare several guards
|
270
|
-
#
|
271
|
-
# @see Guard.add_group
|
272
|
-
# @see Dsl#guard
|
273
|
-
# @see Guard::DslDescriber
|
274
|
-
#
|
275
|
-
def group(name, options = {})
|
276
|
-
@groups = @@options[:group] || []
|
277
|
-
name = name.to_sym
|
278
|
-
|
279
|
-
if block_given? && (@groups.empty? || @groups.map(&:to_sym).include?(name))
|
280
|
-
::Guard.add_group(name.to_s.downcase, options)
|
281
|
-
@current_group = name
|
282
|
-
|
283
|
-
yield if block_given?
|
284
|
-
|
285
|
-
@current_group = nil
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
# Declare a guard to be used when running `guard start`.
|
290
|
-
#
|
291
|
-
# The name parameter is usually the name of the gem without
|
292
|
-
# the 'guard-' prefix.
|
293
|
-
#
|
294
|
-
# The available options are different for each Guard implementation.
|
295
|
-
#
|
296
|
-
# @example Declare a Guard
|
297
|
-
#
|
298
|
-
# guard 'rspec' do
|
299
|
-
# end
|
300
|
-
#
|
301
|
-
# @param [String] name the Guard name
|
302
|
-
# @param [Hash] options the options accepted by the Guard
|
303
|
-
# @yield a block where you can declare several watch patterns and actions
|
304
|
-
#
|
305
|
-
# @see Guard.add_guard
|
306
|
-
# @see Dsl#group
|
307
|
-
# @see Dsl#watch
|
308
|
-
# @see Guard::DslDescriber
|
309
|
-
#
|
310
|
-
def guard(name, options = {})
|
311
|
-
@watchers = []
|
312
|
-
@callbacks = []
|
313
|
-
|
314
|
-
yield if block_given?
|
315
|
-
|
316
|
-
options.update(:group => (@current_group || :default))
|
317
|
-
::Guard.add_guard(name.to_s.downcase, @watchers, @callbacks, options)
|
318
|
-
end
|
319
|
-
|
320
|
-
# Define a pattern to be watched in order to run actions on file modification.
|
321
|
-
#
|
322
|
-
# @example Declare watchers for a Guard
|
323
|
-
#
|
324
|
-
# guard 'rspec' do
|
325
|
-
# watch('spec/spec_helper.rb')
|
326
|
-
# watch(%r{^.+_spec.rb})
|
327
|
-
# watch(%r{^app/controllers/(.+).rb}) { |m| 'spec/acceptance/#{m[1]}s_spec.rb' }
|
328
|
-
# end
|
329
|
-
#
|
330
|
-
# @param [String, Regexp] pattern the pattern to be watched by the guard
|
331
|
-
# @yield a block to be run when the pattern is matched
|
332
|
-
# @yieldparam [MatchData] m matches of the pattern
|
333
|
-
# @yieldreturn a directory, a filename, an array of directories / filenames, or nothing (can be an arbitrary command)
|
334
|
-
#
|
335
|
-
# @see Guard::Watcher
|
336
|
-
# @see Dsl#guard
|
337
|
-
#
|
338
|
-
def watch(pattern, &action)
|
339
|
-
@watchers << ::Guard::Watcher.new(pattern, action)
|
340
|
-
end
|
341
|
-
|
342
|
-
# Define a callback to execute arbitrary code before or after any of
|
343
|
-
# the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method.
|
344
|
-
#
|
345
|
-
# @param [Array] args the callback arguments
|
346
|
-
# @yield a block with listeners
|
347
|
-
#
|
348
|
-
# @see Guard::Hook
|
349
|
-
#
|
350
|
-
def callback(*args, &listener)
|
351
|
-
listener, events = args.size > 1 ? args : [listener, args[0]]
|
352
|
-
@callbacks << { :events => events, :listener => listener }
|
353
|
-
end
|
354
|
-
|
355
|
-
# Ignore certain paths globally.
|
356
|
-
#
|
357
|
-
# @example Ignore some paths
|
358
|
-
# ignore_paths ".git", ".svn"
|
359
|
-
#
|
360
|
-
# @param [Array] paths the list of paths to ignore
|
361
|
-
#
|
362
|
-
# @see Guard::Listener
|
363
|
-
#
|
364
|
-
def ignore_paths(*paths)
|
365
|
-
UI.info "Ignoring paths: #{ paths.join(', ') }"
|
366
|
-
::Guard.listener.ignore_paths.push(*paths)
|
367
|
-
end
|
368
|
-
|
369
|
-
end
|
370
|
-
end
|
1
|
+
module Guard
|
2
|
+
|
3
|
+
# The DSL class provides the methods that are used in each `Guardfile` to describe
|
4
|
+
# the behaviour of Guard.
|
5
|
+
#
|
6
|
+
# The main keywords of the DSL are `guard` and `watch`. These are necessary to define
|
7
|
+
# the used Guards and the file changes they are watching.
|
8
|
+
#
|
9
|
+
# You can optionally group the Guards with the `group` keyword and ignore certain paths
|
10
|
+
# with the `ignore_paths` keyword.
|
11
|
+
#
|
12
|
+
# A more advanced DSL use is the `callback` keyword that allows you to execute arbitrary
|
13
|
+
# code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change`
|
14
|
+
# Guards' method. You can even insert more hooks inside these methods.
|
15
|
+
# Please [checkout the Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for more details.
|
16
|
+
#
|
17
|
+
# The DSL will also evaluate normal Ruby code.
|
18
|
+
#
|
19
|
+
# There are two possible locations for the `Guardfile`:
|
20
|
+
# - The `Guardfile` in the current directory where Guard has been started
|
21
|
+
# - The `.Guardfile` in your home directory.
|
22
|
+
#
|
23
|
+
# In addition, if a user configuration `.guard.rb` in your home directory is found, it will
|
24
|
+
# be appended to the current project `Guardfile`.
|
25
|
+
#
|
26
|
+
# @example A sample of a complex Guardfile
|
27
|
+
#
|
28
|
+
# group 'frontend' do
|
29
|
+
# guard 'passenger', :ping => true do
|
30
|
+
# watch('config/application.rb')
|
31
|
+
# watch('config/environment.rb')
|
32
|
+
# watch(%r{^config/environments/.+\.rb})
|
33
|
+
# watch(%r{^config/initializers/.+\.rb})
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# guard 'livereload', :apply_js_live => false do
|
37
|
+
# watch(%r{^app/.+\.(erb|haml)})
|
38
|
+
# watch(%r{^app/helpers/.+\.rb})
|
39
|
+
# watch(%r{^public/javascripts/.+\.js})
|
40
|
+
# watch(%r{^public/stylesheets/.+\.css})
|
41
|
+
# watch(%r{^public/.+\.html})
|
42
|
+
# watch(%r{^config/locales/.+\.yml})
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# group 'backend' do
|
47
|
+
# # Reload the bundle when the Gemfile is modified
|
48
|
+
# guard 'bundler' do
|
49
|
+
# watch('Gemfile')
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# # for big project you can fine tune the "timeout" before Spork's launch is considered failed
|
53
|
+
# guard 'spork', :wait => 40 do
|
54
|
+
# watch('Gemfile')
|
55
|
+
# watch('config/application.rb')
|
56
|
+
# watch('config/environment.rb')
|
57
|
+
# watch(%r{^config/environments/.+\.rb})
|
58
|
+
# watch(%r{^config/initializers/.+\.rb})
|
59
|
+
# watch('spec/spec_helper.rb')
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# # use RSpec 2, from the system's gem and with some direct RSpec CLI options
|
63
|
+
# guard 'rspec', :version => 2, :cli => "--color --drb -f doc", :bundler => false do
|
64
|
+
# watch('spec/spec_helper.rb') { "spec" }
|
65
|
+
# watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
66
|
+
# watch('config/routes.rb') { "spec/routing" }
|
67
|
+
# watch(%r{^spec/support/(controllers|acceptance)_helpers\.rb}) { |m| "spec/#{m[1]}" }
|
68
|
+
# watch(%r{^spec/.+_spec\.rb})
|
69
|
+
#
|
70
|
+
# watch(%r{^app/controllers/(.+)_(controller)\.rb}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
71
|
+
#
|
72
|
+
# watch(%r{^app/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
|
73
|
+
# watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
class Dsl
|
78
|
+
class << self
|
79
|
+
|
80
|
+
@@options = nil
|
81
|
+
|
82
|
+
# Evaluate the DSL methods in the `Guardfile`.
|
83
|
+
#
|
84
|
+
# @option options [Array<Symbol,String>] groups the groups to evaluate
|
85
|
+
# @option options [String] guardfile the path to a valid Guardfile
|
86
|
+
# @option options [String] guardfile_contents a string representing the content of a valid Guardfile
|
87
|
+
# @raise [ArgumentError] when options are not a Hash
|
88
|
+
#
|
89
|
+
def evaluate_guardfile(options = {})
|
90
|
+
raise ArgumentError.new('No option hash passed to evaluate_guardfile!') unless options.is_a?(Hash)
|
91
|
+
|
92
|
+
@@options = options.dup
|
93
|
+
|
94
|
+
fetch_guardfile_contents
|
95
|
+
instance_eval_guardfile(guardfile_contents_with_user_config)
|
96
|
+
|
97
|
+
UI.error 'No guards found in Guardfile, please add at least one.' if !::Guard.guards.nil? && ::Guard.guards.empty?
|
98
|
+
end
|
99
|
+
|
100
|
+
# Re-evaluate the `Guardfile` to update the current Guard configuration.
|
101
|
+
#
|
102
|
+
def reevaluate_guardfile
|
103
|
+
::Guard.guards.clear
|
104
|
+
::Guard.groups.clear
|
105
|
+
@@options.delete(:guardfile_contents)
|
106
|
+
Dsl.evaluate_guardfile(@@options)
|
107
|
+
msg = 'Guardfile has been re-evaluated.'
|
108
|
+
UI.info(msg)
|
109
|
+
Notifier.notify(msg)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Evaluate the content of the `Guardfile`.
|
113
|
+
#
|
114
|
+
# @param [String] contents the content to evaluate.
|
115
|
+
#
|
116
|
+
def instance_eval_guardfile(contents)
|
117
|
+
new.instance_eval(contents, @@options[:guardfile_path], 1)
|
118
|
+
rescue
|
119
|
+
UI.error "Invalid Guardfile, original error is:\n#{ $! }"
|
120
|
+
exit 1
|
121
|
+
end
|
122
|
+
|
123
|
+
# Test if the current `Guardfile` contains a specific Guard.
|
124
|
+
#
|
125
|
+
# @param [String] guard_name the name of the Guard
|
126
|
+
# @return [Boolean] whether the Guard has been declared
|
127
|
+
#
|
128
|
+
def guardfile_include?(guard_name)
|
129
|
+
guardfile_contents.match(/^guard\s*\(?\s*['":]#{ guard_name }['"]?/)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Read the current `Guardfile` content.
|
133
|
+
#
|
134
|
+
# @param [String] the path to the Guardfile
|
135
|
+
#
|
136
|
+
def read_guardfile(guardfile_path)
|
137
|
+
@@options[:guardfile_path] = guardfile_path
|
138
|
+
@@options[:guardfile_contents] = File.read(guardfile_path)
|
139
|
+
rescue
|
140
|
+
UI.error("Error reading file #{ guardfile_path }")
|
141
|
+
exit 1
|
142
|
+
end
|
143
|
+
|
144
|
+
# Get the content to evaluate and stores it into
|
145
|
+
# the options as `:guardfile_contents`.
|
146
|
+
#
|
147
|
+
def fetch_guardfile_contents
|
148
|
+
if @@options[:guardfile_contents]
|
149
|
+
UI.info 'Using inline Guardfile.'
|
150
|
+
@@options[:guardfile_path] = 'Inline Guardfile'
|
151
|
+
|
152
|
+
elsif @@options[:guardfile]
|
153
|
+
if File.exist?(@@options[:guardfile])
|
154
|
+
read_guardfile(@@options[:guardfile])
|
155
|
+
UI.info "Using Guardfile at #{ @@options[:guardfile] }."
|
156
|
+
else
|
157
|
+
UI.error "No Guardfile exists at #{ @@options[:guardfile] }."
|
158
|
+
exit 1
|
159
|
+
end
|
160
|
+
|
161
|
+
else
|
162
|
+
if File.exist?(guardfile_default_path)
|
163
|
+
read_guardfile(guardfile_default_path)
|
164
|
+
else
|
165
|
+
UI.error 'No Guardfile found, please create one with `guard init`.'
|
166
|
+
exit 1
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
unless guardfile_contents_usable?
|
171
|
+
UI.error "The command file(#{ @@options[:guardfile] }) seems to be empty."
|
172
|
+
exit 1
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Get the content of the `Guardfile`.
|
177
|
+
#
|
178
|
+
# @return [String] the Guardfile content
|
179
|
+
#
|
180
|
+
def guardfile_contents
|
181
|
+
@@options ? @@options[:guardfile_contents] : ''
|
182
|
+
end
|
183
|
+
|
184
|
+
# Get the content of the `Guardfile` and the global
|
185
|
+
# user configuration file.
|
186
|
+
#
|
187
|
+
# @see #user_config_path
|
188
|
+
#
|
189
|
+
# @return [String] the Guardfile content
|
190
|
+
#
|
191
|
+
def guardfile_contents_with_user_config
|
192
|
+
config = File.read(user_config_path) if File.exist?(user_config_path)
|
193
|
+
[guardfile_contents, config].join("\n")
|
194
|
+
end
|
195
|
+
|
196
|
+
# Get the file path to the project `Guardfile`.
|
197
|
+
#
|
198
|
+
# @return [String] the path to the Guardfile
|
199
|
+
#
|
200
|
+
def guardfile_path
|
201
|
+
@@options ? @@options[:guardfile_path] : ''
|
202
|
+
end
|
203
|
+
|
204
|
+
# Tests if the current `Guardfile` content is usable.
|
205
|
+
#
|
206
|
+
# @return [Boolean] if the Guardfile is usable
|
207
|
+
#
|
208
|
+
def guardfile_contents_usable?
|
209
|
+
guardfile_contents && guardfile_contents.size >= 'guard :a'.size # Smallest Guard definition
|
210
|
+
end
|
211
|
+
|
212
|
+
# Gets the default path of the `Guardfile`. This returns the `Guardfile`
|
213
|
+
# from the current directory when existing, or the global `.Guardfile`
|
214
|
+
# at the home directory.
|
215
|
+
#
|
216
|
+
# @return [String] the path to the Guardfile
|
217
|
+
#
|
218
|
+
def guardfile_default_path
|
219
|
+
File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
|
220
|
+
end
|
221
|
+
|
222
|
+
private
|
223
|
+
|
224
|
+
# The path to the `Guardfile` that is located at
|
225
|
+
# the directory, where Guard has been started from.
|
226
|
+
#
|
227
|
+
# @param [String] the path to the local Guardfile
|
228
|
+
#
|
229
|
+
def local_guardfile_path
|
230
|
+
File.join(Dir.pwd, 'Guardfile')
|
231
|
+
end
|
232
|
+
|
233
|
+
# The path to the `.Guardfile` that is located at
|
234
|
+
# the users home directory.
|
235
|
+
#
|
236
|
+
# @param [String] the path to ~/.Guardfile
|
237
|
+
#
|
238
|
+
def home_guardfile_path
|
239
|
+
File.expand_path(File.join('~', '.Guardfile'))
|
240
|
+
end
|
241
|
+
|
242
|
+
# The path to the user configuration `.guard.rb`
|
243
|
+
# that is located at the users home directory.
|
244
|
+
#
|
245
|
+
# @param [String] the path to ~/.guard.rb
|
246
|
+
#
|
247
|
+
def user_config_path
|
248
|
+
File.expand_path(File.join('~', '.guard.rb'))
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
# Declares a group of guards to be run with `guard start --group group_name`.
|
254
|
+
#
|
255
|
+
# @example Declare two groups of Guards
|
256
|
+
#
|
257
|
+
# group 'backend' do
|
258
|
+
# guard 'spork'
|
259
|
+
# guard 'rspec'
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
# group 'frontend' do
|
263
|
+
# guard 'passenger'
|
264
|
+
# guard 'livereload'
|
265
|
+
# end
|
266
|
+
#
|
267
|
+
# @param [Symbol, String] name the group's name called from the CLI
|
268
|
+
# @param [Hash] options the options accepted by the group
|
269
|
+
# @yield a block where you can declare several guards
|
270
|
+
#
|
271
|
+
# @see Guard.add_group
|
272
|
+
# @see Dsl#guard
|
273
|
+
# @see Guard::DslDescriber
|
274
|
+
#
|
275
|
+
def group(name, options = {})
|
276
|
+
@groups = @@options[:group] || []
|
277
|
+
name = name.to_sym
|
278
|
+
|
279
|
+
if block_given? && (@groups.empty? || @groups.map(&:to_sym).include?(name))
|
280
|
+
::Guard.add_group(name.to_s.downcase, options)
|
281
|
+
@current_group = name
|
282
|
+
|
283
|
+
yield if block_given?
|
284
|
+
|
285
|
+
@current_group = nil
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Declare a guard to be used when running `guard start`.
|
290
|
+
#
|
291
|
+
# The name parameter is usually the name of the gem without
|
292
|
+
# the 'guard-' prefix.
|
293
|
+
#
|
294
|
+
# The available options are different for each Guard implementation.
|
295
|
+
#
|
296
|
+
# @example Declare a Guard
|
297
|
+
#
|
298
|
+
# guard 'rspec' do
|
299
|
+
# end
|
300
|
+
#
|
301
|
+
# @param [String] name the Guard name
|
302
|
+
# @param [Hash] options the options accepted by the Guard
|
303
|
+
# @yield a block where you can declare several watch patterns and actions
|
304
|
+
#
|
305
|
+
# @see Guard.add_guard
|
306
|
+
# @see Dsl#group
|
307
|
+
# @see Dsl#watch
|
308
|
+
# @see Guard::DslDescriber
|
309
|
+
#
|
310
|
+
def guard(name, options = {})
|
311
|
+
@watchers = []
|
312
|
+
@callbacks = []
|
313
|
+
|
314
|
+
yield if block_given?
|
315
|
+
|
316
|
+
options.update(:group => (@current_group || :default))
|
317
|
+
::Guard.add_guard(name.to_s.downcase, @watchers, @callbacks, options)
|
318
|
+
end
|
319
|
+
|
320
|
+
# Define a pattern to be watched in order to run actions on file modification.
|
321
|
+
#
|
322
|
+
# @example Declare watchers for a Guard
|
323
|
+
#
|
324
|
+
# guard 'rspec' do
|
325
|
+
# watch('spec/spec_helper.rb')
|
326
|
+
# watch(%r{^.+_spec.rb})
|
327
|
+
# watch(%r{^app/controllers/(.+).rb}) { |m| 'spec/acceptance/#{m[1]}s_spec.rb' }
|
328
|
+
# end
|
329
|
+
#
|
330
|
+
# @param [String, Regexp] pattern the pattern to be watched by the guard
|
331
|
+
# @yield a block to be run when the pattern is matched
|
332
|
+
# @yieldparam [MatchData] m matches of the pattern
|
333
|
+
# @yieldreturn a directory, a filename, an array of directories / filenames, or nothing (can be an arbitrary command)
|
334
|
+
#
|
335
|
+
# @see Guard::Watcher
|
336
|
+
# @see Dsl#guard
|
337
|
+
#
|
338
|
+
def watch(pattern, &action)
|
339
|
+
@watchers << ::Guard::Watcher.new(pattern, action)
|
340
|
+
end
|
341
|
+
|
342
|
+
# Define a callback to execute arbitrary code before or after any of
|
343
|
+
# the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method.
|
344
|
+
#
|
345
|
+
# @param [Array] args the callback arguments
|
346
|
+
# @yield a block with listeners
|
347
|
+
#
|
348
|
+
# @see Guard::Hook
|
349
|
+
#
|
350
|
+
def callback(*args, &listener)
|
351
|
+
listener, events = args.size > 1 ? args : [listener, args[0]]
|
352
|
+
@callbacks << { :events => events, :listener => listener }
|
353
|
+
end
|
354
|
+
|
355
|
+
# Ignore certain paths globally.
|
356
|
+
#
|
357
|
+
# @example Ignore some paths
|
358
|
+
# ignore_paths ".git", ".svn"
|
359
|
+
#
|
360
|
+
# @param [Array] paths the list of paths to ignore
|
361
|
+
#
|
362
|
+
# @see Guard::Listener
|
363
|
+
#
|
364
|
+
def ignore_paths(*paths)
|
365
|
+
UI.info "Ignoring paths: #{ paths.join(', ') }"
|
366
|
+
::Guard.listener.ignore_paths.push(*paths)
|
367
|
+
end
|
368
|
+
|
369
|
+
end
|
370
|
+
end
|