Class: Furi::Uri

Inherits:
Object
  • Object
show all
Defined in:
lib/furi/uri.rb

Instance Method Summary collapse

Constructor Details

#initialize(argument) ⇒ Uri

Returns a new instance of Uri.



20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/furi/uri.rb', line 20

def initialize(argument)
  @query_tokens = []
  case argument
  when String
    parse_uri_string(argument)
  when Hash
    replace(argument)
  when ::URI::Generic
    parse_uri_string(argument.to_s)
  else
    raise ParseError, "wrong Uri argument"
  end
end

Instance Method Details

#==(other) ⇒ Object



479
480
481
# File 'lib/furi/uri.rb', line 479

def ==(other)
  to_s == other.to_s
end

#[](part) ⇒ Object



492
493
494
# File 'lib/furi/uri.rb', line 492

def [](part)
  send(part)
end

#[]=(part, value) ⇒ Object



496
497
498
# File 'lib/furi/uri.rb', line 496

def []=(part, value)
  send(:"#{part}=", value)
end

#abstract_protocol? ⇒ Boolean

Returns:

  • (Boolean)


450
451
452
# File 'lib/furi/uri.rb', line 450

def abstract_protocol?
  protocol == ""
end

#anchor=(string) ⇒ Object



487
488
489
490
# File 'lib/furi/uri.rb', line 487

def anchor=(string)
  string = string.to_s
  @anchor = string.empty? ? nil : string
end

#authority ⇒ Object



169
170
171
172
# File 'lib/furi/uri.rb', line 169

def authority
  return hostinfo unless userinfo
  [userinfo, hostinfo].join("@")
end

#authority=(string) ⇒ Object



174
175
176
177
178
179
180
181
182
# File 'lib/furi/uri.rb', line 174

def authority=(string)
  if string.include?("@")
    userinfo, string = string.split("@", 2)
    self.userinfo = userinfo
  else
    self.userinfo = nil
  end
  self.hostinfo = string
end

#custom_port? ⇒ Boolean

Returns:

  • (Boolean)


519
520
521
# File 'lib/furi/uri.rb', line 519

def custom_port?
  port && port != default_port
end

#default_port ⇒ Object



415
416
417
# File 'lib/furi/uri.rb', line 415

def default_port
  Furi::PROTOCOLS.fetch(protocol, {})[:port]
end

#default_web_port? ⇒ Boolean

Returns:

  • (Boolean)


440
441
442
443
444
# File 'lib/furi/uri.rb', line 440

def default_web_port?
  Furi::WEB_PROTOCOL.any? do |web_protocol|
    Furi::PROTOCOLS[web_protocol][:port] == port!
  end
end

#defaults(parts) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/furi/uri.rb', line 62

def defaults(parts)
  parts.each do |part, value|
    case part.to_sym
    when :query, :query_tokens
      Furi.parse_query(value).each do |key, default_value|
        unless query.key?(key)
          query[key] = default_value
        end
      end
    else
      unless self[part]
        self[part] = value
      end
    end
  end
  self
end

#directory ⇒ Object



330
331
332
# File 'lib/furi/uri.rb', line 330

def directory
  path_tokens[0..-2].join("/")
end

#directory=(string) ⇒ Object



334
335
336
337
338
339
340
# File 'lib/furi/uri.rb', line 334

def directory=(string)
  string ||= "/"
  if file && string !~ %r{/\z}
    string += '/'
  end
  self.path = string + file.to_s
end

#domain ⇒ Object



132
133
134
# File 'lib/furi/uri.rb', line 132

def domain
  join_domain(parsed_host[1..2].flatten)
end

#domain=(new_domain) ⇒ Object



136
137
138
# File 'lib/furi/uri.rb', line 136

def domain=(new_domain)
  self.host= [subdomain, new_domain]
end

#domainname ⇒ Object



124
125
126
# File 'lib/furi/uri.rb', line 124

def domainname
  parsed_host[1]
end

#domainname=(new_domainname) ⇒ Object



128
129
130
# File 'lib/furi/uri.rb', line 128

def domainname=(new_domainname)
  self.domain = join_domain([subdomain, new_domainname, domainzone])
end

#domainzone ⇒ Object



116
117
118
# File 'lib/furi/uri.rb', line 116

def domainzone
  parsed_host.last
end

#domainzone=(new_zone) ⇒ Object



120
121
122
# File 'lib/furi/uri.rb', line 120

def domainzone=(new_zone)
  self.host = [subdomain, domainname, new_zone]
end

#email ⇒ Object



515
516
517
# File 'lib/furi/uri.rb', line 515

def email
  authority
end

#email=(email) ⇒ Object



510
511
512
513
# File 'lib/furi/uri.rb', line 510

def email=(email)
  self.protocol ||= "mailto"
  self.authority = email
end

#endpoint ⇒ Object



210
211
212
# File 'lib/furi/uri.rb', line 210

def endpoint
  [location, path].join
end

#endpoint=(string) ⇒ Object



214
215
216
217
218
219
220
# File 'lib/furi/uri.rb', line 214

def endpoint=(string)
  string ||= ""
  string = parse_protocol(string)
  authority, path = string.split("/", 2)
  self.authority = authority
  self.path = path ? "/#{path}" : nil
end

#extension ⇒ Object



353
354
355
356
357
# File 'lib/furi/uri.rb', line 353

def extension
  return nil unless file
  tokens = file_tokens[1..-1]
  tokens.any? ? tokens.join(".") : nil
end

#extension=(string) ⇒ Object



359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/furi/uri.rb', line 359

def extension=(string)
  tokens = file_tokens
  case tokens.size
  when 0
    raise Furi::FormattingError, "can not assign extension when there is no file"
  when 1
    tokens.push(string)
  else
    if string
      tokens = [tokens.first, string]
    else
      tokens.pop
    end
  end
  self.file = tokens.join(".")
end

#file ⇒ Object



431
432
433
434
# File 'lib/furi/uri.rb', line 431

def file
  result = path_tokens.last
  result == "" ? nil : result
end

#file! ⇒ Object



436
437
438
# File 'lib/furi/uri.rb', line 436

def file!
  file || ''
end

#file=(name) ⇒ Object



376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/furi/uri.rb', line 376

def file=(name)
  unless name
    return unless path
  else
    name = name.gsub(%r{\A/}, "")
  end

  self.path = path_tokens.tap do |p|
    filename_index = [p.size-1, 0].max
    p[filename_index] = name
  end.join("/")
end

#filename ⇒ Object



342
343
344
345
# File 'lib/furi/uri.rb', line 342

def filename
  return nil unless file
  file_tokens.first
end

#filename=(value) ⇒ Object



347
348
349
350
351
# File 'lib/furi/uri.rb', line 347

def filename=(value)
  t = file_tokens
  t[0] = value
  self.file = t.join(".")
end

#home_page? ⇒ Boolean

Returns:

  • (Boolean)


247
248
249
# File 'lib/furi/uri.rb', line 247

def home_page?
  path! == Furi::ROOT || path! == "/index.html"
end

#host! ⇒ Object



475
476
477
# File 'lib/furi/uri.rb', line 475

def host!
  host || ""
end

#host=(host) ⇒ Object



105
106
107
108
109
110
111
112
113
114
# File 'lib/furi/uri.rb', line 105

def host=(host)
  @host = case host
          when Array
            join_domain(host)
          when "", nil
            nil
          else
            host.to_s.downcase
          end
end

#hostinfo ⇒ Object



148
149
150
151
152
153
154
# File 'lib/furi/uri.rb', line 148

def hostinfo
  return host unless custom_port?
  if port && !host
    raise Furi::FormattingError, "can not build URI with port but without host"
  end
  [host, port].join(":")
end

#hostinfo=(string) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/furi/uri.rb', line 156

def hostinfo=(string)
  if string.match(%r{\A\[.+\]\z}) #ipv6 host
    self.host = string
  else
    if match = string.match(/\A(.+):(.*)\z/)
      self.host, self.port = match.captures
    else
      self.host = string
      self.port = nil
    end
  end
end

#inspect ⇒ Object



483
484
485
# File 'lib/furi/uri.rb', line 483

def inspect
  "#<#{self.class} #{to_s.inspect}>"
end

#location ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/furi/uri.rb', line 197

def location
  if protocol
    if !host && !mailto?
      raise Furi::FormattingError, "can not build URI with protocol but without host"
    end
    [
      protocol.empty? ? "" : "#{protocol}:", authority
    ].join(mailto? ? "" : "//")
  else
    authority
  end
end

#location=(string) ⇒ Object



222
223
224
225
226
227
228
# File 'lib/furi/uri.rb', line 222

def location=(string)
  string ||= ""
  string  = string.gsub(%r(/\Z), '')
  self.protocol = nil
  string = parse_protocol(string)
  self.authority = string
end

#mailto? ⇒ Boolean

Returns:

  • (Boolean)


523
524
525
# File 'lib/furi/uri.rb', line 523

def mailto?
  protocol == "mailto"
end

#merge_query(query) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/furi/uri.rb', line 80

def merge_query(query)
  case query
  when Hash
    self.query = Furi::Utils.deep_merge(
      self.query,
      Furi::Utils.stringify_keys(query)
    )
  when String, Array
    self.query_tokens += Furi.query_tokens(query)
  when nil
  else
    raise QueryParseError, "#{query.inspect} can not be merged"
  end
end

#password=(password) ⇒ Object



305
306
307
# File 'lib/furi/uri.rb', line 305

def password=(password)
  @password = password.nil? ? nil : password.to_s
end

#path! ⇒ Object



467
468
469
# File 'lib/furi/uri.rb', line 467

def path!
  path || Furi::ROOT
end

#path=(path) ⇒ Object



315
316
317
318
319
320
# File 'lib/furi/uri.rb', line 315

def path=(path)
  @path = path.to_s
  if !@path.empty? && !@path.start_with?("/")
    @path = "/" + @path
  end
end

#path_tokens ⇒ Object



389
390
391
392
# File 'lib/furi/uri.rb', line 389

def path_tokens
  return [] unless path
  path.split("/", -1)
end

#port! ⇒ Object



411
412
413
# File 'lib/furi/uri.rb', line 411

def port!
  port || default_port
end

#port=(port) ⇒ Object



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/furi/uri.rb', line 272

def port=(port)
  @port = case port
          when String
            if port.empty?
              nil
            else
              unless port =~ /\A\s*\d+\s*\z/
                raise ArgumentError, "port should be an Integer >= 0"
              end
              port.to_i
            end
          when Integer
            if port < 0
              raise ArgumentError, "port should be an Integer >= 0"
            end
            port
          when nil
            nil
          else
            raise ArgumentError, "can not parse port: #{port.inspect}"
          end
  @port
end

#protocol! ⇒ Object



326
327
328
# File 'lib/furi/uri.rb', line 326

def protocol!
  protocol || default_protocol_for_port || 'http' # Web Rules Them All!
end

#protocol=(protocol) ⇒ Object



322
323
324
# File 'lib/furi/uri.rb', line 322

def protocol=(protocol)
  @protocol = protocol ? protocol.gsub(%r{:?/?/?\Z}, "").downcase : nil
end

#query ⇒ Object



251
252
253
254
# File 'lib/furi/uri.rb', line 251

def query
  return @query if query_level?
  @query = Furi.parse_query(query_tokens)
end

#query=(value) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/furi/uri.rb', line 257

def query=(value)
  case value
  when true
    # Assuming that current query needs to be parsed to Hash
    query
  when String, Array
    self.query_tokens = value
  when Hash
    self.query_tokens = value
  when nil
  else
    raise QueryParseError, 'Query can only be Hash or String'
  end
end

#query_string ⇒ Object



395
396
397
398
399
400
401
# File 'lib/furi/uri.rb', line 395

def query_string
  if query_level?
    Furi.serialize(query)
  else
    query_tokens.any? ? query_tokens.join("&") : nil
  end
end

#query_string! ⇒ Object



403
404
405
# File 'lib/furi/uri.rb', line 403

def query_string!
  query_string || ""
end

#query_string=(string) ⇒ Object



407
408
409
# File 'lib/furi/uri.rb', line 407

def query_string=(string)
  self.query_tokens = string.to_s
end

#query_tokens=(tokens) ⇒ Object



296
297
298
299
# File 'lib/furi/uri.rb', line 296

def query_tokens=(tokens)
  @query = tokens.is_a?(Hash) ? tokens : nil
  @query_tokens = Furi.query_tokens(tokens)
end

#replace(parts) ⇒ Object



34
35
36
37
38
39
40
41
# File 'lib/furi/uri.rb', line 34

def replace(parts)
  if parts
    parts.each do |part, value|
      self[part] = value
    end
  end
  self
end

#request ⇒ Object



230
231
232
233
234
235
236
# File 'lib/furi/uri.rb', line 230

def request
  return nil if !path && query_tokens.empty?
  result = []
  result << path!
  result << "?" << query_string if query_tokens.any?
  result.join
end

#request! ⇒ Object



238
239
240
# File 'lib/furi/uri.rb', line 238

def request!
  request || path!
end

#request=(string) ⇒ Object



242
243
244
245
# File 'lib/furi/uri.rb', line 242

def request=(string)
  string = parse_anchor_and_query(string)
  self.path = string
end

#resource ⇒ Object



454
455
456
457
# File 'lib/furi/uri.rb', line 454

def resource
  return nil unless request
  request + encoded_anchor
end

#resource! ⇒ Object



471
472
473
# File 'lib/furi/uri.rb', line 471

def resource!
  resource || request!
end

#resource=(value) ⇒ Object



459
460
461
462
463
464
465
# File 'lib/furi/uri.rb', line 459

def resource=(value)
  self.anchor = nil
  self.query_tokens = []
  self.path = nil
  value = parse_anchor_and_query(value)
  self.path = value
end

#rfc3986? ⇒ Boolean

Returns:

  • (Boolean)


504
505
506
507
508
# File 'lib/furi/uri.rb', line 504

def rfc3986?
  uri = to_s
  !!(uri.match(URI::RFC3986_Parser::RFC3986_URI) ||
     uri.match(URI::RFC3986_Parser::RFC3986_relative_ref))
end

#rfc? ⇒ Boolean

Returns:

  • (Boolean)


500
501
502
# File 'lib/furi/uri.rb', line 500

def rfc?
  rfc3986?
end

#ssl ⇒ Object



423
424
425
# File 'lib/furi/uri.rb', line 423

def ssl
  ssl?
end

#ssl=(ssl) ⇒ Object



427
428
429
# File 'lib/furi/uri.rb', line 427

def ssl=(ssl)
  self.protocol = find_protocol_for_ssl(ssl)
end

#ssl? ⇒ Boolean

Returns:

  • (Boolean)


419
420
421
# File 'lib/furi/uri.rb', line 419

def ssl?
  !!(Furi::PROTOCOLS.fetch(protocol, {})[:ssl])
end

#subdomain ⇒ Object



140
141
142
# File 'lib/furi/uri.rb', line 140

def subdomain
  parsed_host.first
end

#subdomain=(new_subdomain) ⇒ Object



144
145
146
# File 'lib/furi/uri.rb', line 144

def subdomain=(new_subdomain)
  self.host = [new_subdomain, domain]
end

#to_s ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/furi/uri.rb', line 184

def to_s
  result = []
  result << location
  result << (host || mailto? ? path : path!)
  if query_tokens.any?
    result << "?" << query_string
  end
  if anchor
    result << encoded_anchor
  end
  result.join
end

#update(parts) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/furi/uri.rb', line 43

def update(parts)
  return self unless parts
  parts.each do |part, value|
    case part.to_sym
    when :query, :query_tokens, :query_string
      merge_query(value)
    when :path
      if value && self[part]
        self[part] = Pathname.new(self[part]).join(value).to_s
      else
        self[part] = value
      end
    else
      self[part] = value
    end
  end
  self
end

#userinfo ⇒ Object



95
96
97
98
99
100
101
102
103
# File 'lib/furi/uri.rb', line 95

def userinfo
  if username
    [username, password].compact.join(":")
  elsif password
    raise Furi::FormattingError, "can not build URI with password but without username"
  else
    nil
  end
end

#userinfo=(userinfo) ⇒ Object



309
310
311
312
313
# File 'lib/furi/uri.rb', line 309

def userinfo=(userinfo)
  username, password = (userinfo || "").split(":", 2)
  self.username = username
  self.password = password
end

#username=(username) ⇒ Object



301
302
303
# File 'lib/furi/uri.rb', line 301

def username=(username)
  @username = username.nil? ? nil : username.to_s
end

#web_protocol? ⇒ Boolean

Returns:

  • (Boolean)


446
447
448
# File 'lib/furi/uri.rb', line 446

def web_protocol?
  Furi::WEB_PROTOCOL.include?(protocol)
end