Skip to content

Commit

Permalink
MediaType refactoring step 1, extract from Conneg module. See #1.
Browse files Browse the repository at this point in the history
  • Loading branch information
seancribbs committed Sep 1, 2011
1 parent 618ebe7 commit 53264b8
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 70 deletions.
69 changes: 1 addition & 68 deletions lib/webmachine/decision/conneg.rb
@@ -1,4 +1,5 @@
require 'webmachine/translation'
require 'webmachine/media_type'

module Webmachine
module Decision
Expand Down Expand Up @@ -116,74 +117,6 @@ def do_choose(choices, header, default)
# Matches acceptable items that include 'q' values
CONNEG_REGEX = /^\s*(\S+);\s*q=(\S*)\s*$/

# Matches sub-type parameters
PARAMS_REGEX = /;([^=]+)=([^;=\s]+)/

# Matches valid media types
MEDIA_TYPE_REGEX = /^\s*([^;\s]+)\s*((?:;\S+\s*)*)\s*$/

# Encapsulates a MIME media type, with logic for matching types.
class MediaType
# Creates a new MediaType by parsing its string representation.
def self.parse(str)
if str =~ MEDIA_TYPE_REGEX
type, raw_params = $1, $2
params = Hash[raw_params.scan(PARAMS_REGEX)]
new(type, params)
end
end

# @return [String] the MIME media type
attr_accessor :type

# @return [Hash] any type parameters, e.g. charset
attr_accessor :params

def initialize(type, params={})
@type, @params = type, params
end

# Detects whether the {MediaType} represents an open wildcard
# type, that is, "*/*" without any {#params}.
def matches_all?
@type == "*/*" && @params.empty?
end

def ==(other)
other = self.class.parse(other) if String === other
other.type == type && other.params == params
end

# Detects whether this {MediaType} matches the other {MediaType},
# taking into account wildcards.
def match?(other)
type_matches?(other) && other.params == params
end

# Reconstitutes the type into a String
def to_s
[type, *params.map {|k,v| "#{k}=#{v}" }].join(";")
end

# @return [String] The major type, e.g. "application", "text", "image"
def major
type.split("/").first
end

# @return [String] the minor or sub-type, e.g. "json", "html", "jpeg"
def minor
type.split("/").last
end

def type_matches?(other)
if ["*", "*/*", type].include?(other.type)
true
else
other.major == major && other.minor == "*"
end
end
end

# Matches the requested media type (with potential modifiers)
# against the provided types (with potential modifiers).
# @param [MediaType] requested the requested media type
Expand Down
6 changes: 4 additions & 2 deletions lib/webmachine/decision/helpers.rb
@@ -1,4 +1,6 @@
require 'webmachine/streaming'
require 'webmachine/media_type'

module Webmachine
module Decision
# Methods that assist the Decision {Flow}.
Expand Down Expand Up @@ -62,9 +64,9 @@ def unquote_header(value)
# Assists in receiving request bodies
def accept_helper
content_type = request.content_type || 'application/octet-stream'
mt = Conneg::MediaType.parse(content_type)
mt = MediaType.parse(content_type)
metadata['mediaparams'] = mt.params
acceptable = resource.content_types_accepted.find {|ct, _| mt.type_matches?(Conneg::MediaType.parse(ct)) }
acceptable = resource.content_types_accepted.find {|ct, _| mt.type_matches?(MediaType.parse(ct)) }
if acceptable
resource.send(acceptable.last)
else
Expand Down
79 changes: 79 additions & 0 deletions lib/webmachine/media_type.rb
@@ -0,0 +1,79 @@
module Webmachine
# Encapsulates a MIME media type, with logic for matching types.
class MediaType
# Matches valid media types
MEDIA_TYPE_REGEX = /^\s*([^;\s]+)\s*((?:;\S+\s*)*)\s*$/

# Matches sub-type parameters
PARAMS_REGEX = /;([^=]+)=([^;=\s]+)/

# Creates a new MediaType by parsing its string representation.
def self.parse(obj)
case obj
when MediaType
obj
when Array
unless String == obj[0] && Hash === obj[1]
raise ArgumentError, t('invalid_media_type', :type => obj.inspect)
end
type = parse(obj)
type.params.merge! obj[1]
type
when MEDIA_TYPE_REGEX
type, raw_params = $1, $2
params = Hash[raw_params.scan(PARAMS_REGEX)]
new(type, params)
end
end

# @return [String] the MIME media type
attr_accessor :type

# @return [Hash] any type parameters, e.g. charset
attr_accessor :params

def initialize(type, params={})
@type, @params = type, params
end

# Detects whether the {MediaType} represents an open wildcard
# type, that is, "*/*" without any {#params}.
def matches_all?
@type == "*/*" && @params.empty?
end

def ==(other)
other = self.class.parse(other) if String === other
other.type == type && other.params == params
end

# Detects whether this {MediaType} matches the other {MediaType},
# taking into account wildcards.
def match?(other)
type_matches?(other) && other.params == params
end

# Reconstitutes the type into a String
def to_s
[type, *params.map {|k,v| "#{k}=#{v}" }].join(";")
end

# @return [String] The major type, e.g. "application", "text", "image"
def major
type.split("/").first
end

# @return [String] the minor or sub-type, e.g. "json", "html", "jpeg"
def minor
type.split("/").last
end

def type_matches?(other)
if ["*", "*/*", type].include?(other.type)
true
else
other.major == major && other.minor == "*"
end
end
end
end

0 comments on commit 53264b8

Please sign in to comment.