guard 2.13.0 → 2.14.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.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +10 -20
- data/lib/guard.rb +6 -9
- data/lib/guard/cli/environments/valid.rb +11 -5
- data/lib/guard/commander.rb +1 -1
- data/lib/guard/deprecated/evaluator.rb +1 -1
- data/lib/guard/deprecated/guard.rb +5 -11
- data/lib/guard/deprecated/watcher.rb +2 -2
- data/lib/guard/dsl.rb +1 -1
- data/lib/guard/guardfile/evaluator.rb +4 -6
- data/lib/guard/guardfile/generator.rb +22 -9
- data/lib/guard/interactor.rb +4 -13
- data/lib/guard/internals/debugging.rb +1 -0
- data/lib/guard/internals/groups.rb +2 -2
- data/lib/guard/internals/plugins.rb +4 -4
- data/lib/guard/internals/queue.rb +3 -2
- data/lib/guard/internals/session.rb +1 -3
- data/lib/guard/plugin.rb +1 -1
- data/lib/guard/plugin_util.rb +16 -13
- data/lib/guard/ui.rb +20 -25
- data/lib/guard/version.rb +1 -1
- data/lib/guard/watcher.rb +8 -40
- data/lib/guard/watcher/pattern.rb +24 -0
- data/lib/guard/watcher/pattern/deprecated_regexp.rb +45 -0
- data/lib/guard/watcher/pattern/match_result.rb +18 -0
- data/lib/guard/watcher/pattern/matcher.rb +23 -0
- data/lib/guard/watcher/pattern/pathname_path.rb +15 -0
- data/lib/guard/watcher/pattern/simple_path.rb +23 -0
- data/lib/tasks/releaser.rb +116 -0
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee12e6183bd7f80c82c6c173e73da077423efd8b
|
4
|
+
data.tar.gz: 658e7a0eaf51b6d4ba16cf8126f1cebe9ef75e10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d40ddd8ef223df2e0bbf59791583eeaf4b460eac7a32a674e3b2d16c51ca9f8158b71b1458c605c0a3e9b078d724362443ea1f7dd73a3a85e419ff6fbc28fa8
|
7
|
+
data.tar.gz: 5fb257cf77358225a124a6b659812e78f9aea514c66578993f1b8d2dd874b7f0a174e68d3d8fda209dc515674897b72e32775f6c0d8186db36dfdfedd29e7f17
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
-
:
|
1
|
+
## IMPORTANT: [Ruby 2.1 is officially outdated and unsupported!](https://www.ruby-lang.org/en/news/2016/03/30/ruby-2-1-9-released/) Please upgrade to Ruby 2.2 before installing Guard!
|
2
2
|
|
3
|
+
### (To install for older versions, update Bundler to a prerelease version: `gem install bundler --pre` and Bundler should correctly resolve to an earlier gems for your given Ruby version).
|
3
4
|
|
4
|
-
:exclamation
|
5
|
+
:exclamation: Guard is currently accepting more maintainers. Please [read this](https://github.com/guard/guard/wiki/Maintainers) if you're interested in joining the team.
|
5
6
|
|
6
7
|
Guard
|
7
8
|
=====
|
8
9
|
|
9
10
|
[](https://rubygems.org/gems/guard) [](https://travis-ci.org/guard/guard) [](https://gemnasium.com/guard/guard) [](https://codeclimate.com/github/guard/guard) [](https://codeclimate.com/github/guard/guard) [](http://inch-ci.org/github/guard/guard)
|
10
11
|
|
11
|
-
<img src="http://cl.ly/
|
12
|
+
<img src="http://f.cl.ly/items/0A0M3W2x3I1P450z341U/guard-Icon.png" alt="Guard Icon" align="right" />
|
12
13
|
Guard automates various tasks by running custom rules whenever file or directories are modified.
|
13
14
|
|
14
15
|
It's frequently used by software developers, web designers, writers and other specialists to avoid mundane, repetitive actions and commands such as "relaunching" tools after changing source files or configurations.
|
@@ -22,7 +23,7 @@ any questions about Guard or want to share some information with the Guard commu
|
|
22
23
|
the following places:
|
23
24
|
|
24
25
|
* [Guard Wiki](https://github.com/guard/guard/wiki)
|
25
|
-
* [Google+ community](https://plus.google.com/
|
26
|
+
* [Google+ community](https://plus.google.com/communities/110022199336250745477).
|
26
27
|
* [Google group](http://groups.google.com/group/guard-dev).
|
27
28
|
* [StackOverflow](http://stackoverflow.com/questions/tagged/guard).
|
28
29
|
* IRC channel `#guard` (irc.freenode.net) for chatting.
|
@@ -46,7 +47,7 @@ Two nice screencasts are available to help you get started:
|
|
46
47
|
Installation
|
47
48
|
------------
|
48
49
|
|
49
|
-
The simplest way to install Guard is to use [Bundler](http://
|
50
|
+
The simplest way to install Guard is to use [Bundler](http://bundler.io).
|
50
51
|
|
51
52
|
Add Guard (and any other dependencies) to a `Gemfile` in your project’s root:
|
52
53
|
|
@@ -98,7 +99,7 @@ If you're getting sick of typing `bundle exec` all the time, try one of the foll
|
|
98
99
|
|
99
100
|
*NOTE: this Rubygems feature is still under development still lacks many features of bundler*
|
100
101
|
|
101
|
-
* Or, for RubyGems < 2.2.0 check out the [Rubygems Bundler](https://github.com/
|
102
|
+
* Or, for RubyGems < 2.2.0 check out the [Rubygems Bundler](https://github.com/rvm/rubygems-bundler).
|
102
103
|
|
103
104
|
#### Add Guard plugins
|
104
105
|
|
@@ -156,26 +157,15 @@ Development / Contributing
|
|
156
157
|
|
157
158
|
See the [Contributing Guide](https://github.com/guard/guard/blob/master/CONTRIBUTING.md#development).
|
158
159
|
|
159
|
-
|
160
|
-
#### Open Commit Bit
|
161
|
-
|
162
|
-
Guard has an open commit bit policy: Anyone with an accepted pull request gets added as a repository collaborator.
|
163
|
-
Please try to follow these simple rules:
|
164
|
-
|
165
|
-
* Commit directly onto the master branch only for typos, improvements to the readme and documentation (please add
|
166
|
-
`[ci skip]` to the commit message).
|
167
|
-
* Create a feature branch and open a pull-request early for any new features to get feedback.
|
168
|
-
* Make sure you adhere to the general pull request rules above.
|
169
|
-
|
170
160
|
#### Author
|
171
161
|
|
172
|
-
[Thibaud Guillaume-Gentil](https://github.com/thibaudgg) ([@thibaudgg](
|
162
|
+
[Thibaud Guillaume-Gentil](https://github.com/thibaudgg) ([@thibaudgg](https://twitter.com/thibaudgg))
|
173
163
|
|
174
164
|
#### Core Team
|
175
165
|
|
176
|
-
* R.I.P. :broken_heart: [Michael Kessler](https://github.com/netzpirat) ([
|
166
|
+
* R.I.P. :broken_heart: [Michael Kessler](https://github.com/netzpirat) ([flinkfinger.com](http://www.flinkfinger.com)).
|
177
167
|
* [Rémy Coutable](https://github.com/rymai).
|
178
|
-
* [Thibaud Guillaume-Gentil](https://github.com/thibaudgg) ([@thibaudgg](
|
168
|
+
* [Thibaud Guillaume-Gentil](https://github.com/thibaudgg) ([@thibaudgg](https://twitter.com/thibaudgg), [thibaud.gg](http://thibaud.gg/)).
|
179
169
|
|
180
170
|
#### Contributors
|
181
171
|
|
data/lib/guard.rb
CHANGED
@@ -151,7 +151,8 @@ module Guard
|
|
151
151
|
# TODO: not tested because collides with ongoing refactoring
|
152
152
|
def _guardfile_deprecated_check(modified)
|
153
153
|
modified.map!(&:to_s)
|
154
|
-
|
154
|
+
regexp = %r{^(?:.+/)?Guardfile$}
|
155
|
+
guardfiles = modified.select { |path| regexp.match(path) }
|
155
156
|
return if guardfiles.empty?
|
156
157
|
|
157
158
|
guardfile = Pathname("Guardfile").realpath
|
@@ -159,14 +160,10 @@ module Guard
|
|
159
160
|
/^Guardfile$/.match(path) || Pathname(path).expand_path == guardfile
|
160
161
|
end
|
161
162
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
msg = "Config changed: %s - Guard will exit so it can be restarted."
|
167
|
-
UI.info format(msg, guardfiles.inspect)
|
168
|
-
exit 0 # 0 so any shell while loop can continue
|
169
|
-
end
|
163
|
+
return unless real_guardfiles
|
164
|
+
|
165
|
+
UI.warning "Guardfile changed -- _guard-core will exit.\n"
|
166
|
+
exit 2 # nonzero to break any while loop
|
170
167
|
end
|
171
168
|
end
|
172
169
|
end
|
@@ -46,13 +46,19 @@ module Guard
|
|
46
46
|
rescue Guard::Guardfile::Evaluator::NoPluginsError
|
47
47
|
end
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
begin
|
50
|
+
if plugin_names.empty?
|
51
|
+
generator.initialize_all_templates
|
52
|
+
else
|
53
|
+
plugin_names.each do |plugin_name|
|
54
|
+
generator.initialize_template(plugin_name)
|
55
|
+
end
|
54
56
|
end
|
57
|
+
rescue Guardfile::Generator::Error => e
|
58
|
+
UI.error(e.message)
|
59
|
+
return 1
|
55
60
|
end
|
61
|
+
|
56
62
|
# TODO: capture exceptions to show msg and return exit code on
|
57
63
|
# failures
|
58
64
|
0 # exit code
|
data/lib/guard/commander.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require "guard/config"
|
2
2
|
fail "Deprecations disabled (strict mode)" if Guard::Config.new.strict?
|
3
3
|
|
4
|
+
require "forwardable"
|
5
|
+
|
4
6
|
require "guard/ui"
|
5
7
|
require "guard/internals/session"
|
6
8
|
require "guard/internals/state"
|
@@ -209,9 +211,9 @@ module Guard
|
|
209
211
|
}
|
210
212
|
end
|
211
213
|
|
212
|
-
|
213
|
-
|
214
|
-
|
214
|
+
extend Forwardable
|
215
|
+
delegate [:to_a, :keys] => :to_hash
|
216
|
+
delegate [:include?] => :keys
|
215
217
|
|
216
218
|
def fetch(key, *args)
|
217
219
|
hash = to_hash
|
@@ -230,14 +232,6 @@ module Guard
|
|
230
232
|
end
|
231
233
|
end
|
232
234
|
|
233
|
-
def keys
|
234
|
-
to_hash.keys
|
235
|
-
end
|
236
|
-
|
237
|
-
def include?(value)
|
238
|
-
keys.include? value
|
239
|
-
end
|
240
|
-
|
241
235
|
private
|
242
236
|
|
243
237
|
def verify_key!(hash, key)
|
@@ -4,8 +4,8 @@ fail "Deprecations disabled (strict mode)" if Guard::Config.new.strict?
|
|
4
4
|
module Guard
|
5
5
|
module Deprecated
|
6
6
|
module Watcher
|
7
|
-
def self.add_deprecated(
|
8
|
-
|
7
|
+
def self.add_deprecated(klass)
|
8
|
+
klass.send(:extend, ClassMethods)
|
9
9
|
end
|
10
10
|
|
11
11
|
module ClassMethods
|
data/lib/guard/dsl.rb
CHANGED
@@ -326,7 +326,7 @@ module Guard
|
|
326
326
|
options[:level] = options[:level].to_sym
|
327
327
|
|
328
328
|
unless [:debug, :info, :warn, :error].include? options[:level]
|
329
|
-
UI.warning
|
329
|
+
UI.warning(format(WARN_INVALID_LOG_LEVEL, options[:level]))
|
330
330
|
options.delete :level
|
331
331
|
end
|
332
332
|
end
|
@@ -58,11 +58,9 @@ module Guard
|
|
58
58
|
if opts[:contents]
|
59
59
|
@type = :inline
|
60
60
|
@contents = opts[:contents]
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
@path = Pathname(opts[:guardfile]) # may be updated by _read
|
65
|
-
end
|
61
|
+
elsif opts[:guardfile]
|
62
|
+
@type = :custom
|
63
|
+
@path = Pathname(opts[:guardfile]) # may be updated by _read
|
66
64
|
end
|
67
65
|
end
|
68
66
|
|
@@ -162,7 +160,7 @@ module Guard
|
|
162
160
|
source_from_option = @source.nil? && options[:guardfile_contents]
|
163
161
|
inline = @source == :inline
|
164
162
|
|
165
|
-
return false unless
|
163
|
+
return false unless source_from_option || inline
|
166
164
|
|
167
165
|
@source = :inline
|
168
166
|
@guardfile_contents = options[:guardfile_contents]
|
@@ -27,7 +27,8 @@ module Guard
|
|
27
27
|
# The Guardfile template for `guard init`
|
28
28
|
GUARDFILE_TEMPLATE = File.expand_path(
|
29
29
|
"../../../guard/templates/Guardfile",
|
30
|
-
__FILE__
|
30
|
+
__FILE__
|
31
|
+
)
|
31
32
|
|
32
33
|
# The location of user defined templates
|
33
34
|
begin
|
@@ -38,6 +39,24 @@ module Guard
|
|
38
39
|
HOME_TEMPLATES = Pathname("/").expand_path
|
39
40
|
end
|
40
41
|
|
42
|
+
class Error < RuntimeError
|
43
|
+
end
|
44
|
+
|
45
|
+
class NoSuchPlugin < Error
|
46
|
+
attr_reader :plugin_name, :class_name
|
47
|
+
|
48
|
+
def initialize(plugin_name)
|
49
|
+
@plugin_name = plugin_name
|
50
|
+
@class_name = plugin_name.delete("-").capitalize
|
51
|
+
end
|
52
|
+
|
53
|
+
def message
|
54
|
+
"Could not load 'guard/#{plugin_name}'"\
|
55
|
+
" or '~/.guard/templates/#{plugin_name}'"\
|
56
|
+
" or find class Guard::#{class_name}\n"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
41
60
|
# Creates the initial Guardfile template when it does not
|
42
61
|
# already exist.
|
43
62
|
#
|
@@ -86,14 +105,8 @@ module Guard
|
|
86
105
|
|
87
106
|
_ui(:info, format(INFO_TEMPLATE_ADDED, plugin_name))
|
88
107
|
|
89
|
-
rescue Errno::ENOENT
|
90
|
-
|
91
|
-
name = plugin_name.downcase
|
92
|
-
class_name = name.gsub("-", "").capitalize
|
93
|
-
_ui(:error, "Could not load 'guard/#{name}'"\
|
94
|
-
" or '~/.guard/templates/#{name}'"\
|
95
|
-
" or find class Guard::#{class_name}")
|
96
|
-
_ui(:error, "Error is: #{error}")
|
108
|
+
rescue Errno::ENOENT
|
109
|
+
fail NoSuchPlugin, plugin_name.downcase
|
97
110
|
end
|
98
111
|
|
99
112
|
# Adds the templates of all installed Guard implementations to an
|
data/lib/guard/interactor.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
1
3
|
module Guard
|
2
4
|
class Interactor
|
3
5
|
# Initializes the interactor. This configures
|
@@ -19,19 +21,8 @@ module Guard
|
|
19
21
|
@interactive
|
20
22
|
end
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
idle_job.foreground
|
25
|
-
end
|
26
|
-
|
27
|
-
# Remove interactor so other tasks can run in foreground
|
28
|
-
def background
|
29
|
-
idle_job.background
|
30
|
-
end
|
31
|
-
|
32
|
-
def handle_interrupt
|
33
|
-
idle_job.handle_interrupt
|
34
|
-
end
|
24
|
+
extend Forwardable
|
25
|
+
delegate [:foreground, :background, :handle_interrupt] => :idle_job
|
35
26
|
|
36
27
|
# TODO: everything below is just so the DSL can set options
|
37
28
|
# before setup() is called, which makes it useless for when
|
@@ -28,9 +28,9 @@ module Guard
|
|
28
28
|
def matcher_for(filter)
|
29
29
|
case filter
|
30
30
|
when String, Symbol
|
31
|
-
|
31
|
+
->(group) { group.name == filter.to_sym }
|
32
32
|
when Regexp
|
33
|
-
|
33
|
+
->(group) { group.name.to_s =~ filter }
|
34
34
|
else
|
35
35
|
fail "Invalid filter: #{filter.inspect}"
|
36
36
|
end
|
@@ -31,16 +31,16 @@ module Guard
|
|
31
31
|
def matcher_for(filter)
|
32
32
|
case filter
|
33
33
|
when String, Symbol
|
34
|
-
shortname = filter.to_s.downcase.
|
35
|
-
|
34
|
+
shortname = filter.to_s.downcase.delete("-")
|
35
|
+
->(plugin) { plugin.name == shortname }
|
36
36
|
when Regexp
|
37
|
-
|
37
|
+
->(plugin) { plugin.name =~ filter }
|
38
38
|
when Hash
|
39
39
|
lambda do |plugin|
|
40
40
|
filter.all? do |k, v|
|
41
41
|
case k
|
42
42
|
when :name
|
43
|
-
plugin.name == v.to_s.downcase.
|
43
|
+
plugin.name == v.to_s.downcase.delete("-")
|
44
44
|
when :group
|
45
45
|
plugin.group.name == v.to_sym
|
46
46
|
end
|
@@ -8,7 +8,8 @@ module Guard
|
|
8
8
|
|
9
9
|
# Process the change queue, running tasks within the main Guard thread
|
10
10
|
def process
|
11
|
-
actions
|
11
|
+
actions = []
|
12
|
+
changes = { modified: [], added: [], removed: [] }
|
12
13
|
|
13
14
|
while pending?
|
14
15
|
if (item = @queue.pop).first.is_a?(Symbol)
|
@@ -24,7 +25,7 @@ module Guard
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def pending?
|
27
|
-
|
28
|
+
!@queue.empty?
|
28
29
|
end
|
29
30
|
|
30
31
|
def <<(changes)
|
@@ -156,9 +156,7 @@ module Guard
|
|
156
156
|
@guardfile_notifier_options.merge!(config)
|
157
157
|
end
|
158
158
|
|
159
|
-
|
160
|
-
@interactor_name
|
161
|
-
end
|
159
|
+
attr_reader :interactor_name
|
162
160
|
|
163
161
|
# TODO: call this from within action, not within interactor command
|
164
162
|
def convert_scope(entries)
|
data/lib/guard/plugin.rb
CHANGED
@@ -160,7 +160,7 @@ module Guard
|
|
160
160
|
# @param [String] plugin_location the plugin location
|
161
161
|
#
|
162
162
|
def self.template(plugin_location)
|
163
|
-
File.read
|
163
|
+
File.read(format(TEMPLATE_FORMAT, plugin_location, non_namespaced_name))
|
164
164
|
end
|
165
165
|
|
166
166
|
# Called once when Guard starts. Please override initialize method to
|
data/lib/guard/plugin_util.rb
CHANGED
@@ -112,9 +112,10 @@ module Guard
|
|
112
112
|
# TODO: return value or move exception higher
|
113
113
|
rescue LoadError => error
|
114
114
|
unless options[:fail_gracefully]
|
115
|
-
|
116
|
-
UI.error
|
117
|
-
UI.error error
|
115
|
+
msg = format(ERROR_NO_GUARD_OR_CLASS, name.downcase, _constant_name)
|
116
|
+
UI.error(msg)
|
117
|
+
UI.error("Error is: #{error}")
|
118
|
+
UI.error(error.backtrace.join("\n"))
|
118
119
|
# TODO: return value or move exception higher
|
119
120
|
end
|
120
121
|
end
|
@@ -158,7 +159,7 @@ module Guard
|
|
158
159
|
#
|
159
160
|
def _plugin_constant
|
160
161
|
@_plugin_constant ||= Guard.constants.detect do |c|
|
161
|
-
c.to_s.
|
162
|
+
c.to_s.casecmp(_constant_name.downcase).zero?
|
162
163
|
end
|
163
164
|
end
|
164
165
|
|
@@ -169,20 +170,22 @@ module Guard
|
|
169
170
|
# => "Rspec"
|
170
171
|
#
|
171
172
|
def _constant_name
|
172
|
-
@_constant_name ||= name.gsub(
|
173
|
+
@_constant_name ||= name.gsub(%r{/(.?)}) { "::#{ $1.upcase }" }.
|
173
174
|
gsub(/(?:^|[_-])(.)/) { $1.upcase }
|
174
175
|
end
|
175
176
|
|
176
|
-
def self._gem_valid?(gem)
|
177
|
-
return false if gem.name == "guard-compat"
|
178
|
-
return true if gem.name =~ /^guard-/
|
179
|
-
full_path = gem.full_gem_path
|
180
|
-
file = File.join(full_path, "lib", "guard", "#{gem.name}.rb")
|
181
|
-
File.exist?(file)
|
182
|
-
end
|
183
|
-
|
184
177
|
def _full_gem_path(name)
|
185
178
|
Gem::Specification.find_by_name(name).full_gem_path
|
186
179
|
end
|
180
|
+
|
181
|
+
class << self
|
182
|
+
def _gem_valid?(gem)
|
183
|
+
return false if gem.name == "guard-compat"
|
184
|
+
return true if gem.name =~ /^guard-/
|
185
|
+
full_path = gem.full_gem_path
|
186
|
+
file = File.join(full_path, "lib", "guard", "#{gem.name}.rb")
|
187
|
+
File.exist?(file)
|
188
|
+
end
|
189
|
+
end
|
187
190
|
end
|
188
191
|
end
|
data/lib/guard/ui.rb
CHANGED
@@ -2,6 +2,7 @@ require "guard/ui/colors"
|
|
2
2
|
|
3
3
|
require "guard/terminal"
|
4
4
|
require "guard/options"
|
5
|
+
require "forwardable"
|
5
6
|
|
6
7
|
# TODO: rework this class from the bottom-up
|
7
8
|
# - remove dependency on Session and Scope
|
@@ -26,7 +27,8 @@ module Guard
|
|
26
27
|
require "lumberjack"
|
27
28
|
Lumberjack::Logger.new(
|
28
29
|
options.fetch(:device) { $stderr },
|
29
|
-
options
|
30
|
+
options
|
31
|
+
)
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
@@ -46,7 +48,8 @@ module Guard
|
|
46
48
|
@options ||= Options.new(
|
47
49
|
level: :info,
|
48
50
|
template: ":time - :severity - :message",
|
49
|
-
time_format: "%H:%M:%S"
|
51
|
+
time_format: "%H:%M:%S"
|
52
|
+
)
|
50
53
|
end
|
51
54
|
|
52
55
|
# Set the logger options
|
@@ -62,9 +65,8 @@ module Guard
|
|
62
65
|
end
|
63
66
|
|
64
67
|
# Assigns a log level
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
+
extend Forwardable
|
69
|
+
def_delegator :logger, :level=
|
68
70
|
|
69
71
|
# Show an info message.
|
70
72
|
#
|
@@ -202,7 +204,7 @@ module Guard
|
|
202
204
|
# @return [String] the Guard plugin name
|
203
205
|
#
|
204
206
|
def calling_plugin_name(depth = 2)
|
205
|
-
name =
|
207
|
+
name = %r{(guard\/[a-z_]*)(/[a-z_]*)?.rb:}i.match(caller[depth])
|
206
208
|
return "Guard" unless name
|
207
209
|
name[1].split("/").map do |part|
|
208
210
|
part.split(/[^a-z0-9]/i).map(&:capitalize).join
|
@@ -217,26 +219,19 @@ module Guard
|
|
217
219
|
@color_enabled_initialized ||= false
|
218
220
|
@color_enabled = nil unless @color_enabled_initialized
|
219
221
|
@color_enabled_initialized = true
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
@color_enabled = true
|
224
|
-
else
|
225
|
-
begin
|
226
|
-
require "rubygems" unless ENV["NO_RUBYGEMS"]
|
227
|
-
require "Win32/Console/ANSI"
|
228
|
-
@color_enabled = true
|
229
|
-
rescue LoadError
|
230
|
-
@color_enabled = false
|
231
|
-
info "Run 'gem install win32console' to use color on Windows"
|
232
|
-
end
|
233
|
-
end
|
234
|
-
else
|
235
|
-
@color_enabled = true
|
236
|
-
end
|
237
|
-
end
|
222
|
+
return @color_enabled unless @color_enabled.nil?
|
223
|
+
return (@color_enabled = true) unless Gem.win_platform?
|
224
|
+
return (@color_enabled = true) if ENV["ANSICON"]
|
238
225
|
|
239
|
-
@color_enabled
|
226
|
+
@color_enabled =
|
227
|
+
begin
|
228
|
+
require "rubygems" unless ENV["NO_RUBYGEMS"]
|
229
|
+
require "Win32/Console/ANSI"
|
230
|
+
true
|
231
|
+
rescue LoadError
|
232
|
+
info "Run 'gem install win32console' to use color on Windows"
|
233
|
+
false
|
234
|
+
end
|
240
235
|
end
|
241
236
|
|
242
237
|
# Colorizes a text message. See the constant in the UI class for possible
|
data/lib/guard/version.rb
CHANGED
data/lib/guard/watcher.rb
CHANGED
@@ -2,6 +2,7 @@ require "guard/config"
|
|
2
2
|
require "guard/deprecated/watcher" unless Guard::Config.new.strict?
|
3
3
|
|
4
4
|
require "guard/ui"
|
5
|
+
require "guard/watcher/pattern"
|
5
6
|
|
6
7
|
module Guard
|
7
8
|
# The watcher defines a RegExp that will be matched against file system
|
@@ -21,30 +22,8 @@ module Guard
|
|
21
22
|
# the Guard plugin
|
22
23
|
#
|
23
24
|
def initialize(pattern, action = nil)
|
24
|
-
@
|
25
|
-
|
26
|
-
|
27
|
-
# deprecation warning
|
28
|
-
regexp = /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/
|
29
|
-
return unless @pattern.is_a?(String) && @pattern =~ regexp
|
30
|
-
|
31
|
-
unless @@warning_printed
|
32
|
-
UI.info "*" * 20 + "\nDEPRECATION WARNING!\n" + "*" * 20
|
33
|
-
UI.info <<-MSG
|
34
|
-
You have a string in your Guardfile watch patterns that seem to
|
35
|
-
represent a Regexp.
|
36
|
-
|
37
|
-
Guard matches String with == and Regexp with Regexp#match.
|
38
|
-
|
39
|
-
You should either use plain String (without Regexp special
|
40
|
-
characters) or real Regexp.
|
41
|
-
MSG
|
42
|
-
@@warning_printed = true
|
43
|
-
end
|
44
|
-
|
45
|
-
new_regexp = Regexp.new(@pattern).inspect
|
46
|
-
UI.info "\"#{@pattern}\" has been converted to #{ new_regexp }\n"
|
47
|
-
@pattern = Regexp.new(@pattern)
|
25
|
+
@action = action
|
26
|
+
@pattern = Pattern.create(pattern)
|
48
27
|
end
|
49
28
|
|
50
29
|
# Finds the files that matches a Guard plugin.
|
@@ -59,7 +38,7 @@ module Guard
|
|
59
38
|
files.inject([]) do |paths, file|
|
60
39
|
guard.watchers.each do |watcher|
|
61
40
|
matches = watcher.match(file)
|
62
|
-
next unless matches
|
41
|
+
next(paths) unless matches
|
63
42
|
|
64
43
|
if watcher.action
|
65
44
|
result = watcher.call_action(matches)
|
@@ -68,7 +47,7 @@ module Guard
|
|
68
47
|
elsif result.respond_to?(:empty?) && !result.empty?
|
69
48
|
paths << Array(result)
|
70
49
|
else
|
71
|
-
next
|
50
|
+
next(paths)
|
72
51
|
end
|
73
52
|
else
|
74
53
|
paths << matches[0]
|
@@ -81,20 +60,9 @@ module Guard
|
|
81
60
|
end
|
82
61
|
end
|
83
62
|
|
84
|
-
# Test the watchers pattern against a file.
|
85
|
-
#
|
86
|
-
# @param [String] file the file to test
|
87
|
-
# @return [Array<String>] an array of matches (or containing a single path
|
88
|
-
# if the pattern is a string)
|
89
|
-
#
|
90
63
|
def match(string_or_pathname)
|
91
|
-
|
92
|
-
|
93
|
-
return (file == @pattern ? [file] : nil) unless @pattern.is_a?(Regexp)
|
94
|
-
return unless (m = @pattern.match(file))
|
95
|
-
m = m.to_a
|
96
|
-
m[0] = file
|
97
|
-
m
|
64
|
+
m = pattern.match(string_or_pathname)
|
65
|
+
m.nil? ? nil : Pattern::MatchResult.new(m, string_or_pathname)
|
98
66
|
end
|
99
67
|
|
100
68
|
# Executes a watcher action.
|
@@ -106,7 +74,7 @@ module Guard
|
|
106
74
|
def call_action(matches)
|
107
75
|
@action.arity > 0 ? @action.call(matches) : @action.call
|
108
76
|
rescue => ex
|
109
|
-
UI.error "Problem with watch action!\n#{
|
77
|
+
UI.error "Problem with watch action!\n#{ex.message}"
|
110
78
|
UI.error ex.backtrace.join("\n")
|
111
79
|
end
|
112
80
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "guard/ui"
|
2
|
+
|
3
|
+
require_relative "pattern/match_result"
|
4
|
+
require_relative "pattern/matcher"
|
5
|
+
require_relative "pattern/deprecated_regexp"
|
6
|
+
require_relative "pattern/simple_path"
|
7
|
+
require_relative "pattern/pathname_path"
|
8
|
+
|
9
|
+
module Guard
|
10
|
+
class Watcher
|
11
|
+
class Pattern
|
12
|
+
def self.create(pattern)
|
13
|
+
if DeprecatedRegexp.new(pattern).deprecated?
|
14
|
+
DeprecatedRegexp.show_deprecation(pattern)
|
15
|
+
return DeprecatedRegexp.convert(pattern)
|
16
|
+
end
|
17
|
+
|
18
|
+
return PathnamePath.new(pattern) if pattern.is_a?(Pathname)
|
19
|
+
return SimplePath.new(pattern) if pattern.is_a?(String)
|
20
|
+
Matcher.new(pattern)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative "matcher"
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
class Watcher
|
5
|
+
class Pattern
|
6
|
+
# TODO: remove before Guard 3.x
|
7
|
+
class DeprecatedRegexp
|
8
|
+
def initialize(pattern)
|
9
|
+
@original_pattern = pattern
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.convert(pattern)
|
13
|
+
Matcher.new(Regexp.new(pattern))
|
14
|
+
end
|
15
|
+
|
16
|
+
def deprecated?
|
17
|
+
regexp = /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/
|
18
|
+
@original_pattern.is_a?(String) && regexp.match(@original_pattern)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.show_deprecation(pattern)
|
22
|
+
@warning_printed ||= false
|
23
|
+
|
24
|
+
unless @warning_printed
|
25
|
+
msg = "*" * 20 + "\nDEPRECATION WARNING!\n" + "*" * 20
|
26
|
+
msg += <<-MSG
|
27
|
+
You have a string in your Guardfile watch patterns that seem to
|
28
|
+
represent a Regexp.
|
29
|
+
|
30
|
+
Guard matches String with == and Regexp with Regexp#match.
|
31
|
+
|
32
|
+
You should either use plain String (without Regexp special
|
33
|
+
characters) or real Regexp.
|
34
|
+
MSG
|
35
|
+
UI.deprecation(msg)
|
36
|
+
@warning_printed = true
|
37
|
+
end
|
38
|
+
|
39
|
+
new_regexp = Regexp.new(pattern).inspect
|
40
|
+
UI.info "\"#{pattern}\" will be converted to #{new_regexp}\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Guard
|
2
|
+
class Watcher
|
3
|
+
class Pattern
|
4
|
+
class MatchResult
|
5
|
+
def initialize(match_result, original_value)
|
6
|
+
@match_result = match_result
|
7
|
+
@original_value = original_value
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](index)
|
11
|
+
return @match_result[index] if index.is_a?(Symbol)
|
12
|
+
return @original_value if index.zero?
|
13
|
+
@match_result.to_a[index]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Guard
|
2
|
+
class Watcher
|
3
|
+
class Pattern
|
4
|
+
class Matcher
|
5
|
+
def initialize(obj)
|
6
|
+
@matcher = obj
|
7
|
+
end
|
8
|
+
|
9
|
+
def match(string_or_pathname)
|
10
|
+
@matcher.match(normalized(string_or_pathname))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def normalized(string_or_pathname)
|
16
|
+
path = Pathname.new(string_or_pathname).cleanpath
|
17
|
+
return path.to_s if @matcher.is_a?(Regexp)
|
18
|
+
path
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Guard
|
2
|
+
class Watcher
|
3
|
+
class Pattern
|
4
|
+
class SimplePath
|
5
|
+
def initialize(string_or_pathname)
|
6
|
+
@path = normalize(string_or_pathname)
|
7
|
+
end
|
8
|
+
|
9
|
+
def match(string_or_pathname)
|
10
|
+
cleaned = normalize(string_or_pathname)
|
11
|
+
return nil unless @path == cleaned
|
12
|
+
[cleaned]
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def normalize(string_or_pathname)
|
18
|
+
Pathname.new(string_or_pathname).cleanpath.to_s
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# TODO: extract to gem?
|
2
|
+
|
3
|
+
class Releaser
|
4
|
+
def initialize(options = {})
|
5
|
+
@project_name = options.delete(:project_name) do
|
6
|
+
fail "project_name is needed!"
|
7
|
+
end
|
8
|
+
|
9
|
+
@gem_name = options.delete(:gem_name) do
|
10
|
+
fail "gem_name is needed!"
|
11
|
+
end
|
12
|
+
|
13
|
+
@github_repo = options.delete(:github_repo) do
|
14
|
+
fail "github_repo is needed!"
|
15
|
+
end
|
16
|
+
|
17
|
+
@version = options.delete(:version) do
|
18
|
+
fail "version is needed!"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def full
|
23
|
+
rubygems
|
24
|
+
github
|
25
|
+
end
|
26
|
+
|
27
|
+
def rubygems
|
28
|
+
input = nil
|
29
|
+
loop do
|
30
|
+
STDOUT.puts "Release #{@project_name} #{@version} to RubyGems? (y/n)"
|
31
|
+
input = STDIN.gets.chomp.downcase
|
32
|
+
break if %w(y n).include?(input)
|
33
|
+
end
|
34
|
+
|
35
|
+
exit if input == "n"
|
36
|
+
|
37
|
+
Rake::Task["release"].invoke
|
38
|
+
end
|
39
|
+
|
40
|
+
def github
|
41
|
+
tag_name = "v#{@version}"
|
42
|
+
|
43
|
+
require "gems"
|
44
|
+
|
45
|
+
_verify_released
|
46
|
+
_verify_tag_pushed
|
47
|
+
|
48
|
+
require "octokit"
|
49
|
+
gh_client = Octokit::Client.new(netrc: true)
|
50
|
+
|
51
|
+
gh_release = _detect_gh_release(gh_client, tag_name, true)
|
52
|
+
return unless gh_release
|
53
|
+
|
54
|
+
STDOUT.puts "Draft release for #{tag_name}:\n"
|
55
|
+
STDOUT.puts gh_release.body
|
56
|
+
STDOUT.puts "\n-------------------------\n\n"
|
57
|
+
|
58
|
+
_confirm_publish
|
59
|
+
|
60
|
+
return unless _update_release(gh_client, gh_release, tag_name)
|
61
|
+
|
62
|
+
gh_release = _detect_gh_release(gh_client, tag_name, false)
|
63
|
+
|
64
|
+
_success_summary(gh_release, tag_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def _verify_released
|
70
|
+
if @version != Gems.info(@gem_name)["version"]
|
71
|
+
STDOUT.puts "#{@project_name} #{@version} is not yet released."
|
72
|
+
STDOUT.puts "Please release it first with: rake release:gem"
|
73
|
+
exit
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def _verify_tag_pushed
|
78
|
+
tags = `git ls-remote --tags origin`.split("\n")
|
79
|
+
return if tags.detect { |tag| tag =~ /v#{@version}$/ }
|
80
|
+
|
81
|
+
STDOUT.puts "The tag v#{@version} has not yet been pushed."
|
82
|
+
STDOUT.puts "Please push it first with: rake release:gem"
|
83
|
+
exit
|
84
|
+
end
|
85
|
+
|
86
|
+
def _success_summary(gh_release, tag_name)
|
87
|
+
href = gh_release.rels[:html].href
|
88
|
+
STDOUT.puts "GitHub release #{tag_name} has been published!"
|
89
|
+
STDOUT.puts "\nPlease enjoy and spread the word!"
|
90
|
+
STDOUT.puts "Lack of inspiration? Here's a tweet you could improve:\n\n"
|
91
|
+
STDOUT.puts "Just released #{@project_name} #{@version}! #{href}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def _detect_gh_release(gh_client, tag_name, draft)
|
95
|
+
gh_releases = gh_client.releases(@github_repo)
|
96
|
+
gh_releases.detect { |r| r.tag_name == tag_name && r.draft == draft }
|
97
|
+
end
|
98
|
+
|
99
|
+
def _confirm_publish
|
100
|
+
input = nil
|
101
|
+
loop do
|
102
|
+
STDOUT.puts "Would you like to publish this GitHub release now? (y/n)"
|
103
|
+
input = STDIN.gets.chomp.downcase
|
104
|
+
break if %w(y n).include?(input)
|
105
|
+
end
|
106
|
+
|
107
|
+
exit if input == "n"
|
108
|
+
end
|
109
|
+
|
110
|
+
def _update_release(gh_client, gh_release, tag_name)
|
111
|
+
result = gh_client.update_release(gh_release.rels[:self].href, draft: false)
|
112
|
+
return true if result
|
113
|
+
STDOUT.puts "GitHub release #{tag_name} couldn't be published!"
|
114
|
+
false
|
115
|
+
end
|
116
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: guard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thibaud Guillaume-Gentil
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -31,7 +31,7 @@ dependencies:
|
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '2.7'
|
34
|
-
- - "
|
34
|
+
- - "<"
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: '4.0'
|
37
37
|
type: :runtime
|
@@ -41,7 +41,7 @@ dependencies:
|
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '2.7'
|
44
|
-
- - "
|
44
|
+
- - "<"
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '4.0'
|
47
47
|
- !ruby/object:Gem::Dependency
|
@@ -198,6 +198,13 @@ files:
|
|
198
198
|
- lib/guard/ui/colors.rb
|
199
199
|
- lib/guard/version.rb
|
200
200
|
- lib/guard/watcher.rb
|
201
|
+
- lib/guard/watcher/pattern.rb
|
202
|
+
- lib/guard/watcher/pattern/deprecated_regexp.rb
|
203
|
+
- lib/guard/watcher/pattern/match_result.rb
|
204
|
+
- lib/guard/watcher/pattern/matcher.rb
|
205
|
+
- lib/guard/watcher/pattern/pathname_path.rb
|
206
|
+
- lib/guard/watcher/pattern/simple_path.rb
|
207
|
+
- lib/tasks/releaser.rb
|
201
208
|
- man/guard.1
|
202
209
|
- man/guard.1.html
|
203
210
|
homepage: http://guardgem.org
|
@@ -220,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
220
227
|
version: '0'
|
221
228
|
requirements: []
|
222
229
|
rubyforge_project:
|
223
|
-
rubygems_version: 2.
|
230
|
+
rubygems_version: 2.5.1
|
224
231
|
signing_key:
|
225
232
|
specification_version: 4
|
226
233
|
summary: Guard keeps an eye on your file modifications
|