module Hanami::Action::Mime

Constants

ANY_TYPE
TYPES

Most commom MIME Types used for responses

@since 1.0.0 @api private

Public Class Methods

best_q_match(q_value_header, available_mimes = TYPES.values) click to toggle source

Patched version of Rack::Utils.best_q_match.

@since 2.0.0 @api private

@see www.rubydoc.info/gems/rack/Rack/Utils#best_q_match-class_method @see github.com/rack/rack/pull/659 @see github.com/hanami/controller/issues/59 @see github.com/hanami/controller/issues/104 @see github.com/hanami/controller/issues/275

# File lib/hanami/action/mime.rb, line 182
def best_q_match(q_value_header, available_mimes = TYPES.values)
  ::Rack::Utils.q_values(q_value_header).each_with_index.map { |(req_mime, quality), index|
    match = available_mimes.find { |am| ::Rack::Mime.match?(am, req_mime) }
    next unless match

    RequestMimeWeight.new(req_mime, quality, index, match)
  }.compact.max&.format
end
content_type_with_charset(content_type, charset) click to toggle source

Returns a string combining the given content type and charset, intended for setting as a ‘Content-Type` header.

@example

Mime.content_type_with_charset("application/json", "utf-8")
# => "application/json; charset=utf-8"

@param content_type [String] @param charset [String]

@return [String]

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 149
def content_type_with_charset(content_type, charset)
  "#{content_type}; charset=#{charset}"
end
detect_format(content_type, config) click to toggle source

Returns a format name for the given content type.

The format name will come from the configured formats, if such a format is configured there, or instead from the default list of formats in ‘Mime::TYPES`.

Returns nil if no matching format can be found.

This is used to return the format name a {Response}.

@example

detect_format("application/jsonl charset=utf-8", config) # => :json

@return [Symbol, nil]

@see Response#format @see Action#finish

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 93
def detect_format(content_type, config)
  return if content_type.nil?

  ct = content_type.split(";").first
  config.formats.format_for(ct) || TYPES.key(ct)
end
detect_format_and_content_type(value, config) click to toggle source

Returns a format name and content type pair for a given format name or content type string.

@example

detect_format_and_content_type(:json, config)
# => [:json, "application/json"]

detect_format_and_content_type("application/json", config)
# => [:json, "application/json"]

@example Unknown format name

detect_format_and_content_type(:unknown, config)
# raises Hanami::Action::UnknownFormatError

@example Unknown content type

detect_format_and_content_type("application/unknown", config)
# => [nil, "application/unknown"]

@return [Array<(Symbol, String)>]

@raise [Hanami::Action::UnknownFormatError] if an unknown format name is given

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 124
def detect_format_and_content_type(value, config)
  case value
  when Symbol
    [value, format_to_mime_type(value, config)]
  when String
    [detect_format(value, config), value]
  else
    raise UnknownFormatError.new(value)
  end
end
enforce_accept(request, config) { || ... } click to toggle source

Yields if an action is configured with ‘formats`, the request has an `Accept` header, an none of the Accept types matches the accepted formats. The given block is expected to halt the request handling.

If any of these conditions are not met, then the request is acceptable and the method returns without yielding.

@see Action#enforce_accepted_mime_types @see Config#formats

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 203
def enforce_accept(request, config)
  return unless request.accept_header?

  accept_types = ::Rack::Utils.q_values(request.accept).map(&:first)
  return if accept_types.any? { |mime_type| accepted_mime_type?(mime_type, config) }

  yield
end
enforce_content_type(request, config) { || ... } click to toggle source

Yields if an action is configured with ‘formats`, the request has a `Content-Type` header (or a `default_requst_format` is configured), and the content type does not match the accepted formats. The given block is expected to halt the request handling.

If any of these conditions are not met, then the request is acceptable and the method returns without yielding.

@see Action#enforce_accepted_mime_types @see Config#formats

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 224
def enforce_content_type(request, config)
  content_type = request.content_type

  return if content_type.nil?

  return if accepted_mime_type?(content_type, config)

  yield
end
response_content_type_with_charset(request, config) click to toggle source

Returns a string combining a MIME type and charset, intended for setting as the ‘Content-Type` header for the response to the given request.

This uses the request’s ‘Accept` header (if present) along with the configured formats to determine the best content type to return.

@return [String]

@see Action#call

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 165
def response_content_type_with_charset(request, config)
  content_type_with_charset(
    response_content_type(request, config),
    config.default_charset || Action::DEFAULT_CHARSET
  )
end

Private Class Methods

accepted_mime_type?(mime_type, config) click to toggle source

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 238
def accepted_mime_type?(mime_type, config)
  accepted_mime_types(config).any? { |accepted_mime_type|
    ::Rack::Mime.match?(mime_type, accepted_mime_type)
  }
end
accepted_mime_types(config) click to toggle source

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 246
def accepted_mime_types(config)
  return [ANY_TYPE] if config.formats.empty?

  config.formats.map { |format| format_to_mime_types(format, config) }.flatten(1)
end
format_to_mime_type(format, config) click to toggle source

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 271
def format_to_mime_type(format, config)
  config.formats.mime_type_for(format) ||
    TYPES.fetch(format) { raise Hanami::Action::UnknownFormatError.new(format) }
end
format_to_mime_types(format, config) click to toggle source

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 278
def format_to_mime_types(format, config)
  config.formats.mime_types_for(format).tap { |types|
    types << TYPES[format] if TYPES.key?(format)
  }
end
response_content_type(request, config) click to toggle source

@since 2.0.0 @api private

# File lib/hanami/action/mime.rb, line 254
def response_content_type(request, config)
  if request.accept_header?
    all_mime_types = TYPES.values + config.formats.mapping.keys
    content_type = best_q_match(request.accept, all_mime_types)

    return content_type if content_type
  end

  if config.formats.default
    return format_to_mime_type(config.formats.default, config)
  end

  Action::DEFAULT_CONTENT_TYPE
end