mime-types 1.16 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mime/type/columnar"
4
+
5
+ # MIME::Types::Columnar is used to extend a MIME::Types container to load data
6
+ # by columns instead of from JSON or YAML. Column loads of MIME types loaded
7
+ # through the columnar store are synchronized with a Mutex.
8
+ #
9
+ # MIME::Types::Columnar is not intended to be used directly, but will be added
10
+ # to an instance of MIME::Types when it is loaded with
11
+ # MIME::Types::Loader#load_columnar.
12
+ module MIME::Types::Columnar
13
+ LOAD_MUTEX = Mutex.new # :nodoc:
14
+
15
+ def self.extended(obj) # :nodoc:
16
+ super
17
+ obj.instance_variable_set(:@__mime_data__, [])
18
+ obj.instance_variable_set(:@__files__, Set.new)
19
+ end
20
+
21
+ # Load the first column data file (type and extensions).
22
+ def load_base_data(path) # :nodoc:
23
+ @__root__ = path
24
+
25
+ each_file_line("content_type", false) do |line|
26
+ line = line.split
27
+ content_type = line.shift
28
+ extensions = line
29
+ # content_type, *extensions = line.split
30
+
31
+ type = MIME::Type::Columnar.new(self, content_type, extensions)
32
+ @__mime_data__ << type
33
+ add(type)
34
+ end
35
+
36
+ self
37
+ end
38
+
39
+ private
40
+
41
+ def each_file_line(name, lookup = true)
42
+ LOAD_MUTEX.synchronize do
43
+ next if @__files__.include?(name)
44
+
45
+ i = -1
46
+ column = File.join(@__root__, "mime.#{name}.column")
47
+
48
+ IO.readlines(column, encoding: "UTF-8").each do |line|
49
+ line.chomp!
50
+
51
+ if lookup
52
+ (type = @__mime_data__[i += 1]) || next
53
+ yield type, line
54
+ else
55
+ yield line
56
+ end
57
+ end
58
+
59
+ @__files__ << name
60
+ end
61
+ end
62
+
63
+ def load_encoding
64
+ each_file_line("encoding") do |type, line|
65
+ pool ||= {}
66
+ type.instance_variable_set(:@encoding, (pool[line] ||= line))
67
+ end
68
+ end
69
+
70
+ def load_docs
71
+ each_file_line("docs") do |type, line|
72
+ type.instance_variable_set(:@docs, opt(line))
73
+ end
74
+ end
75
+
76
+ def load_preferred_extension
77
+ each_file_line("pext") do |type, line|
78
+ type.instance_variable_set(:@preferred_extension, opt(line))
79
+ end
80
+ end
81
+
82
+ def load_flags
83
+ each_file_line("flags") do |type, line|
84
+ line = line.split
85
+ type.instance_variable_set(:@obsolete, flag(line.shift))
86
+ type.instance_variable_set(:@registered, flag(line.shift))
87
+ type.instance_variable_set(:@signature, flag(line.shift))
88
+ type.instance_variable_set(:@provisional, flag(line.shift))
89
+ end
90
+ end
91
+
92
+ def load_xrefs
93
+ each_file_line("xrefs") { |type, line|
94
+ type.instance_variable_set(:@xrefs, dict(line, array: true))
95
+ }
96
+ end
97
+
98
+ def load_friendly
99
+ each_file_line("friendly") { |type, line|
100
+ type.instance_variable_set(:@friendly, dict(line))
101
+ }
102
+ end
103
+
104
+ def load_use_instead
105
+ each_file_line("use_instead") do |type, line|
106
+ type.instance_variable_set(:@use_instead, opt(line))
107
+ end
108
+ end
109
+
110
+ def dict(line, array: false)
111
+ if line == "-"
112
+ {}
113
+ else
114
+ line.split("|").each_with_object({}) { |l, h|
115
+ k, v = l.split("^")
116
+ v = nil if v.empty?
117
+ h[k] = array ? Array(v) : v
118
+ }
119
+ end
120
+ end
121
+
122
+ def arr(line)
123
+ if line == "-"
124
+ []
125
+ else
126
+ line.split("|").flatten.compact.uniq
127
+ end
128
+ end
129
+
130
+ def opt(line)
131
+ line unless line == "-"
132
+ end
133
+
134
+ def flag(line)
135
+ line == "1"
136
+ end
137
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ MIME::Types::Cache = Struct.new(:version, :data) # :nodoc:
4
+
5
+ # Caching of MIME::Types registries is advisable if you will be loading
6
+ # the default registry relatively frequently. With the class methods on
7
+ # MIME::Types::Cache, any MIME::Types registry can be marshaled quickly
8
+ # and easily.
9
+ #
10
+ # The cache is invalidated on a per-data-version basis; a cache file for
11
+ # version 3.2015.1118 will not be reused with version 3.2015.1201.
12
+ class << MIME::Types::Cache
13
+ # Attempts to load the cache from the file provided as a parameter or in
14
+ # the environment variable +RUBY_MIME_TYPES_CACHE+. Returns +nil+ if the
15
+ # file does not exist, if the file cannot be loaded, or if the data in
16
+ # the cache version is different than this version.
17
+ def load(cache_file = nil)
18
+ cache_file ||= ENV["RUBY_MIME_TYPES_CACHE"]
19
+ return nil unless cache_file && File.exist?(cache_file)
20
+
21
+ cache = Marshal.load(File.binread(cache_file))
22
+ if cache.version == MIME::Types::Data::VERSION
23
+ Marshal.load(cache.data)
24
+ else
25
+ MIME::Types.logger.error <<-WARNING.chomp.strip
26
+ Could not load MIME::Types cache: invalid version
27
+ WARNING
28
+ nil
29
+ end
30
+ rescue => e
31
+ MIME::Types.logger.error <<-WARNING.chomp.strip
32
+ Could not load MIME::Types cache: #{e}
33
+ WARNING
34
+ nil
35
+ end
36
+
37
+ # Attempts to save the types provided to the cache file provided.
38
+ #
39
+ # If +types+ is not provided or is +nil+, the cache will contain the
40
+ # current MIME::Types default registry.
41
+ #
42
+ # If +cache_file+ is not provided or is +nil+, the cache will be written
43
+ # to the file specified in the environment variable
44
+ # +RUBY_MIME_TYPES_CACHE+. If there is no cache file specified either
45
+ # directly or through the environment, this method will return +nil+
46
+ def save(types = nil, cache_file = nil)
47
+ cache_file ||= ENV["RUBY_MIME_TYPES_CACHE"]
48
+ return nil unless cache_file
49
+
50
+ types ||= MIME::Types.send(:__types__)
51
+
52
+ File.binwrite(cache_file, Marshal.dump(new(MIME::Types::Data::VERSION, Marshal.dump(types))))
53
+ end
54
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mime/types"
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "forwardable"
5
+
6
+ # MIME::Types requires a serializable keyed container that returns an empty Set
7
+ # on a key miss. Hash#default_value cannot be used because, while it traverses
8
+ # the Marshal format correctly, it won't survive any other serialization
9
+ # format (plus, a default of a mutable object resuls in a shared mess).
10
+ # Hash#default_proc cannot be used without a wrapper because it prevents
11
+ # Marshal serialization (and doesn't survive the round-trip).
12
+ class MIME::Types::Container # :nodoc:
13
+ extend Forwardable
14
+
15
+ def initialize(hash = {})
16
+ @container = {}
17
+ merge!(hash)
18
+ end
19
+
20
+ def [](key)
21
+ container[key] || EMPTY_SET
22
+ end
23
+
24
+ def []=(key, value)
25
+ container[key] =
26
+ case value
27
+ when Set
28
+ value
29
+ else
30
+ Set[*value]
31
+ end
32
+ end
33
+
34
+ def merge(other)
35
+ self.class.new(other)
36
+ end
37
+
38
+ def merge!(other)
39
+ tap {
40
+ other = other.is_a?(MIME::Types::Container) ? other.container : other
41
+ container.merge!(other)
42
+ normalize
43
+ }
44
+ end
45
+
46
+ def to_hash
47
+ container
48
+ end
49
+
50
+ def_delegators :@container,
51
+ :==,
52
+ :count,
53
+ :each,
54
+ :each_value,
55
+ :empty?,
56
+ :flat_map,
57
+ :keys,
58
+ :select,
59
+ :values
60
+
61
+ def add(key, value)
62
+ (container[key] ||= Set.new).add(value)
63
+ end
64
+
65
+ def marshal_dump
66
+ {}.merge(container)
67
+ end
68
+
69
+ def marshal_load(hash)
70
+ @container = hash
71
+ end
72
+
73
+ def encode_with(coder)
74
+ container.each { |k, v| coder[k] = v.to_a }
75
+ end
76
+
77
+ def init_with(coder)
78
+ @container = {}
79
+ coder.map.each { |k, v| container[k] = Set[*v] }
80
+ end
81
+
82
+ protected
83
+
84
+ attr_accessor :container
85
+
86
+ def normalize
87
+ container.each do |k, v|
88
+ next if v.is_a?(Set)
89
+
90
+ container[k] = Set[*v]
91
+ end
92
+ end
93
+
94
+ EMPTY_SET = Set.new.freeze
95
+ private_constant :EMPTY_SET
96
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mime/types/logger"
4
+
5
+ # The namespace for MIME applications, tools, and libraries.
6
+ module MIME
7
+ ##
8
+ class Types
9
+ # Used to mark a method as deprecated in the mime-types interface.
10
+ def self.deprecated(klass, sym, message = nil, &block) # :nodoc:
11
+ level =
12
+ case klass
13
+ when Class, Module
14
+ "."
15
+ else
16
+ klass = klass.class
17
+ "#"
18
+ end
19
+ message =
20
+ case message
21
+ when :private, :protected
22
+ "and will be #{message}"
23
+ when nil
24
+ "and will be removed"
25
+ else
26
+ message
27
+ end
28
+ MIME::Types.logger.debug <<-WARNING.chomp.strip
29
+ #{caller(2..2).first}: #{klass}#{level}#{sym} is deprecated #{message}.
30
+ WARNING
31
+
32
+ return unless block
33
+ block.call
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ module MIME
5
+ ##
6
+ class Types
7
+ unless private_method_defined?(:load_mode)
8
+ class << self
9
+ private
10
+
11
+ def load_mode
12
+ {columnar: false}
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ require "mime/types"
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ module MIME; end
5
+
6
+ ##
7
+ class MIME::Types; end
8
+
9
+ require "mime/types/data"
10
+
11
+ # This class is responsible for initializing the MIME::Types registry from
12
+ # the data files supplied with the mime-types library.
13
+ #
14
+ # The Loader will use one of the following paths:
15
+ # 1. The +path+ provided in its constructor argument;
16
+ # 2. The value of ENV['RUBY_MIME_TYPES_DATA']; or
17
+ # 3. The value of MIME::Types::Data::PATH.
18
+ #
19
+ # When #load is called, the +path+ will be searched recursively for all YAML
20
+ # (.yml or .yaml) files. By convention, there is one file for each media
21
+ # type (application.yml, audio.yml, etc.), but this is not required.
22
+ class MIME::Types::Loader
23
+ # The path that will be read for the MIME::Types files.
24
+ attr_reader :path
25
+ # The MIME::Types container instance that will be loaded. If not provided
26
+ # at initialization, a new MIME::Types instance will be constructed.
27
+ attr_reader :container
28
+
29
+ # Creates a Loader object that can be used to load MIME::Types registries
30
+ # into memory, using YAML, JSON, or Columnar registry format loaders.
31
+ def initialize(path = nil, container = nil)
32
+ path = path || ENV["RUBY_MIME_TYPES_DATA"] || MIME::Types::Data::PATH
33
+ @container = container || MIME::Types.new
34
+ @path = File.expand_path(path)
35
+ end
36
+
37
+ # Loads a MIME::Types registry from YAML files (<tt>*.yml</tt> or
38
+ # <tt>*.yaml</tt>) recursively found in +path+.
39
+ #
40
+ # It is expected that the YAML objects contained within the registry array
41
+ # will be tagged as <tt>!ruby/object:MIME::Type</tt>.
42
+ #
43
+ # Note that the YAML format is about 2½ times *slower* than the JSON format.
44
+ #
45
+ # NOTE: The purpose of this format is purely for maintenance reasons.
46
+ def load_yaml
47
+ Dir[yaml_path].sort.each do |f|
48
+ container.add(*self.class.load_from_yaml(f), :silent)
49
+ end
50
+ container
51
+ end
52
+
53
+ # Loads a MIME::Types registry from JSON files (<tt>*.json</tt>)
54
+ # recursively found in +path+.
55
+ #
56
+ # It is expected that the JSON objects will be an array of hash objects.
57
+ # The JSON format is the registry format for the MIME types registry
58
+ # shipped with the mime-types library.
59
+ def load_json
60
+ Dir[json_path].sort.each do |f|
61
+ types = self.class.load_from_json(f)
62
+ container.add(*types, :silent)
63
+ end
64
+ container
65
+ end
66
+
67
+ # Loads a MIME::Types registry from columnar files recursively found in
68
+ # +path+.
69
+ def load_columnar
70
+ require "mime/types/columnar" unless defined?(MIME::Types::Columnar)
71
+ container.extend(MIME::Types::Columnar)
72
+ container.load_base_data(path)
73
+
74
+ container
75
+ end
76
+
77
+ # Loads a MIME::Types registry. Loads from JSON files by default
78
+ # (#load_json).
79
+ #
80
+ # This will load from columnar files (#load_columnar) if <tt>columnar:
81
+ # true</tt> is provided in +options+ and there are columnar files in +path+.
82
+ def load(options = {columnar: false})
83
+ if options[:columnar] && !Dir[columnar_path].empty?
84
+ load_columnar
85
+ else
86
+ load_json
87
+ end
88
+ end
89
+
90
+ class << self
91
+ # Loads the default MIME::Type registry.
92
+ def load(options = {columnar: false})
93
+ new.load(options)
94
+ end
95
+
96
+ # Loads MIME::Types from a single YAML file.
97
+ #
98
+ # It is expected that the YAML objects contained within the registry
99
+ # array will be tagged as <tt>!ruby/object:MIME::Type</tt>.
100
+ #
101
+ # Note that the YAML format is about 2½ times *slower* than the JSON
102
+ # format.
103
+ #
104
+ # NOTE: The purpose of this format is purely for maintenance reasons.
105
+ def load_from_yaml(filename)
106
+ begin
107
+ require "psych"
108
+ rescue LoadError
109
+ nil
110
+ end
111
+
112
+ require "yaml"
113
+
114
+ if old_yaml?
115
+ YAML.safe_load(read_file(filename), [MIME::Type])
116
+ else
117
+ YAML.safe_load(read_file(filename), permitted_classes: [MIME::Type])
118
+ end
119
+ end
120
+
121
+ # Loads MIME::Types from a single JSON file.
122
+ #
123
+ # It is expected that the JSON objects will be an array of hash objects.
124
+ # The JSON format is the registry format for the MIME types registry
125
+ # shipped with the mime-types library.
126
+ def load_from_json(filename)
127
+ require "json"
128
+ JSON.parse(read_file(filename)).map { |type| MIME::Type.new(type) }
129
+ end
130
+
131
+ private
132
+
133
+ def read_file(filename)
134
+ File.open(filename, "r:UTF-8:-", &:read)
135
+ end
136
+
137
+ def old_yaml?
138
+ @old_yaml ||=
139
+ begin
140
+ require "rubygems/version"
141
+ Gem::Version.new(YAML::VERSION) < Gem::Version.new("3.1")
142
+ end
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ def yaml_path
149
+ File.join(path, "*.y{,a}ml")
150
+ end
151
+
152
+ def json_path
153
+ File.join(path, "*.json")
154
+ end
155
+
156
+ def columnar_path
157
+ File.join(path, "*.column")
158
+ end
159
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+
5
+ ##
6
+ module MIME
7
+ ##
8
+ class Types
9
+ class << self
10
+ # Configure the MIME::Types logger. This defaults to an instance of a
11
+ # logger that passes messages (unformatted) through to Kernel#warn.
12
+ attr_accessor :logger
13
+ end
14
+
15
+ class WarnLogger < ::Logger # :nodoc:
16
+ class WarnLogDevice < ::Logger::LogDevice # :nodoc:
17
+ def initialize(*)
18
+ end
19
+
20
+ def write(m)
21
+ Kernel.warn(m)
22
+ end
23
+
24
+ def close
25
+ end
26
+ end
27
+
28
+ def initialize(_one, _two = nil, _three = nil)
29
+ super(nil)
30
+ @logdev = WarnLogDevice.new
31
+ @formatter = ->(_s, _d, _p, m) { m }
32
+ end
33
+ end
34
+
35
+ self.logger = WarnLogger.new(nil)
36
+ end
37
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ class << MIME::Types
4
+ include Enumerable
5
+
6
+ ##
7
+ def new(*) # :nodoc:
8
+ super.tap do |types|
9
+ __instances__.add types
10
+ end
11
+ end
12
+
13
+ # MIME::Types#[] against the default MIME::Types registry.
14
+ def [](type_id, complete: false, registered: false)
15
+ __types__[type_id, complete: complete, registered: registered]
16
+ end
17
+
18
+ # MIME::Types#count against the default MIME::Types registry.
19
+ def count
20
+ __types__.count
21
+ end
22
+
23
+ # MIME::Types#each against the default MIME::Types registry.
24
+ def each
25
+ if block_given?
26
+ __types__.each { |t| yield t }
27
+ else
28
+ enum_for(:each)
29
+ end
30
+ end
31
+
32
+ # MIME::Types#type_for against the default MIME::Types registry.
33
+ def type_for(filename)
34
+ __types__.type_for(filename)
35
+ end
36
+ alias_method :of, :type_for
37
+
38
+ # MIME::Types#add against the default MIME::Types registry.
39
+ def add(*types)
40
+ __types__.add(*types)
41
+ end
42
+
43
+ private
44
+
45
+ def lazy_load?
46
+ return unless ENV.key?("RUBY_MIME_TYPES_LAZY_LOAD")
47
+
48
+ MIME::Types.logger.debug <<-WARNING.chomp.strip
49
+ Lazy loading ($RUBY_MIME_TYPES_LAZY_LOAD) is deprecated and will be removed.
50
+ WARNING
51
+
52
+ (lazy = ENV["RUBY_MIME_TYPES_LAZY_LOAD"]) && (lazy != "false")
53
+ end
54
+
55
+ def __types__
56
+ (defined?(@__types__) && @__types__) || load_default_mime_types
57
+ end
58
+
59
+ unless private_method_defined?(:load_mode)
60
+ def load_mode
61
+ {columnar: true}
62
+ end
63
+ end
64
+
65
+ def load_default_mime_types(mode = load_mode)
66
+ if (@__types__ = MIME::Types::Cache.load)
67
+ __instances__.add(@__types__)
68
+ else
69
+ @__types__ = MIME::Types::Loader.load(mode)
70
+ MIME::Types::Cache.save(@__types__)
71
+ end
72
+ @__types__
73
+ end
74
+
75
+ def __instances__
76
+ @__instances__ ||= Set.new
77
+ end
78
+
79
+ def reindex_extensions(type)
80
+ __instances__.each do |instance|
81
+ instance.send(:reindex_extensions!, type)
82
+ end
83
+ true
84
+ end
85
+ end
86
+
87
+ ##
88
+ class MIME::Types
89
+ load_default_mime_types(load_mode) unless lazy_load?
90
+ end