Class: DomainExtractor::ParsedURL

Inherits:
Object
  • Object
show all
Defined in:
lib/domain_extractor/parsed_url.rb

Overview

ParsedURL wraps the parsing result and provides convenient accessor methods with support for bang (!) and question mark (?) variants.

Examples:

parsed = DomainExtractor.parse('https://api.example.com')
parsed.host           # => 'api.example.com'
parsed.subdomain      # => 'api'
parsed.subdomain?     # => true
parsed.www_subdomain? # => false

parsed = DomainExtractor.parse('invalid')
parsed.host           # => nil
parsed.host?          # => false
parsed.host!          # raises InvalidURLError

rubocop:disable Metrics/ClassLength

Constant Summary collapse

EMPTY_STRING =
''
RESULT_KEYS =

List of valid result keys that should have method accessors

i[
  subdomain domain tld root_domain host path query_params
  scheme port fragment user password userinfo decoded_user decoded_password
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(result, uri = nil) ⇒ ParsedURL

Returns a new instance of ParsedURL.



38
39
40
41
42
# File 'lib/domain_extractor/parsed_url.rb', line 38

def initialize(result, uri = nil)
  @result = (result || {}).dup
  @uri = uri
  sync_uri_state!
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args) ⇒ Object

Dynamically handle method calls for all result keys Supports three variants:

  • method_name: returns value or nil

  • method_name!: returns value or raises InvalidURLError

  • method_name?: returns boolean (true if value exists and not nil/empty)



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/domain_extractor/parsed_url.rb', line 65

def method_missing(method_name, *args, &)
  method_str = method_name.to_s

  # Handle bang methods (method_name!)
  return handle_bang_method(method_str) if method_str.end_with?('!')

  # Handle question mark methods (method_name?)
  return handle_question_method(method_str) if method_str.end_with?('?')

  # Handle regular methods (method_name)
  key = method_name.to_sym
  return @result[key] if RESULT_KEYS.include?(key)

  super
end

Instance Attribute Details

#resultObject (readonly)

Expose the underlying hash for backward compatibility



27
28
29
# File 'lib/domain_extractor/parsed_url.rb', line 27

def result
  @result
end

#uriObject (readonly)

Store the original URI object for advanced operations



30
31
32
# File 'lib/domain_extractor/parsed_url.rb', line 30

def uri
  @uri
end

Instance Method Details

#[](key) ⇒ Object

Hash-style access for backward compatibility result, result, etc.



46
47
48
# File 'lib/domain_extractor/parsed_url.rb', line 46

def [](key)
  @result[key]
end

#absolute?Boolean

Check if this is an absolute URI

Returns:

  • (Boolean)

    True if absolute



254
255
256
# File 'lib/domain_extractor/parsed_url.rb', line 254

def absolute?
  !@result[:scheme].nil?
end

#basic_auth_headerString?

Generate Basic Authentication header from current credentials

Returns:

  • (String, nil)

    Authorization header value or nil if no credentials



212
213
214
215
216
# File 'lib/domain_extractor/parsed_url.rb', line 212

def basic_auth_header
  return nil if user.nil? || password.nil?

  URIHelpers.basic_auth_header(decoded_user || user, decoded_password || password)
end

#bearer_auth_header(token) ⇒ String

Generate Bearer token header

Parameters:

  • token (String)

    The bearer token

Returns:

  • (String)

    Authorization header value



221
222
223
# File 'lib/domain_extractor/parsed_url.rb', line 221

def bearer_auth_header(token)
  URIHelpers.bearer_auth_header(token)
end

#build_urlString

Build a complete URL string from components

Returns:

  • (String)

    The complete URL



272
273
274
# File 'lib/domain_extractor/parsed_url.rb', line 272

def build_url
  to_s
end

#default_portInteger?

Get the default port for the scheme

Returns:

  • (Integer, nil)

    Default port or nil



266
267
268
# File 'lib/domain_extractor/parsed_url.rb', line 266

def default_port
  URIHelpers.default_port_for(@uri || scheme)
end

#find_proxyURI::Generic?

Find proxy for this URL

Returns:

  • (URI::Generic, nil)

    Proxy URI or nil



227
228
229
230
231
# File 'lib/domain_extractor/parsed_url.rb', line 227

def find_proxy
  return nil unless @uri

  URIHelpers.find_proxy(@uri)
end

#fragmentObject



137
138
139
# File 'lib/domain_extractor/parsed_url.rb', line 137

def fragment
  @result[:fragment]
end

#fragment=(value) ⇒ Object



192
193
194
# File 'lib/domain_extractor/parsed_url.rb', line 192

def fragment=(value)
  mutate_uri! { @uri.fragment = value }
end

#hostObject



125
126
127
# File 'lib/domain_extractor/parsed_url.rb', line 125

def host
  @result[:host]
end

#host=(value) ⇒ Object



172
173
174
# File 'lib/domain_extractor/parsed_url.rb', line 172

def host=(value)
  mutate_uri! { replace_host(value) }
end

#hostnameObject

hostname returns host without IPv6 brackets (URI compatibility)



154
155
156
157
158
# File 'lib/domain_extractor/parsed_url.rb', line 154

def hostname
  return nil unless @uri || host

  @uri&.hostname || host.to_s.gsub(/^\[|\]$/, '')
end

#hostname=(value) ⇒ Object



176
177
178
# File 'lib/domain_extractor/parsed_url.rb', line 176

def hostname=(value)
  self.host = value
end

#inspectObject

Provide hash-like inspection



100
101
102
# File 'lib/domain_extractor/parsed_url.rb', line 100

def inspect
  "#<DomainExtractor::ParsedURL #{@result.inspect}>"
end

#merge(relative) ⇒ ParsedURL

Merge with a relative URI

Parameters:

  • relative (String, URI::Generic)

    The relative URI

Returns:

  • (ParsedURL)

    New ParsedURL with merged URI



236
237
238
239
240
241
# File 'lib/domain_extractor/parsed_url.rb', line 236

def merge(relative)
  return self unless @uri

  merged_uri = URIHelpers.merge_uri(@uri, relative)
  DomainExtractor.parse(merged_uri.to_s)
end

#normalizeParsedURL

Normalize the URI (lowercase scheme/host, remove default ports)

Returns:

  • (ParsedURL)

    New ParsedURL with normalized URI



245
246
247
248
249
250
# File 'lib/domain_extractor/parsed_url.rb', line 245

def normalize
  return self unless @uri

  normalized_uri = URIHelpers.normalize_uri(@uri)
  DomainExtractor.parse(normalized_uri.to_s)
end

#passwordObject



145
146
147
# File 'lib/domain_extractor/parsed_url.rb', line 145

def password
  @result[:password]
end

#password=(value) ⇒ Object



200
201
202
# File 'lib/domain_extractor/parsed_url.rb', line 200

def password=(value)
  mutate_uri! { @uri.password = value }
end

#pathObject



133
134
135
# File 'lib/domain_extractor/parsed_url.rb', line 133

def path
  @result[:path]
end

#path=(value) ⇒ Object



184
185
186
# File 'lib/domain_extractor/parsed_url.rb', line 184

def path=(value)
  mutate_uri! { @uri.path = value.to_s }
end

#portObject



129
130
131
# File 'lib/domain_extractor/parsed_url.rb', line 129

def port
  @result[:port]
end

#port=(value) ⇒ Object



180
181
182
# File 'lib/domain_extractor/parsed_url.rb', line 180

def port=(value)
  mutate_uri! { @uri.port = value }
end

#queryObject

query returns the query string (not parsed params)



161
162
163
164
165
# File 'lib/domain_extractor/parsed_url.rb', line 161

def query
  return nil unless @uri

  @uri.query
end

#query=(value) ⇒ Object



188
189
190
# File 'lib/domain_extractor/parsed_url.rb', line 188

def query=(value)
  mutate_uri! { @uri.query = value }
end

#relative?Boolean

Check if this is a relative URI

Returns:

  • (Boolean)

    True if relative



260
261
262
# File 'lib/domain_extractor/parsed_url.rb', line 260

def relative?
  @result[:scheme].nil?
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/domain_extractor/parsed_url.rb', line 81

def respond_to_missing?(method_name, include_private = false)
  method_str = method_name.to_s

  # Check for www_subdomain? special case
  return true if method_name == :www_subdomain?

  # Check if it's a bang or question mark variant
  if method_str.end_with?('!') || method_str.end_with?('?')
    key = method_str[0...-1].to_sym
    return true if RESULT_KEYS.include?(key)
  end

  # Check if it's a regular method
  return true if RESULT_KEYS.include?(method_name.to_sym)

  super
end

#schemeObject



121
122
123
# File 'lib/domain_extractor/parsed_url.rb', line 121

def scheme
  @result[:scheme]
end

#scheme=(value) ⇒ Object

Setter methods for URI compatibility



168
169
170
# File 'lib/domain_extractor/parsed_url.rb', line 168

def scheme=(value)
  mutate_uri! { @uri.scheme = normalize_scheme(value) }
end

#to_hObject Also known as: to_hash

Allow to_h conversion for hash compatibility



111
112
113
# File 'lib/domain_extractor/parsed_url.rb', line 111

def to_h
  @result.dup
end

#to_sObject Also known as: to_str



104
105
106
107
108
# File 'lib/domain_extractor/parsed_url.rb', line 104

def to_s
  return EMPTY_STRING unless valid? && @uri

  @uri.to_s
end

#userObject



141
142
143
# File 'lib/domain_extractor/parsed_url.rb', line 141

def user
  @result[:user]
end

#user=(value) ⇒ Object



196
197
198
# File 'lib/domain_extractor/parsed_url.rb', line 196

def user=(value)
  mutate_uri! { @uri.user = value }
end

#userinfoObject



149
150
151
# File 'lib/domain_extractor/parsed_url.rb', line 149

def userinfo
  @result[:userinfo]
end

#userinfo=(value) ⇒ Object



204
205
206
# File 'lib/domain_extractor/parsed_url.rb', line 204

def userinfo=(value)
  mutate_uri! { @uri.userinfo = value }
end

#valid?Boolean

Check if the parsed result is valid (not nil/empty)

Returns:

  • (Boolean)


51
52
53
# File 'lib/domain_extractor/parsed_url.rb', line 51

def valid?
  !@result.empty?
end

#www_subdomain?Boolean

Special helper: check if subdomain is specifically ‘www’

Returns:

  • (Boolean)


56
57
58
# File 'lib/domain_extractor/parsed_url.rb', line 56

def www_subdomain?
  @result[:subdomain] == 'www'
end