furi 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +105 -7
- data/lib/furi.rb +101 -374
- data/lib/furi/query_token.rb +57 -0
- data/lib/furi/uri.rb +533 -0
- data/lib/furi/utils.rb +16 -0
- data/lib/furi/version.rb +1 -1
- data/spec/furi_spec.rb +339 -46
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b454ab79a6ac9b95fc85b8b8b2381c8e93715af
|
4
|
+
data.tar.gz: d84272345cd55a7aad224ce2b124423806f38301
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc4227555a121e3c25d7c4ba32765659fa78678ff7aa2b500f0f6cbe384f9507e11bac3828bfd9eae097879e786395ac9b8113049447b5d1a1aff6a834a91108
|
7
|
+
data.tar.gz: 4b8d902ab961bff2d68a369da05c96f1cc142e6d71980a3cd24691de930807057b65016aa9ec16e8d7e52e2cdd5173068d108ac2d0128ced4ac9d497e671adbb
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# Furi
|
2
2
|
|
3
|
-
|
3
|
+
Furi is a Friendly URI parsing library.
|
4
|
+
Furi's philosophy is to make any operation possible in ONE LINE OF CODE.
|
5
|
+
|
6
|
+
If there is an operation that takes more than one line of code to do with Furi, this is considered a terrible bug and you should create an issue.
|
4
7
|
|
5
8
|
## Installation
|
6
9
|
|
@@ -20,12 +23,107 @@ Or install it yourself as:
|
|
20
23
|
|
21
24
|
## Usage
|
22
25
|
|
23
|
-
|
26
|
+
I'll say it again: any operation should take exacly one line of code!
|
27
|
+
Here are basic:
|
28
|
+
|
29
|
+
### Utility Methods
|
30
|
+
|
31
|
+
|
32
|
+
``` ruby
|
33
|
+
Furi.host("http://gusiev.com") # => "gusiev.com"
|
34
|
+
Furi.port("http://gusiev.com") # => nil
|
35
|
+
Furi.port!("http://gusiev.com") # => 80
|
36
|
+
|
37
|
+
Furi.update("http://gusiev.com", protocol: '') # => "//gusiev.com"
|
38
|
+
Furi.update("http://gusiev.com?source=google", query: {email: "a@b.com"})
|
39
|
+
# => "http://gusiev.com?email=a@b.com"
|
40
|
+
Furi.merge("http://gusiev.com?source=google", query: {email: "a@b.com"})
|
41
|
+
# => "http://gusiev.com?source=google&email=a@b.com"
|
42
|
+
|
43
|
+
Furi.build(protocol: '//', host: 'gusiev.com', path: '/assets/application.js')
|
44
|
+
# => "//gusiev.com/assets/application.js"
|
45
|
+
|
46
|
+
Furi.defaults("http://gusiev.com", subdomain: 'www') # => "http://www.gusiev.com"
|
47
|
+
```
|
48
|
+
|
49
|
+
### Working with Object
|
50
|
+
|
51
|
+
``` ruby
|
52
|
+
uri = Furi.parse("gusiev.com")
|
53
|
+
# => #<Furi::Uri "gusiev.com">
|
54
|
+
|
55
|
+
uri.port # => nil
|
56
|
+
uri.port! # => 80
|
57
|
+
uri.path # => nil
|
58
|
+
uri.path! # => '/'
|
59
|
+
uri.subdomain ||= 'www'
|
60
|
+
uri.protocol = "//" # protocol abstract URL
|
61
|
+
```
|
62
|
+
|
63
|
+
### Processing Query String
|
64
|
+
|
65
|
+
``` ruby
|
66
|
+
uri = Furi.parse("/?person[first_name]=Bogdan&person[last_name]=Gusiev")
|
67
|
+
|
68
|
+
uri.query_string # => "person[first_name]=Bogdan&person[last_name]=Gusiev"
|
69
|
+
uri.query_tokens # => [person[first_name]=Bogdan, person[last_name]=Gusiev]
|
70
|
+
uri.query # => {person: {first_name: Bogdan, last_name: 'Gusiev'}}
|
71
|
+
|
72
|
+
uri.merge_query(person: {email: 'a@b.com'})
|
73
|
+
# => {person: {email: 'a@b.com', first_name: Bogdan, last_name: 'Gusiev'}}
|
74
|
+
|
75
|
+
uri.merge_query(person: {email: 'a@b.com'})
|
76
|
+
# => {person: {email: 'a@b.com', first_name: Bogdan, last_name: 'Gusiev'}}
|
77
|
+
```
|
78
|
+
|
79
|
+
## Reference
|
80
|
+
|
81
|
+
```
|
82
|
+
location resource
|
83
|
+
| ___|___
|
84
|
+
_______|_______ / \
|
85
|
+
/ \ / \
|
86
|
+
/ authority request \
|
87
|
+
/ __________|_________ | \
|
88
|
+
/ / \ ______|______ \
|
89
|
+
/ userinfo hostinfo / \ \
|
90
|
+
/ __|___ ___|___ / \ \
|
91
|
+
/ / \ / \ / \ \
|
92
|
+
/ username password host port path query anchor
|
93
|
+
/ __|___ __|__ ______|______ | _________|__________ ____|____ |
|
94
|
+
/ / \ / \ / \ / \/ \ / \ / \
|
95
|
+
http://username:zhongguo@www.example.com:80/hello/world/article.html?name=bogdan#info
|
96
|
+
\_/ \_/ \___/ \_/ \__________/ \ \_/
|
97
|
+
| | | | | \ |
|
98
|
+
protocol subdomain | domainzone directory \ extension
|
99
|
+
| | \_____/
|
100
|
+
domainname / |
|
101
|
+
\___/ filename
|
102
|
+
|
|
103
|
+
domain
|
104
|
+
```
|
105
|
+
|
106
|
+
|
107
|
+
Originated from [URI.js](http://medialize.github.io/URI.js/about-uris.html) parsing library.
|
108
|
+
Giving credit...
|
109
|
+
|
110
|
+
|
111
|
+
## TODO
|
112
|
+
|
113
|
+
* rfc3986 validation
|
114
|
+
* mailto protocol
|
115
|
+
* escaping in path
|
116
|
+
* case insensetivity:
|
117
|
+
* domain
|
118
|
+
* protocol
|
119
|
+
* case sensitivity:
|
120
|
+
* path
|
121
|
+
* query
|
122
|
+
* anchor
|
123
|
+
* basic auth data ?
|
24
124
|
|
25
125
|
## Contributing
|
26
126
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
-
5. Create a new Pull Request
|
127
|
+
Contribute in the way you want. Branch names and other bla-bla-bla do not matter.
|
128
|
+
|
129
|
+
|
data/lib/furi.rb
CHANGED
@@ -3,18 +3,30 @@ require "uri"
|
|
3
3
|
|
4
4
|
module Furi
|
5
5
|
|
6
|
-
|
6
|
+
autoload :QueryToken, 'furi/query_token'
|
7
|
+
autoload :Uri, 'furi/uri'
|
8
|
+
autoload :Utils, 'furi/utils'
|
9
|
+
|
10
|
+
ESSENTIAL_PARTS = [
|
7
11
|
:anchor, :protocol, :query_tokens,
|
8
12
|
:path, :host, :port, :username, :password
|
9
13
|
]
|
14
|
+
COMBINED_PARTS = [
|
15
|
+
:hostinfo, :userinfo, :authority, :ssl, :domain, :domainname,
|
16
|
+
:domainzone, :request, :location, :query,
|
17
|
+
:extension, :filename
|
18
|
+
]
|
19
|
+
PARTS = ESSENTIAL_PARTS + COMBINED_PARTS
|
20
|
+
|
10
21
|
ALIASES = {
|
11
22
|
protocol: [:schema, :scheme],
|
12
23
|
anchor: [:fragment],
|
13
24
|
host: [:hostname],
|
14
25
|
username: [:user],
|
26
|
+
request: [:request_uri]
|
15
27
|
}
|
16
28
|
|
17
|
-
DELEGATES = [:port!, :host
|
29
|
+
DELEGATES = [:port!, :host!, :path!, :home_page?]
|
18
30
|
|
19
31
|
PROTOCOLS = {
|
20
32
|
"http" => {port: 80, ssl: false},
|
@@ -32,6 +44,7 @@ module Furi
|
|
32
44
|
"prospero" => {port: 1525},
|
33
45
|
}
|
34
46
|
|
47
|
+
|
35
48
|
SSL_MAPPING = {
|
36
49
|
'http' => 'https',
|
37
50
|
'ftp' => 'sftp',
|
@@ -42,22 +55,15 @@ module Furi
|
|
42
55
|
|
43
56
|
ROOT = '/'
|
44
57
|
|
45
|
-
|
46
|
-
attr_accessor :protocol
|
47
|
-
|
48
|
-
def initialize
|
49
|
-
@protocol = /^[a-z][a-z0-9.+-]*$/i
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.expressions
|
54
|
-
Expressions.new
|
55
|
-
end
|
56
|
-
|
58
|
+
# Parses a given string and return an URL object
|
57
59
|
def self.parse(argument)
|
58
60
|
Uri.new(argument)
|
59
61
|
end
|
60
62
|
|
63
|
+
# Builds an URL from given parts
|
64
|
+
#
|
65
|
+
# Furi.build(path: "/dashboard", host: 'example.com', protocol: "https")
|
66
|
+
# # => "https://example.com/dashboard"
|
61
67
|
def self.build(argument)
|
62
68
|
Uri.new(argument).to_s
|
63
69
|
end
|
@@ -65,19 +71,99 @@ module Furi
|
|
65
71
|
class << self
|
66
72
|
(PARTS + ALIASES.values.flatten + DELEGATES).each do |part|
|
67
73
|
define_method(part) do |string|
|
68
|
-
Uri.new(string)
|
74
|
+
Uri.new(string)[part]
|
69
75
|
end
|
70
76
|
end
|
71
77
|
end
|
72
78
|
|
79
|
+
# Replaces a given URL string with given parts
|
80
|
+
#
|
81
|
+
# Furi.update("http://gusiev.com", protocol: 'https', subdomain: 'www')
|
82
|
+
# # => "https://www.gusiev.com"
|
73
83
|
def self.update(string, parts)
|
74
84
|
parse(string).update(parts).to_s
|
75
85
|
end
|
76
86
|
|
87
|
+
# Puts the default values for given URL that are not defined
|
88
|
+
#
|
89
|
+
# Furi.defaults("gusiev.com/hello.html", protocol: 'http', path: '/index.html')
|
90
|
+
# # => "http://gusiev.com/hello.html"
|
91
|
+
def self.defaults(string, parts)
|
92
|
+
parse(string).defaults(parts).to_s
|
93
|
+
end
|
94
|
+
|
95
|
+
# Replaces a given URL string with given parts.
|
96
|
+
# Same as update but works different for URL query parameter:
|
97
|
+
# merges newly specified parameters instead of replacing existing ones
|
98
|
+
#
|
99
|
+
# Furi.merge("/hello.html?a=1", host: 'gusiev.com', query: {b: 2})
|
100
|
+
# # => "gusiev.com/hello.html?a=1&b=2"
|
101
|
+
#
|
77
102
|
def self.merge(string, parts)
|
78
103
|
parse(string).merge(parts).to_s
|
79
104
|
end
|
80
105
|
|
106
|
+
|
107
|
+
# Parses a query into nested paramters hash using a rack convension with square brackets.
|
108
|
+
#
|
109
|
+
# Furi.parse_query("a[]=1&a[]=2") # => {a: [1,2]}
|
110
|
+
# Furi.parse_query("p[email]=a&a[two]=2") # => {a: {one: 1, two: 2}}
|
111
|
+
# Furi.parse_query("p[one]=1&a[two]=2") # => {a: {one: 1, two: 2}}
|
112
|
+
# Furi.serialize({p: {name: 'Bogdan Gusiev', email: 'bogdan@example.com', data: {one: 1, two: 2}}})
|
113
|
+
# # => "p%5Bname%5D=Bogdan&p%5Bemail%5D=bogdan%40example.com&p%5Bdata%5D%5Bone%5D=1&p%5Bdata%5D%5Btwo%5D=2"
|
114
|
+
def self.parse_query(query)
|
115
|
+
return Furi::Utils.stringify_keys(query) if query.is_a?(Hash)
|
116
|
+
|
117
|
+
params = {}
|
118
|
+
query_tokens(query).each do |token|
|
119
|
+
parse_query_token(params, token.name, token.value)
|
120
|
+
end
|
121
|
+
|
122
|
+
return params
|
123
|
+
end
|
124
|
+
|
125
|
+
# Parses query key/value pairs from query string and returns them raw
|
126
|
+
# without organising them into hashes and without normalising them.
|
127
|
+
#
|
128
|
+
# Furi.query_tokens("a=1&b=2").map {|k,v| "#{k} -> #{v}"} # => ['a -> 1', 'b -> 2']
|
129
|
+
# Furi.query_tokens("a=1&a=1&a=2").map {|k,v| "#{k} -> #{v}"} # => ['a -> 1', 'a -> 1', 'a -> 2']
|
130
|
+
# Furi.query_tokens("name=Bogdan&email=bogdan%40example.com") # => [name=Bogdan, email=bogdan@example.com]
|
131
|
+
# Furi.query_tokens("a[one]=1&a[two]=2") # => [a[one]=1, a[two]=2]
|
132
|
+
def self.query_tokens(query)
|
133
|
+
case query
|
134
|
+
when Enumerable, Enumerator
|
135
|
+
query.map do |token|
|
136
|
+
QueryToken.parse(token)
|
137
|
+
end
|
138
|
+
when nil, ''
|
139
|
+
[]
|
140
|
+
when String
|
141
|
+
query.gsub(/\A\?/, '').split(/[&;] */n, -1).map do |p|
|
142
|
+
QueryToken.parse(p)
|
143
|
+
end
|
144
|
+
else
|
145
|
+
raise ArgumentError, "Can not parse #{query.inspect} query tokens"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Serializes query parameters into query string.
|
150
|
+
# Optionaly accepts a basic name space.
|
151
|
+
#
|
152
|
+
# Furi.serialize({a: 1, b: 2}) # => "a=1&b=2"
|
153
|
+
# Furi.serialize({a: [1,2]}) # => "a[]=1&a[]=2"
|
154
|
+
# Furi.serialize({a: {b: 1, c:2}}) # => "a[b]=1&a[c]=2"
|
155
|
+
# Furi.serialize({name: 'Bogdan', email: 'bogdan@example.com'}, "person")
|
156
|
+
# # => "person[name]=Bogdan&person[email]=bogdan%40example.com"
|
157
|
+
#
|
158
|
+
def self.serialize(query, namespace = nil)
|
159
|
+
serialize_tokens(query, namespace).join("&")
|
160
|
+
end
|
161
|
+
|
162
|
+
class FormattingError < StandardError
|
163
|
+
end
|
164
|
+
|
165
|
+
protected
|
166
|
+
|
81
167
|
def self.serialize_tokens(query, namespace = nil)
|
82
168
|
case query
|
83
169
|
when Hash
|
@@ -112,42 +198,6 @@ module Furi
|
|
112
198
|
end
|
113
199
|
end
|
114
200
|
|
115
|
-
def self.parse_nested_query(qs)
|
116
|
-
|
117
|
-
params = {}
|
118
|
-
query_tokens(qs).each do |token|
|
119
|
-
parse_query_token(params, token.name, token.value)
|
120
|
-
end
|
121
|
-
|
122
|
-
return params
|
123
|
-
end
|
124
|
-
|
125
|
-
def self.query_tokens(query)
|
126
|
-
case query
|
127
|
-
when Enumerable, Enumerator
|
128
|
-
query.map do |token|
|
129
|
-
case token
|
130
|
-
when QueryToken
|
131
|
-
token
|
132
|
-
when String
|
133
|
-
QueryToken.parse(token)
|
134
|
-
when Array
|
135
|
-
QueryToken.new(*token)
|
136
|
-
else
|
137
|
-
raise ArgumentError, "Can not parse query token #{token.inspect}"
|
138
|
-
end
|
139
|
-
end
|
140
|
-
when nil, ''
|
141
|
-
[]
|
142
|
-
when String
|
143
|
-
query.gsub(/\A\?/, '').split(/[&;] */n).map do |p|
|
144
|
-
QueryToken.parse(p)
|
145
|
-
end
|
146
|
-
else
|
147
|
-
raise ArgumentError, "Can not parse #{query.inspect} query tokens"
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
201
|
def self.parse_query_token(params, name, value)
|
152
202
|
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
153
203
|
namespace = $1 || ''
|
@@ -187,327 +237,4 @@ module Furi
|
|
187
237
|
return params
|
188
238
|
end
|
189
239
|
|
190
|
-
def self.serialize(query, namespace = nil)
|
191
|
-
serialize_tokens(query, namespace).join("&")
|
192
|
-
end
|
193
|
-
|
194
|
-
class QueryToken
|
195
|
-
attr_reader :name, :value
|
196
|
-
|
197
|
-
def self.parse(token)
|
198
|
-
k,v = token.split('=', 2).map { |s| ::URI.decode_www_form_component(s) }
|
199
|
-
new(k,v)
|
200
|
-
end
|
201
|
-
|
202
|
-
def initialize(name, value)
|
203
|
-
@name = name
|
204
|
-
@value = value
|
205
|
-
end
|
206
|
-
|
207
|
-
def to_a
|
208
|
-
[name, value]
|
209
|
-
end
|
210
|
-
|
211
|
-
def ==(other)
|
212
|
-
to_s == other.to_s
|
213
|
-
end
|
214
|
-
|
215
|
-
def to_s
|
216
|
-
"#{::URI.encode_www_form_component(name.to_s)}=#{::URI.encode_www_form_component(value.to_s)}"
|
217
|
-
end
|
218
|
-
|
219
|
-
def inspect
|
220
|
-
[name, value].join('=')
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
class Uri
|
225
|
-
|
226
|
-
attr_reader(*PARTS)
|
227
|
-
|
228
|
-
ALIASES.each do |origin, aliases|
|
229
|
-
aliases.each do |aliaz|
|
230
|
-
define_method(aliaz) do
|
231
|
-
send(origin)
|
232
|
-
end
|
233
|
-
|
234
|
-
define_method(:"#{aliaz}=") do |*args|
|
235
|
-
send(:"#{origin}=", *args)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
def initialize(argument)
|
241
|
-
@query_tokens = []
|
242
|
-
case argument
|
243
|
-
when String
|
244
|
-
parse_uri_string(argument)
|
245
|
-
when Hash
|
246
|
-
update(argument)
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
def update(parts)
|
251
|
-
parts.each do |part, value|
|
252
|
-
send(:"#{part}=", value)
|
253
|
-
end
|
254
|
-
self
|
255
|
-
end
|
256
|
-
|
257
|
-
def merge(parts)
|
258
|
-
parts.each do |part, value|
|
259
|
-
case part.to_sym
|
260
|
-
when :query
|
261
|
-
merge_query(value)
|
262
|
-
else
|
263
|
-
send(:"#{part}=", value)
|
264
|
-
end
|
265
|
-
end
|
266
|
-
self
|
267
|
-
end
|
268
|
-
|
269
|
-
def merge_query(query)
|
270
|
-
case query
|
271
|
-
when Hash
|
272
|
-
self.query = self.query.merge(Furi::Utils.stringify_keys(query))
|
273
|
-
when String, Array
|
274
|
-
self.query_tokens += Furi.query_tokens(query)
|
275
|
-
when nil
|
276
|
-
else
|
277
|
-
raise ArgumentError, "#{query.inspect} can not be merged"
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
def userinfo
|
282
|
-
if username
|
283
|
-
[username, password].compact.join(":")
|
284
|
-
elsif password
|
285
|
-
raise FormattingError, "can not build URI with password but without username"
|
286
|
-
else
|
287
|
-
nil
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
def host=(host)
|
292
|
-
@host = host
|
293
|
-
end
|
294
|
-
|
295
|
-
def to_s
|
296
|
-
result = []
|
297
|
-
if protocol
|
298
|
-
result.push(protocol.empty? ? "//" : "#{protocol}://")
|
299
|
-
end
|
300
|
-
if userinfo
|
301
|
-
result << userinfo
|
302
|
-
end
|
303
|
-
result << host if host
|
304
|
-
result << ":" << port if explicit_port
|
305
|
-
result << (host ? path : path!)
|
306
|
-
if query_tokens.any?
|
307
|
-
result << "?" << query_string
|
308
|
-
end
|
309
|
-
if anchor
|
310
|
-
result << "#" << anchor
|
311
|
-
end
|
312
|
-
result.join
|
313
|
-
end
|
314
|
-
|
315
|
-
|
316
|
-
def request
|
317
|
-
result = []
|
318
|
-
result << path!
|
319
|
-
result << "?" << query_string if query_tokens.any?
|
320
|
-
result.join
|
321
|
-
end
|
322
|
-
|
323
|
-
def request_uri
|
324
|
-
request
|
325
|
-
end
|
326
|
-
|
327
|
-
def query
|
328
|
-
return @query if query_level?
|
329
|
-
@query = Furi.parse_nested_query(query_tokens)
|
330
|
-
end
|
331
|
-
|
332
|
-
|
333
|
-
def query=(value)
|
334
|
-
@query = nil
|
335
|
-
@query_tokens = []
|
336
|
-
case value
|
337
|
-
when String, Array
|
338
|
-
@query_tokens = Furi.query_tokens(value)
|
339
|
-
when Hash
|
340
|
-
@query = value
|
341
|
-
@query_tokens = Furi.serialize_tokens(value)
|
342
|
-
when nil
|
343
|
-
else
|
344
|
-
raise ArgumentError, 'Query can only be Hash or String'
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
def port=(port)
|
349
|
-
if port != nil
|
350
|
-
@port = port.to_i
|
351
|
-
if @port == 0
|
352
|
-
raise ArgumentError, "port should be an Integer > 0"
|
353
|
-
end
|
354
|
-
else
|
355
|
-
@port = nil
|
356
|
-
end
|
357
|
-
@port
|
358
|
-
end
|
359
|
-
|
360
|
-
def query_tokens=(tokens)
|
361
|
-
@query = nil
|
362
|
-
@query_tokens = tokens
|
363
|
-
end
|
364
|
-
|
365
|
-
def username=(username)
|
366
|
-
@username = username.nil? ? nil : username.to_s
|
367
|
-
end
|
368
|
-
|
369
|
-
def password=(password)
|
370
|
-
@password = password.nil? ? nil : password.to_s
|
371
|
-
end
|
372
|
-
|
373
|
-
def path=(path)
|
374
|
-
@path = path.to_s
|
375
|
-
end
|
376
|
-
|
377
|
-
def protocol=(protocol)
|
378
|
-
@protocol = protocol ? protocol.gsub(%r{:/?/?\Z}, "") : nil
|
379
|
-
end
|
380
|
-
|
381
|
-
def query_string
|
382
|
-
if query_level?
|
383
|
-
Furi.serialize(@query)
|
384
|
-
else
|
385
|
-
query_tokens.join("&")
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
def expressions
|
390
|
-
Furi.expressions
|
391
|
-
end
|
392
|
-
|
393
|
-
def port!
|
394
|
-
port || default_port
|
395
|
-
end
|
396
|
-
|
397
|
-
def default_port
|
398
|
-
protocol && PROTOCOLS[protocol] ? PROTOCOLS[protocol][:port] : nil
|
399
|
-
end
|
400
|
-
|
401
|
-
def ssl?
|
402
|
-
!!(protocol && PROTOCOLS[protocol][:ssl])
|
403
|
-
end
|
404
|
-
|
405
|
-
def ssl
|
406
|
-
ssl?
|
407
|
-
end
|
408
|
-
|
409
|
-
def ssl=(ssl)
|
410
|
-
self.protocol = find_protocol_for_ssl(ssl)
|
411
|
-
end
|
412
|
-
|
413
|
-
def filename
|
414
|
-
path.split("/").last
|
415
|
-
end
|
416
|
-
|
417
|
-
def default_web_port?
|
418
|
-
WEB_PROTOCOL.any? do |web_protocol|
|
419
|
-
PROTOCOLS[web_protocol][:port] == port!
|
420
|
-
end
|
421
|
-
end
|
422
|
-
|
423
|
-
def web_protocol?
|
424
|
-
WEB_PROTOCOL.include?(protocol)
|
425
|
-
end
|
426
|
-
|
427
|
-
def resource
|
428
|
-
[request, anchor].compact.join("#")
|
429
|
-
end
|
430
|
-
|
431
|
-
def path!
|
432
|
-
path || ROOT
|
433
|
-
end
|
434
|
-
|
435
|
-
def host!
|
436
|
-
host || ""
|
437
|
-
end
|
438
|
-
|
439
|
-
def ==(other)
|
440
|
-
to_s == other.to_s
|
441
|
-
end
|
442
|
-
|
443
|
-
protected
|
444
|
-
|
445
|
-
def query_level?
|
446
|
-
!!@query
|
447
|
-
end
|
448
|
-
|
449
|
-
def explicit_port
|
450
|
-
port == default_port ? nil : port
|
451
|
-
end
|
452
|
-
|
453
|
-
def parse_uri_string(string)
|
454
|
-
string, *@anchor = string.split("#")
|
455
|
-
@anchor = @anchor.empty? ? nil : @anchor.join("#")
|
456
|
-
if string.include?("?")
|
457
|
-
string, query_string = string.split("?", 2)
|
458
|
-
self.query_tokens = Furi.query_tokens(query_string)
|
459
|
-
end
|
460
|
-
|
461
|
-
if string.include?("://")
|
462
|
-
@protocol, string = string.split(":", 2)
|
463
|
-
@protocol = '' if @protocol.empty?
|
464
|
-
end
|
465
|
-
if string.start_with?("//")
|
466
|
-
@protocol ||= ''
|
467
|
-
string = string[2..-1]
|
468
|
-
end
|
469
|
-
parse_authority(string)
|
470
|
-
end
|
471
|
-
|
472
|
-
def parse_authority(string)
|
473
|
-
if string.include?("/")
|
474
|
-
string, @path = string.split("/", 2)
|
475
|
-
@path = "/" + @path
|
476
|
-
end
|
477
|
-
|
478
|
-
if string.include?("@")
|
479
|
-
userinfo, string = string.split("@", 2)
|
480
|
-
@username, @password = userinfo.split(":", 2)
|
481
|
-
end
|
482
|
-
host, port = string.split(":", 2)
|
483
|
-
self.host = host if host
|
484
|
-
self.port = port if port
|
485
|
-
end
|
486
|
-
|
487
|
-
def find_protocol_for_ssl(ssl)
|
488
|
-
if SSL_MAPPING.key?(protocol)
|
489
|
-
ssl ? SSL_MAPPING[protocol] : protocol
|
490
|
-
elsif SSL_MAPPING.values.include?(protocol)
|
491
|
-
ssl ? protocol : SSL_MAPPING.invert[protocol]
|
492
|
-
else
|
493
|
-
raise ArgumentError, "Can not specify ssl for #{protocol.inspect} protocol"
|
494
|
-
end
|
495
|
-
end
|
496
|
-
|
497
|
-
end
|
498
|
-
|
499
|
-
class FormattingError < StandardError
|
500
|
-
end
|
501
|
-
|
502
|
-
class Utils
|
503
|
-
class << self
|
504
|
-
def stringify_keys(hash)
|
505
|
-
result = {}
|
506
|
-
hash.each_key do |key|
|
507
|
-
result[key.to_s] = hash[key]
|
508
|
-
end
|
509
|
-
result
|
510
|
-
end
|
511
|
-
end
|
512
|
-
end
|
513
240
|
end
|