Module: LinkIO::Utils

Defined in:
lib/linkio/utils.rb

Overview

Stateless helpers for fingerprinting, platform detection and deep link building. Mirrors the utilities from the Node.js LinkIO backend.

Constant Summary collapse

UNRESERVED =

Characters left unescaped by JavaScript's encodeURIComponent.

/[^A-Za-z0-9\-_.!~*'()]/n

Class Method Summary collapse

Class Method Details

Build a custom-scheme deep link URI.

Parameters:

  • scheme (String)

    e.g. "rokart"

  • path (String)

    e.g. "link"

  • params (Hash)

Returns:

  • (String)

    e.g. "rokart://link?foo=bar"



83
84
85
86
# File 'lib/linkio/utils.rb', line 83

def build_deep_link(scheme, path, params)
  query = encode_query(params)
  "#{scheme}://#{path}#{"?#{query}" unless query.empty?}"
end

.client_ip(forwarded_for: nil, ip: nil, remote_address: nil) ⇒ String

Extract the client IP from request data, honouring the X-Forwarded-For header set by proxies.

Parameters:

  • forwarded_for (String, nil) (defaults to: nil)

    value of the X-Forwarded-For header

  • ip (String, nil) (defaults to: nil)

    framework-resolved remote IP

  • remote_address (String, nil) (defaults to: nil)

    raw socket remote address

Returns:

  • (String)

    the resolved client IP, or "unknown"



41
42
43
44
45
46
# File 'lib/linkio/utils.rb', line 41

def client_ip(forwarded_for: nil, ip: nil, remote_address: nil)
  return forwarded_for.to_s.split(",").first.to_s.strip if forwarded_for && !forwarded_for.to_s.empty?

  value = ip || remote_address
  value.nil? || value.to_s.empty? ? "unknown" : value.to_s
end

.detect_platform(user_agent) ⇒ String

Detect the platform from a User-Agent string.

Parameters:

  • user_agent (String, nil)

Returns:

  • (String)


52
53
54
# File 'lib/linkio/utils.rb', line 52

def detect_platform(user_agent)
  Platform.detect(user_agent)
end

.encode_query(params) ⇒ String

URL-encode a params hash into a query string, matching the Node backend's encodeURIComponent behaviour.

Parameters:

  • params (Hash)

Returns:

  • (String)


93
94
95
96
97
# File 'lib/linkio/utils.rb', line 93

def encode_query(params)
  params.map do |key, value|
    "#{escape_component(key)}=#{escape_component(value)}"
  end.join("&")
end

.escape_component(value) ⇒ String

Percent-encode a single component the way JavaScript's encodeURIComponent does (spaces become "%20", not "+").

Parameters:

  • value (#to_s)

Returns:

  • (String)


104
105
106
# File 'lib/linkio/utils.rb', line 104

def escape_component(value)
  value.to_s.b.gsub(UNRESERVED) { |byte| format("%%%02X", byte.ord) }
end

.generate_fingerprint(ip, user_agent) ⇒ String

Generate a fingerprint from IP + User-Agent for deferred deep linking. This allows matching users before and after app install.

Parameters:

  • ip (String)
  • user_agent (String)

Returns:

  • (String)

    32-character hex digest



21
22
23
# File 'lib/linkio/utils.rb', line 21

def generate_fingerprint(ip, user_agent)
  Digest::SHA256.hexdigest("#{ip}|#{user_agent}")[0, 32]
end

.generate_ip_fingerprint(ip) ⇒ String

Generate an IP-only fingerprint for cross-browser/app matching. Less precise but works when User-Agent differs between browser and app.

Parameters:

  • ip (String)

Returns:

  • (String)

    32-character hex digest



30
31
32
# File 'lib/linkio/utils.rb', line 30

def generate_ip_fingerprint(ip)
  Digest::SHA256.hexdigest(ip.to_s)[0, 32]
end

.interpolate(template, params) ⇒ String

Substitute {name} placeholders in a template with URL-encoded values from params (string or symbol keys). Missing keys become empty strings. Used to build role-specific fallback URLs, e.g. "https://example.com/refer?code=code&role=role"

Parameters:

  • template (String)
  • params (Hash)

Returns:

  • (String)


116
117
118
119
120
121
122
123
# File 'lib/linkio/utils.rb', line 116

def interpolate(template, params)
  template.to_s.gsub(/\{(\w+)\}/) do
    key = Regexp.last_match(1)
    value = params[key]
    value = params[key.to_sym] if value.nil? && params.respond_to?(:key?)
    escape_component(value)
  end
end

.parse_query_params(url) ⇒ Hash{String => String}

Parse the query parameters of a URL into a string-keyed hash.

Parameters:

  • url (String)

Returns:

  • (Hash{String => String})


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/linkio/utils.rb', line 60

def parse_query_params(url)
  query = URI.parse(url).query
  return {} if query.nil? || query.empty?

  params = {}
  URI.decode_www_form(query).each do |key, value|
    params[key] = if params.key?(key)
                    Array(params[key]) << value
                  else
                    value
                  end
  end
  params
rescue URI::InvalidURIError, ArgumentError
  {}
end