module Furi

Constants

ALIASES
COMBINED_PARTS
DELEGATES
ESSENTIAL_PARTS
PARTS
PROTOCOLS
ROOT
SSL_MAPPING
VERSION
WEB_PROTOCOL

Public Class Methods

build(argument) click to toggle source

Builds an URL from given parts

Furi.build(path: "/dashboard", host: 'example.com', protocol: "https")
  # => "https://example.com/dashboard"
# File lib/furi.rb, line 73
def self.build(argument)
  Uri.new(argument).to_s
end
defaults(string, parts) click to toggle source

Puts the default values for given URL that are not defined

Furi.defaults("gusiev.com/hello.html", protocol: 'http', path: '/index.html')
  # => "http://gusiev.com/hello.html"
# File lib/furi.rb, line 100
def self.defaults(string, parts)
  parse(string).defaults(parts).to_s
end
join(*uris) click to toggle source
# File lib/furi.rb, line 172
def self.join(*uris)
  uris.map do |uri|
    Furi.parse(uri)
  end.reduce do |memo, uri|
    memo.send(:join, uri)
  end
end
merge(string, parts)
Alias for: update
parse(argument, parts = nil) click to toggle source

Parses a given string and return an URL object Optionally accepts parts to update the parsed URL object

# File lib/furi.rb, line 65
def self.parse(argument, parts = nil)
  Uri.new(argument).update(parts)
end
parse_query(query) click to toggle source

Parses a query into nested paramters hash using a rack convension with square brackets.

Furi.parse_query("a[]=1&a[]=2")       # => {a: [1,2]}
Furi.parse_query("p[email]=a&a[two]=2") # => {a: {one: 1, two: 2}}
Furi.parse_query("p[one]=1&a[two]=2") # => {a: {one: 1, two: 2}}
Furi.serialize({p: {name: 'Bogdan Gusiev', email: 'bogdan@example.com', data: {one: 1, two: 2}}})
  # => "p%5Bname%5D=Bogdan&p%5Bemail%5D=bogdan%40example.com&p%5Bdata%5D%5Bone%5D=1&p%5Bdata%5D%5Btwo%5D=2"
# File lib/furi.rb, line 124
def self.parse_query(query)
  return Furi::Utils.stringify_keys(query) if query.is_a?(Hash)

  params = {}
  query_tokens(query).each do |token|
    parse_query_token(params, token.name, token.value)
  end

  return params
end
query_tokens(query) click to toggle source

Parses query key/value pairs from query string and returns them raw without organising them into hashes and without normalising them.

Furi.query_tokens("a=1&b=2").map {|k,v| "#{k} -> #{v}"}  # => ['a -> 1', 'b -> 2']
Furi.query_tokens("a=1&a=1&a=2").map {|k,v| "#{k} -> #{v}"}  # => ['a -> 1', 'a -> 1', 'a -> 2']
Furi.query_tokens("name=Bogdan&email=bogdan%40example.com") # => [name=Bogdan, email=bogdan@example.com]
Furi.query_tokens("a[one]=1&a[two]=2") # => [a[one]=1, a[two]=2]
# File lib/furi.rb, line 142
def self.query_tokens(query)
  case query
  when Enumerable, Enumerator
    query.map do |token|
      QueryToken.parse(token)
    end
  when nil, ''
    []
  when String
    query.gsub(/\A\?/, '').split(/[&;] */n, -1).map do |p|
      QueryToken.parse(p)
    end
  else
    raise QueryParseError, "can not parse #{query.inspect} query tokens"
  end
end
replace(string, parts) click to toggle source

Replaces a given URL string with given parts. Same as update but works different for URL query parameter: replaces newly specified parameters instead of merging to existing ones

Furi.update("/hello.html?a=1", host: 'gusiev.com', query: {b: 2})
  # => "gusiev.com/hello.html?a=1&b=2"
# File lib/furi.rb, line 111
def self.replace(string, parts)
  parse(string).replace(parts).to_s
end
serialize(query, namespace = nil) click to toggle source

Serializes query parameters into query string. Optionaly accepts a basic name space.

Furi.serialize({a: 1, b: 2}) # => "a=1&b=2"
Furi.serialize({a: [1,2]}) # => "a[]=1&a[]=2"
Furi.serialize({a: {b: 1, c:2}}) # => "a[b]=1&a[c]=2"
Furi.serialize({name: 'Bogdan', email: 'bogdan@example.com'}, "person")
  # => "person[name]=Bogdan&person[email]=bogdan%40example.com"
# File lib/furi.rb, line 168
def self.serialize(query, namespace = nil)
  serialize_tokens(query, namespace).join("&")
end
update(string, parts) click to toggle source

Replaces a given URL string with given parts

Furi.update("http://gusiev.com", protocol: 'https', subdomain: 'www')
  # => "https://www.gusiev.com"
# File lib/furi.rb, line 89
def self.update(string, parts)
  parse(string).update(parts).to_s
end
Also aliased as: merge

Protected Class Methods

parse_query_token(params, name, value) click to toggle source
# File lib/furi.rb, line 228
def self.parse_query_token(params, name, value)
  name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
  namespace = $1 || ''
  after = $' || ''

  return if namespace.empty?

  current = params[namespace]
  if after == ""
    current = value
  elsif after == "[]"
    current ||= []
    unless current.is_a?(Array)
      raise QueryParseError, "expected Array (got #{current.class}) for param `#{namespace}'"
    end
    current << value
  elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
    child_key = $1
    current ||= []
    unless current.is_a?(Array)
      raise QueryParseError, "expected Array (got #{current.class}) for param `#{namespace}'"
    end
    if current.last.is_a?(Hash) && !current.last.key?(child_key)
      parse_query_token(current.last, child_key, value)
    else
      current << parse_query_token({}, child_key, value)
    end
  else
    current ||= {}
    unless current.is_a?(Hash)
      raise QueryParseError, "expected Hash (got #{current.class}) for param `#{namespace}'"
    end
    current = parse_query_token(current, after, value)
  end
  params[namespace] = current

  return params
end
serialize_tokens(query, namespace = nil) click to toggle source
# File lib/furi.rb, line 194
def self.serialize_tokens(query, namespace = nil)
  case query
  when Hash
    result = query.map do |key, value|
      unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
        serialize_tokens(value, namespace ? "#{namespace}[#{key}]" : key)
      else
        nil
      end
    end
    result.flatten!
    result.compact!
    result
  when Array
    if namespace.nil? || namespace.empty?
      raise FormattingError, "Can not serialize Array without namespace"
    end

    namespace = "#{namespace}[]"
    query.map do |item|
      if item.is_a?(Array)
        raise FormattingError, "Can not serialize #{item.inspect} as element of an Array"
      end
      serialize_tokens(item, namespace)
    end
  else
    if namespace
      QueryToken.new(namespace, query)
    else
      []
    end
  end
end