class Imgix::Path

Constants

ALIASES

Public Class Methods

new(prefix, secure_url_token, path = "/") click to toggle source
# File lib/imgix/path.rb, line 10
def initialize(prefix, secure_url_token, path = "/")
  @prefix = prefix
  @secure_url_token = secure_url_token
  @path = path
  @options = {}
end

Public Instance Methods

defaults() click to toggle source
# File lib/imgix/path.rb, line 33
def defaults
  @options = {}
  self
end
ixlib(lib_version) click to toggle source
# File lib/imgix/path.rb, line 38
def ixlib(lib_version)
  @options[:ixlib] = lib_version
end
ixlib=(lib_version) click to toggle source
# File lib/imgix/path.rb, line 42
def ixlib=(lib_version)
  @options[:ixlib] = lib_version
end
method_missing(method, *args, &block) click to toggle source

Define query parameters on a Path (via method_missing). Normally, when overriding method_missing, it is a best practice to fall back to super, but this method works differently.

method_missing intercepts messages sent to objects of this class and acts as a getter, setter, and deleter. If there are no args, e.g. ‘path.width`, then this method acts like a getter.

Likewise, if the first argument is nil and the method name exists as a key in @options, e.g. ‘path.param_name = nil`, then this method acts like a deleter and the `param_name` is removed from the list of @options.

Finally, in all other cases, the ‘method` name is used as the `key` and the `*args` are used as the value.

# File lib/imgix/path.rb, line 102
def method_missing(method, *args, &block)
  key = method.to_s.gsub('=', '')

  if args.length == 0 # Get, or
    return @options[key]
  elsif args.first.nil? && @options.has_key?(key) # Delete, or
    @options.delete(key) and return self
  end

  @options[key] = args.join(',') # Set the option.
  self
end
to_srcset(options: {}, **params) click to toggle source
# File lib/imgix/path.rb, line 46
def to_srcset(options: {}, **params)
  prev_options = @options.dup
  @options.merge!(params)

  width = @options[:w]
  height = @options[:h]
  aspect_ratio = @options[:ar]

  srcset = if width || height
      build_dpr_srcset(options: options, params: @options)
    else
      build_srcset_pairs(options: options, params: @options)
    end

  @options = prev_options
  srcset
end
to_url(params = {}, options = {}) click to toggle source
# File lib/imgix/path.rb, line 17
def to_url(params = {}, options = {})
  sanitized_path = sanitize_path(@path, options)
  prev_options = @options.dup
  @options.merge!(params)

  current_path_and_params = path_and_params(sanitized_path)
  url = @prefix + current_path_and_params

  if @secure_url_token
    url += (has_query? ? "&" : "?") + "s=#{signature(current_path_and_params)}"
  end

  @options = prev_options
  url
end

Private Instance Methods

build_dpr_srcset(options:, params:) click to toggle source
# File lib/imgix/path.rb, line 218
def build_dpr_srcset(options:, params:)
  srcset = ""

  disable_variable_quality = options[:disable_variable_quality] || false
  validate_variable_qualities!(disable_variable_quality)

  target_ratios = [1, 2, 3, 4, 5]
  quality = params[:q]

  target_ratios.each do |ratio|
    params[:dpr] = ratio

    params[:q] = quality || DPR_QUALITY[ratio] unless disable_variable_quality

    srcset += "#{to_url(params, options)} #{ratio}x,\n"
  end

  srcset[0..-3]
end
build_srcset_pairs(options:, params:) click to toggle source
# File lib/imgix/path.rb, line 191
def build_srcset_pairs(options:, params:)
  srcset = ""

  widths = options[:widths] || []
  width_tolerance = options[:width_tolerance] || DEFAULT_WIDTH_TOLERANCE
  min_width = options[:min_width] || MIN_WIDTH
  max_width = options[:max_width] || MAX_WIDTH

  if !widths.empty?
    validate_widths!(widths)
    srcset_widths = widths
  elsif width_tolerance != DEFAULT_WIDTH_TOLERANCE || min_width != MIN_WIDTH || max_width != MAX_WIDTH
    validate_range!(min_width, max_width)
    validate_width_tolerance!(width_tolerance)
    srcset_widths = TARGET_WIDTHS.call(width_tolerance, min_width, max_width)
  else
    srcset_widths = DEFAULT_TARGET_WIDTHS
  end

  srcset_widths.each do |width|
    params[:w] = width
    srcset += "#{to_url(params, options)} #{width}w,\n"
  end

  srcset[0..-3]
end
encode_URI(path) click to toggle source

URL encode every character in the path, including “ +?:#” characters.

# File lib/imgix/path.rb, line 156
def encode_URI(path)
  # For each component in the path, URL encode it and add it
  # to the array path component.
  path_components = []
  path.split("/").each do |str|
    path_components << ERB::Util.url_encode(str)
  end
  # Prefix and join the encoded path components.
  "/#{path_components.join('/')}"
end
encode_URI_Component(path) click to toggle source

URL encode the entire path

# File lib/imgix/path.rb, line 150
def encode_URI_Component(path)
  return "/" + ERB::Util.url_encode(path)
end
has_query?() click to toggle source
# File lib/imgix/path.rb, line 187
def has_query?
  !query.empty?
end
path_and_params(path) click to toggle source
# File lib/imgix/path.rb, line 171
def path_and_params(path)
  has_query? ? "#{path}?#{query}" : path
end
query() click to toggle source
# File lib/imgix/path.rb, line 175
def query
  @options.map do |key, val|
    escaped_key = ERB::Util.url_encode(key.to_s)

    if escaped_key.end_with? '64'
      escaped_key << "=" << Base64.urlsafe_encode64(val.to_s).delete('=')
    else
      escaped_key << "=" << ERB::Util.url_encode(val.to_s)
    end
  end.join("&")
end
sanitize_path(path, options = {}) click to toggle source

Escape and encode any characters in path that are reserved and not utf8 encoded. This includes “ +?:#” characters. If a path is being used as a proxy, utf8 encode everything. If it is not being used as proxy, leave certain chars, like “/”, alone. Method assumes path is not already encoded.

# File lib/imgix/path.rb, line 135
def sanitize_path(path, options = {})
  # remove the leading "/", we'll add it back after encoding
  path = path.slice(1, path.length) if Regexp.new('^/') =~ path
  if options[:disable_path_encoding]
    return "/" + path
  # if path is being used as a proxy, encode the entire thing
  elsif /^https?/ =~ path
    return encode_URI_Component(path)
  else
    # otherwise, encode only specific characters
    return encode_URI(path)
  end
end
signature(rest) click to toggle source
# File lib/imgix/path.rb, line 167
def signature(rest)
  Digest::MD5.hexdigest(@secure_url_token + rest)
end
validate_range!(min_width, max_width) click to toggle source
# File lib/imgix/path.rb, line 252
def validate_range!(min_width, max_width)
  range_numeric_error = "error: `min_width` and `max_width` must be positive `Numeric` values"
  raise ArgumentError, range_numeric_error unless min_width.is_a?(Numeric) && max_width.is_a?(Numeric)

  raise ArgumentError, range_numeric_error unless min_width > 0 && max_width > 0
end
validate_variable_qualities!(disable_quality) click to toggle source
# File lib/imgix/path.rb, line 259
def validate_variable_qualities!(disable_quality)
  disable_quality_error = "error: `disable_quality` must be a Boolean value"
  unless disable_quality.is_a?(TrueClass) || disable_quality.is_a?(FalseClass)
    raise ArgumentError, disable_quality_error
  end
end
validate_width_tolerance!(width_tolerance) click to toggle source
# File lib/imgix/path.rb, line 238
def validate_width_tolerance!(width_tolerance)
  width_increment_error = "error: `width_tolerance` must be a positive `Numeric` value"

  raise ArgumentError, width_increment_error if !width_tolerance.is_a?(Numeric) || width_tolerance <= 0
end
validate_widths!(widths) click to toggle source
# File lib/imgix/path.rb, line 244
def validate_widths!(widths)
  widths_error = "error: `widths` must be an array of positive `Numeric` values"
  raise ArgumentError, widths_error unless widths.is_a?(Array)

  all_positive_integers = widths.all? { |i| i.is_a?(Integer) && i > 0 }
  raise ArgumentError, widths_error unless all_positive_integers
end