From db0905ebfe5e874b3e9b81f1bb3711eb52be9d8a Mon Sep 17 00:00:00 2001 From: Matthew Robertson Date: Mon, 4 Feb 2013 17:24:41 -0800 Subject: [PATCH] can now pass a regex to the canonical subdomain --- lib/ress.rb | 1 + lib/ress/canonical_version.rb | 32 ++++---- lib/ress/subdomain.rb | 78 ++++++++++++++++++ spec/ress/canonical_version_spec.rb | 16 +--- spec/ress/category_collection_spec.rb | 4 +- spec/ress/subdomain_spec.rb | 112 ++++++++++++++++++++++++++ spec/ress_spec.rb | 2 +- 7 files changed, 209 insertions(+), 36 deletions(-) create mode 100644 lib/ress/subdomain.rb create mode 100644 spec/ress/subdomain_spec.rb diff --git a/lib/ress.rb b/lib/ress.rb index e883c4b..c3098ba 100644 --- a/lib/ress.rb +++ b/lib/ress.rb @@ -1,6 +1,7 @@ require "action_view" require "ress/version" +require "ress/subdomain" require "ress/alternate_version" require "ress/canonical_version" require "ress/category_collection" diff --git a/lib/ress/canonical_version.rb b/lib/ress/canonical_version.rb index 033e31c..858348d 100644 --- a/lib/ress/canonical_version.rb +++ b/lib/ress/canonical_version.rb @@ -2,35 +2,31 @@ module Ress class CanonicalVersion - attr_reader :subdomain - def initialize(options = {}) - @subdomain = options.fetch(:subdomain, false) + @subdomain = Ress::Subdomain.create(options.fetch(:subdomain, nil)) end - def matches?(subdomain) - if self.subdomain - self.subdomain == subdomain - else - subdomain.empty? - end + def matches?(req_subdomain) + subdomain.matches?(req_subdomain) end # Create a tag of this format: # `` - def link_tag(protocol, fullpath, subdomain, view) - view.tag :link, :rel => 'canonical', :href => url(protocol, fullpath, subdomain) + def link_tag(protocol, fullpath, req_subdomain, view) + + view.tag :link, :rel => 'canonical', :href => url(protocol, fullpath, req_subdomain) end - def url(protocol, fullpath, subdomain) - fullpath = fullpath[(subdomain.length + 1)..-1] unless subdomain.empty? - if self.subdomain - "#{protocol}#{self.subdomain}.#{fullpath}" - else - "#{protocol}#{fullpath}" - end + def url(protocol, fullpath, req_subdomain) + subdomain.url(protocol, fullpath, req_subdomain) end + private + + def subdomain + @subdomain + end + end end \ No newline at end of file diff --git a/lib/ress/subdomain.rb b/lib/ress/subdomain.rb new file mode 100644 index 0000000..3af31b5 --- /dev/null +++ b/lib/ress/subdomain.rb @@ -0,0 +1,78 @@ +module Ress + + class Subdomain + + # this class is a factory, don't instantiate it directly + private_class_method :new + + def self.create(subdomain) + case subdomain + when String + StringSubdomain.new(subdomain) + when Regexp + RegexpSubdomain.new(subdomain) + else + NilSubdomain.new + end + end + + class NilSubdomain + def matches?(subdomain) + subdomain.empty? + end + + def url(protocol, fullpath, subdomain) + fullpath = fullpath[(subdomain.length + 1)..-1] unless subdomain.empty? + "#{protocol}#{fullpath}" + end + end + + class StringSubdomain + + attr_reader :subdomain + + def initialize(subdomain) + @subdomain = subdomain + end + + def matches?(subdomain) + self.subdomain == subdomain + end + + def url(protocol, fullpath, subdomain) + fullpath = fullpath[(subdomain.length + 1)..-1] unless subdomain.empty? + "#{protocol}#{self.subdomain}.#{fullpath}" + end + + end + + # The main inspiration for this class is to match xip.io + # subdomains for many devs working in development mode with + # the same configuration. + class RegexpSubdomain + + attr_reader :subdomain + + def initialize(subdomain) + @subdomain = subdomain + end + + def matches?(subdomain) + self.subdomain =~ subdomain + end + + def url(protocol, fullpath, subdomain) + fullpath = fullpath[(subdomain.length + 1)..-1] + begin + if matches?(subdomain) + return "#{protocol}#{subdomain}.#{fullpath}" + end + subdomain = subdomain.split('.')[1..-1].join('.') + end while(!subdomain.empty?) + end + + end + + end + +end diff --git a/spec/ress/canonical_version_spec.rb b/spec/ress/canonical_version_spec.rb index cbfb21d..c07bef3 100644 --- a/spec/ress/canonical_version_spec.rb +++ b/spec/ress/canonical_version_spec.rb @@ -1,21 +1,7 @@ -require_relative '../../lib/ress/canonical_version' +require_relative '../../lib/ress' describe Ress::CanonicalVersion do - describe '#subdomain' do - - it 'defaults to false' do - category = Ress::CanonicalVersion.new - category.subdomain.should == false - end - - it 'can be overridden by an optional parameter' do - category = Ress::CanonicalVersion.new(:subdomain => 'foo') - category.subdomain.should == 'foo' - end - - end - describe '#matches?' do context 'with no canonical subdomain' do diff --git a/spec/ress/category_collection_spec.rb b/spec/ress/category_collection_spec.rb index 274c6ca..d0860ac 100644 --- a/spec/ress/category_collection_spec.rb +++ b/spec/ress/category_collection_spec.rb @@ -27,12 +27,12 @@ describe 'canonical_version' do it 'defaults to a canonical_version with no subdomain' do - collection.canonical_version.subdomain.should be_false + collection.canonical_version.matches?('').should be_true end it 'can be altered through set_canonical' do collection.set_canonical :subdomain => 'foo' - collection.canonical_version.subdomain.should == 'foo' + collection.canonical_version.matches?('foo').should be_true end end diff --git a/spec/ress/subdomain_spec.rb b/spec/ress/subdomain_spec.rb new file mode 100644 index 0000000..f81218a --- /dev/null +++ b/spec/ress/subdomain_spec.rb @@ -0,0 +1,112 @@ +require_relative '../../lib/ress/subdomain' + +describe Ress::Subdomain do + + describe '.create' do + + it 'returns a NilSubdomain when the subdomain is nil' do + Ress::Subdomain.create(nil).should be_a(Ress::Subdomain::NilSubdomain) + end + + it 'returns a StringSubdomain when the subdomain is a String' do + Ress::Subdomain.create('foo').should be_a(Ress::Subdomain::StringSubdomain) + end + + it 'returns a RegexpSubdomain when the subdomain is a Regex' do + Ress::Subdomain.create(/foo/).should be_a(Ress::Subdomain::RegexpSubdomain) + end + + end + + describe Ress::Subdomain::NilSubdomain do + + let(:subdomain) { Ress::Subdomain::NilSubdomain.new } + + describe '#matches?' do + it 'matches the empty string' do + subdomain.matches?('').should be_true + subdomain.matches?('s').should be_false + end + end + + describe '#url' do + it 'strips urls from the fullpath if there is one' do + subdomain.url('http://', 'foo.bar.com/some/stuff', 'foo').should == + 'http://bar.com/some/stuff' + end + + it 'concatenates the protocol and fullpath together if there is no subdomain' do + subdomain.url('http://', 'bar.com/some/stuff', '').should == + 'http://bar.com/some/stuff' + end + end + end + + describe Ress::Subdomain::StringSubdomain do + + let(:subdomain) { Ress::Subdomain::StringSubdomain.new('foo') } + + describe '#matches?' do + + it 'returns true if the url is exact match of that passed to the ctor' do + subdomain.matches?('foo').should be_true + end + + it 'returns false if the url is not an exact match of that passed to the ctor' do + subdomain.matches?('boo').should be_false + end + end + + describe '#url' do + + it 'strips off extra subdomains from the fullpath' do + subdomain.url('http://', 'm.foo.bar.com/some/stuff', 'm.foo').should == + 'http://foo.bar.com/some/stuff' + end + + it 'handles cannonical fullpaths' do + subdomain.url('http://', 'foo.bar.com/some/stuff', 'foo').should == + 'http://foo.bar.com/some/stuff' + end + + end + end + + describe Ress::Subdomain::RegexpSubdomain do + describe '#matches?' do + let(:subdomain) { Ress::Subdomain::RegexpSubdomain.new(/[fb]oo/) } + + it 'returns true if the subdomain matches regex that passed to the ctor' do + subdomain.matches?('foo').should be_true + subdomain.matches?('boo').should be_true + end + + it 'returns false if the subdomain doesnt matches regex that is passed to the ctor' do + subdomain.matches?('baz').should be_false + end + + it 'can handle the xip.io format' do + subdomain = Ress::Subdomain::RegexpSubdomain.new(/^([0-9]+\.){3}([0-9]+)/) + subdomain.matches?('172.30.251.3').should be_true + + subdomain.matches?('mobile.172.30.251.3').should be_false + end + end + + describe '#url' do + + let(:subdomain) { Ress::Subdomain::RegexpSubdomain.new(/^[fb]oo/) } + + it 'echos back the url for cannonical requests' do + subdomain.url('http://', 'foo.bar.com/some/stuff', 'foo').should == + 'http://foo.bar.com/some/stuff' + end + + it 'strips off prepended subdomains that dont matche the regex' do + subdomain.url('http://', 'm.a.foo.bar.com/some/stuff', 'm.a.foo').should == + 'http://foo.bar.com/some/stuff' + end + + end + end +end \ No newline at end of file diff --git a/spec/ress_spec.rb b/spec/ress_spec.rb index c0e12fe..5e622d1 100644 --- a/spec/ress_spec.rb +++ b/spec/ress_spec.rb @@ -18,7 +18,7 @@ it 'can be altered through Ress.configure' do Ress.configure { |r| r.set_canonical :subdomain => 'foo' } - Ress.canonical_version.subdomain.should == 'foo' + Ress.canonical_version.matches?('foo').should be_true end end