guard 2.13.0 → 2.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://img.shields.io/gem/v/guard.svg?style=flat)](https://rubygems.org/gems/guard) [![Build Status](https://travis-ci.org/guard/guard.svg)](https://travis-ci.org/guard/guard) [![Dependency Status](https://gemnasium.com/guard/guard.png)](https://gemnasium.com/guard/guard) [![Code Climate](https://codeclimate.com/github/guard/guard/badges/gpa.svg)](https://codeclimate.com/github/guard/guard) [![Test Coverage](https://codeclimate.com/github/guard/guard/badges/coverage.svg)](https://codeclimate.com/github/guard/guard) [![Inline docs](http://inch-ci.org/github/guard/guard.svg)](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
|