Skip to content

Media Range Map #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions lib/protocol/http/media_types.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2016-2024, by Samuel Williams.

module Protocol
module HTTP
class MediaTypes
WILDCARD = "*/*".freeze

def initialize
@map = {}
end

def freeze
return self if frozen?

@map.freeze
@map.each_value(&:freeze)

return super
end

# Given a list of content types (e.g. from browser_preferred_content_types), return the best converter. Media types can be an array of MediaRange or String values.
def for(media_ranges)
media_ranges.each do |media_range|
range_string = media_range.range_string

if object = @map[range_string]
return object
end
end

return nil
end

def []= media_range, object
@map[media_range] = object
end

def [] media_range
@map[media_range]
end

# Add a converter to the collection. A converter can be anything that responds to #content_type. Objects will be considered in the order they are added, subsequent objects cannot override previously defined media types. `object` must respond to #split('/', 2) which should give the type and subtype.
def << object
media_range = object.media_range

# We set the default if not specified already:
@map[WILDCARD] = object if @map.empty?

type = media_range.type
if type != "*"
@map["#{type}/*"] ||= object

subtype = media_range.subtype
if subtype != "*"
@map["#{type}/#{subtype}"] ||= object
end
end

return self
end
end
end
end
71 changes: 71 additions & 0 deletions test/protocol/http/media_types.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2016-2024, by Samuel Williams.

require "protocol/http/accept/media_types"
require "protocol/http/accept/media_types/map"
require "protocol/http/accept/content_type"

describe Protocol::HTTP::MediaTypes do
let(:converter) do
Struct.new(:content_type) do
def split(*args)
self.content_type.split(*args)
end
end
end

let(:text_html_converter) {converter.new("text/html")}

let(:text_plain_content_type) {Protocol::HTTP::Accept::ContentType.new("text", "plain", charset: "utf-8")}
let(:text_plain_converter) {converter.new(text_plain_content_type)}

let(:map) {subject.new}

it "should give the correct converter when specified completely" do
map << text_html_converter
map << text_plain_converter

media_types = Protocol::HTTP::Accept::MediaTypes.parse("text/plain, text/*, */*")
expect(map.for(media_types).first).to be == text_plain_converter

media_types = Protocol::HTTP::Accept::MediaTypes.parse("text/html, text/*, */*")
expect(map.for(media_types).first).to be == text_html_converter
end

it "should match the wildcard subtype converter" do
map << text_html_converter
map << text_plain_converter

media_types = Protocol::HTTP::Accept::MediaTypes.parse("text/*, */*")
expect(map.for(media_types).first).to be == text_html_converter

media_types = Protocol::HTTP::Accept::MediaTypes.parse("*/*")
expect(map.for(media_types).first).to be == text_html_converter
end

it "should fail to match if no media types match" do
map << text_plain_converter

expect(map.for(["application/json"])).to be_nil
end

it "should fail to match if no media types specified" do
expect(map.for(["text/*", "*/*"])).to be_nil
end

it "should freeze converters" do
map << text_html_converter

map.freeze

expect(text_html_converter).to be(:frozen?)
end

it "should assign and retrive media ranges" do
map["*/*"] = :test

expect(map["*/*"]).to be == :test
end
end
Loading