mini_mime 0.1.0

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,173 @@
1
+ require "mini_mime/version"
2
+ require "thread"
3
+
4
+ module MiniMime
5
+ BINARY_ENCODINGS = %w(base64 8bit)
6
+
7
+ # return true if this filename is known to have binary encoding
8
+ #
9
+ # puts MiniMime.binary?("file.gif") => true
10
+ # puts MiniMime.binary?("file.txt") => false
11
+ def self.binary?(filename)
12
+ info = Db.lookup_by_filename(filename)
13
+ !!(info && BINARY_ENCODINGS.include?(info.encoding))
14
+ end
15
+
16
+ # return true if this content type is known to have binary encoding
17
+ #
18
+ # puts MiniMime.binary_content_type?("text/plain") => false
19
+ # puts MiniMime.binary?("application/x-compress") => true
20
+ def self.binary_content_type?(mime)
21
+ info = Db.lookup_by_content_type(mime)
22
+ !!(info && BINARY_ENCODINGS.include?(info.encoding))
23
+ end
24
+
25
+ # return first matching content type for a file
26
+ #
27
+ # puts MiniMime.content_type("test.xml") => "application/xml"
28
+ # puts MiniMime.content_type("test.gif") => "image/gif"
29
+ def self.content_type(filename)
30
+ info = Db.lookup_by_filename(filename)
31
+ info && info.content_type
32
+ end
33
+
34
+ class Info
35
+ attr_accessor :extension, :content_type, :encoding
36
+ def initialize(buffer)
37
+ @extension,@content_type,@encoding = buffer.split(/\s+/).map!(&:freeze)
38
+ end
39
+
40
+ def [](idx)
41
+ if idx == 0
42
+ @extension
43
+ elsif idx == 1
44
+ @content_type
45
+ elsif idx == 2
46
+ @encoding
47
+ end
48
+ end
49
+ end
50
+
51
+ class Db
52
+ LOCK = Mutex.new
53
+
54
+ def self.lookup_by_filename(filename)
55
+ extension = File.extname(filename)
56
+ if extension
57
+ extension.sub!(".", "")
58
+ extension.downcase!
59
+ if extension.length > 0
60
+ LOCK.synchronize do
61
+ @db ||= new
62
+ @db.lookup_by_extension(extension)
63
+ end
64
+ else
65
+ nil
66
+ end
67
+ end
68
+ end
69
+
70
+ def self.lookup_by_content_type(content_type)
71
+ LOCK.synchronize do
72
+ @db ||= new
73
+ @db.lookup_by_content_type(content_type)
74
+ end
75
+ end
76
+
77
+
78
+ class Cache
79
+ def initialize(size)
80
+ @size = size
81
+ @hash = {}
82
+ end
83
+
84
+ def []=(key,val)
85
+ rval = @hash[key] = val
86
+ if @hash.length > @size
87
+ @hash.shift
88
+ end
89
+ rval
90
+ end
91
+
92
+ def fetch(key, &blk)
93
+ @hash.fetch(key, &blk)
94
+ end
95
+ end
96
+
97
+ class RandomAccessDb
98
+
99
+ MAX_CACHED = 100
100
+
101
+ def initialize(name, sort_order)
102
+ @path = File.expand_path("../db/#{name}", __FILE__)
103
+ @file = File.open(@path)
104
+
105
+ @row_length = @file.readline.length
106
+ @file_length = @file.size
107
+ @rows = @file_length / @row_length
108
+
109
+ @hit_cache = Cache.new(MAX_CACHED)
110
+ @miss_cache = Cache.new(MAX_CACHED)
111
+
112
+ @sort_order = sort_order
113
+ end
114
+
115
+ def lookup(val)
116
+ @hit_cache.fetch(val) do
117
+ @miss_cache.fetch(val) do
118
+ data = lookup_uncached(val)
119
+ if data
120
+ @hit_cache[val] = data
121
+ else
122
+ @miss_cache[val] = nil
123
+ end
124
+
125
+ data
126
+ end
127
+ end
128
+ end
129
+
130
+ #lifted from marcandre/backports
131
+ def lookup_uncached(val)
132
+ from = 0
133
+ to = @rows - 1
134
+ result = nil
135
+
136
+ while from <= to do
137
+ midpoint = from + (to-from).div(2)
138
+ current = resolve(midpoint)
139
+ data = current[@sort_order]
140
+ if data > val
141
+ to = midpoint - 1
142
+ elsif data < val
143
+ from = midpoint + 1
144
+ else
145
+ result = current
146
+ break
147
+ end
148
+ end
149
+ result
150
+ end
151
+
152
+ def resolve(row)
153
+ @file.seek(row*@row_length)
154
+ Info.new(@file.readline)
155
+ end
156
+
157
+ end
158
+
159
+ def initialize
160
+ @ext_db = RandomAccessDb.new("ext_mime.db", 0)
161
+ @content_type_db = RandomAccessDb.new("content_type_mime.db", 1)
162
+ end
163
+
164
+ def lookup_by_extension(extension)
165
+ @ext_db.lookup(extension)
166
+ end
167
+
168
+ def lookup_by_content_type(content_type)
169
+ @content_type_db.lookup(content_type)
170
+ end
171
+
172
+ end
173
+ end
@@ -0,0 +1,3 @@
1
+ module MiniMime
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'mini_mime/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "mini_mime"
9
+ spec.version = MiniMime::VERSION
10
+ spec.authors = ["Sam Saffron"]
11
+ spec.email = ["sam.saffron@gmail.com"]
12
+
13
+ spec.summary = %q{A lightweight mime type lookup toy}
14
+ spec.description = %q{A lightweight mime type lookup toy}
15
+ spec.homepage = "https://github.com/discourse/mini_mime"
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.13"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "minitest", "~> 5.0"
28
+ spec.add_development_dependency "mime-types", "~> 3"
29
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mini_mime
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sam Saffron
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-12-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mime-types
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3'
69
+ description: A lightweight mime type lookup toy
70
+ email:
71
+ - sam.saffron@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - CHANGELOG
79
+ - CODE_OF_CONDUCT.md
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - bin/console
85
+ - bin/setup
86
+ - lib/db/content_type_mime.db
87
+ - lib/db/ext_mime.db
88
+ - lib/mini_mime.rb
89
+ - lib/mini_mime/version.rb
90
+ - mini_mime.gemspec
91
+ homepage: https://github.com/discourse/mini_mime
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.5.1
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: A lightweight mime type lookup toy
115
+ test_files: []