distorted 0.5.2 → 0.5.7
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.
- checksums.yaml +4 -4
- data/LICENSE +661 -0
- data/README.md +4 -139
- data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/Less_Perfect_DOS_VGA.png +0 -0
- data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/More_Perfect_DOS_VGA.png +0 -0
- data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/img/Perfect_DOS_VGA.png +0 -0
- data/font/1252/LICENSE/MoreLessPerfectDOSVGA437/less_more_perfect_dos_vga_437.html +52 -0
- data/font/1252/LICENSE/PerfectDOSVGA437/font-comment.php@file=perfect_dos_vga_437.html +5 -0
- data/font/1252/LessPerfectDOSVGA.ttf +0 -0
- data/font/1252/MorePerfectDOSVGA.ttf +0 -0
- data/font/1252/Perfect DOS VGA 437 Win.ttf +0 -0
- data/font/437/Perfect DOS VGA 437.ttf +0 -0
- data/font/437/dos437.txt +72 -0
- data/font/65001/Anonymous Pro B.ttf +0 -0
- data/font/65001/Anonymous Pro BI.ttf +0 -0
- data/font/65001/Anonymous Pro I.ttf +0 -0
- data/font/65001/Anonymous Pro.ttf +0 -0
- data/font/65001/LICENSE/AnonymousPro/FONTLOG.txt +45 -0
- data/font/65001/LICENSE/AnonymousPro/OFL-FAQ.txt +235 -0
- data/font/65001/LICENSE/AnonymousPro/OFL.txt +94 -0
- data/font/65001/LICENSE/AnonymousPro/README.txt +55 -0
- data/font/850/ProFont-Bold-01/LICENSE +22 -0
- data/font/850/ProFont-Bold-01/readme.txt +28 -0
- data/font/850/ProFontWindows-Bold.ttf +0 -0
- data/font/850/ProFontWindows.ttf +0 -0
- data/font/850/Profont/LICENSE +22 -0
- data/font/850/Profont/readme.txt +31 -0
- data/font/932/LICENSE/README-ttf.txt +213 -0
- data/font/932/mona.ttf +0 -0
- data/lib/distorted/error_code.rb +8 -0
- data/lib/distorted/font.rb +192 -0
- data/lib/distorted/image.rb +121 -0
- data/lib/distorted/modular_technology/pango.rb +75 -0
- data/lib/distorted/monkey_business/hash.rb +33 -0
- data/lib/distorted/monkey_business/mnemoniq.rb +8 -0
- data/lib/distorted/monkey_business/set.rb +15 -0
- data/lib/distorted/monkey_business/string.rb +6 -0
- data/lib/distorted/pdf.rb +110 -0
- data/lib/distorted/svg.rb +21 -0
- data/lib/distorted/text.rb +241 -0
- data/lib/distorted/version.rb +20 -0
- data/lib/distorted/video.rb +193 -0
- data/test/distorted_test.rb +11 -0
- data/test/test_helper.rb +4 -0
- metadata +77 -5
data/font/932/mona.ttf
ADDED
Binary file
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# https://ruby-doc.org/core/Exception.html sez:
|
2
|
+
# "It is recommended that a library should have one subclass of StandardError
|
3
|
+
# or RuntimeError and have specific exception types inherit from it.
|
4
|
+
# This allows the user to rescue a generic exception type to catch
|
5
|
+
# all exceptions the library may raise even if future versions of
|
6
|
+
# the library add new exception subclasses."
|
7
|
+
class StandardDistorteDError < StandardError
|
8
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
# Font metadata extraction
|
4
|
+
require 'ttfunk'
|
5
|
+
|
6
|
+
require 'mime/types'
|
7
|
+
|
8
|
+
# No need to do all the fancy library versioning in a subclass.
|
9
|
+
require 'vips'
|
10
|
+
|
11
|
+
require 'distorted/text'
|
12
|
+
|
13
|
+
|
14
|
+
module Cooltrainer
|
15
|
+
module DistorteD
|
16
|
+
class Font < Text
|
17
|
+
|
18
|
+
MEDIA_TYPE = 'font'.freeze
|
19
|
+
|
20
|
+
# TODO: Test OTF, OTB, and others.
|
21
|
+
# NOTE: Traditional bitmap fonts won't be supported due to Pango 1.44
|
22
|
+
# and later switching to Harfbuzz from Freetype:
|
23
|
+
# https://gitlab.gnome.org/GNOME/pango/-/issues/386
|
24
|
+
# https://blogs.gnome.org/mclasen/2019/05/25/pango-future-directions/
|
25
|
+
MIME_TYPES = MIME::Types[/^#{self::MEDIA_TYPE}\/ttf/].to_set
|
26
|
+
|
27
|
+
ATTRS = Set[
|
28
|
+
:alt,
|
29
|
+
]
|
30
|
+
ATTRS_VALUES = {
|
31
|
+
}
|
32
|
+
ATTRS_DEFAULT = {
|
33
|
+
}
|
34
|
+
|
35
|
+
|
36
|
+
# irb(main):089:0> chars.take(5)
|
37
|
+
# => [[1, 255], [2, 1], [3, 2], [4, 3], [5, 4]]
|
38
|
+
# irb(main):090:0> chars.values.take(5)
|
39
|
+
# => [255, 1, 2, 3, 4]
|
40
|
+
# irb(main):091:0> chars.values.map(&:chr).take(5)
|
41
|
+
# => ["\xFF", "\x01", "\x02", "\x03", "\x04"]
|
42
|
+
def to_pango
|
43
|
+
output = '' << cr << '<span>' << cr
|
44
|
+
|
45
|
+
output << "<span size='35387'> #{font_name}</span>" << cr << cr
|
46
|
+
|
47
|
+
output << "<span size='24576'> #{font_description}</span>" << cr
|
48
|
+
output << "<span size='24576'> #{font_copyright}</span>" << cr
|
49
|
+
output << "<span size='24576'> #{font_version}</span>" << cr << cr
|
50
|
+
|
51
|
+
# Print a preview String in using the loaded font. Or don't.
|
52
|
+
if @demo
|
53
|
+
output << cr << cr << "<span size='24576' foreground='grey'> #{g_markup_escape_text(@demo)}</span>" << cr << cr << cr
|
54
|
+
end
|
55
|
+
|
56
|
+
# /!\ MANDATORY READING /!\
|
57
|
+
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html
|
58
|
+
#
|
59
|
+
# "The 'cmap' table maps character codes to glyph indices.
|
60
|
+
# The choice of encoding for a particular font is dependent upon the conventions
|
61
|
+
# used by the intended platform. A font intended to run on multiple platforms
|
62
|
+
# with different encoding conventions will require multiple encoding tables.
|
63
|
+
# As a result, the 'cmap' table may contain multiple subtables,
|
64
|
+
# one for each supported encoding scheme."
|
65
|
+
#
|
66
|
+
# Cmap#unicode is a convenient shortcut to sorting the subtables
|
67
|
+
# and removing any unusable ones:
|
68
|
+
# https://github.com/prawnpdf/ttfunk/blob/master/lib/ttfunk/table/cmap.rb
|
69
|
+
#
|
70
|
+
# irb(main):174:0> font_meta.cmap.tables.count
|
71
|
+
# => 3
|
72
|
+
# irb(main):175:0> font_meta.cmap.unicode.count
|
73
|
+
# => 2
|
74
|
+
@font_meta.cmap.tables.each do |table|
|
75
|
+
next if !table.unicode?
|
76
|
+
# Each subtable's `code_map` is a Hash map of character codes (the Hash keys)
|
77
|
+
# to the glyph IDs from the original font (the Hash's values).
|
78
|
+
#
|
79
|
+
# Subtable::encode takes:
|
80
|
+
# - a Hash mapping character codes to original font glyph IDs.
|
81
|
+
# - the desired output encoding — Set[:mac_roman, :unicode, :unicode_ucs4]
|
82
|
+
# https://github.com/prawnpdf/ttfunk/blob/master/lib/ttfunk/table/cmap/subtable.rb
|
83
|
+
# …and returns a Hash with keys:
|
84
|
+
# - :charmap — Hash mapping the characters in the input charmap
|
85
|
+
# to a another hash containing both the `:old`
|
86
|
+
# and `:new` glyph ids for each character code.
|
87
|
+
# - :subtable — String encoded subtable for the given encoding.
|
88
|
+
encoded = TTFunk::Table::Cmap::Subtable::encode(table&.code_map, :unicode).dig(:charmap)
|
89
|
+
|
90
|
+
output << "<span size='49152'>"
|
91
|
+
|
92
|
+
i = 0
|
93
|
+
encoded.each_pair { |c, (old, new)|
|
94
|
+
|
95
|
+
begin
|
96
|
+
if glyph = @font_meta.glyph_outlines.for(c)
|
97
|
+
# Add a space on either side of the character so they aren't
|
98
|
+
# all smooshed up against each other and unreadable.
|
99
|
+
output << ' ' << g_markup_escape_char(c) << ' '
|
100
|
+
if i >= 15
|
101
|
+
output << cr
|
102
|
+
i = 0
|
103
|
+
else
|
104
|
+
i = i + 1
|
105
|
+
end
|
106
|
+
else
|
107
|
+
end
|
108
|
+
rescue NoMethodError => nme
|
109
|
+
# TTFunk's `glyph_outlines.for()` will raise this if we call it
|
110
|
+
# for a codepoint that does not exist in the font, which we will
|
111
|
+
# not do because we are enumerating the codepoints in the font,
|
112
|
+
# but we should still handle the possibility.
|
113
|
+
# irb(main):060:0> font.glyph_outlines.for(555555)
|
114
|
+
#
|
115
|
+
# Traceback (most recent call last):
|
116
|
+
# 6: from /usr/bin/irb:23:in `<main>'
|
117
|
+
# 5: from /usr/bin/irb:23:in `load'
|
118
|
+
# 4: from /home/okeeblow/.gems/gems/irb-1.2.4/exe/irb:11:in `<top (required)>'
|
119
|
+
# 3: from (irb):60
|
120
|
+
# 2: from /home/okeeblow/.gems/gems/ttfunk-1.6.2.1/lib/ttfunk/table/glyf.rb:35:in `for'
|
121
|
+
# 1: from /home/okeeblow/.gems/gems/ttfunk-1.6.2.1/lib/ttfunk/table/loca.rb:35:in `size_of'
|
122
|
+
# NoMethodError (undefined method `-' for nil:NilClass)
|
123
|
+
end
|
124
|
+
}
|
125
|
+
|
126
|
+
output << '</span>' << cr
|
127
|
+
end
|
128
|
+
|
129
|
+
output << '</span>'
|
130
|
+
output
|
131
|
+
end
|
132
|
+
|
133
|
+
# Return the `src` as the font_path since we aren't using
|
134
|
+
# any of the built-in fonts.
|
135
|
+
def font_path
|
136
|
+
@src
|
137
|
+
end
|
138
|
+
|
139
|
+
def initialize(src, demo: nil)
|
140
|
+
@src = src
|
141
|
+
@demo = demo
|
142
|
+
|
143
|
+
# TODO: Check that src exists, because TTFunk won't and will just
|
144
|
+
# give us an unusable object instead.
|
145
|
+
@font_meta = TTFunk::File.open(src)
|
146
|
+
|
147
|
+
# https://libvips.github.io/libvips/API/current/libvips-create.html#vips-text
|
148
|
+
@image = Vips::Image.text(
|
149
|
+
# This string must be well-escaped Pango Markup:
|
150
|
+
# https://developer.gnome.org/pango/stable/pango-Markup.html
|
151
|
+
# However the official function for escaping text is
|
152
|
+
# not implemented in Ruby GLib, so we have to do it ourselves.
|
153
|
+
to_pango,
|
154
|
+
**{
|
155
|
+
# String absolute path to TTF
|
156
|
+
:fontfile => font_path,
|
157
|
+
# It's not enough to just specify the TTF path;
|
158
|
+
# we must also specify a font family, subfamily, and size.
|
159
|
+
:font => "#{font_name}",
|
160
|
+
# Space between lines (in Points).
|
161
|
+
:spacing => @font_meta.line_gap,
|
162
|
+
# Requires libvips 8.8
|
163
|
+
:justify => false,
|
164
|
+
:dpi => 144,
|
165
|
+
},
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
169
|
+
end # Font
|
170
|
+
end # DistorteD
|
171
|
+
end # Cooltrainer
|
172
|
+
|
173
|
+
|
174
|
+
# Notes on file-format specifics and software-library-specifics
|
175
|
+
#
|
176
|
+
# # TTF (via TTFunk)
|
177
|
+
#
|
178
|
+
# ## Cmap
|
179
|
+
#
|
180
|
+
# Each TTFunk::Table::Cmap::Format<whatever> class responds to `:supported?`
|
181
|
+
# with its own internal boolean telling us if that Format is usable in TTFunk.
|
182
|
+
# This has nothing to do with any font file itself, just the library code.
|
183
|
+
# irb(main)> font.cmap.tables.map{|t| t.supported?}
|
184
|
+
# => [true, true, true]
|
185
|
+
#
|
186
|
+
# Any subclass of TTFunk::Table::Cmap::Subtable responds to `:unicode?`
|
187
|
+
# with a boolean calculated from the instance `@platform_id` and `@encoding_id`,
|
188
|
+
# and those numeric IDs are assigned to the symbolic (e.g. `:macroman`) names in:
|
189
|
+
# https://github.com/prawnpdf/ttfunk/blob/master/lib/ttfunk/table/cmap/subtable.rb
|
190
|
+
# irb(main)> font.cmap.tables.map{|t| t.unicode?}
|
191
|
+
# => [true, false, true]
|
192
|
+
#
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# Requiring libvips 8.8 for HEIC/HEIF (moo) support, `justify` support in the
|
2
|
+
# Vips::Image text operator, animated WebP support, and more:
|
3
|
+
# https://libvips.github.io/libvips/2019/04/22/What's-new-in-8.8.html
|
4
|
+
VIPS_MINIMUM_VER = [8, 8, 0]
|
5
|
+
|
6
|
+
# Tell the user to install the shared library if it's missing.
|
7
|
+
begin
|
8
|
+
require 'vips'
|
9
|
+
|
10
|
+
we_good = false
|
11
|
+
if Vips::version(0) >= VIPS_MINIMUM_VER[0]
|
12
|
+
if Vips::version(1) >= VIPS_MINIMUM_VER[1]
|
13
|
+
if Vips::version(2) >= VIPS_MINIMUM_VER[2]
|
14
|
+
we_good = true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
unless we_good
|
19
|
+
raise LoadError.new("libvips is older than DistorteD's minimum requirement: needed #{VIPS_MINIMUM_VER.join('.'.freeze)} vs available '#{Vips::version_string}'")
|
20
|
+
end
|
21
|
+
|
22
|
+
rescue LoadError => le
|
23
|
+
# Only match libvips.so load failure
|
24
|
+
raise unless le.message =~ /libvips.so/
|
25
|
+
|
26
|
+
# Multiple OS help
|
27
|
+
help = <<~INSTALL
|
28
|
+
|
29
|
+
Please install the libvips image processing library.
|
30
|
+
|
31
|
+
FreeBSD:
|
32
|
+
pkg install graphics/vips
|
33
|
+
|
34
|
+
macOS:
|
35
|
+
brew install vips
|
36
|
+
|
37
|
+
Debian/Ubuntu/Mint:
|
38
|
+
apt install libvips libvips-dev
|
39
|
+
INSTALL
|
40
|
+
|
41
|
+
# Re-raise with install message
|
42
|
+
raise $!, "#{help}\n#{$!}", $!.backtrace
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'set'
|
46
|
+
|
47
|
+
require 'mime/types'
|
48
|
+
|
49
|
+
module Cooltrainer
|
50
|
+
module DistorteD
|
51
|
+
class Image
|
52
|
+
|
53
|
+
MEDIA_TYPE = 'image'.freeze
|
54
|
+
|
55
|
+
# SVG support is a sub-class and not directly supported here:
|
56
|
+
# `write_to_file': No known saver for '/home/okeeblow/Works/cooltrainer/_site/IIDX-turntable.svg'. (Vips::Error)
|
57
|
+
MIME_TYPES = MIME::Types[/^#{MEDIA_TYPE}\/(?!svg)/, :complete => true].to_set
|
58
|
+
|
59
|
+
# Attributes for our <picture>/<img>.
|
60
|
+
# Automatically enabled as attrs for DD Liquid Tag.
|
61
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture#Attributes
|
62
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes
|
63
|
+
# https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading
|
64
|
+
# :crop is a Vips-only attr
|
65
|
+
ATTRS = Set[:alt, :caption, :href, :crop, :loading]
|
66
|
+
|
67
|
+
# Defaults for HTML Element attributes.
|
68
|
+
# Not every attr has to be listed here.
|
69
|
+
# Many need no default and just won't render.
|
70
|
+
ATTRS_DEFAULT = {
|
71
|
+
:crop => :attention,
|
72
|
+
:loading => :eager,
|
73
|
+
}
|
74
|
+
ATTRS_VALUES = {
|
75
|
+
# https://www.rubydoc.info/gems/ruby-vips/Vips/Interesting
|
76
|
+
:crop => Set[:none, :centre, :entropy, :attention],
|
77
|
+
:loading => Set[:eager, :lazy],
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
def initialize(src)
|
82
|
+
@image = Vips::Image.new_from_file(src)
|
83
|
+
@src = src
|
84
|
+
end
|
85
|
+
|
86
|
+
def rotate(angle: nil)
|
87
|
+
if angle == :auto
|
88
|
+
@image = @image&.autorot
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def clean
|
93
|
+
# Nuke the entire site from orbit. It's the only way to be sure.
|
94
|
+
@image.get_fields.grep(/exif-ifd/).each {|field| @image.remove field}
|
95
|
+
end
|
96
|
+
|
97
|
+
def save(dest, width: nil, crop: nil)
|
98
|
+
begin
|
99
|
+
if width.nil? or width == :full
|
100
|
+
return @image.write_to_file(dest)
|
101
|
+
elsif width.respond_to?(:to_i)
|
102
|
+
ver = @image.thumbnail_image(
|
103
|
+
width.to_i,
|
104
|
+
# Use `self` namespace for constants so subclasses can redefine
|
105
|
+
**{:crop => crop || self.singleton_class.const_get(:ATTRS_DEFAULT)[:crop]},
|
106
|
+
)
|
107
|
+
return ver.write_to_file(dest)
|
108
|
+
end
|
109
|
+
rescue Vips::Error => v
|
110
|
+
if v.message.include?('No known saver')
|
111
|
+
# TODO: Handle missing output formats. Replacements? Skip it? Die?
|
112
|
+
return nil
|
113
|
+
else
|
114
|
+
raise
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end # save
|
118
|
+
|
119
|
+
end # Image
|
120
|
+
end # DistorteD
|
121
|
+
end # Cooltrainer
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Cooltrainer
|
2
|
+
module DistorteD
|
3
|
+
module Tech
|
4
|
+
module Pango
|
5
|
+
|
6
|
+
# Escape text as necessary for Pango Markup, which is what Vips::Image.text()
|
7
|
+
# expects for its argv. This code should be in GLib but is unimplemented in Ruby's:
|
8
|
+
#
|
9
|
+
# https://ruby-gnome2.osdn.jp/hiki.cgi?Gtk%3A%3ALabel#Markup+%28styled+text%29
|
10
|
+
# "The markup passed to Gtk::Label#set_markup() must be valid; for example,
|
11
|
+
# literal </>/& characters must be escaped as <, >, and &.
|
12
|
+
# If you pass text obtained from the user, file, or a network to
|
13
|
+
# Gtk::Label#set_markup(), you'll want to escape it
|
14
|
+
# with GLib::Markup.escape_text?(not implemented yet)."
|
15
|
+
#
|
16
|
+
# Base my own implementation on the original C version found in gmarkup:
|
17
|
+
# https://gitlab.gnome.org/GNOME/glib/-/blob/master/glib/gmarkup.c
|
18
|
+
def g_markup_escape_text(text)
|
19
|
+
text.map{ |c| g_markup_escape_char(c) }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns a Pango-escaped Carriage Return.
|
23
|
+
# Use this for linebreaking Pango Markup output.
|
24
|
+
def cr
|
25
|
+
g_markup_escape_char(0x0D)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns a Pango-escapped Line Feed.
|
29
|
+
# This isn't used/needed for anything with Pango
|
30
|
+
# but it felt weird to include CR and not LF lmao
|
31
|
+
def lf
|
32
|
+
g_markup_escape_char(0x0A)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a Pango'escaped CRLF pair.
|
36
|
+
# Also not needed for anything.
|
37
|
+
def crlf
|
38
|
+
cr << lf
|
39
|
+
end
|
40
|
+
|
41
|
+
# The char-by-char actual function used by g_markup_escape_text
|
42
|
+
def g_markup_escape_char(c)
|
43
|
+
# I think a fully-working version of this function would
|
44
|
+
# be as simple `sprintf('&#x%x;', c.ord)`, but I want to copy
|
45
|
+
# the C implementation as closely as possible, which means using
|
46
|
+
# the named escape sequences for common characters and separating
|
47
|
+
# the Unicode control characters (> 0x7f) even though three's no
|
48
|
+
# need to in Ruby.
|
49
|
+
case c.ord
|
50
|
+
when '&'.ord
|
51
|
+
'&'
|
52
|
+
when '<'.ord
|
53
|
+
'<'
|
54
|
+
when '>'.ord
|
55
|
+
'>'
|
56
|
+
when '\''.ord
|
57
|
+
'''
|
58
|
+
when '"'.ord
|
59
|
+
'"'
|
60
|
+
when 0x1..0x8, 0xb..0xc, 0xe..0x1f, 0x7f
|
61
|
+
sprintf('&#x%x;', c.ord)
|
62
|
+
when 0x7f..0x84, 0x86..0x9f
|
63
|
+
sprintf('&#x%x;', c.ord)
|
64
|
+
when 0x0 # what's this…?
|
65
|
+
# Avoid a `ArgumentError: string contains null byte`
|
66
|
+
# by not printing one :)
|
67
|
+
else
|
68
|
+
c
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end # Pango
|
73
|
+
end # Tech
|
74
|
+
end # DistorteD
|
75
|
+
end # Cooltrainer
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
|
5
|
+
# Complement Ruby::YAML behavior, where usage of Set syntax
|
6
|
+
# returns a Hash with all-nil values.
|
7
|
+
# Calling :to_set on a Hash with all-nil values should return
|
8
|
+
# a Set of the Hash's keys.
|
9
|
+
this_old_set = instance_method(:to_set)
|
10
|
+
define_method(:to_set) do
|
11
|
+
if self.values.all?{ |v| v.nil? }
|
12
|
+
self.keys.to_set
|
13
|
+
else
|
14
|
+
this_old_set.bind(self).()
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# https://github.com/dam13n/ruby-bury/blob/master/hash.rb
|
19
|
+
# This is not packaged as a Gem or I'd be using it instead of including my own.
|
20
|
+
def bury(*args)
|
21
|
+
if args.count < 2
|
22
|
+
raise ArgumentError.new('2 or more arguments required')
|
23
|
+
elsif args.count == 2
|
24
|
+
self[args[0]] = args[1]
|
25
|
+
else
|
26
|
+
arg = args.shift
|
27
|
+
self[arg] = {} unless self[arg]
|
28
|
+
self[arg].bury(*args) unless args.empty?
|
29
|
+
end
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'mime/types'
|
2
|
+
|
3
|
+
# MIME::Types#preferred_extension returns @extensions.first unless
|
4
|
+
# otherwise set. I don't like some of the defaults, so this file
|
5
|
+
# changes them.
|
6
|
+
# Normally I don't like to monkey patch just on import without calling
|
7
|
+
# some method, but this is one time I explicitly want to do that.
|
8
|
+
MIME::Types['image/jpeg'].last.preferred_extension = 'jpg'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
# Override Set.to_hash to complement Ruby::YAML's Set implementation,
|
4
|
+
# where the YAML Set syntax returns a Hash with all-nil values,
|
5
|
+
# at least without some decorator sugar in the YAML itself:
|
6
|
+
# https://rhnh.net/2011/01/31/yaml-tutorial/
|
7
|
+
#
|
8
|
+
# Since Set is implemented using a Hash internally I think it makes
|
9
|
+
# more sense for Set.to_hash to return a Hash with all-nil values
|
10
|
+
# with keys matching the contents of the original Set.
|
11
|
+
class Set
|
12
|
+
def to_hash
|
13
|
+
Hash[self.map { |s| [s, nil] }]
|
14
|
+
end
|
15
|
+
end
|