diff --git a/lib/mime/pooled_attr_accessor.rb b/lib/mime/pooled_attr_accessor.rb
new file mode 100644
index 0000000..272bd6e
--- /dev/null
+++ b/lib/mime/pooled_attr_accessor.rb
@@ -0,0 +1,16 @@
+require "mime/value_pool"
+
+module MIME
+ module PooledAttrAccessor
+ private
+ def pooled_attr_accessor(sym, opts = {})
+ attr_reader sym
+ ivar = :"@#{sym}"
+ writer = :"#{sym}="
+ define_method writer do |val|
+ instance_variable_set(ivar, ValuePool[val])
+ end
+ private writer if opts[:private_writer]
+ end
+ end
+end
diff --git a/lib/mime/type.rb b/lib/mime/type.rb
index 7421fe0..0a2c3e2 100644
--- a/lib/mime/type.rb
+++ b/lib/mime/type.rb
@@ -1,6 +1,9 @@
+require "mime/pooled_attr_accessor"
+
module MIME
class Type
include Comparable
+ extend PooledAttrAccessor
MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}o
UNREG_RE = %r{[Xx]-}o
@@ -91,27 +94,31 @@ def eql?(other)
#
# text/plain => text/plain
# x-chemical/x-pdb => x-chemical/x-pdb
- attr_reader :content_type
+ pooled_attr_accessor :content_type, private_writer: true
# Returns the media type of the simplified MIME type.
#
# text/plain => text
# x-chemical/x-pdb => chemical
- attr_reader :media_type
+ pooled_attr_accessor :media_type, private_writer: true
+
# Returns the media type of the unmodified MIME type.
#
# text/plain => text
# x-chemical/x-pdb => x-chemical
- attr_reader :raw_media_type
+ pooled_attr_accessor :raw_media_type, private_writer: true
+
# Returns the sub-type of the simplified MIME type.
#
# text/plain => plain
# x-chemical/x-pdb => pdb
- attr_reader :sub_type
+ pooled_attr_accessor :sub_type, private_writer: true
+
# Returns the media type of the unmodified MIME type.
#
# text/plain => plain
# x-chemical/x-pdb => x-pdb
- attr_reader :raw_sub_type
+ pooled_attr_accessor :raw_sub_type, private_writer: true
+
# The MIME types main- and sub-label can both start with x-,
# which indicates that it is a non-registered name. Of course, after
# registration this flag can disappear, adds to the confusing
@@ -120,14 +127,15 @@ def eql?(other)
#
# text/plain => text/plain
# x-chemical/x-pdb => chemical/pdb
- attr_reader :simplified
+ pooled_attr_accessor :simplified, private_writer: true
+
# The list of extensions which are known to be used for this MIME::Type.
# Non-array values will be coerced into an array with #to_a. Array
# values will be flattened and +nil+ values removed.
- attr_accessor :extensions
- remove_method :extensions= ;
- def extensions=(ext) #:nodoc:
- @extensions = [ext].flatten.compact
+ attr_reader :extensions
+
+ def extensions=(ext)
+ @extensions = [ext].flatten.compact.map { |e| ValuePool[e] }
end
# The encoding (7bit, 8bit, quoted-printable, or base64) required to
@@ -140,13 +148,13 @@ def extensions=(ext) #:nodoc:
# If the encoding is not provided on construction, this will be either
# 'quoted-printable' (for text/* media types) and 'base64' for eveything
# else.
- attr_accessor :encoding
- remove_method :encoding= ;
+ attr_reader :encoding
+
def encoding=(enc) #:nodoc:
if enc.nil? or enc == :default
- @encoding = self.default_encoding
+ @encoding = ValuePool[default_encoding]
elsif enc =~ ENCODING_RE
- @encoding = enc
+ @encoding = ValuePool[enc]
else
raise ArgumentError, "The encoding must be nil, :default, base64, 7bit, 8bit, or quoted-printable."
end
@@ -154,28 +162,34 @@ def encoding=(enc) #:nodoc:
# The regexp for the operating system that this MIME::Type is specific
# to.
- attr_accessor :system
- remove_method :system= ;
+ attr_reader :system
+
def system=(os) #:nodoc:
- if os.nil? or os.kind_of?(Regexp)
- @system = os
- else
- @system = %r|#{os}|
- end
+ @system = ValuePool[
+ if os.nil? or os.kind_of?(Regexp)
+ os
+ else
+ %r|#{os}|
+ end
+ ]
end
+
# Returns the default encoding for the MIME::Type based on the media
# type.
attr_reader :default_encoding
- remove_method :default_encoding
+
+ TEXT = "text".freeze
+ QUOTED_PRINTABLE = "quoted-printable".freeze
+ BASE64 = "base64".freeze
+ private_constant :TEXT, :QUOTED_PRINTABLE, :BASE64
+
def default_encoding
- (@media_type == 'text') ? 'quoted-printable' : 'base64'
+ (@media_type == TEXT) ? QUOTED_PRINTABLE : BASE64
end
# Returns the media type or types that should be used instead of this
# media type, if it is obsolete. If there is no replacement media type,
# or it is not obsolete, +nil+ will be returned.
- attr_reader :use_instead
- remove_method :use_instead
def use_instead
return nil unless @obsolete
@use_instead
@@ -185,14 +199,15 @@ def use_instead
def obsolete?
@obsolete ? true : false
end
+
# Sets the obsolescence indicator for this media type.
attr_writer :obsolete
# The documentation for this MIME::Type. Documentation about media
# types will be found on a media type definition as a comment.
# Documentation will be found through #docs.
- attr_accessor :docs
- remove_method :docs= ;
+ attr_reader :docs
+
def docs=(d)
if d
a = d.scan(%r{use-instead:#{MEDIA_TYPE_RE}})
@@ -200,15 +215,20 @@ def docs=(d)
if a.empty?
@use_instead = nil
else
- @use_instead = a.map { |el| "#{el[0]}/#{el[1]}" }
+ @use_instead = a.map { |el| ValuePool["#{el[0]}/#{el[1]}"] }
end
end
- @docs = d
+ @docs = ValuePool[d]
end
# The encoded URL list for this MIME::Type. See #urls for more
# information.
- attr_accessor :url
+ attr_reader :url
+
+ def url=(url)
+ @url = url.is_a?(Array) ? url.map { |u| ValuePool[u] } : url
+ end
+
# The decoded URL list for this MIME::Type.
# The special URL value IANA will be translated into:
# http://www.iana.org/assignments/media-types//
@@ -380,14 +400,14 @@ def initialize(content_type) #:yields self:
raise InvalidContentType, "Invalid Content-Type provided ('#{content_type}')"
end
- @content_type = content_type
- @raw_media_type = matchdata.captures[0]
- @raw_sub_type = matchdata.captures[1]
+ self.content_type = content_type
+ self.raw_media_type = matchdata.captures[0]
+ self.raw_sub_type = matchdata.captures[1]
- @simplified = MIME::Type.simplified(@content_type)
+ self.simplified = MIME::Type.simplified(@content_type)
matchdata = MEDIA_TYPE_RE.match(@simplified)
- @media_type = matchdata.captures[0]
- @sub_type = matchdata.captures[1]
+ self.media_type = matchdata.captures[0]
+ self.sub_type = matchdata.captures[1]
self.extensions = nil
self.encoding = :default
@@ -418,7 +438,7 @@ def registered?
# formats. This method returns +true+ when the MIME type encoding is set
# to base64.
def binary?
- @encoding == 'base64'
+ @encoding == BASE64
end
# MIME types can be specified to be sent across a network in particular
diff --git a/lib/mime/value_pool.rb b/lib/mime/value_pool.rb
new file mode 100644
index 0000000..a28377f
--- /dev/null
+++ b/lib/mime/value_pool.rb
@@ -0,0 +1,12 @@
+module MIME
+ ValuePool = Hash.new { |h,k|
+ begin
+ k = k.dup
+ rescue TypeError
+ else
+ k.freeze
+ end
+
+ h[k] = k
+ }
+end