Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/lib/db/*.db text eol=lf
/test/fixtures/*.db text eol=lf
56 changes: 29 additions & 27 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,44 @@ on:
- main

jobs:
build:
runs-on: ${{ matrix.os }}-latest
name: "Ruby ${{ matrix.ruby }} / ${{ matrix.os }} / Failure allowed: ${{ matrix.experimental }}"
continue-on-error: ${{ matrix.experimental }}
timeout-minutes: 5
test:
runs-on: ${{ matrix.os }}
name: "${{ matrix.ruby }} / ${{ matrix.os }}"
timeout-minutes: 10

strategy:
fail-fast: false
matrix:
os: ["ubuntu"]
ruby: ["3.1", "3.2"]
experimental: [false]
os: [ubuntu-24.04]
ruby:
- "3.3"
- "3.4"
- "4.0"
- "jruby-10.0"
- "jruby-10.1"
- "truffleruby-34.0"
include:
- ruby: "3.2"
os: "windows"
experimental: false
- ruby: "ruby-head"
os: "ubuntu"
experimental: true
- ruby: "truffleruby-head"
os: "ubuntu"
experimental: true
- ruby: "jruby-head"
os: "ubuntu"
experimental: true
- ruby: "jruby-9.4.0.0"
os: "ubuntu"
experimental: true
- ruby: "4.0"
os: windows-2025

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Rubocop
run: bundle exec rubocop
if: "!contains(matrix.ruby, 'jruby')"
- name: Tests
run: bundle exec rake test

lint:
runs-on: ubuntu-24.04
timeout-minutes: 5

steps:
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: "4.0"
bundler-cache: true
- name: Rubocop
run: bundle exec rubocop
14 changes: 10 additions & 4 deletions .github/workflows/db.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,27 @@ on:
- cron: "0 10 1 * *"
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
update_db:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
name: "Update MIME type DB"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- uses: ruby/setup-ruby@v1
with:
ruby-version: "2.7"
ruby-version: "4.0"
bundler-cache: true
- name: Update mime-types-data
run: bundle update mime-types-data
- name: Update DB
run: bundle exec rake rebuild_db
- name: Test updated DB
run: bundle exec rake test
- name: Create PR
run: bin/db_pull_request
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/.bundle/
/.yardoc
/Gemfile.lock
/mise.toml
/_yardoc/
/coverage/
/doc/
Expand Down
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4.0.6
10 changes: 10 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
02-09-2026
- Version 2.0.0
- Require Ruby 3.3 or newer
- Update the bundled MIME database from mime-types-data 3.2026.0701; some extension and content-type mappings changed to match current upstream priorities
- Use byte offsets for reliable lookups in Unicode custom databases (#65, thanks @OskarEichler)
- Avoid duplicate database reads for lowercase extension misses (#64, thanks @OskarEichler)
- Preserve preferred extensions and extension-specific priorities when rebuilding the database (#66, thanks @OskarEichler)
- Reload custom MIME databases when their configured paths change
- Validate custom database row widths, row structure, and UTF-8 encoding

08-08-2023
- Version 1.1.5
- Update mime types from upstream
Expand Down
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in mini_mime.gemspec
gemspec

gem "mime-types", "~> 3" if RUBY_VERSION > '2'
gem "mime-types", "~> 3.7.0"
gem "memory_profiler"
gem "benchmark-ips"
gem "rubocop", platforms: :mri
gem "rubocop-discourse", platforms: :mri
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Minimal mime type implementation for use with the mail and rest-client gem.

## Installation

MiniMime 2.0 requires Ruby 3.3 or newer. Supported JRuby and TruffleRuby releases must implement a maintained Ruby language version.

Add this line to your application's Gemfile:

```ruby
Expand Down Expand Up @@ -46,6 +48,8 @@ MiniMime::Configuration.ext_db_path = "path_to_file_extension_db"
MiniMime::Configuration.content_type_db_path = "path_to_content_type_db"
```

Configuration is process-global and should normally be set during application startup. Changing either path resets the lookup database so subsequent lookups use the new files. Custom databases must contain UTF-8 rows with three whitespace-separated columns, a final newline, and a consistent byte width.

Check out the [default databases](lib/db) for proper formatting and structure hints.

## Performance
Expand Down
16 changes: 9 additions & 7 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ def pad(array)
array.each do |row|
i = 0
row.each do |col|
max[i] = [max[i] || 0, col.length].max
max[i] = [max[i] || 0, col.bytesize].max
i += 1
end
end

array.each do |row|
i = 0
row.each do |col|
col << " " * (max[i] - col.length)
col << " " * (max[i] - col.bytesize)
i += 1
end
end
Expand All @@ -36,13 +36,14 @@ task :rebuild_db do
puts "Generating mime type DB"
require 'mime/types'
index = {}
preferred_extensions = {}

MIME::Types.each do |type|
type.extensions.each { |ext| (index[ext.downcase] ||= []) << type }
end

index.each do |k, list|
list.sort! { |a, b| a.priority_compare(b) }
index.each do |extension, list|
list.sort! { |a, b| a.__extension_priority_compare(b, [extension]) }
end

buffer = []
Expand All @@ -51,14 +52,15 @@ task :rebuild_db do
mime_type = list.detect { |t| !t.obsolete? }
mime_type ||= list.detect(&:registered)
mime_type ||= list.first
preferred_extensions[mime_type.content_type] ||= mime_type.preferred_extension
buffer << [ext.dup, mime_type.content_type.dup, mime_type.encoding.dup]
end

pad(buffer)

buffer.sort! { |a, b| a[0] <=> b[0] }

File.open("lib/db/ext_mime.db", File::CREAT | File::TRUNC | File::RDWR) do |f|
File.open("lib/db/ext_mime.db", File::BINARY | File::CREAT | File::TRUNC | File::RDWR) do |f|
buffer.each do |row|
f.write "#{row[0]} #{row[1]} #{row[2]}\n"
end
Expand All @@ -77,12 +79,12 @@ task :rebuild_db do

# we got to confirm we pick the right extension for each type
buffer.each do |row|
row[0] = MIME::Types.type_for("xyz.#{row[0].strip}")[0].extensions[0].dup
row[0] = preferred_extensions.fetch(row[1]).dup
end

pad(buffer)

File.open("lib/db/content_type_mime.db", File::CREAT | File::TRUNC | File::RDWR) do |f|
File.open("lib/db/content_type_mime.db", File::BINARY | File::CREAT | File::TRUNC | File::RDWR) do |f|
last = nil
count = 0
buffer.each do |row|
Expand Down
Loading