Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #835 from guard/named_matches
rework watch matching + allow regexp named groups
- Loading branch information
Showing
14 changed files
with
471 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
require "guard/ui" | ||
|
||
require_relative "pattern/match_result" | ||
require_relative "pattern/matcher" | ||
require_relative "pattern/deprecated_regexp" | ||
require_relative "pattern/simple_path" | ||
require_relative "pattern/pathname_path" | ||
|
||
module Guard | ||
class Watcher | ||
class Pattern | ||
def self.create(pattern) | ||
if DeprecatedRegexp.new(pattern).deprecated? | ||
DeprecatedRegexp.show_deprecation(pattern) | ||
return DeprecatedRegexp.convert(pattern) | ||
end | ||
|
||
return PathnamePath.new(pattern) if pattern.is_a?(Pathname) | ||
return SimplePath.new(pattern) if pattern.is_a?(String) | ||
Matcher.new(pattern) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
require_relative "matcher" | ||
|
||
module Guard | ||
class Watcher | ||
class Pattern | ||
# TODO: remove before Guard 3.x | ||
class DeprecatedRegexp | ||
def initialize(pattern) | ||
@original_pattern = pattern | ||
end | ||
|
||
def self.convert(pattern) | ||
Matcher.new(Regexp.new(pattern)) | ||
end | ||
|
||
def deprecated? | ||
regexp = /(^(\^))|(>?(\\\.)|(\.\*))|(\(.*\))|(\[.*\])|(\$$)/ | ||
@original_pattern.is_a?(String) && regexp.match(@original_pattern) | ||
end | ||
|
||
def self.show_deprecation(pattern) | ||
@warning_printed ||= false | ||
|
||
unless @warning_printed | ||
msg = "*" * 20 + "\nDEPRECATION WARNING!\n" + "*" * 20 | ||
msg += <<-MSG | ||
You have a string in your Guardfile watch patterns that seem to | ||
represent a Regexp. | ||
Guard matches String with == and Regexp with Regexp#match. | ||
You should either use plain String (without Regexp special | ||
characters) or real Regexp. | ||
MSG | ||
UI.deprecation(msg) | ||
@warning_printed = true | ||
end | ||
|
||
new_regexp = Regexp.new(pattern).inspect | ||
UI.info "\"#{pattern}\" will be converted to #{new_regexp}\n" | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
module Guard | ||
class Watcher | ||
class Pattern | ||
class MatchResult | ||
def initialize(match_result, original_value) | ||
@match_result = match_result | ||
@original_value = original_value | ||
end | ||
|
||
def [](index) | ||
return @match_result[index] if index.is_a?(Symbol) | ||
return @original_value if index.zero? | ||
@match_result.to_a[index] | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
module Guard | ||
class Watcher | ||
class Pattern | ||
class Matcher | ||
def initialize(obj) | ||
@matcher = obj | ||
end | ||
|
||
def match(string_or_pathname) | ||
@matcher.match(normalized(string_or_pathname)) | ||
end | ||
|
||
private | ||
|
||
def normalized(string_or_pathname) | ||
path = Pathname.new(string_or_pathname).cleanpath | ||
return path.to_s if @matcher.is_a?(Regexp) | ||
path | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
require_relative "simple_path" | ||
|
||
module Guard | ||
class Watcher | ||
class Pattern | ||
class PathnamePath < SimplePath | ||
protected | ||
|
||
def normalize(string_or_pathname) | ||
Pathname.new(string_or_pathname).cleanpath | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
module Guard | ||
class Watcher | ||
class Pattern | ||
class SimplePath | ||
def initialize(string_or_pathname) | ||
@path = normalize(string_or_pathname) | ||
end | ||
|
||
def match(string_or_pathname) | ||
cleaned = normalize(string_or_pathname) | ||
return nil unless @path == cleaned | ||
[cleaned] | ||
end | ||
|
||
protected | ||
|
||
def normalize(string_or_pathname) | ||
Pathname.new(string_or_pathname).cleanpath.to_s | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
require "guard/watcher/pattern/deprecated_regexp" | ||
|
||
RSpec.describe Guard::Watcher::Pattern::DeprecatedRegexp do | ||
describe ".deprecated?" do | ||
specify { expect(described_class.new("^spec_helper.rb")).to be_deprecated } | ||
specify { expect(described_class.new("spec_helper.rb$")).to be_deprecated } | ||
end | ||
|
||
describe "Matcher returned by .convert" do | ||
let(:matcher) { Guard::Watcher::Pattern::Matcher } | ||
|
||
before { allow(matcher).to receive(:new) } | ||
|
||
{ | ||
"^foo.rb" => /^foo.rb/, | ||
"foo.rb$" => /foo.rb$/, | ||
'foo\.rb' => /foo\.rb/, | ||
".*rb" => /.*rb/, | ||
}.each do |pattern, regexp| | ||
context "with #{pattern}" do | ||
it "creates a Matcher with #{regexp}" do | ||
expect(matcher).to receive(:new).with(regexp) | ||
described_class.convert(pattern) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
require "guard/watcher/pattern/match_result" | ||
|
||
RSpec.describe Guard::Watcher::Pattern::MatchResult do | ||
let(:match_result) { double("match_data") } | ||
let(:original_value) { "foo/bar.rb" } | ||
subject { described_class.new(match_result, original_value) } | ||
|
||
describe "#initialize" do | ||
context "with valid arguments" do | ||
it "does not fail" do | ||
expect { subject }.to_not raise_error | ||
end | ||
end | ||
end | ||
|
||
describe "#[]" do | ||
context "with a valid match" do | ||
let(:match_result) { double("match_data", to_a: %w(foo bar baz)) } | ||
|
||
context "when asked for the non-first item" do | ||
let(:index) { 1 } | ||
it "returns the value at given index" do | ||
expect(subject[index]).to eq("bar") | ||
end | ||
end | ||
|
||
context "when asked for the first item" do | ||
let(:index) { 0 } | ||
it "returns the full original value" do | ||
expect(subject[index]).to eq("foo/bar.rb") | ||
end | ||
end | ||
|
||
context "when asked for a name match via a symbol" do | ||
let(:index) { :foo } | ||
before do | ||
allow(match_result).to receive(:[]).with(:foo).and_return("baz") | ||
end | ||
|
||
it "returns the value by name" do | ||
expect(subject[index]).to eq("baz") | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
require "guard/watcher/pattern/matcher" | ||
|
||
RSpec.describe Guard::Watcher::Pattern::Matcher do | ||
subject { described_class.new(obj) } | ||
describe "#match" do | ||
let(:expected) { double("match_result") } | ||
|
||
context "when constructed with valid matcher object" do | ||
let(:obj) { double("matcher") } | ||
|
||
context "when matched against a Pathname" do | ||
before do | ||
allow(obj).to receive(:match).and_return(expected) | ||
end | ||
let(:filename) { Pathname("foo.rb") } | ||
|
||
it "returns the match result" do | ||
expect(subject.match(filename)).to be(expected) | ||
end | ||
|
||
it "passes the Pathname to the matcher" do | ||
allow(obj).to receive(:match).with(filename) | ||
subject.match(filename) | ||
end | ||
end | ||
|
||
context "when matched against a String" do | ||
before do | ||
allow(obj).to receive(:match).and_return(expected) | ||
end | ||
let(:filename) { "foo.rb" } | ||
|
||
it "returns the match result" do | ||
expect(subject.match(filename)).to be(expected) | ||
end | ||
|
||
it "passes a Pathname to the matcher" do | ||
allow(obj).to receive(:match).with(Pathname(filename)) | ||
subject.match(filename) | ||
end | ||
end | ||
end | ||
end | ||
|
||
describe "integration" do | ||
describe "#match result" do | ||
subject { described_class.new(obj).match(filename) } | ||
context "when constructed with valid regexp" do | ||
let(:obj) { /foo.rb$/ } | ||
|
||
context "when matched file is a string" do | ||
context "when filename matches" do | ||
let(:filename) { "foo.rb" } | ||
specify { expect(subject.to_a).to eq(["foo.rb"]) } | ||
end | ||
|
||
context "when filename does not match" do | ||
let(:filename) { "bar.rb" } | ||
specify { expect(subject).to be_nil } | ||
end | ||
end | ||
|
||
context "when matched file is an unclean Pathname" do | ||
context "when filename matches" do | ||
let(:filename) { Pathname("./foo.rb") } | ||
specify { expect(subject.to_a).to eq(["foo.rb"]) } | ||
end | ||
|
||
context "when filename does not match" do | ||
let(:filename) { Pathname("./bar.rb") } | ||
specify { expect(subject).to be_nil } | ||
end | ||
end | ||
|
||
context "when matched file contains a $" do | ||
let(:filename) { Pathname("lib$/foo.rb") } | ||
specify { expect(subject.to_a).to eq(["foo.rb"]) } | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.