| # coding: utf-8 |
| # Copyright (C) Bob Aman |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| |
| require "spec_helper" |
| |
| require "bigdecimal" |
| require "addressable/template" |
| |
| shared_examples_for 'expands' do |tests| |
| tests.each do |template, expansion| |
| exp = expansion.is_a?(Array) ? expansion.first : expansion |
| it "#{template} to #{exp}" do |
| tmpl = Addressable::Template.new(template).expand(subject) |
| if expansion.is_a?(Array) |
| expect(expansion.any?{|i| i == tmpl.to_str}).to be true |
| else |
| expect(tmpl.to_str).to eq(expansion) |
| end |
| end |
| end |
| end |
| |
| describe "eql?" do |
| let(:template) { Addressable::Template.new('https://www.example.com/{foo}') } |
| it 'is equal when the pattern matches' do |
| other_template = Addressable::Template.new('https://www.example.com/{foo}') |
| expect(template).to be_eql(other_template) |
| expect(other_template).to be_eql(template) |
| end |
| it 'is not equal when the pattern differs' do |
| other_template = Addressable::Template.new('https://www.example.com/{bar}') |
| expect(template).to_not be_eql(other_template) |
| expect(other_template).to_not be_eql(template) |
| end |
| it 'is not equal to non-templates' do |
| uri = 'https://www.example.com/foo/bar' |
| addressable_template = Addressable::Template.new uri |
| addressable_uri = Addressable::URI.parse uri |
| expect(addressable_template).to_not be_eql(addressable_uri) |
| expect(addressable_uri).to_not be_eql(addressable_template) |
| end |
| end |
| |
| describe "==" do |
| let(:template) { Addressable::Template.new('https://www.example.com/{foo}') } |
| it 'is equal when the pattern matches' do |
| other_template = Addressable::Template.new('https://www.example.com/{foo}') |
| expect(template).to eq other_template |
| expect(other_template).to eq template |
| end |
| it 'is not equal when the pattern differs' do |
| other_template = Addressable::Template.new('https://www.example.com/{bar}') |
| expect(template).not_to eq other_template |
| expect(other_template).not_to eq template |
| end |
| it 'is not equal to non-templates' do |
| uri = 'https://www.example.com/foo/bar' |
| addressable_template = Addressable::Template.new uri |
| addressable_uri = Addressable::URI.parse uri |
| expect(addressable_template).not_to eq addressable_uri |
| expect(addressable_uri).not_to eq addressable_template |
| end |
| end |
| |
| describe "Type conversion" do |
| subject { |
| { |
| :var => true, |
| :hello => 1234, |
| :nothing => nil, |
| :sym => :symbolic, |
| :decimal => BigDecimal.new('1') |
| } |
| } |
| |
| it_behaves_like 'expands', { |
| '{var}' => 'true', |
| '{hello}' => '1234', |
| '{nothing}' => '', |
| '{sym}' => 'symbolic', |
| '{decimal}' => RUBY_VERSION < '2.4.0' ? '0.1E1' : '0.1e1' |
| } |
| end |
| |
| describe "Level 1:" do |
| subject { |
| {:var => "value", :hello => "Hello World!"} |
| } |
| it_behaves_like 'expands', { |
| '{var}' => 'value', |
| '{hello}' => 'Hello%20World%21' |
| } |
| end |
| |
| describe "Level 2" do |
| subject { |
| { |
| :var => "value", |
| :hello => "Hello World!", |
| :path => "/foo/bar" |
| } |
| } |
| context "Operator +:" do |
| it_behaves_like 'expands', { |
| '{+var}' => 'value', |
| '{+hello}' => 'Hello%20World!', |
| '{+path}/here' => '/foo/bar/here', |
| 'here?ref={+path}' => 'here?ref=/foo/bar' |
| } |
| end |
| context "Operator #:" do |
| it_behaves_like 'expands', { |
| 'X{#var}' => 'X#value', |
| 'X{#hello}' => 'X#Hello%20World!' |
| } |
| end |
| end |
| |
| describe "Level 3" do |
| subject { |
| { |
| :var => "value", |
| :hello => "Hello World!", |
| :empty => "", |
| :path => "/foo/bar", |
| :x => "1024", |
| :y => "768" |
| } |
| } |
| context "Operator nil (multiple vars):" do |
| it_behaves_like 'expands', { |
| 'map?{x,y}' => 'map?1024,768', |
| '{x,hello,y}' => '1024,Hello%20World%21,768' |
| } |
| end |
| context "Operator + (multiple vars):" do |
| it_behaves_like 'expands', { |
| '{+x,hello,y}' => '1024,Hello%20World!,768', |
| '{+path,x}/here' => '/foo/bar,1024/here' |
| } |
| end |
| context "Operator # (multiple vars):" do |
| it_behaves_like 'expands', { |
| '{#x,hello,y}' => '#1024,Hello%20World!,768', |
| '{#path,x}/here' => '#/foo/bar,1024/here' |
| } |
| end |
| context "Operator ." do |
| it_behaves_like 'expands', { |
| 'X{.var}' => 'X.value', |
| 'X{.x,y}' => 'X.1024.768' |
| } |
| end |
| context "Operator /" do |
| it_behaves_like 'expands', { |
| '{/var}' => '/value', |
| '{/var,x}/here' => '/value/1024/here' |
| } |
| end |
| context "Operator ;" do |
| it_behaves_like 'expands', { |
| '{;x,y}' => ';x=1024;y=768', |
| '{;x,y,empty}' => ';x=1024;y=768;empty' |
| } |
| end |
| context "Operator ?" do |
| it_behaves_like 'expands', { |
| '{?x,y}' => '?x=1024&y=768', |
| '{?x,y,empty}' => '?x=1024&y=768&empty=' |
| } |
| end |
| context "Operator &" do |
| it_behaves_like 'expands', { |
| '?fixed=yes{&x}' => '?fixed=yes&x=1024', |
| '{&x,y,empty}' => '&x=1024&y=768&empty=' |
| } |
| end |
| end |
| |
| describe "Level 4" do |
| subject { |
| { |
| :var => "value", |
| :hello => "Hello World!", |
| :path => "/foo/bar", |
| :semi => ";", |
| :list => %w(red green blue), |
| :keys => {"semi" => ';', "dot" => '.', "comma" => ','} |
| } |
| } |
| context "Expansion with value modifiers" do |
| it_behaves_like 'expands', { |
| '{var:3}' => 'val', |
| '{var:30}' => 'value', |
| '{list}' => 'red,green,blue', |
| '{list*}' => 'red,green,blue', |
| '{keys}' => [ |
| 'semi,%3B,dot,.,comma,%2C', |
| 'dot,.,semi,%3B,comma,%2C', |
| 'comma,%2C,semi,%3B,dot,.', |
| 'semi,%3B,comma,%2C,dot,.', |
| 'dot,.,comma,%2C,semi,%3B', |
| 'comma,%2C,dot,.,semi,%3B' |
| ], |
| '{keys*}' => [ |
| 'semi=%3B,dot=.,comma=%2C', |
| 'dot=.,semi=%3B,comma=%2C', |
| 'comma=%2C,semi=%3B,dot=.', |
| 'semi=%3B,comma=%2C,dot=.', |
| 'dot=.,comma=%2C,semi=%3B', |
| 'comma=%2C,dot=.,semi=%3B' |
| ] |
| } |
| end |
| context "Operator + with value modifiers" do |
| it_behaves_like 'expands', { |
| '{+path:6}/here' => '/foo/b/here', |
| '{+list}' => 'red,green,blue', |
| '{+list*}' => 'red,green,blue', |
| '{+keys}' => [ |
| 'semi,;,dot,.,comma,,', |
| 'dot,.,semi,;,comma,,', |
| 'comma,,,semi,;,dot,.', |
| 'semi,;,comma,,,dot,.', |
| 'dot,.,comma,,,semi,;', |
| 'comma,,,dot,.,semi,;' |
| ], |
| '{+keys*}' => [ |
| 'semi=;,dot=.,comma=,', |
| 'dot=.,semi=;,comma=,', |
| 'comma=,,semi=;,dot=.', |
| 'semi=;,comma=,,dot=.', |
| 'dot=.,comma=,,semi=;', |
| 'comma=,,dot=.,semi=;' |
| ] |
| } |
| end |
| context "Operator # with value modifiers" do |
| it_behaves_like 'expands', { |
| '{#path:6}/here' => '#/foo/b/here', |
| '{#list}' => '#red,green,blue', |
| '{#list*}' => '#red,green,blue', |
| '{#keys}' => [ |
| '#semi,;,dot,.,comma,,', |
| '#dot,.,semi,;,comma,,', |
| '#comma,,,semi,;,dot,.', |
| '#semi,;,comma,,,dot,.', |
| '#dot,.,comma,,,semi,;', |
| '#comma,,,dot,.,semi,;' |
| ], |
| '{#keys*}' => [ |
| '#semi=;,dot=.,comma=,', |
| '#dot=.,semi=;,comma=,', |
| '#comma=,,semi=;,dot=.', |
| '#semi=;,comma=,,dot=.', |
| '#dot=.,comma=,,semi=;', |
| '#comma=,,dot=.,semi=;' |
| ] |
| } |
| end |
| context "Operator . with value modifiers" do |
| it_behaves_like 'expands', { |
| 'X{.var:3}' => 'X.val', |
| 'X{.list}' => 'X.red,green,blue', |
| 'X{.list*}' => 'X.red.green.blue', |
| 'X{.keys}' => [ |
| 'X.semi,%3B,dot,.,comma,%2C', |
| 'X.dot,.,semi,%3B,comma,%2C', |
| 'X.comma,%2C,semi,%3B,dot,.', |
| 'X.semi,%3B,comma,%2C,dot,.', |
| 'X.dot,.,comma,%2C,semi,%3B', |
| 'X.comma,%2C,dot,.,semi,%3B' |
| ], |
| 'X{.keys*}' => [ |
| 'X.semi=%3B.dot=..comma=%2C', |
| 'X.dot=..semi=%3B.comma=%2C', |
| 'X.comma=%2C.semi=%3B.dot=.', |
| 'X.semi=%3B.comma=%2C.dot=.', |
| 'X.dot=..comma=%2C.semi=%3B', |
| 'X.comma=%2C.dot=..semi=%3B' |
| ] |
| } |
| end |
| context "Operator / with value modifiers" do |
| it_behaves_like 'expands', { |
| '{/var:1,var}' => '/v/value', |
| '{/list}' => '/red,green,blue', |
| '{/list*}' => '/red/green/blue', |
| '{/list*,path:4}' => '/red/green/blue/%2Ffoo', |
| '{/keys}' => [ |
| '/semi,%3B,dot,.,comma,%2C', |
| '/dot,.,semi,%3B,comma,%2C', |
| '/comma,%2C,semi,%3B,dot,.', |
| '/semi,%3B,comma,%2C,dot,.', |
| '/dot,.,comma,%2C,semi,%3B', |
| '/comma,%2C,dot,.,semi,%3B' |
| ], |
| '{/keys*}' => [ |
| '/semi=%3B/dot=./comma=%2C', |
| '/dot=./semi=%3B/comma=%2C', |
| '/comma=%2C/semi=%3B/dot=.', |
| '/semi=%3B/comma=%2C/dot=.', |
| '/dot=./comma=%2C/semi=%3B', |
| '/comma=%2C/dot=./semi=%3B' |
| ] |
| } |
| end |
| context "Operator ; with value modifiers" do |
| it_behaves_like 'expands', { |
| '{;hello:5}' => ';hello=Hello', |
| '{;list}' => ';list=red,green,blue', |
| '{;list*}' => ';list=red;list=green;list=blue', |
| '{;keys}' => [ |
| ';keys=semi,%3B,dot,.,comma,%2C', |
| ';keys=dot,.,semi,%3B,comma,%2C', |
| ';keys=comma,%2C,semi,%3B,dot,.', |
| ';keys=semi,%3B,comma,%2C,dot,.', |
| ';keys=dot,.,comma,%2C,semi,%3B', |
| ';keys=comma,%2C,dot,.,semi,%3B' |
| ], |
| '{;keys*}' => [ |
| ';semi=%3B;dot=.;comma=%2C', |
| ';dot=.;semi=%3B;comma=%2C', |
| ';comma=%2C;semi=%3B;dot=.', |
| ';semi=%3B;comma=%2C;dot=.', |
| ';dot=.;comma=%2C;semi=%3B', |
| ';comma=%2C;dot=.;semi=%3B' |
| ] |
| } |
| end |
| context "Operator ? with value modifiers" do |
| it_behaves_like 'expands', { |
| '{?var:3}' => '?var=val', |
| '{?list}' => '?list=red,green,blue', |
| '{?list*}' => '?list=red&list=green&list=blue', |
| '{?keys}' => [ |
| '?keys=semi,%3B,dot,.,comma,%2C', |
| '?keys=dot,.,semi,%3B,comma,%2C', |
| '?keys=comma,%2C,semi,%3B,dot,.', |
| '?keys=semi,%3B,comma,%2C,dot,.', |
| '?keys=dot,.,comma,%2C,semi,%3B', |
| '?keys=comma,%2C,dot,.,semi,%3B' |
| ], |
| '{?keys*}' => [ |
| '?semi=%3B&dot=.&comma=%2C', |
| '?dot=.&semi=%3B&comma=%2C', |
| '?comma=%2C&semi=%3B&dot=.', |
| '?semi=%3B&comma=%2C&dot=.', |
| '?dot=.&comma=%2C&semi=%3B', |
| '?comma=%2C&dot=.&semi=%3B' |
| ] |
| } |
| end |
| context "Operator & with value modifiers" do |
| it_behaves_like 'expands', { |
| '{&var:3}' => '&var=val', |
| '{&list}' => '&list=red,green,blue', |
| '{&list*}' => '&list=red&list=green&list=blue', |
| '{&keys}' => [ |
| '&keys=semi,%3B,dot,.,comma,%2C', |
| '&keys=dot,.,semi,%3B,comma,%2C', |
| '&keys=comma,%2C,semi,%3B,dot,.', |
| '&keys=semi,%3B,comma,%2C,dot,.', |
| '&keys=dot,.,comma,%2C,semi,%3B', |
| '&keys=comma,%2C,dot,.,semi,%3B' |
| ], |
| '{&keys*}' => [ |
| '&semi=%3B&dot=.&comma=%2C', |
| '&dot=.&semi=%3B&comma=%2C', |
| '&comma=%2C&semi=%3B&dot=.', |
| '&semi=%3B&comma=%2C&dot=.', |
| '&dot=.&comma=%2C&semi=%3B', |
| '&comma=%2C&dot=.&semi=%3B' |
| ] |
| } |
| end |
| end |
| describe "Modifiers" do |
| subject { |
| { |
| :var => "value", |
| :semi => ";", |
| :year => %w(1965 2000 2012), |
| :dom => %w(example com) |
| } |
| } |
| context "length" do |
| it_behaves_like 'expands', { |
| '{var:3}' => 'val', |
| '{var:30}' => 'value', |
| '{var}' => 'value', |
| '{semi}' => '%3B', |
| '{semi:2}' => '%3B' |
| } |
| end |
| context "explode" do |
| it_behaves_like 'expands', { |
| 'find{?year*}' => 'find?year=1965&year=2000&year=2012', |
| 'www{.dom*}' => 'www.example.com', |
| } |
| end |
| end |
| describe "Expansion" do |
| subject { |
| { |
| :count => ["one", "two", "three"], |
| :dom => ["example", "com"], |
| :dub => "me/too", |
| :hello => "Hello World!", |
| :half => "50%", |
| :var => "value", |
| :who => "fred", |
| :base => "http://example.com/home/", |
| :path => "/foo/bar", |
| :list => ["red", "green", "blue"], |
| :keys => {"semi" => ";","dot" => ".","comma" => ","}, |
| :v => "6", |
| :x => "1024", |
| :y => "768", |
| :empty => "", |
| :empty_keys => {}, |
| :undef => nil |
| } |
| } |
| context "concatenation" do |
| it_behaves_like 'expands', { |
| '{count}' => 'one,two,three', |
| '{count*}' => 'one,two,three', |
| '{/count}' => '/one,two,three', |
| '{/count*}' => '/one/two/three', |
| '{;count}' => ';count=one,two,three', |
| '{;count*}' => ';count=one;count=two;count=three', |
| '{?count}' => '?count=one,two,three', |
| '{?count*}' => '?count=one&count=two&count=three', |
| '{&count*}' => '&count=one&count=two&count=three' |
| } |
| end |
| context "simple expansion" do |
| it_behaves_like 'expands', { |
| '{var}' => 'value', |
| '{hello}' => 'Hello%20World%21', |
| '{half}' => '50%25', |
| 'O{empty}X' => 'OX', |
| 'O{undef}X' => 'OX', |
| '{x,y}' => '1024,768', |
| '{x,hello,y}' => '1024,Hello%20World%21,768', |
| '?{x,empty}' => '?1024,', |
| '?{x,undef}' => '?1024', |
| '?{undef,y}' => '?768', |
| '{var:3}' => 'val', |
| '{var:30}' => 'value', |
| '{list}' => 'red,green,blue', |
| '{list*}' => 'red,green,blue', |
| '{keys}' => [ |
| 'semi,%3B,dot,.,comma,%2C', |
| 'dot,.,semi,%3B,comma,%2C', |
| 'comma,%2C,semi,%3B,dot,.', |
| 'semi,%3B,comma,%2C,dot,.', |
| 'dot,.,comma,%2C,semi,%3B', |
| 'comma,%2C,dot,.,semi,%3B' |
| ], |
| '{keys*}' => [ |
| 'semi=%3B,dot=.,comma=%2C', |
| 'dot=.,semi=%3B,comma=%2C', |
| 'comma=%2C,semi=%3B,dot=.', |
| 'semi=%3B,comma=%2C,dot=.', |
| 'dot=.,comma=%2C,semi=%3B', |
| 'comma=%2C,dot=.,semi=%3B' |
| ] |
| } |
| end |
| context "reserved expansion (+)" do |
| it_behaves_like 'expands', { |
| '{+var}' => 'value', |
| '{+hello}' => 'Hello%20World!', |
| '{+half}' => '50%25', |
| '{base}index' => 'http%3A%2F%2Fexample.com%2Fhome%2Findex', |
| '{+base}index' => 'http://example.com/home/index', |
| 'O{+empty}X' => 'OX', |
| 'O{+undef}X' => 'OX', |
| '{+path}/here' => '/foo/bar/here', |
| 'here?ref={+path}' => 'here?ref=/foo/bar', |
| 'up{+path}{var}/here' => 'up/foo/barvalue/here', |
| '{+x,hello,y}' => '1024,Hello%20World!,768', |
| '{+path,x}/here' => '/foo/bar,1024/here', |
| '{+path:6}/here' => '/foo/b/here', |
| '{+list}' => 'red,green,blue', |
| '{+list*}' => 'red,green,blue', |
| '{+keys}' => [ |
| 'semi,;,dot,.,comma,,', |
| 'dot,.,semi,;,comma,,', |
| 'comma,,,semi,;,dot,.', |
| 'semi,;,comma,,,dot,.', |
| 'dot,.,comma,,,semi,;', |
| 'comma,,,dot,.,semi,;' |
| ], |
| '{+keys*}' => [ |
| 'semi=;,dot=.,comma=,', |
| 'dot=.,semi=;,comma=,', |
| 'comma=,,semi=;,dot=.', |
| 'semi=;,comma=,,dot=.', |
| 'dot=.,comma=,,semi=;', |
| 'comma=,,dot=.,semi=;' |
| ] |
| } |
| end |
| context "fragment expansion (#)" do |
| it_behaves_like 'expands', { |
| '{#var}' => '#value', |
| '{#hello}' => '#Hello%20World!', |
| '{#half}' => '#50%25', |
| 'foo{#empty}' => 'foo#', |
| 'foo{#undef}' => 'foo', |
| '{#x,hello,y}' => '#1024,Hello%20World!,768', |
| '{#path,x}/here' => '#/foo/bar,1024/here', |
| '{#path:6}/here' => '#/foo/b/here', |
| '{#list}' => '#red,green,blue', |
| '{#list*}' => '#red,green,blue', |
| '{#keys}' => [ |
| '#semi,;,dot,.,comma,,', |
| '#dot,.,semi,;,comma,,', |
| '#comma,,,semi,;,dot,.', |
| '#semi,;,comma,,,dot,.', |
| '#dot,.,comma,,,semi,;', |
| '#comma,,,dot,.,semi,;' |
| ], |
| '{#keys*}' => [ |
| '#semi=;,dot=.,comma=,', |
| '#dot=.,semi=;,comma=,', |
| '#comma=,,semi=;,dot=.', |
| '#semi=;,comma=,,dot=.', |
| '#dot=.,comma=,,semi=;', |
| '#comma=,,dot=.,semi=;' |
| ] |
| } |
| end |
| context "label expansion (.)" do |
| it_behaves_like 'expands', { |
| '{.who}' => '.fred', |
| '{.who,who}' => '.fred.fred', |
| '{.half,who}' => '.50%25.fred', |
| 'www{.dom*}' => 'www.example.com', |
| 'X{.var}' => 'X.value', |
| 'X{.empty}' => 'X.', |
| 'X{.undef}' => 'X', |
| 'X{.var:3}' => 'X.val', |
| 'X{.list}' => 'X.red,green,blue', |
| 'X{.list*}' => 'X.red.green.blue', |
| 'X{.keys}' => [ |
| 'X.semi,%3B,dot,.,comma,%2C', |
| 'X.dot,.,semi,%3B,comma,%2C', |
| 'X.comma,%2C,semi,%3B,dot,.', |
| 'X.semi,%3B,comma,%2C,dot,.', |
| 'X.dot,.,comma,%2C,semi,%3B', |
| 'X.comma,%2C,dot,.,semi,%3B' |
| ], |
| 'X{.keys*}' => [ |
| 'X.semi=%3B.dot=..comma=%2C', |
| 'X.dot=..semi=%3B.comma=%2C', |
| 'X.comma=%2C.semi=%3B.dot=.', |
| 'X.semi=%3B.comma=%2C.dot=.', |
| 'X.dot=..comma=%2C.semi=%3B', |
| 'X.comma=%2C.dot=..semi=%3B' |
| ], |
| 'X{.empty_keys}' => 'X', |
| 'X{.empty_keys*}' => 'X' |
| } |
| end |
| context "path expansion (/)" do |
| it_behaves_like 'expands', { |
| '{/who}' => '/fred', |
| '{/who,who}' => '/fred/fred', |
| '{/half,who}' => '/50%25/fred', |
| '{/who,dub}' => '/fred/me%2Ftoo', |
| '{/var}' => '/value', |
| '{/var,empty}' => '/value/', |
| '{/var,undef}' => '/value', |
| '{/var,x}/here' => '/value/1024/here', |
| '{/var:1,var}' => '/v/value', |
| '{/list}' => '/red,green,blue', |
| '{/list*}' => '/red/green/blue', |
| '{/list*,path:4}' => '/red/green/blue/%2Ffoo', |
| '{/keys}' => [ |
| '/semi,%3B,dot,.,comma,%2C', |
| '/dot,.,semi,%3B,comma,%2C', |
| '/comma,%2C,semi,%3B,dot,.', |
| '/semi,%3B,comma,%2C,dot,.', |
| '/dot,.,comma,%2C,semi,%3B', |
| '/comma,%2C,dot,.,semi,%3B' |
| ], |
| '{/keys*}' => [ |
| '/semi=%3B/dot=./comma=%2C', |
| '/dot=./semi=%3B/comma=%2C', |
| '/comma=%2C/semi=%3B/dot=.', |
| '/semi=%3B/comma=%2C/dot=.', |
| '/dot=./comma=%2C/semi=%3B', |
| '/comma=%2C/dot=./semi=%3B' |
| ] |
| } |
| end |
| context "path-style expansion (;)" do |
| it_behaves_like 'expands', { |
| '{;who}' => ';who=fred', |
| '{;half}' => ';half=50%25', |
| '{;empty}' => ';empty', |
| '{;v,empty,who}' => ';v=6;empty;who=fred', |
| '{;v,bar,who}' => ';v=6;who=fred', |
| '{;x,y}' => ';x=1024;y=768', |
| '{;x,y,empty}' => ';x=1024;y=768;empty', |
| '{;x,y,undef}' => ';x=1024;y=768', |
| '{;hello:5}' => ';hello=Hello', |
| '{;list}' => ';list=red,green,blue', |
| '{;list*}' => ';list=red;list=green;list=blue', |
| '{;keys}' => [ |
| ';keys=semi,%3B,dot,.,comma,%2C', |
| ';keys=dot,.,semi,%3B,comma,%2C', |
| ';keys=comma,%2C,semi,%3B,dot,.', |
| ';keys=semi,%3B,comma,%2C,dot,.', |
| ';keys=dot,.,comma,%2C,semi,%3B', |
| ';keys=comma,%2C,dot,.,semi,%3B' |
| ], |
| '{;keys*}' => [ |
| ';semi=%3B;dot=.;comma=%2C', |
| ';dot=.;semi=%3B;comma=%2C', |
| ';comma=%2C;semi=%3B;dot=.', |
| ';semi=%3B;comma=%2C;dot=.', |
| ';dot=.;comma=%2C;semi=%3B', |
| ';comma=%2C;dot=.;semi=%3B' |
| ] |
| } |
| end |
| context "form query expansion (?)" do |
| it_behaves_like 'expands', { |
| '{?who}' => '?who=fred', |
| '{?half}' => '?half=50%25', |
| '{?x,y}' => '?x=1024&y=768', |
| '{?x,y,empty}' => '?x=1024&y=768&empty=', |
| '{?x,y,undef}' => '?x=1024&y=768', |
| '{?var:3}' => '?var=val', |
| '{?list}' => '?list=red,green,blue', |
| '{?list*}' => '?list=red&list=green&list=blue', |
| '{?keys}' => [ |
| '?keys=semi,%3B,dot,.,comma,%2C', |
| '?keys=dot,.,semi,%3B,comma,%2C', |
| '?keys=comma,%2C,semi,%3B,dot,.', |
| '?keys=semi,%3B,comma,%2C,dot,.', |
| '?keys=dot,.,comma,%2C,semi,%3B', |
| '?keys=comma,%2C,dot,.,semi,%3B' |
| ], |
| '{?keys*}' => [ |
| '?semi=%3B&dot=.&comma=%2C', |
| '?dot=.&semi=%3B&comma=%2C', |
| '?comma=%2C&semi=%3B&dot=.', |
| '?semi=%3B&comma=%2C&dot=.', |
| '?dot=.&comma=%2C&semi=%3B', |
| '?comma=%2C&dot=.&semi=%3B' |
| ] |
| } |
| end |
| context "form query expansion (&)" do |
| it_behaves_like 'expands', { |
| '{&who}' => '&who=fred', |
| '{&half}' => '&half=50%25', |
| '?fixed=yes{&x}' => '?fixed=yes&x=1024', |
| '{&x,y,empty}' => '&x=1024&y=768&empty=', |
| '{&x,y,undef}' => '&x=1024&y=768', |
| '{&var:3}' => '&var=val', |
| '{&list}' => '&list=red,green,blue', |
| '{&list*}' => '&list=red&list=green&list=blue', |
| '{&keys}' => [ |
| '&keys=semi,%3B,dot,.,comma,%2C', |
| '&keys=dot,.,semi,%3B,comma,%2C', |
| '&keys=comma,%2C,semi,%3B,dot,.', |
| '&keys=semi,%3B,comma,%2C,dot,.', |
| '&keys=dot,.,comma,%2C,semi,%3B', |
| '&keys=comma,%2C,dot,.,semi,%3B' |
| ], |
| '{&keys*}' => [ |
| '&semi=%3B&dot=.&comma=%2C', |
| '&dot=.&semi=%3B&comma=%2C', |
| '&comma=%2C&semi=%3B&dot=.', |
| '&semi=%3B&comma=%2C&dot=.', |
| '&dot=.&comma=%2C&semi=%3B', |
| '&comma=%2C&dot=.&semi=%3B' |
| ] |
| } |
| end |
| context "non-string key in match data" do |
| subject {Addressable::Template.new("http://example.com/{one}")} |
| |
| it "raises TypeError" do |
| expect { subject.expand(Object.new => "1") }.to raise_error TypeError |
| end |
| end |
| end |
| |
| class ExampleTwoProcessor |
| def self.restore(name, value) |
| return value.gsub(/-/, " ") if name == "query" |
| return value |
| end |
| |
| def self.match(name) |
| return ".*?" if name == "first" |
| return ".*" |
| end |
| def self.validate(name, value) |
| return !!(value =~ /^[\w ]+$/) if name == "query" |
| return true |
| end |
| |
| def self.transform(name, value) |
| return value.gsub(/ /, "+") if name == "query" |
| return value |
| end |
| end |
| |
| class DumbProcessor |
| def self.match(name) |
| return ".*?" if name == "first" |
| end |
| end |
| |
| describe Addressable::Template do |
| describe 'initialize' do |
| context 'with a non-string' do |
| it 'raises a TypeError' do |
| expect { Addressable::Template.new(nil) }.to raise_error(TypeError) |
| end |
| end |
| end |
| |
| describe 'freeze' do |
| subject { Addressable::Template.new("http://example.com/{first}/{+second}/") } |
| it 'freezes the template' do |
| expect(subject.freeze).to be_frozen |
| end |
| end |
| |
| describe "Matching" do |
| let(:uri){ |
| Addressable::URI.parse( |
| "http://example.com/search/an-example-search-query/" |
| ) |
| } |
| let(:uri2){ |
| Addressable::URI.parse("http://example.com/a/b/c/") |
| } |
| let(:uri3){ |
| Addressable::URI.parse("http://example.com/;a=1;b=2;c=3;first=foo") |
| } |
| let(:uri4){ |
| Addressable::URI.parse("http://example.com/?a=1&b=2&c=3&first=foo") |
| } |
| let(:uri5){ |
| "http://example.com/foo" |
| } |
| context "first uri with ExampleTwoProcessor" do |
| subject { |
| Addressable::Template.new( |
| "http://example.com/search/{query}/" |
| ).match(uri, ExampleTwoProcessor) |
| } |
| its(:variables){ should == ["query"] } |
| its(:captures){ should == ["an example search query"] } |
| end |
| |
| context "second uri with ExampleTwoProcessor" do |
| subject { |
| Addressable::Template.new( |
| "http://example.com/{first}/{+second}/" |
| ).match(uri2, ExampleTwoProcessor) |
| } |
| its(:variables){ should == ["first", "second"] } |
| its(:captures){ should == ["a", "b/c"] } |
| end |
| |
| context "second uri with DumbProcessor" do |
| subject { |
| Addressable::Template.new( |
| "http://example.com/{first}/{+second}/" |
| ).match(uri2, DumbProcessor) |
| } |
| its(:variables){ should == ["first", "second"] } |
| its(:captures){ should == ["a", "b/c"] } |
| end |
| |
| context "second uri" do |
| subject { |
| Addressable::Template.new( |
| "http://example.com/{first}{/second*}/" |
| ).match(uri2) |
| } |
| its(:variables){ should == ["first", "second"] } |
| its(:captures){ should == ["a", ["b","c"]] } |
| end |
| context "third uri" do |
| subject { |
| Addressable::Template.new( |
| "http://example.com/{;hash*,first}" |
| ).match(uri3) |
| } |
| its(:variables){ should == ["hash", "first"] } |
| its(:captures){ should == [ |
| {"a" => "1", "b" => "2", "c" => "3", "first" => "foo"}, nil] } |
| end |
| # Note that this expansion is impossible to revert deterministically - the |
| # * operator means first could have been a key of hash or a separate key. |
| # Semantically, a separate key is more likely, but both are possible. |
| context "fourth uri" do |
| subject { |
| Addressable::Template.new( |
| "http://example.com/{?hash*,first}" |
| ).match(uri4) |
| } |
| its(:variables){ should == ["hash", "first"] } |
| its(:captures){ should == [ |
| {"a" => "1", "b" => "2", "c" => "3", "first"=> "foo"}, nil] } |
| end |
| context "fifth uri" do |
| subject { |
| Addressable::Template.new( |
| "http://example.com/{path}{?hash*,first}" |
| ).match(uri5) |
| } |
| its(:variables){ should == ["path", "hash", "first"] } |
| its(:captures){ should == ["foo", nil, nil] } |
| end |
| end |
| |
| describe 'match' do |
| subject { Addressable::Template.new('http://example.com/first/second/') } |
| context 'when the URI is the same as the template' do |
| it 'returns the match data itself with an empty mapping' do |
| uri = Addressable::URI.parse('http://example.com/first/second/') |
| match_data = subject.match(uri) |
| expect(match_data).to be_an Addressable::Template::MatchData |
| expect(match_data.uri).to eq(uri) |
| expect(match_data.template).to eq(subject) |
| expect(match_data.mapping).to be_empty |
| expect(match_data.inspect).to be_an String |
| end |
| end |
| end |
| |
| describe "extract" do |
| let(:template) { |
| Addressable::Template.new( |
| "http://{host}{/segments*}/{?one,two,bogus}{#fragment}" |
| ) |
| } |
| let(:uri){ "http://example.com/a/b/c/?one=1&two=2#foo" } |
| let(:uri2){ "http://example.com/a/b/c/#foo" } |
| it "should be able to extract with queries" do |
| expect(template.extract(uri)).to eq({ |
| "host" => "example.com", |
| "segments" => %w(a b c), |
| "one" => "1", |
| "bogus" => nil, |
| "two" => "2", |
| "fragment" => "foo" |
| }) |
| end |
| it "should be able to extract without queries" do |
| expect(template.extract(uri2)).to eq({ |
| "host" => "example.com", |
| "segments" => %w(a b c), |
| "one" => nil, |
| "bogus" => nil, |
| "two" => nil, |
| "fragment" => "foo" |
| }) |
| end |
| |
| context "issue #137" do |
| subject { Addressable::Template.new('/path{?page,per_page}') } |
| |
| it "can match empty" do |
| data = subject.extract("/path") |
| expect(data["page"]).to eq(nil) |
| expect(data["per_page"]).to eq(nil) |
| expect(data.keys.sort).to eq(['page', 'per_page']) |
| end |
| |
| it "can match first var" do |
| data = subject.extract("/path?page=1") |
| expect(data["page"]).to eq("1") |
| expect(data["per_page"]).to eq(nil) |
| expect(data.keys.sort).to eq(['page', 'per_page']) |
| end |
| |
| it "can match second var" do |
| data = subject.extract("/path?per_page=1") |
| expect(data["page"]).to eq(nil) |
| expect(data["per_page"]).to eq("1") |
| expect(data.keys.sort).to eq(['page', 'per_page']) |
| end |
| |
| it "can match both vars" do |
| data = subject.extract("/path?page=2&per_page=1") |
| expect(data["page"]).to eq("2") |
| expect(data["per_page"]).to eq("1") |
| expect(data.keys.sort).to eq(['page', 'per_page']) |
| end |
| end |
| end |
| |
| describe "Partial expand with symbols" do |
| context "partial_expand with two simple values" do |
| subject { |
| Addressable::Template.new("http://example.com/{one}/{two}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand(:one => "1").pattern).to eq( |
| "http://example.com/1/{two}/" |
| ) |
| end |
| end |
| context "partial_expand query with missing param in middle" do |
| subject { |
| Addressable::Template.new("http://example.com/{?one,two,three}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand(:one => "1", :three => "3").pattern).to eq( |
| "http://example.com/?one=1{&two}&three=3/" |
| ) |
| end |
| end |
| context "partial_expand form style query with missing param at beginning" do |
| subject { |
| Addressable::Template.new("http://example.com/{?one,two}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand(:two => "2").pattern).to eq( |
| "http://example.com/?two=2{&one}/" |
| ) |
| end |
| end |
| context "partial_expand with query string" do |
| subject { |
| Addressable::Template.new("http://example.com/{?two,one}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand(:one => "1").pattern).to eq( |
| "http://example.com/?one=1{&two}/" |
| ) |
| end |
| end |
| context "partial_expand with path operator" do |
| subject { |
| Addressable::Template.new("http://example.com{/one,two}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand(:one => "1").pattern).to eq( |
| "http://example.com/1{/two}/" |
| ) |
| end |
| end |
| context "partial expand with unicode values" do |
| subject do |
| Addressable::Template.new("http://example.com/{resource}/{query}/") |
| end |
| it "normalizes unicode by default" do |
| template = subject.partial_expand("query" => "Cafe\u0301") |
| expect(template.pattern).to eq( |
| "http://example.com/{resource}/Caf%C3%A9/" |
| ) |
| end |
| |
| it "does not normalize unicode when byte semantics requested" do |
| template = subject.partial_expand({"query" => "Cafe\u0301"}, nil, false) |
| expect(template.pattern).to eq( |
| "http://example.com/{resource}/Cafe%CC%81/" |
| ) |
| end |
| end |
| end |
| describe "Partial expand with strings" do |
| context "partial_expand with two simple values" do |
| subject { |
| Addressable::Template.new("http://example.com/{one}/{two}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand("one" => "1").pattern).to eq( |
| "http://example.com/1/{two}/" |
| ) |
| end |
| end |
| context "partial_expand query with missing param in middle" do |
| subject { |
| Addressable::Template.new("http://example.com/{?one,two,three}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq( |
| "http://example.com/?one=1{&two}&three=3/" |
| ) |
| end |
| end |
| context "partial_expand with query string" do |
| subject { |
| Addressable::Template.new("http://example.com/{?two,one}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand("one" => "1").pattern).to eq( |
| "http://example.com/?one=1{&two}/" |
| ) |
| end |
| end |
| context "partial_expand with path operator" do |
| subject { |
| Addressable::Template.new("http://example.com{/one,two}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand("one" => "1").pattern).to eq( |
| "http://example.com/1{/two}/" |
| ) |
| end |
| end |
| end |
| describe "Expand" do |
| context "expand with unicode values" do |
| subject do |
| Addressable::Template.new("http://example.com/search/{query}/") |
| end |
| it "normalizes unicode by default" do |
| uri = subject.expand("query" => "Cafe\u0301").to_str |
| expect(uri).to eq("http://example.com/search/Caf%C3%A9/") |
| end |
| |
| it "does not normalize unicode when byte semantics requested" do |
| uri = subject.expand({ "query" => "Cafe\u0301" }, nil, false).to_str |
| expect(uri).to eq("http://example.com/search/Cafe%CC%81/") |
| end |
| end |
| context "expand with a processor" do |
| subject { |
| Addressable::Template.new("http://example.com/search/{query}/") |
| } |
| it "processes spaces" do |
| expect(subject.expand({"query" => "an example search query"}, |
| ExampleTwoProcessor).to_str).to eq( |
| "http://example.com/search/an+example+search+query/" |
| ) |
| end |
| it "validates" do |
| expect{ |
| subject.expand({"query" => "Bogus!"}, |
| ExampleTwoProcessor).to_str |
| }.to raise_error(Addressable::Template::InvalidTemplateValueError) |
| end |
| end |
| context "partial_expand query with missing param in middle" do |
| subject { |
| Addressable::Template.new("http://example.com/{?one,two,three}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq( |
| "http://example.com/?one=1{&two}&three=3/" |
| ) |
| end |
| end |
| context "partial_expand with query string" do |
| subject { |
| Addressable::Template.new("http://example.com/{?two,one}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand("one" => "1").pattern).to eq( |
| "http://example.com/?one=1{&two}/" |
| ) |
| end |
| end |
| context "partial_expand with path operator" do |
| subject { |
| Addressable::Template.new("http://example.com{/one,two}/") |
| } |
| it "builds a new pattern" do |
| expect(subject.partial_expand("one" => "1").pattern).to eq( |
| "http://example.com/1{/two}/" |
| ) |
| end |
| end |
| end |
| context "Matching with operators" do |
| describe "Level 1:" do |
| subject { Addressable::Template.new("foo{foo}/{bar}baz") } |
| it "can match" do |
| data = subject.match("foofoo/bananabaz") |
| expect(data.mapping["foo"]).to eq("foo") |
| expect(data.mapping["bar"]).to eq("banana") |
| end |
| it "can fail" do |
| expect(subject.match("bar/foo")).to be_nil |
| expect(subject.match("foobaz")).to be_nil |
| end |
| it "can match empty" do |
| data = subject.match("foo/baz") |
| expect(data.mapping["foo"]).to eq(nil) |
| expect(data.mapping["bar"]).to eq(nil) |
| end |
| it "lists vars" do |
| expect(subject.variables).to eq(["foo", "bar"]) |
| end |
| end |
| |
| describe "Level 2:" do |
| subject { Addressable::Template.new("foo{+foo}{#bar}baz") } |
| it "can match" do |
| data = subject.match("foo/test/banana#bazbaz") |
| expect(data.mapping["foo"]).to eq("/test/banana") |
| expect(data.mapping["bar"]).to eq("baz") |
| end |
| it "can match empty level 2 #" do |
| data = subject.match("foo/test/bananabaz") |
| expect(data.mapping["foo"]).to eq("/test/banana") |
| expect(data.mapping["bar"]).to eq(nil) |
| data = subject.match("foo/test/banana#baz") |
| expect(data.mapping["foo"]).to eq("/test/banana") |
| expect(data.mapping["bar"]).to eq("") |
| end |
| it "can match empty level 2 +" do |
| data = subject.match("foobaz") |
| expect(data.mapping["foo"]).to eq(nil) |
| expect(data.mapping["bar"]).to eq(nil) |
| data = subject.match("foo#barbaz") |
| expect(data.mapping["foo"]).to eq(nil) |
| expect(data.mapping["bar"]).to eq("bar") |
| end |
| it "lists vars" do |
| expect(subject.variables).to eq(["foo", "bar"]) |
| end |
| end |
| |
| describe "Level 3:" do |
| context "no operator" do |
| subject { Addressable::Template.new("foo{foo,bar}baz") } |
| it "can match" do |
| data = subject.match("foofoo,barbaz") |
| expect(data.mapping["foo"]).to eq("foo") |
| expect(data.mapping["bar"]).to eq("bar") |
| end |
| it "lists vars" do |
| expect(subject.variables).to eq(["foo", "bar"]) |
| end |
| end |
| context "+ operator" do |
| subject { Addressable::Template.new("foo{+foo,bar}baz") } |
| it "can match" do |
| data = subject.match("foofoo/bar,barbaz") |
| expect(data.mapping["bar"]).to eq("foo/bar,bar") |
| expect(data.mapping["foo"]).to eq("") |
| end |
| it "lists vars" do |
| expect(subject.variables).to eq(["foo", "bar"]) |
| end |
| end |
| context ". operator" do |
| subject { Addressable::Template.new("foo{.foo,bar}baz") } |
| it "can match" do |
| data = subject.match("foo.foo.barbaz") |
| expect(data.mapping["foo"]).to eq("foo") |
| expect(data.mapping["bar"]).to eq("bar") |
| end |
| it "lists vars" do |
| expect(subject.variables).to eq(["foo", "bar"]) |
| end |
| end |
| context "/ operator" do |
| subject { Addressable::Template.new("foo{/foo,bar}baz") } |
| it "can match" do |
| data = subject.match("foo/foo/barbaz") |
| expect(data.mapping["foo"]).to eq("foo") |
| expect(data.mapping["bar"]).to eq("bar") |
| end |
| it "lists vars" do |
| expect(subject.variables).to eq(["foo", "bar"]) |
| end |
| end |
| context "; operator" do |
| subject { Addressable::Template.new("foo{;foo,bar,baz}baz") } |
| it "can match" do |
| data = subject.match("foo;foo=bar%20baz;bar=foo;bazbaz") |
| expect(data.mapping["foo"]).to eq("bar baz") |
| expect(data.mapping["bar"]).to eq("foo") |
| expect(data.mapping["baz"]).to eq("") |
| end |
| it "lists vars" do |
| expect(subject.variables).to eq(%w(foo bar baz)) |
| end |
| end |
| context "? operator" do |
| context "test" do |
| subject { Addressable::Template.new("foo{?foo,bar}baz") } |
| it "can match" do |
| data = subject.match("foo?foo=bar%20baz&bar=foobaz") |
| expect(data.mapping["foo"]).to eq("bar baz") |
| expect(data.mapping["bar"]).to eq("foo") |
| end |
| it "lists vars" do |
| expect(subject.variables).to eq(%w(foo bar)) |
| end |
| end |
| |
| context "issue #137" do |
| subject { Addressable::Template.new('/path{?page,per_page}') } |
| |
| it "can match empty" do |
| data = subject.match("/path") |
| expect(data.mapping["page"]).to eq(nil) |
| expect(data.mapping["per_page"]).to eq(nil) |
| expect(data.mapping.keys.sort).to eq(['page', 'per_page']) |
| end |
| |
| it "can match first var" do |
| data = subject.match("/path?page=1") |
| expect(data.mapping["page"]).to eq("1") |
| expect(data.mapping["per_page"]).to eq(nil) |
| expect(data.mapping.keys.sort).to eq(['page', 'per_page']) |
| end |
| |
| it "can match second var" do |
| data = subject.match("/path?per_page=1") |
| expect(data.mapping["page"]).to eq(nil) |
| expect(data.mapping["per_page"]).to eq("1") |
| expect(data.mapping.keys.sort).to eq(['page', 'per_page']) |
| end |
| |
| it "can match both vars" do |
| data = subject.match("/path?page=2&per_page=1") |
| expect(data.mapping["page"]).to eq("2") |
| expect(data.mapping["per_page"]).to eq("1") |
| expect(data.mapping.keys.sort).to eq(['page', 'per_page']) |
| end |
| end |
| |
| context "issue #71" do |
| subject { Addressable::Template.new("http://cyberscore.dev/api/users{?username}") } |
| it "can match" do |
| data = subject.match("http://cyberscore.dev/api/users?username=foobaz") |
| expect(data.mapping["username"]).to eq("foobaz") |
| end |
| it "lists vars" do |
| expect(subject.variables).to eq(%w(username)) |
| expect(subject.keys).to eq(%w(username)) |
| end |
| end |
| end |
| context "& operator" do |
| subject { Addressable::Template.new("foo{&foo,bar}baz") } |
| it "can match" do |
| data = subject.match("foo&foo=bar%20baz&bar=foobaz") |
| expect(data.mapping["foo"]).to eq("bar baz") |
| expect(data.mapping["bar"]).to eq("foo") |
| end |
| it "lists vars" do |
| expect(subject.variables).to eq(%w(foo bar)) |
| end |
| end |
| end |
| end |
| |
| context "support regexes:" do |
| context "EXPRESSION" do |
| subject { Addressable::Template::EXPRESSION } |
| it "should be able to match an expression" do |
| expect(subject).to match("{foo}") |
| expect(subject).to match("{foo,9}") |
| expect(subject).to match("{foo.bar,baz}") |
| expect(subject).to match("{+foo.bar,baz}") |
| expect(subject).to match("{foo,foo%20bar}") |
| expect(subject).to match("{#foo:20,baz*}") |
| expect(subject).to match("stuff{#foo:20,baz*}things") |
| end |
| it "should fail on non vars" do |
| expect(subject).not_to match("!{foo") |
| expect(subject).not_to match("{foo.bar.}") |
| expect(subject).not_to match("!{}") |
| end |
| end |
| context "VARNAME" do |
| subject { Addressable::Template::VARNAME } |
| it "should be able to match a variable" do |
| expect(subject).to match("foo") |
| expect(subject).to match("9") |
| expect(subject).to match("foo.bar") |
| expect(subject).to match("foo_bar") |
| expect(subject).to match("foo_bar.baz") |
| expect(subject).to match("foo%20bar") |
| expect(subject).to match("foo%20bar.baz") |
| end |
| it "should fail on non vars" do |
| expect(subject).not_to match("!foo") |
| expect(subject).not_to match("foo.bar.") |
| expect(subject).not_to match("foo%2%00bar") |
| expect(subject).not_to match("foo_ba%r") |
| expect(subject).not_to match("foo_bar*") |
| expect(subject).not_to match("foo_bar:20") |
| end |
| end |
| context "VARIABLE_LIST" do |
| subject { Addressable::Template::VARIABLE_LIST } |
| it "should be able to match a variable list" do |
| expect(subject).to match("foo,bar") |
| expect(subject).to match("foo") |
| expect(subject).to match("foo,bar*,baz") |
| expect(subject).to match("foo.bar,bar_baz*,baz:12") |
| end |
| it "should fail on non vars" do |
| expect(subject).not_to match(",foo,bar*,baz") |
| expect(subject).not_to match("foo,*bar,baz") |
| expect(subject).not_to match("foo,,bar*,baz") |
| end |
| end |
| context "VARSPEC" do |
| subject { Addressable::Template::VARSPEC } |
| it "should be able to match a variable with modifier" do |
| expect(subject).to match("9:8") |
| expect(subject).to match("foo.bar*") |
| expect(subject).to match("foo_bar:12") |
| expect(subject).to match("foo_bar.baz*") |
| expect(subject).to match("foo%20bar:12") |
| expect(subject).to match("foo%20bar.baz*") |
| end |
| it "should fail on non vars" do |
| expect(subject).not_to match("!foo") |
| expect(subject).not_to match("*foo") |
| expect(subject).not_to match("fo*o") |
| expect(subject).not_to match("fo:o") |
| expect(subject).not_to match("foo:") |
| end |
| end |
| end |
| end |
| |
| describe Addressable::Template::MatchData do |
| let(:template) { Addressable::Template.new('{foo}/{bar}') } |
| subject(:its) { template.match('ab/cd') } |
| its(:uri) { should == Addressable::URI.parse('ab/cd') } |
| its(:template) { should == template } |
| its(:mapping) { should == { 'foo' => 'ab', 'bar' => 'cd' } } |
| its(:variables) { should == ['foo', 'bar'] } |
| its(:keys) { should == ['foo', 'bar'] } |
| its(:names) { should == ['foo', 'bar'] } |
| its(:values) { should == ['ab', 'cd'] } |
| its(:captures) { should == ['ab', 'cd'] } |
| its(:to_a) { should == ['ab/cd', 'ab', 'cd'] } |
| its(:to_s) { should == 'ab/cd' } |
| its(:string) { should == its.to_s } |
| its(:pre_match) { should == "" } |
| its(:post_match) { should == "" } |
| |
| describe 'values_at' do |
| it 'returns an array with the values' do |
| expect(its.values_at(0, 2)).to eq(['ab/cd', 'cd']) |
| end |
| it 'allows mixing integer an string keys' do |
| expect(its.values_at('foo', 1)).to eq(['ab', 'ab']) |
| end |
| it 'accepts unknown keys' do |
| expect(its.values_at('baz', 'foo')).to eq([nil, 'ab']) |
| end |
| end |
| |
| describe '[]' do |
| context 'string key' do |
| it 'returns the corresponding capture' do |
| expect(its['foo']).to eq('ab') |
| expect(its['bar']).to eq('cd') |
| end |
| it 'returns nil for unknown keys' do |
| expect(its['baz']).to be_nil |
| end |
| end |
| context 'symbol key' do |
| it 'returns the corresponding capture' do |
| expect(its[:foo]).to eq('ab') |
| expect(its[:bar]).to eq('cd') |
| end |
| it 'returns nil for unknown keys' do |
| expect(its[:baz]).to be_nil |
| end |
| end |
| context 'integer key' do |
| it 'returns the full URI for index 0' do |
| expect(its[0]).to eq('ab/cd') |
| end |
| it 'returns the corresponding capture' do |
| expect(its[1]).to eq('ab') |
| expect(its[2]).to eq('cd') |
| end |
| it 'returns nil for unknown keys' do |
| expect(its[3]).to be_nil |
| end |
| end |
| context 'other key' do |
| it 'raises an exception' do |
| expect { its[Object.new] }.to raise_error(TypeError) |
| end |
| end |
| context 'with length' do |
| it 'returns an array starting at index with given length' do |
| expect(its[0, 2]).to eq(['ab/cd', 'ab']) |
| expect(its[2, 1]).to eq(['cd']) |
| end |
| end |
| end |
| end |