earl 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Earl
2
2
 
3
- TODO: Write a gem description
3
+ What URI wishes it could look like.
4
4
 
5
5
  ## Installation
6
6
 
@@ -18,7 +18,19 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- TODO: Write usage instructions here
21
+ ``` rb
22
+ url = Earl::URL.new 'http://www.foo.com'
23
+
24
+ url.scheme # => 'http'
25
+ url.scheme? # => true
26
+
27
+ url.subdomain # => 'www'
28
+ url.subdomain.www? # => true
29
+ url.subdomain.baz? # => false
30
+
31
+ url.host = 'foo.edu'
32
+ url.to_s # => 'http://www.foo.edu'
33
+ ```
22
34
 
23
35
  ## Contributing
24
36
 
@@ -15,5 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Earl::VERSION
17
17
 
18
+ gem.add_dependency 'treetop', '>= 1.4.10'
19
+
18
20
  gem.add_development_dependency 'rspec', '>= 2.9.0'
19
21
  end
@@ -1,7 +1,11 @@
1
+ require 'treetop'
1
2
  require 'earl/version'
3
+ require 'earl/url_parser'
2
4
 
3
5
  module Earl
4
6
  autoload :URL, 'earl/url'
7
+ autoload :UrlAssembler, 'earl/url_assembler'
8
+ autoload :HashInquirer, 'earl/hash_inquirer'
5
9
  autoload :StringInquirer, 'earl/string_inquirer'
6
10
 
7
11
  class << self
@@ -9,4 +13,7 @@ module Earl
9
13
  Earl::URL.new string
10
14
  end
11
15
  end
16
+
17
+ class EarlError < StandardError; end
18
+ class InvalidURLError < EarlError; end
12
19
  end
@@ -0,0 +1,16 @@
1
+ module Earl
2
+ class HashInquirer < ::Hash
3
+ def initialize( hash, &block )
4
+ merge! hash
5
+ super block
6
+ end
7
+
8
+ def method_missing( meth, *args, &block )
9
+ if meth.to_s[ -1 ] == '?'
10
+ self[ meth.to_s[ 0..-2 ].to_sym ] || false
11
+ else
12
+ super
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,78 +1,35 @@
1
- require 'uri'
2
-
3
1
  module Earl
4
- class URL
2
+ class URL < HashInquirer
5
3
  def initialize( source )
6
- @uri = URI.parse( source )
7
- end
8
-
9
- def scheme
10
- StringInquirer.new( parts[ :scheme ] ) rescue nil
11
- end
12
- def scheme=( value )
13
- parts[ :scheme ] = value
14
- end
15
- def scheme?
16
- !( parts[ :scheme ].nil? || parts[ :scheme ].empty? )
4
+ super parser.parse( source ).resolve rescue raise InvalidURLError
17
5
  end
18
6
 
19
- def subdomain
20
- StringInquirer.new( parts[ :subdomain ] ) rescue nil
21
- end
22
- def subdomain=( value )
23
- parts[ :subdomain ] = value
24
- end
25
- def subdomain?
26
- !( parts[ :subdomain ].nil? || parts[ :subdomain ].empty? )
27
- end
28
-
29
- def host
30
- StringInquirer.new( [ domain, top_level_domain ].compact.join( '.' ) ) rescue nil
31
- end
32
- def host=( value )
33
- parts[ :domain ], parts[ :top_level_domain ] = value.split( '.' )
34
- end
35
- def host?
36
- [ domain, top_level_domain ].compact.any?
7
+ %w| scheme subdomain host port path search |.each do |part|
8
+ define_method :"#{part}" do
9
+ if self[ part.to_sym ].is_a? String
10
+ StringInquirer.new self[ part.to_sym ]
11
+ else
12
+ self[ part.to_sym ]
13
+ end
14
+ end
15
+ define_method :"#{part}=" do |value|
16
+ raise InvalidURLError if part.to_sym == :host && value == nil
17
+ self[ part.to_sym ] = value
18
+ end
37
19
  end
38
20
 
39
21
  def to_s
40
- out = ''
41
- out << "#{scheme}://" if scheme?
42
- out << [ subdomain, domain, top_level_domain ].compact.join( '.' )
22
+ assembler.assemble self
43
23
  end
44
24
 
45
25
  protected
46
26
 
47
- def domain
48
- StringInquirer.new( parts[ :domain ] ) rescue nil
49
- end
50
- def top_level_domain
51
- StringInquirer.new( parts[ :top_level_domain ] ) rescue nil
27
+ def parser
28
+ @parser ||= UrlParser.new
52
29
  end
53
30
 
54
- def parts
55
- @parts ||= begin
56
- { }.tap do |hsh|
57
- hsh[ :scheme ] = @uri.scheme
58
- hsh[ :subdomain ], hsh[ :domain ], hsh[ :top_level_domain ] = domain_parts
59
- end
60
- end
61
- end
62
-
63
- def domain_parts
64
- parts = host_parts || [ nil ]
65
- parts << nil if parts.size == 1
66
- parts.unshift nil if parts.size == 2
67
- parts
68
- end
69
-
70
- def host_parts
71
- if @uri.host
72
- @uri.host.split '.'
73
- elsif @uri.path
74
- @uri.path.split '.'
75
- end
31
+ def assembler
32
+ @assembler ||= UrlAssembler.new
76
33
  end
77
34
  end
78
35
  end
@@ -0,0 +1,16 @@
1
+ module Earl
2
+ class UrlAssembler
3
+
4
+ def assemble( parts={} )
5
+ ''.tap do |url|
6
+ url << ( parts[ :scheme ] + '://' ) if parts[ :scheme ]
7
+ url << ( parts[ :subdomain ] + '.' ) if parts[ :subdomain ]
8
+ url << ( parts[ :host ] ) if parts[ :host ]
9
+ url << ( ':' + parts[ :port ] ) if parts[ :port ]
10
+ url << ( '/' + parts[ :path ] ) if parts[ :path ]
11
+ url << ( '?' + parts[ :search ] ) if parts[ :search ]
12
+ url
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,163 @@
1
+ module Earl
2
+ grammar Url
3
+
4
+ rule program
5
+ whitespace v:( url ) whitespace {
6
+ def resolve
7
+ { }.merge v.resolve
8
+ end
9
+ }
10
+ end
11
+
12
+ rule whitespace
13
+ [\s]*
14
+ end
15
+
16
+ rule url
17
+ scheme host port path search {
18
+ def resolve
19
+ scheme.resolve.merge port.resolve.merge host.resolve.merge path.resolve.merge search.resolve
20
+ end
21
+ }
22
+ /
23
+ scheme host port path {
24
+ def resolve
25
+ scheme.resolve.merge port.resolve.merge host.resolve.merge path.resolve
26
+ end
27
+ }
28
+ /
29
+ scheme host port search {
30
+ def resolve
31
+ scheme.resolve.merge port.resolve.merge host.resolve.merge search.resolve
32
+ end
33
+ }
34
+ /
35
+ scheme host port {
36
+ def resolve
37
+ scheme.resolve.merge port.resolve.merge host.resolve
38
+ end
39
+ }
40
+ /
41
+ scheme host path search {
42
+ def resolve
43
+ scheme.resolve.merge host.resolve.merge path.resolve.merge search.resolve
44
+ end
45
+ }
46
+ /
47
+ scheme host path {
48
+ def resolve
49
+ scheme.resolve.merge host.resolve.merge path.resolve
50
+ end
51
+ }
52
+ /
53
+ scheme host search {
54
+ def resolve
55
+ scheme.resolve.merge host.resolve.merge search.resolve
56
+ end
57
+ }
58
+ /
59
+ scheme host {
60
+ def resolve
61
+ scheme.resolve.merge host.resolve
62
+ end
63
+ }
64
+ /
65
+ host port path search {
66
+ def resolve
67
+ port.resolve.merge host.resolve.merge path.resolve.merge search.resolve
68
+ end
69
+ }
70
+ /
71
+ host port path {
72
+ def resolve
73
+ port.resolve.merge host.resolve.merge path.resolve
74
+ end
75
+ }
76
+ /
77
+ host port search {
78
+ def resolve
79
+ port.resolve.merge host.resolve.merge search.resolve
80
+ end
81
+ }
82
+ /
83
+ host port {
84
+ def resolve
85
+ port.resolve.merge host.resolve
86
+ end
87
+ }
88
+ /
89
+ host path {
90
+ def resolve
91
+ host.resolve.merge path.resolve
92
+ end
93
+ }
94
+ /
95
+ host search {
96
+ def resolve
97
+ host.resolve.merge search.resolve
98
+ end
99
+ }
100
+ /
101
+ host
102
+ end
103
+
104
+ rule scheme
105
+ characters '://' {
106
+ def resolve
107
+ { :scheme => characters.text_value }
108
+ end
109
+ }
110
+ end
111
+
112
+ rule host
113
+ subdomain:characters '.' domain:characters '.' tld:characters {
114
+ def resolve
115
+ {
116
+ :subdomain => subdomain.text_value,
117
+ :host => "#{domain.text_value}.#{tld.text_value}"
118
+ }
119
+ end
120
+ }
121
+ /
122
+ domain:characters '.' tld:characters {
123
+ def resolve
124
+ { :host => text_value }
125
+ end
126
+ }
127
+ /
128
+ characters {
129
+ def resolve
130
+ { :host => text_value }
131
+ end
132
+ }
133
+ end
134
+
135
+ rule port
136
+ ':' port:([0-9]1..4) {
137
+ def resolve
138
+ { :port => port.text_value }
139
+ end
140
+ }
141
+ end
142
+
143
+ rule path
144
+ '/' characters {
145
+ def resolve
146
+ { :path => characters.text_value }
147
+ end
148
+ }
149
+ end
150
+
151
+ rule search
152
+ '?' search:( characters '=' characters ) {
153
+ def resolve
154
+ { :search => search.text_value }
155
+ end
156
+ }
157
+ end
158
+
159
+ rule characters
160
+ [a-zA-Z0-9]+
161
+ end
162
+ end
163
+ end
@@ -1,3 +1,3 @@
1
1
  module Earl
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Earl::HashInquirer do
4
+ subject { Earl::HashInquirer.new :foo => 'bar', :baz => 123, :woo => false }
5
+
6
+ it { should be_a( Hash ) }
7
+ it { should eql( :foo => 'bar', :baz => 123, :woo => false ) }
8
+
9
+ describe 'string keys' do
10
+ its( :foo? ){ should be_true }
11
+ end
12
+
13
+ describe 'numeric keys' do
14
+ its( :baz? ){ should be_true }
15
+ end
16
+
17
+ describe 'boolean keys' do
18
+ its( :woo? ){ should be_false }
19
+ end
20
+
21
+ describe 'nonexistant keys' do
22
+ its( :sup? ){ should be_false }
23
+ end
24
+ end
@@ -4,7 +4,7 @@ describe Earl do
4
4
 
5
5
  describe '#parse' do
6
6
  it 'should return an Earl::URL' do
7
- Earl.parse( '' ).should be_an( Earl::URL )
7
+ Earl.parse( 'www.foo.com' ).should be_an( Earl::URL )
8
8
  end
9
9
  end
10
10
  end
@@ -68,12 +68,23 @@ describe Earl::URL do
68
68
 
69
69
  context 'when not provided' do
70
70
  it 'should raise an error' do
71
- expect { Earl::URL.new 'http://' }.to raise_error( URI::InvalidURIError )
71
+ expect { Earl::URL.new( 'http://' ).host }.to raise_error( Earl::InvalidURLError )
72
72
  end
73
73
  end
74
74
  end
75
75
 
76
76
  describe '#path' do
77
- pending
77
+ context 'when provided' do
78
+ subject { Earl::URL.new 'http://www.foo.com/bar' }
79
+ its( :path ){ should eq( 'bar' ) }
80
+ its( :path? ){ should be_true }
81
+ end
82
+
83
+ context 'when not provided' do
84
+ subject { Earl::URL.new 'http://www.foo.com' }
85
+
86
+ its( :path ){ should eq( nil ) }
87
+ its( :path? ){ should be_false }
88
+ end
78
89
  end
79
90
  end
@@ -0,0 +1,189 @@
1
+ require 'spec_helper'
2
+
3
+ describe Earl do
4
+ let( :parser ){ Earl::UrlParser.new }
5
+ let( :assembler ){ Earl::UrlAssembler.new }
6
+
7
+ [
8
+ [ 'localhost', {
9
+ :host => 'localhost'
10
+ } ],
11
+ [ 'foo.com', {
12
+ :host => 'foo.com'
13
+ } ],
14
+
15
+ [ 'foo.edu', {
16
+ :host => 'foo.edu'
17
+ } ],
18
+
19
+ [ 'foo2bar.biz', {
20
+ :host => 'foo2bar.biz'
21
+ } ],
22
+
23
+ [ 'www.foo.com', {
24
+ :host => 'foo.com',
25
+ :subdomain => 'www'
26
+ } ],
27
+
28
+ [ 'http://localhost', {
29
+ :scheme => 'http',
30
+ :host => 'localhost'
31
+ } ],
32
+
33
+ [ 'http://foo.com', {
34
+ :scheme => 'http',
35
+ :host => 'foo.com'
36
+ } ],
37
+
38
+ [ 'http://www.foo.com', {
39
+ :scheme => 'http',
40
+ :host => 'foo.com',
41
+ :subdomain => 'www'
42
+ } ],
43
+
44
+ [ 'localhost:3000', {
45
+ :host => 'localhost',
46
+ :port => '3000'
47
+ } ],
48
+
49
+ [ 'http://localhost:3000', {
50
+ :scheme => 'http',
51
+ :host => 'localhost',
52
+ :port => '3000'
53
+ } ],
54
+
55
+ [ 'www.foo.com:8080', {
56
+ :subdomain => 'www',
57
+ :host => 'foo.com',
58
+ :port => '8080'
59
+ } ],
60
+
61
+ [ 'http://www.foo.com:8080', {
62
+ :scheme => 'http',
63
+ :subdomain => 'www',
64
+ :host => 'foo.com',
65
+ :port => '8080'
66
+ } ],
67
+
68
+ [ 'localhost/bar', {
69
+ :host => 'localhost',
70
+ :path => 'bar'
71
+ } ],
72
+
73
+ [ 'foo.com/bar', {
74
+ :host => 'foo.com',
75
+ :path => 'bar'
76
+ } ],
77
+
78
+ [ 'www.foo.com/bar', {
79
+ :subdomain => 'www',
80
+ :host => 'foo.com',
81
+ :path => 'bar'
82
+ } ],
83
+
84
+ [ 'http://localhost/bar', {
85
+ :scheme => 'http',
86
+ :host => 'localhost',
87
+ :path => 'bar'
88
+ } ],
89
+
90
+ [ 'http://foo.com/bar', {
91
+ :scheme => 'http',
92
+ :host => 'foo.com',
93
+ :path => 'bar'
94
+ } ],
95
+
96
+ [ 'http://www.foo.com/bar', {
97
+ :scheme => 'http',
98
+ :subdomain => 'www',
99
+ :host => 'foo.com',
100
+ :path => 'bar'
101
+ } ],
102
+
103
+ [ 'localhost?baz=woo', {
104
+ :host => 'localhost',
105
+ :search => 'baz=woo'
106
+ } ],
107
+
108
+ [ 'localhost:3000?baz=woo', {
109
+ :host => 'localhost',
110
+ :port => '3000',
111
+ :search => 'baz=woo'
112
+ } ],
113
+
114
+ [ 'localhost:3000/bar?baz=woo', {
115
+ :host => 'localhost',
116
+ :port => '3000',
117
+ :path => 'bar',
118
+ :search => 'baz=woo'
119
+ } ],
120
+
121
+ [ 'foo.com?baz=woo', {
122
+ :host => 'foo.com',
123
+ :search => 'baz=woo'
124
+ } ],
125
+
126
+ [ 'www.foo.com?baz=woo', {
127
+ :subdomain => 'www',
128
+ :host => 'foo.com',
129
+ :search => 'baz=woo'
130
+ } ],
131
+
132
+ [ 'http://foo.com?baz=woo', {
133
+ :scheme => 'http',
134
+ :host => 'foo.com',
135
+ :search => 'baz=woo'
136
+ } ],
137
+
138
+ [ 'http://www.foo.com?baz=woo', {
139
+ :scheme => 'http',
140
+ :subdomain => 'www',
141
+ :host => 'foo.com',
142
+ :search => 'baz=woo'
143
+ } ],
144
+
145
+ [ 'http://foo.com/bar?baz=woo', {
146
+ :scheme => 'http',
147
+ :host => 'foo.com',
148
+ :path => 'bar',
149
+ :search => 'baz=woo'
150
+ } ],
151
+
152
+ [ 'http://www.foo.com/bar?baz=woot', {
153
+ :scheme => 'http',
154
+ :subdomain => 'www',
155
+ :host => 'foo.com',
156
+ :path => 'bar',
157
+ :search => 'baz=woot'
158
+ } ],
159
+
160
+ [ 'http://localhost:3000?baz=woo', {
161
+ :scheme => 'http',
162
+ :host => 'localhost',
163
+ :port => '3000',
164
+ :search => 'baz=woo'
165
+ } ],
166
+
167
+ [ 'http://foo.com:8080?baz=woooo', {
168
+ :scheme => 'http',
169
+ :host => 'foo.com',
170
+ :port => '8080',
171
+ :search => 'baz=woooo'
172
+ } ],
173
+
174
+ [ 'http://foo.com:8080/bar?baz=woo', {
175
+ :scheme => 'http',
176
+ :host => 'foo.com',
177
+ :port => '8080',
178
+ :path => 'bar',
179
+ :search => 'baz=woo'
180
+ } ]
181
+ ].each do |string, parts|
182
+ it "should correctly parse the url parts for #{string}" do
183
+ parser.parse( string ).resolve.should eql( parts )
184
+ end
185
+ it "should correctly assemble the url parts to #{string}" do
186
+ assembler.assemble( parts ).should eql( string )
187
+ end
188
+ end
189
+ end
@@ -33,4 +33,17 @@ describe Earl::URL do
33
33
  url.subdomain = nil
34
34
  url.to_s.should eq( 'bar.com' )
35
35
  end
36
+
37
+ it 'should be able to change the host' do
38
+ url = Earl::URL.new 'www.foo.com'
39
+ url.host = 'foo.edu'
40
+ url.to_s.should eq( 'www.foo.edu' )
41
+ end
42
+ it 'should not be able to add a host' do
43
+ expect { url = Earl::URL.new 'http://' }.to raise_error( Earl::InvalidURLError )
44
+ end
45
+ it 'should not be able to remove a host' do
46
+ url = Earl::URL.new 'http://foo.com'
47
+ expect { url.host = nil }.to raise_error( Earl::InvalidURLError )
48
+ end
36
49
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: earl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,24 +9,30 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-13 00:00:00.000000000 Z
12
+ date: 2012-04-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: rspec
16
- requirement: !ruby/object:Gem::Requirement
15
+ name: treetop
16
+ requirement: &705960 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 2.9.0
22
- type: :development
21
+ version: 1.4.10
22
+ type: :runtime
23
23
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
24
+ version_requirements: *705960
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &705680 !ruby/object:Gem::Requirement
25
28
  none: false
26
29
  requirements:
27
30
  - - ! '>='
28
31
  - !ruby/object:Gem::Version
29
32
  version: 2.9.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *705680
30
36
  description: What URI wishes it could look like
31
37
  email:
32
38
  - jeremy.ruppel@gmail.com
@@ -42,12 +48,17 @@ files:
42
48
  - Rakefile
43
49
  - earl.gemspec
44
50
  - lib/earl.rb
51
+ - lib/earl/hash_inquirer.rb
45
52
  - lib/earl/string_inquirer.rb
46
53
  - lib/earl/url.rb
54
+ - lib/earl/url_assembler.rb
55
+ - lib/earl/url_parser.tt
47
56
  - lib/earl/version.rb
57
+ - spec/earl/hash_inquirer_spec.rb
48
58
  - spec/earl/parse_spec.rb
49
59
  - spec/earl/parts_spec.rb
50
60
  - spec/earl/string_inquirer_spec.rb
61
+ - spec/earl/url_parser_spec.rb
51
62
  - spec/earl/url_spec.rb
52
63
  - spec/spec_helper.rb
53
64
  homepage: https://github.com/remind101/earl
@@ -70,13 +81,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
81
  version: '0'
71
82
  requirements: []
72
83
  rubyforge_project:
73
- rubygems_version: 1.8.19
84
+ rubygems_version: 1.8.15
74
85
  signing_key:
75
86
  specification_version: 3
76
87
  summary: What URI wishes it could look like
77
88
  test_files:
89
+ - spec/earl/hash_inquirer_spec.rb
78
90
  - spec/earl/parse_spec.rb
79
91
  - spec/earl/parts_spec.rb
80
92
  - spec/earl/string_inquirer_spec.rb
93
+ - spec/earl/url_parser_spec.rb
81
94
  - spec/earl/url_spec.rb
82
95
  - spec/spec_helper.rb