furi 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 208063d16947ce85a4d80a12bd3834cb26c0f6ab
4
- data.tar.gz: ab3eff6b787f0df3e04781908477bb69d3924333
3
+ metadata.gz: 62a4088dd18cb4ac9857d1512d78497ea28edd82
4
+ data.tar.gz: 3603fa3688b41b7b2ba700676db29f930bb11bf3
5
5
  SHA512:
6
- metadata.gz: 500c3e4bb3ed0866c0cef1ca68794d9d82e9d2b679bb1590f69e14428bb1020a64d5c357c166e25b0c522902b670e5138269e649f395455f48acc4bca50785eb
7
- data.tar.gz: bae87908fe1a04c26d1073d569199adda1645bc25f561ec0aa9b2e05943e55d0993223c3d329335c07afcac8c814e87c2a738573a2837be2996cb38339e3078f
6
+ metadata.gz: 762e38c65d27a36323bf0ed410b25633e95788ce10a8a97163195918a47e038610defe4fe8f7f5b635d554c42b09cc3c5d4fbc1974dee9ec5d4e645d646923c1
7
+ data.tar.gz: 59f5e066fb0f09bcac768b99b11589d4257f391352e27d4d6874c2ae1d2d783f8bc56fde491906669b5aed5d22f16c17a8f424cbab486bd2b3123243c7b38873
data/Rakefile CHANGED
@@ -1,2 +1,13 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
+
4
+ begin
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ rescue LoadError
8
+ end
9
+
10
+ task :default => :spec do
11
+
12
+ end
13
+
@@ -4,32 +4,44 @@ require "uri"
4
4
  module Furi
5
5
 
6
6
  PARTS = [
7
- :anchor, :protocol, :query_string,
8
- :path, :hostname, :port, :username, :password
7
+ :anchor, :protocol, :query_tokens,
8
+ :path, :host, :port, :username, :password
9
9
  ]
10
10
  ALIASES = {
11
- protocol: [:schema],
11
+ protocol: [:schema, :scheme],
12
12
  anchor: [:fragment],
13
+ host: [:hostname],
14
+ username: [:user],
13
15
  }
14
16
 
15
- DELEGATES = [:port!]
16
-
17
- PORT_MAPPING = {
18
- "http" => 80,
19
- "https" => 443,
20
- "ftp" => 21,
21
- "tftp" => 69,
22
- "sftp" => 22,
23
- "ssh" => 22,
24
- "svn+ssh" => 22,
25
- "telnet" => 23,
26
- "nntp" => 119,
27
- "gopher" => 70,
28
- "wais" => 210,
29
- "ldap" => 389,
30
- "prospero" => 1525
17
+ DELEGATES = [:port!, :host!]
18
+
19
+ PROTOCOLS = {
20
+ "http" => {port: 80, ssl: false},
21
+ "https" => {port: 443, ssl: true},
22
+ "ftp" => {port: 21},
23
+ "tftp" => {port: 69},
24
+ "sftp" => {port: 22},
25
+ "ssh" => {port: 22, ssl: true},
26
+ "svn+ssh" => {port: 22, ssl: true},
27
+ "telnet" => {port: 23},
28
+ "nntp" => {port: 119},
29
+ "gopher" => {port: 70},
30
+ "wais" => {port: 210},
31
+ "ldap" => {port: 389},
32
+ "prospero" => {port: 1525},
31
33
  }
32
34
 
35
+ SSL_MAPPING = {
36
+ 'http' => 'https',
37
+ 'ftp' => 'sftp',
38
+ 'svn' => 'svn+ssh',
39
+ }
40
+
41
+ WEB_PROTOCOL = ['http', 'https']
42
+
43
+ ROOT = '/'
44
+
33
45
  class Expressions
34
46
  attr_accessor :protocol
35
47
 
@@ -62,6 +74,10 @@ module Furi
62
74
  parse(string).update(parts).to_s
63
75
  end
64
76
 
77
+ def self.merge(string, parts)
78
+ parse(string).merge(parts).to_s
79
+ end
80
+
65
81
  def self.serialize_tokens(query, namespace = nil)
66
82
  case query
67
83
  when Hash
@@ -107,7 +123,8 @@ module Furi
107
123
  end
108
124
 
109
125
  def self.query_tokens(query)
110
- if query.is_a?(Array)
126
+ case query
127
+ when Enumerable, Enumerator
111
128
  query.map do |token|
112
129
  case token
113
130
  when QueryToken
@@ -120,10 +137,14 @@ module Furi
120
137
  raise ArgumentError, "Can not parse query token #{token.inspect}"
121
138
  end
122
139
  end
123
- else
124
- (query || '').split(/[&;] */n).map do |p|
140
+ when nil, ''
141
+ []
142
+ when String
143
+ query.gsub(/\A\?/, '').split(/[&;] */n).map do |p|
125
144
  QueryToken.parse(p)
126
145
  end
146
+ else
147
+ raise ArgumentError, "Can not parse #{query.inspect} query tokens"
127
148
  end
128
149
  end
129
150
 
@@ -166,8 +187,8 @@ module Furi
166
187
  return params
167
188
  end
168
189
 
169
- def self.serialize(string, namespace = nil)
170
- serialize_tokens(string, namespace).join("&")
190
+ def self.serialize(query, namespace = nil)
191
+ serialize_tokens(query, namespace).join("&")
171
192
  end
172
193
 
173
194
  class QueryToken
@@ -187,6 +208,10 @@ module Furi
187
208
  [name, value]
188
209
  end
189
210
 
211
+ def ==(other)
212
+ to_s == other.to_s
213
+ end
214
+
190
215
  def to_s
191
216
  "#{::URI.encode_www_form_component(name.to_s)}=#{::URI.encode_www_form_component(value.to_s)}"
192
217
  end
@@ -213,6 +238,7 @@ module Furi
213
238
  end
214
239
 
215
240
  def initialize(argument)
241
+ @query_tokens = []
216
242
  case argument
217
243
  when String
218
244
  parse_uri_string(argument)
@@ -230,7 +256,25 @@ module Furi
230
256
 
231
257
  def merge(parts)
232
258
  parts.each do |part, value|
233
-
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"
234
278
  end
235
279
  end
236
280
 
@@ -243,24 +287,9 @@ module Furi
243
287
  nil
244
288
  end
245
289
  end
246
-
247
- def host
248
- if hostname
249
- [hostname, explicit_port].compact.join(":")
250
- elsif port
251
- raise FormattingError, "can not build URI with port but without hostname"
252
- else
253
- nil
254
- end
255
- end
256
-
257
- def host=(string)
258
- @port = nil
259
- if string.include?(":")
260
- string, @port = string.split(":", 2)
261
- @port = @port.to_i
262
- end
263
- @hostname = string.empty? ? nil : string
290
+
291
+ def host=(host)
292
+ @host = host
264
293
  end
265
294
 
266
295
  def to_s
@@ -272,8 +301,9 @@ module Furi
272
301
  result << userinfo
273
302
  end
274
303
  result << host if host
275
- result << path
276
- if query_string
304
+ result << ":" << port if explicit_port
305
+ result << (host ? path : path!)
306
+ if query_tokens.any?
277
307
  result << "?" << query_string
278
308
  end
279
309
  if anchor
@@ -282,45 +312,66 @@ module Furi
282
312
  result.join
283
313
  end
284
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
+
285
327
  def query
286
328
  return @query if query_level?
287
- @query = Furi.parse_nested_query(@query_string)
329
+ @query = Furi.parse_nested_query(query_tokens)
288
330
  end
289
331
 
290
332
 
291
333
  def query=(value)
292
334
  @query = nil
335
+ @query_tokens = []
293
336
  case value
294
- when String
295
- @query_string = value
296
- when Array
297
- @query = Furi.query_tokens(value)
337
+ when String, Array
338
+ @query_tokens = Furi.query_tokens(value)
298
339
  when Hash
299
340
  @query = value
341
+ @query_tokens = Furi.serialize_tokens(value)
300
342
  when nil
301
343
  else
302
344
  raise ArgumentError, 'Query can only be Hash or String'
303
345
  end
304
346
  end
305
347
 
306
- def hostname=(hostname)
307
- @hostname = hostname
308
- end
309
-
310
348
  def port=(port)
311
- @port = port.to_i
312
- if @port == 0
313
- raise ArgumentError, "port should be an Integer > 0"
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
314
356
  end
315
357
  @port
316
358
  end
317
359
 
360
+ def query_tokens=(tokens)
361
+ @query = nil
362
+ @query_tokens = tokens
363
+ end
364
+
318
365
  def username=(username)
319
- @username = username
366
+ @username = username.nil? ? nil : username.to_s
320
367
  end
321
368
 
322
369
  def password=(password)
323
- @password = password
370
+ @password = password.nil? ? nil : password.to_s
371
+ end
372
+
373
+ def path=(path)
374
+ @path = path.to_s
324
375
  end
325
376
 
326
377
  def protocol=(protocol)
@@ -328,8 +379,11 @@ module Furi
328
379
  end
329
380
 
330
381
  def query_string
331
- return @query_string unless query_level?
332
- Furi.serialize(@query)
382
+ if query_level?
383
+ Furi.serialize(@query)
384
+ else
385
+ query_tokens.join("&")
386
+ end
333
387
  end
334
388
 
335
389
  def expressions
@@ -341,7 +395,49 @@ module Furi
341
395
  end
342
396
 
343
397
  def default_port
344
- protocol ? PORT_MAPPING[protocol] : nil
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
345
441
  end
346
442
 
347
443
  protected
@@ -359,7 +455,7 @@ module Furi
359
455
  @anchor = @anchor.empty? ? nil : @anchor.join("#")
360
456
  if string.include?("?")
361
457
  string, query_string = string.split("?", 2)
362
- @query_string = query_string
458
+ self.query_tokens = Furi.query_tokens(query_string)
363
459
  end
364
460
 
365
461
  if string.include?("://")
@@ -383,11 +479,35 @@ module Furi
383
479
  userinfo, string = string.split("@", 2)
384
480
  @username, @password = userinfo.split(":", 2)
385
481
  end
386
- self.host = string
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
387
495
  end
388
496
 
389
497
  end
390
498
 
391
499
  class FormattingError < StandardError
392
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
393
513
  end
@@ -1,3 +1,3 @@
1
1
  module Furi
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -69,10 +69,13 @@ describe Furi do
69
69
  expect("http://gusiev.com").to have_parts(
70
70
  protocol: 'http',
71
71
  hostname: 'gusiev.com',
72
- query_string: nil,
72
+ query_string: "",
73
73
  query: {},
74
74
  path: nil,
75
+ path!: '/',
75
76
  port: nil,
77
+ request: '/',
78
+ resource: '/',
76
79
  )
77
80
  end
78
81
 
@@ -83,6 +86,8 @@ describe Furi do
83
86
  path: '/posts/index.html',
84
87
  port: nil,
85
88
  protocol: 'http',
89
+ resource: '/posts/index.html?a=b#zz',
90
+ request: '/posts/index.html?a=b',
86
91
  )
87
92
  end
88
93
 
@@ -100,7 +105,7 @@ describe Furi do
100
105
  username: 'user',
101
106
  password: 'pass',
102
107
  hostname: 'gusiev.com',
103
- query_string: nil,
108
+ query_string: "",
104
109
  anchor: nil,
105
110
  )
106
111
  end
@@ -110,7 +115,7 @@ describe Furi do
110
115
  username: 'user',
111
116
  password: nil,
112
117
  hostname: 'gusiev.com',
113
- query_string: nil,
118
+ query_string: "",
114
119
  anchor: nil,
115
120
  )
116
121
  end
@@ -130,14 +135,16 @@ describe Furi do
130
135
  userinfo: 'user:pass',
131
136
  protocol: 'http',
132
137
  port: 80,
133
- query_string: nil,
138
+ query_string: "",
134
139
  )
135
140
  end
136
141
  it "parses url with query" do
137
142
  expect("/index.html?a=b&c=d").to have_parts(
138
143
  host: nil,
144
+ host!: '',
139
145
  query_string: 'a=b&c=d',
140
- query: {'a' => 'b', 'c' => 'd'}
146
+ query: {'a' => 'b', 'c' => 'd'},
147
+ request: '/index.html?a=b&c=d',
141
148
  )
142
149
  end
143
150
 
@@ -148,6 +155,23 @@ describe Furi do
148
155
  "port!" => 80
149
156
  )
150
157
  end
158
+ it "parses nested query" do
159
+ expect("gusiev.com?a[]=1&a[]=2&b[c]=1&b[d]=2").to have_parts(
160
+ host: 'gusiev.com',
161
+ query: {"a" => ["1","2"], "b" => {"c" => "1", "d" => "2"}},
162
+ )
163
+ end
164
+
165
+ it "find out protocol security" do
166
+ expect("gusiev.com:443").to have_parts(
167
+ host: 'gusiev.com',
168
+ :"ssl" => false
169
+ )
170
+ expect("https://gusiev.com:443").to have_parts(
171
+ host: 'gusiev.com',
172
+ :"ssl" => true
173
+ )
174
+ end
151
175
  end
152
176
  describe ".update" do
153
177
 
@@ -160,6 +184,8 @@ describe Furi do
160
184
  expect(Furi.update("/index.html", hostname: 'gusiev.com')).to eq('gusiev.com/index.html')
161
185
  expect(Furi.update("http://www.gusiev.com/index.html", hostname: 'gusiev.com')).to eq('http://gusiev.com/index.html')
162
186
  expect(Furi.update("/index.html", hostname: 'gusiev.com')).to eq('gusiev.com/index.html')
187
+ expect(Furi.update("gusiev.com/index.html?a=b", hostname: nil)).to eq('/index.html?a=b')
188
+ expect(Furi.update("gusiev.com?a=b", hostname: nil)).to eq('/?a=b')
163
189
  end
164
190
 
165
191
  it "updates port" do
@@ -168,6 +194,13 @@ describe Furi do
168
194
  expect(Furi.update("gusiev.com:33/index.html", port: 80)).to eq('gusiev.com:80/index.html')
169
195
  expect(Furi.update("http://gusiev.com:33/index.html", port: 80)).to eq('http://gusiev.com/index.html')
170
196
  end
197
+
198
+ it "updates ssl" do
199
+ expect(Furi.update("http://gusiev.com", ssl: true)).to eq('https://gusiev.com')
200
+ expect(Furi.update("https://gusiev.com", ssl: true)).to eq('https://gusiev.com')
201
+ expect(Furi.update("https://gusiev.com", ssl: false)).to eq('http://gusiev.com')
202
+ expect(Furi.update("http://gusiev.com", ssl: false)).to eq('http://gusiev.com')
203
+ end
171
204
  it "updates protocol" do
172
205
  expect(Furi.update("http://gusiev.com", protocol: '')).to eq('//gusiev.com')
173
206
  expect(Furi.update("http://gusiev.com", protocol: nil)).to eq('gusiev.com')
@@ -187,9 +220,42 @@ describe Furi do
187
220
  expect(Furi.build(hostname: 'hello.com', port: 88)).to eq('hello.com:88')
188
221
  expect(Furi.build(schema: 'https', hostname: 'hello.com', port: 88)).to eq('https://hello.com:88')
189
222
  expect(Furi.build(schema: 'http', hostname: 'hello.com', port: 80)).to eq('http://hello.com')
223
+ expect(Furi.build(path: '/index.html', query: {a: 1, b: 2})).to eq('/index.html?a=1&b=2')
190
224
  end
191
225
  end
192
226
 
227
+ describe ".merge" do
228
+ it "should work" do
229
+ expect(Furi.merge("//gusiev.com", query: {a: 1})).to eq('//gusiev.com?a=1')
230
+ expect(Furi.merge("//gusiev.com?a=1", query: {b: 2})).to eq('//gusiev.com?a=1&b=2')
231
+ expect(Furi.merge("//gusiev.com?a=1", query: {a: 2})).to eq('//gusiev.com?a=2')
232
+ expect(Furi.merge("//gusiev.com?a=1", query: [['a', 2], ['b', 3]])).to eq('//gusiev.com?a=1&a=2&b=3')
233
+ expect(Furi.merge("//gusiev.com?a=1&b=2", query: '?a=3')).to eq('//gusiev.com?a=1&b=2&a=3')
234
+ end
235
+ end
236
+
237
+ describe "#==" do
238
+ it "should work" do
239
+ expect(Furi.parse('http://gusiev.com:80') == Furi.parse('http://gusiev.com')).to be_truthy
240
+ expect(Furi.parse('http://gusiev.com') == Furi.parse('https://gusiev.com')).to be_falsey
241
+ expect(Furi.parse('http://gusiev.com') == Furi.parse('http://gusiev.com')).to be_truthy
242
+ expect(Furi.parse('http://gusiev.com.ua') == Furi.parse('http://gusiev.com')).to be_falsey
243
+ expect(Furi.parse('http://gusiev.com?a=1&a=1') == Furi.parse('http://gusiev.com?a=1')).to be_falsey
244
+ end
245
+ end
246
+
247
+ describe "#clone" do
248
+ it "should work" do
249
+
250
+ uri = Furi.parse('http://gusiev.com')
251
+ expect(uri.clone == uri).to be_truthy
252
+ expect(uri.clone.merge_query([[:a, 1]]) == uri).to be_falsey
253
+ expect(Furi.parse('http://gusiev.com') == Furi.parse('https://gusiev.com')).to be_falsey
254
+ expect(Furi.parse('http://gusiev.com') == Furi.parse('http://gusiev.com')).to be_truthy
255
+ expect(Furi.parse('http://gusiev.com.ua') == Furi.parse('http://gusiev.com')).to be_falsey
256
+ expect(Furi.parse('http://gusiev.com?a=1&a=1') == Furi.parse('http://gusiev.com?a=1')).to be_falsey
257
+ end
258
+ end
193
259
 
194
260
  describe "serialize" do
195
261
  it "should work" do
@@ -302,7 +368,6 @@ describe Furi do
302
368
 
303
369
  lambda { Furi.parse_nested_query("x[y]=1&x[y][][w]=2") }.
304
370
  should raise_error(TypeError, "expected Array (got String) for param `y'")
305
-
306
371
  end
307
372
 
308
373
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: furi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bogdan Gusiev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-07 00:00:00.000000000 Z
11
+ date: 2015-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler