class SitePrism::AddressableUrlMatcher

SitePrism::AddressableUrlMatcher

Used in a couple of places to allow users to …

Specify patterns for loading webpages
Pass in hashable args or query parameters for loading dynamic pages

Attributes

pattern[R]

Public Class Methods

new(pattern) click to toggle source
# File lib/site_prism/addressable_url_matcher.rb, line 14
def initialize(pattern)
  @pattern = pattern
end

Public Instance Methods

mappings(url) click to toggle source

@return the hash of extracted mappings from parsing the provided URL according to our pattern, or nil if the URL doesn't conform to the matcher template.

# File lib/site_prism/addressable_url_matcher.rb, line 21
def mappings(url)
  uri = Addressable::URI.parse(url)
  result = {}
  component_names.each do |component|
    component_result = component_matches(component, uri)
    return nil unless component_result

    result.merge!(component_result)
  end
  result
end
matches?(url, expected_mappings = {}) click to toggle source

Determine whether URL matches our pattern, and optionally whether the extracted mappings match a hash of expected values. You can specify values as strings, numbers or regular expressions.

# File lib/site_prism/addressable_url_matcher.rb, line 37
def matches?(url, expected_mappings = {})
  actual_mappings = mappings(url)
  return false unless actual_mappings

  expected_mappings.empty? ||
    all_expected_mappings_match?(expected_mappings, actual_mappings)
end

Private Instance Methods

all_expected_mappings_match?(expected_mappings, actual_mappings) click to toggle source
# File lib/site_prism/addressable_url_matcher.rb, line 47
def all_expected_mappings_match?(expected_mappings, actual_mappings)
  expected_mappings.all? do |key, expected_value|
    actual_value = actual_mappings[key.to_s]
    case expected_value
    when Numeric; then actual_value == expected_value.to_s
    when Regexp;  then actual_value.match(expected_value)
    else               expected_value == actual_value
    end
  end
end
component_matches(component, uri) click to toggle source

Returns empty hash if the template omits the component or a set of substitutions if the provided URI component matches the template component or nil if the match fails.

# File lib/site_prism/addressable_url_matcher.rb, line 79
def component_matches(component, uri)
  component_template = component_templates[component]
  return {} unless component_template

  component_url = uri.public_send(component).to_s
  mappings = component_template.extract(component_url)
  return mappings if mappings

  # to support Addressable's expansion of queries
  # ensure it's parsing the fragment as appropriate (e.g. {?params*})
  prefix = component_prefixes[component]
  return nil unless prefix

  component_template.extract(prefix + component_url)
end
component_names() click to toggle source
# File lib/site_prism/addressable_url_matcher.rb, line 145
def component_names
  %i[scheme user password host port path query fragment]
end
component_prefixes() click to toggle source
# File lib/site_prism/addressable_url_matcher.rb, line 149
def component_prefixes
  { query: '?', fragment: '#' }
end
component_templates() click to toggle source
# File lib/site_prism/addressable_url_matcher.rb, line 58
def component_templates
  @component_templates ||= extract_component_templates
end
extract_component_templates() click to toggle source
# File lib/site_prism/addressable_url_matcher.rb, line 62
def extract_component_templates
  component_names.each_with_object({}) do |component, templates|
    component_url = to_substituted_uri.public_send(component).to_s

    next unless component_url && !component_url.empty?

    reverse_substitutions.each_pair do |substituted_value, template_value|
      component_url = component_url.sub(substituted_value, template_value)
    end

    templates[component] = Addressable::Template.new(component_url)
  end
end
reverse_substitutions() click to toggle source
# File lib/site_prism/addressable_url_matcher.rb, line 115
def reverse_substitutions
  @reverse_substitutions ||=
    slugs.each_with_index.reduce({}) do |memo, slug_index|
      slug, index = slug_index
      memo.merge(
        slug_prefix(slug) + substitution_value(index) => slug,
        substitution_value(index) => slug
      )
    end
end
slug_prefix(slug) click to toggle source

If a slug begins with non-alpha characters, it may denote the start of a new component (e.g. query or fragment). We emit this prefix as part of the substituted slug so that Addressable's URI parser can see it as such.

# File lib/site_prism/addressable_url_matcher.rb, line 133
def slug_prefix(slug)
  prefix = slug.match(/\A{([^A-Za-z]+)/)
  prefix && prefix[1] || ''
end
slugs() click to toggle source
# File lib/site_prism/addressable_url_matcher.rb, line 126
def slugs
  pattern.scan(/{[^}]+}/)
end
substitution_value(index) click to toggle source

Generate a repeatable 5 character uniform alphabetical nonsense string to allow parsing as a URI

# File lib/site_prism/addressable_url_matcher.rb, line 140
def substitution_value(index)
  sha = Digest::SHA1.digest(index.to_s)
  Base64.urlsafe_encode64(sha).gsub(/[^A-Za-z]/, '')[0..5]
end
substitutions() click to toggle source
# File lib/site_prism/addressable_url_matcher.rb, line 108
def substitutions
  @substitutions ||= slugs.each_with_index.reduce({}) do |memo, slug_index|
    slug, index = slug_index
    memo.merge(slug => slug_prefix(slug) + substitution_value(index))
  end
end
to_substituted_uri() click to toggle source

Convert the pattern into an Addressable URI by substituting the template slugs with nonsense strings.

# File lib/site_prism/addressable_url_matcher.rb, line 97
def to_substituted_uri
  url = pattern
  substitutions.each_pair { |slug, value| url = url.sub(slug, value) }
  begin
    Addressable::URI.parse(url)
  rescue Addressable::URI::InvalidURIError
    SitePrism.logger.fatal("Ensure you don't use templated port numbers.")
    raise SitePrism::InvalidUrlMatcherError
  end
end