distorted-jekyll 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/distorted-jekyll.rb +0 -4
- data/lib/distorted-jekyll/{template/13th-style.css → 13th-style.css} +0 -0
- data/lib/distorted-jekyll/13th-style.rb +1 -1
- data/lib/distorted-jekyll/_config_default.yml +3 -21
- data/lib/distorted-jekyll/invoker.rb +194 -219
- data/lib/distorted-jekyll/liquid_liquid.rb +255 -0
- data/lib/distorted-jekyll/liquid_liquid/anchor.liquid +5 -0
- data/lib/distorted-jekyll/liquid_liquid/anchor_inline.liquid +1 -0
- data/lib/distorted-jekyll/liquid_liquid/embed.liquid +1 -0
- data/lib/distorted-jekyll/liquid_liquid/img.liquid +1 -0
- data/lib/distorted-jekyll/liquid_liquid/object.liquid +5 -0
- data/lib/distorted-jekyll/liquid_liquid/picture.liquid +15 -0
- data/lib/distorted-jekyll/liquid_liquid/picture.rb +48 -0
- data/lib/distorted-jekyll/liquid_liquid/picture_source.liquid +1 -0
- data/lib/distorted-jekyll/liquid_liquid/root.liquid +5 -0
- data/lib/distorted-jekyll/liquid_liquid/video.liquid +5 -0
- data/lib/distorted-jekyll/liquid_liquid/video_source.liquid +1 -0
- data/lib/distorted-jekyll/md_injection.rb +30 -25
- data/lib/distorted-jekyll/media_molecule.rb +20 -0
- data/lib/distorted-jekyll/media_molecule/font.rb +21 -0
- data/lib/distorted-jekyll/media_molecule/image.rb +15 -0
- data/lib/distorted-jekyll/media_molecule/never_let_you_down.rb +28 -0
- data/lib/distorted-jekyll/media_molecule/pdf.rb +108 -0
- data/lib/distorted-jekyll/media_molecule/svg.rb +20 -0
- data/lib/distorted-jekyll/media_molecule/text.rb +23 -0
- data/lib/distorted-jekyll/media_molecule/video.rb +45 -0
- data/lib/distorted-jekyll/monkey_business/jekyll/cleaner.rb +68 -1
- data/lib/distorted-jekyll/static_state.rb +19 -60
- data/lib/distorted-jekyll/the_setting_sun.rb +179 -0
- metadata +26 -21
- data/lib/distorted-jekyll/floor.rb +0 -266
- data/lib/distorted-jekyll/molecule/font.rb +0 -62
- data/lib/distorted-jekyll/molecule/image.rb +0 -94
- data/lib/distorted-jekyll/molecule/lastresort.rb +0 -51
- data/lib/distorted-jekyll/molecule/pdf.rb +0 -79
- data/lib/distorted-jekyll/molecule/svg.rb +0 -47
- data/lib/distorted-jekyll/molecule/text.rb +0 -62
- data/lib/distorted-jekyll/molecule/video.rb +0 -85
- data/lib/distorted-jekyll/template/error_code.liquid +0 -3
- data/lib/distorted-jekyll/template/font.liquid +0 -32
- data/lib/distorted-jekyll/template/image.liquid +0 -32
- data/lib/distorted-jekyll/template/lastresort.liquid +0 -20
- data/lib/distorted-jekyll/template/pdf.liquid +0 -14
- data/lib/distorted-jekyll/template/svg.liquid +0 -32
- data/lib/distorted-jekyll/template/text.liquid +0 -32
- data/lib/distorted-jekyll/template/video.liquid +0 -11
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'distorted/media_molecule'
|
3
|
+
|
4
|
+
module Jekyll::DistorteD
|
5
|
+
# Load Jekyll Molecules which will implicitly also load
|
6
|
+
# the Floor Molecules they're based on if they aren't already.
|
7
|
+
@@loaded_molecules rescue begin
|
8
|
+
Dir[File.join(__dir__, 'media_molecule', '*.rb')].each { |molecule| require molecule }
|
9
|
+
@@loaded_molecules = true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Cooltrainer::DistorteD
|
14
|
+
# Override default Molecule Set with their Liquid-rendering submolecules.
|
15
|
+
def self.media_molecules
|
16
|
+
Jekyll::DistorteD::Molecule.constants.map { |molecule|
|
17
|
+
Jekyll::DistorteD::Molecule::const_get(molecule)
|
18
|
+
}.to_set
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'distorted/media_molecule/font'
|
4
|
+
require 'distorted-jekyll/liquid_liquid/picture'
|
5
|
+
|
6
|
+
|
7
|
+
module Jekyll; end
|
8
|
+
module Jekyll::DistorteD; end
|
9
|
+
module Jekyll::DistorteD::Molecule; end
|
10
|
+
module Jekyll::DistorteD::Molecule::Font
|
11
|
+
|
12
|
+
include Cooltrainer::DistorteD::Molecule::Font
|
13
|
+
include Jekyll::DistorteD::LiquidLiquid::Picture
|
14
|
+
|
15
|
+
Cooltrainer::DistorteD::IMPLANTATION(:LOWER_WORLD, Cooltrainer::DistorteD::Molecule::Font).each_key { |type|
|
16
|
+
define_method(type.distorted_template_method) { |change|
|
17
|
+
Cooltrainer::ElementalCreation.new(:anchor_inline, change, **{})
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'distorted-jekyll/liquid_liquid/picture'
|
4
|
+
require 'distorted/media_molecule/image'
|
5
|
+
|
6
|
+
|
7
|
+
module Jekyll; end
|
8
|
+
module Jekyll::DistorteD; end
|
9
|
+
module Jekyll::DistorteD::Molecule; end
|
10
|
+
module Jekyll::DistorteD::Molecule::Image
|
11
|
+
|
12
|
+
include Cooltrainer::DistorteD::Molecule::Image
|
13
|
+
include Jekyll::DistorteD::LiquidLiquid::Picture
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'distorted/checking_you_out'
|
4
|
+
require 'distorted-jekyll/liquid_liquid'
|
5
|
+
|
6
|
+
module Jekyll; end
|
7
|
+
module Jekyll::DistorteD; end
|
8
|
+
module Jekyll::DistorteD::Molecule; end
|
9
|
+
module Jekyll::DistorteD::Molecule::NeverLetYouDown
|
10
|
+
|
11
|
+
FALLBACK_TYPE = CHECKING::YOU::OUT['application/x.distorted.never-let-you-down']
|
12
|
+
LOWER_WORLD = Hash[
|
13
|
+
FALLBACK_TYPE => Hash[
|
14
|
+
:alt => Cooltrainer::Compound.new(:alt, blurb: 'Alternate text to display when this element cannot be rendered.'),
|
15
|
+
:title => Cooltrainer::Compound.new(:title, blurb: 'Extra information about this element — usually displayed as tooltip text.'),
|
16
|
+
:href => Cooltrainer::Compound.new(:href, blurb: 'Hyperlink reference for this element.')
|
17
|
+
]
|
18
|
+
]
|
19
|
+
OUTER_LIMITS = Hash[FALLBACK_TYPE => nil]
|
20
|
+
|
21
|
+
define_method(FALLBACK_TYPE.distorted_file_method) { |dest_root, change|
|
22
|
+
copy_file(change.path(dest_root))
|
23
|
+
}
|
24
|
+
define_method(FALLBACK_TYPE.distorted_template_method) { |change|
|
25
|
+
Cooltrainer::ElementalCreation.new(:anchor_inline, change, **{})
|
26
|
+
}
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'distorted/media_molecule/pdf'
|
4
|
+
|
5
|
+
|
6
|
+
module Jekyll; end
|
7
|
+
module Jekyll::DistorteD; end
|
8
|
+
module Jekyll::DistorteD::Molecule; end
|
9
|
+
module Jekyll::DistorteD::Molecule::PDF
|
10
|
+
|
11
|
+
include Cooltrainer::DistorteD::Molecule::PDF
|
12
|
+
|
13
|
+
# https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_open_parameters.pdf
|
14
|
+
#
|
15
|
+
# Adobe's PDF Open Parameters documentation sez:
|
16
|
+
# "Individual parameters, together with their values (separated by & or #),
|
17
|
+
# can be no greater then 32 characters in length."
|
18
|
+
# …but then goes on to show some examples (like `comment`)
|
19
|
+
# that are clearly longer than 32 characters.
|
20
|
+
# Dunno. I'll err on the side of giving you a footgun.
|
21
|
+
#
|
22
|
+
# Keep the PDF Open Params in the order they are defined
|
23
|
+
# in the Adobe documentation, since it says they should
|
24
|
+
# be specified in the URL in that same order.
|
25
|
+
#
|
26
|
+
# "You cannot use the reserved characters =, #, and &.
|
27
|
+
# There is no way to escape these special characters."
|
28
|
+
RESERVED_CHARACTERS_FRAGMENT = '[^=#&]+'.freeze
|
29
|
+
FLOAT_INT_FRAGMENT = '[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)'.freeze
|
30
|
+
ZERO_TO_ONE_HUNDRED = /^(([1-9]\d?|1\d{1})([.,]\d{0,1})?|100([.,]0{1})?)$/
|
31
|
+
PDF_OPEN_PARAMS = Array[
|
32
|
+
Cooltrainer::Compound.new(:nameddest, valid: /^#{RESERVED_CHARACTERS_FRAGMENT}$/, blurb: 'Jump to a named destination in the document.'),
|
33
|
+
Cooltrainer::Compound.new(:page, valid: Integer, default: 1, blurb: 'Jump to a numbered page in the document.'),
|
34
|
+
Cooltrainer::Compound.new(:comment, valid: /^#{RESERVED_CHARACTERS_FRAGMENT}$/, blurb: 'Jump to a comment on a given page.'),
|
35
|
+
Cooltrainer::Compound.new(:collab, valid: /^(DAVFDF|FSFDF|DB)@#{RESERVED_CHARACTERS_FRAGMENT}$/, blurb: 'Sets the comment repository to be used to supply and store comments for the document.'),
|
36
|
+
Cooltrainer::Compound.new(:zoom, valid: /^#{FLOAT_INT_FRAGMENT}(,#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT})?$/, blurb: 'Sets the zoom and scroll factors, using float or integer values.'),
|
37
|
+
Cooltrainer::Compound.new(:view, valid: /^Fit(H|V|B|BH|BV(,#{FLOAT_INT_FRAGMENT})?)?$/, default: :Fit, blurb: 'Set the view of the displayed page, using the keyword values defined in the PDF language specification. For more information, see the PDF Reference.'),
|
38
|
+
Cooltrainer::Compound.new(:viewrect, valid: /^#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT}$/, blurb: 'Sets the view rectangle using float or integer values in a coordinate system where 0,0 represents the top left corner of the visible page, regardless of document rotation.'),
|
39
|
+
Cooltrainer::Compound.new(:pagemode, valid: Set[:none, :thumbs, :bookmarks], default: :none, blurb: 'Displays bookmarks or thumbnails.'),
|
40
|
+
Cooltrainer::Compound.new(:scrollbar, valid: Cooltrainer::BOOLEAN_VALUES, default: true, blurb: 'Turns scrollbars on or off.'),
|
41
|
+
Cooltrainer::Compound.new(:search, valid: /^#{RESERVED_CHARACTERS_FRAGMENT}(,\s#{RESERVED_CHARACTERS_FRAGMENT})*$/ , blurb: 'Opens the Search panel and performs a search for any of the words in the specified word list. The first matching word is highlighted in the document.'),
|
42
|
+
Cooltrainer::Compound.new(:toolbar, valid: Cooltrainer::BOOLEAN_VALUES, default: true, blurb: 'Turns the toolbar on or off.'),
|
43
|
+
Cooltrainer::Compound.new(:statusbar, valid: Cooltrainer::BOOLEAN_VALUES, default: true, blurb: 'Turns the status bar on or off.'),
|
44
|
+
Cooltrainer::Compound.new(:messages, valid: Cooltrainer::BOOLEAN_VALUES, default: false, blurb: 'Turns the document message bar on or off.'),
|
45
|
+
Cooltrainer::Compound.new(:navpanes, valid: Cooltrainer::BOOLEAN_VALUES, default: true, blurb: 'Turns the navigation panes and tabs on or off.'),
|
46
|
+
Cooltrainer::Compound.new(:highlight, valid: /^#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT},#{FLOAT_INT_FRAGMENT}$/, blurb: 'Highlights a specified rectangle on the displayed page. Use the `page` command before this command.'),
|
47
|
+
Cooltrainer::Compound.new(:fdf, valid: /^#{RESERVED_CHARACTERS_FRAGMENT}$/, blurb: 'Specifies an FDF file to populate form fields in the PDF file being
|
48
|
+
opened.'),
|
49
|
+
]
|
50
|
+
|
51
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object#Attributes
|
52
|
+
CONTAINER_ATTRIBUTES = Array[
|
53
|
+
Cooltrainer::Compound.new(:alt, valid: String),
|
54
|
+
Cooltrainer::Compound.new(:caption, valid: String),
|
55
|
+
Cooltrainer::Compound.new(:height, valid: String, default: '100%'.freeze, blurb: '<object> viewer container height.'),
|
56
|
+
Cooltrainer::Compound.new(:width, valid: String, default: '100%'.freeze, blurb: '<object> viewer container width.'),
|
57
|
+
]
|
58
|
+
|
59
|
+
OUTER_LIMITS = Hash[
|
60
|
+
CHECKING::YOU::OUT['application/pdf'] => PDF_OPEN_PARAMS.concat(CONTAINER_ATTRIBUTES).reduce(Hash[]) {|aka, compound|
|
61
|
+
aka.tap { |a| a.store(compound.element, compound) }
|
62
|
+
}
|
63
|
+
]
|
64
|
+
|
65
|
+
# Generate a Hash of our PDF Open Params based on any given to the Liquid tag
|
66
|
+
# and any loaded from the defaults.
|
67
|
+
# https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_open_parameters.pdf
|
68
|
+
def pdf_open_params
|
69
|
+
PDF_OPEN_PARAMS.reduce(Hash[]) {|params, compound|
|
70
|
+
# Only include those params whose user-given value exists and differs from its default.
|
71
|
+
params.tap { |p|
|
72
|
+
p.store(compound.element, abstract(compound.element)) unless [
|
73
|
+
nil, ''.freeze, compound.default,
|
74
|
+
].include?(abstract(compound.element))
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
# Generate the URL fragment version of the PDF Open Params.
|
80
|
+
# This would be difficult / impossible to construct within Liquid
|
81
|
+
# from the individual variables, so let's just do it out here.
|
82
|
+
def pdf_open_params_url
|
83
|
+
pdf_open_params.map{ |(k,v)|
|
84
|
+
case
|
85
|
+
when k == :search
|
86
|
+
# The PDF Open Params docs specify `search` should be quoted.
|
87
|
+
"#{k}=\"#{v}\""
|
88
|
+
when Cooltrainer::BOOLEAN_VALUES.include?(v)
|
89
|
+
# Convert booleans to the numeric representation Adobe use here.
|
90
|
+
"#{k}=#{v ? 1 : 0}"
|
91
|
+
else
|
92
|
+
"#{k}=#{v}"
|
93
|
+
end
|
94
|
+
}.join('&')
|
95
|
+
end
|
96
|
+
|
97
|
+
# http://joliclic.free.fr/html/object-tag/en/
|
98
|
+
# TODO: iOS treats our <object> like an <img>,
|
99
|
+
# showing only the first page with transparency and stretched to the
|
100
|
+
# size of the container element.
|
101
|
+
# We will need something like PDF.js in an <iframe> to handle this.
|
102
|
+
define_method(CHECKING::YOU::OUT['application/pdf'].distorted_template_method) { |change|
|
103
|
+
Cooltrainer::ElementalCreation.new(:object, change, children: [:embed, :anchor_inline]).tap { |e|
|
104
|
+
e.fragment = pdf_open_params.empty? ? ''.freeze : "##{pdf_open_params_url}"
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
end # PDF
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'distorted/media_molecule/svg'
|
4
|
+
require 'distorted-jekyll/liquid_liquid/picture'
|
5
|
+
|
6
|
+
|
7
|
+
module Jekyll; end
|
8
|
+
module Jekyll::DistorteD; end
|
9
|
+
module Jekyll::DistorteD::Molecule; end
|
10
|
+
module Jekyll::DistorteD::Molecule::SVG
|
11
|
+
|
12
|
+
include Cooltrainer::DistorteD::Molecule::SVG
|
13
|
+
include Jekyll::DistorteD::LiquidLiquid::Picture
|
14
|
+
|
15
|
+
define_method(
|
16
|
+
CHECKING::YOU::OUT['image/svg+xml'].distorted_template_method,
|
17
|
+
Jekyll::DistorteD::LiquidLiquid::Picture::render_picture_source,
|
18
|
+
)
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require 'distorted/media_molecule/text'
|
4
|
+
require 'distorted-jekyll/liquid_liquid/picture'
|
5
|
+
|
6
|
+
|
7
|
+
module Jekyll; end
|
8
|
+
module Jekyll::DistorteD; end
|
9
|
+
module Jekyll::DistorteD::Molecule; end
|
10
|
+
module Jekyll::DistorteD::Molecule::Text
|
11
|
+
|
12
|
+
include Cooltrainer::DistorteD::Molecule::Text
|
13
|
+
include Jekyll::DistorteD::LiquidLiquid::Picture
|
14
|
+
|
15
|
+
Cooltrainer::DistorteD::IMPLANTATION(:LOWER_WORLD, Cooltrainer::DistorteD::Molecule::Text).each_key { |type|
|
16
|
+
define_method(type.distorted_template_method) { |change|
|
17
|
+
# Remove the destructured empty Hash once we drop Ruby 2.7
|
18
|
+
# so we don't get auto-destructured due to Change#to_hash.
|
19
|
+
Cooltrainer::ElementalCreation.new(:anchor_inline, change, **{})
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
end # Text
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'distorted/media_molecule/video'
|
2
|
+
|
3
|
+
|
4
|
+
module Jekyll; end
|
5
|
+
module Jekyll::DistorteD; end
|
6
|
+
module Jekyll::DistorteD::Molecule; end
|
7
|
+
module Jekyll::DistorteD::Molecule::Video
|
8
|
+
|
9
|
+
include Cooltrainer::DistorteD::Molecule::Video
|
10
|
+
|
11
|
+
Cooltrainer::DistorteD::IMPLANTATION(:LOWER_WORLD, Cooltrainer::DistorteD::Molecule::Video).each_key { |type|
|
12
|
+
define_method(type.distorted_template_method) { |change|
|
13
|
+
Cooltrainer::ElementalCreation.new(:video_source, change, parents: Array[:video])
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
# Override wanted-filenames method from StaticState with one that prevents our generated
|
18
|
+
# video segments from being deleted.
|
19
|
+
# This is still very hacky until I can guarantee/control the number of segments we get.
|
20
|
+
def wanted_files
|
21
|
+
dd_dest = File.join(the_setting_sun(:jekyll, :destination).to_s, @relative_dest)
|
22
|
+
changes.each_with_object(Set[]) { |change, wanted|
|
23
|
+
case change.type
|
24
|
+
# Treat HLS and MPEG-DASH the same, with slightly different naming conventions.
|
25
|
+
# Add their main playlist file, but then also glob any segments that happen to exist.
|
26
|
+
when CHECKING::YOU::OUT['application/dash+xml']
|
27
|
+
hls_dir = File.join(dd_dest, "#{basename}.hls")
|
28
|
+
wanted.add(File.join(hls_dir, "#{basename}.m3u8"))
|
29
|
+
if Dir.exist?(hls_dir)
|
30
|
+
Dir.entries(hls_dir).to_set.subtract(Set["#{basename}.m3u8"]).each { |hls| wanted.add(File.join(hls_dir, hls)) }
|
31
|
+
end
|
32
|
+
when CHECKING::YOU::OUT['application/vnd.apple.mpegurl']
|
33
|
+
dash_dir = File.join(dd_dest, "#{basename}.dash")
|
34
|
+
wanted.add(File.join(dash_dir, "#{basename}.mpd"))
|
35
|
+
if Dir.exist?(dash_dir)
|
36
|
+
Dir.entries(dash_dir).to_set.subtract(Set["#{basename}.mpd"]).each { |dash| wanted.add(File.join(dash_dir, dash)) }
|
37
|
+
end
|
38
|
+
else
|
39
|
+
# Treat any other type (including single-file video types) like normal.
|
40
|
+
wanted.add(change.name)
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
end # Video
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'set'
|
2
|
+
require 'distorted-jekyll/media_molecule'
|
2
3
|
|
3
4
|
module Jekyll
|
4
5
|
# Handles the cleanup of a site's destination before it is built or re-built.
|
@@ -49,6 +50,72 @@ module Jekyll
|
|
49
50
|
Jekyll.logger.debug('DistorteD', "Monkey-patched Jekyll::Cleaner#new_files backtrace: #{e.backtrace}")
|
50
51
|
the_old_new_thing.bind(self).()
|
51
52
|
end
|
52
|
-
end
|
53
|
+
end # define_method :new_files
|
54
|
+
|
55
|
+
|
56
|
+
# Private: Creates a regular expression from the config's keep_files array
|
57
|
+
#
|
58
|
+
# Examples
|
59
|
+
# ['.git','.svn'] with site.dest "/myblog/_site" creates
|
60
|
+
# the following regex: /\A\/myblog\/_site\/(\.git|\/.svn)/
|
61
|
+
#
|
62
|
+
# Returns the regular expression
|
63
|
+
#
|
64
|
+
# Monkey-patch this to protect DistorteD-generated files from destruction
|
65
|
+
# https://jekyllrb.com/docs/configuration/incremental-regeneration/
|
66
|
+
# when running Jekyll in Incremental mode twice in a row.
|
67
|
+
#
|
68
|
+
# The first Incremental build will process our Liquid tags on every post/page
|
69
|
+
# which will add our generated files to Jekyll::Cleaner's :new_files (See above!)
|
70
|
+
# A second build, however, will not re-process any posts/pages that haven't changed.
|
71
|
+
# Our Tags never get initialized, so their previously-generated files now appear
|
72
|
+
# to be spurious and will get purged.
|
73
|
+
#
|
74
|
+
# Work around this by merging Jekyll::Cleaner#keep_file_regex with a second Regexp
|
75
|
+
# based on the :preferred_extension for every MIME::Type DistorteD can output.
|
76
|
+
mr_regular = instance_method(:keep_file_regex)
|
77
|
+
define_method(:keep_file_regex) do
|
78
|
+
begin
|
79
|
+
# We're going to use it either way, so go ahead and get what the :keep_file_regex
|
80
|
+
# would have been in unpatched Jekyll, e.g.:
|
81
|
+
# (?-mix:\A/home/okeeblow/Works/cooltrainer/_site\/(\.git|\.svn))
|
82
|
+
super_regexp = mr_regular.bind(self).()
|
83
|
+
|
84
|
+
# If we aren't in Incremental mode then each Tag will explicitly declare
|
85
|
+
# the files they write, and that's preferrable to this shotgun approach
|
86
|
+
# since the Regexp approach may preserve unwanted files, but "Some unwanted files"
|
87
|
+
# is way nicer than "fifteen minutes rebuilding everything" rofl
|
88
|
+
if site&.incremental?
|
89
|
+
# Discover every supported output MIME::Type based on every loaded MediaMolecule.
|
90
|
+
outer_limits = Cooltrainer::DistorteD::IMPLANTATION(
|
91
|
+
:OUTER_LIMITS,
|
92
|
+
Cooltrainer::DistorteD::media_molecules,
|
93
|
+
).values.flat_map(&:keys)
|
94
|
+
|
95
|
+
# Build a new Regexp globbing the preferred extension of every Type we support, e.g.:
|
96
|
+
# (?-mix:\A/home/okeeblow/Works/cooltrainer/_site/.*(txt|nfo|v|ppm|pgm|pbm|hdr|png|jpg|webp|tiff|fits|gif|bmp|ttf|svg|pdf|mpd|m3u8|mp4))
|
97
|
+
#
|
98
|
+
# Some Types may have duplicate preferred_extensions, and some might have nil
|
99
|
+
# (e.g. our own application/x.distorted.never-let-you-down), so :uniq and :compact them out.
|
100
|
+
outer_regexp = %r!\A#{Regexp.quote(site.dest)}/.*(#{Regexp.union(outer_limits&.map(&:preferred_extension).uniq.compact).source})!
|
101
|
+
|
102
|
+
# Do the thing.
|
103
|
+
combined_regexp = Regexp.union(outer_regexp, super_regexp)
|
104
|
+
Jekyll.logger.debug(
|
105
|
+
'Protecting DistorteD-generated files from Incremental-mode destruction with new Jekyll::Cleaner#keep_file_regex',
|
106
|
+
combined_regexp.source)
|
107
|
+
return combined_regexp
|
108
|
+
else
|
109
|
+
# Feels like I'm patching nothin' at all… nothin' at all… nothin' at all!
|
110
|
+
return super_regexp
|
111
|
+
end
|
112
|
+
rescue RuntimeError => e
|
113
|
+
Jekyll.logger.warn('DistorteD', "Monkey-patching Jekyll::Cleaner#keep_file_regex failed: #{e.message}")
|
114
|
+
Jekyll.logger.debug('DistorteD', "Monkey-patched Jekyll::Cleaner#keep_file_regex backtrace: #{e.backtrace}")
|
115
|
+
# Bail out by returning what the :keep_file_regex would have been without this patch.
|
116
|
+
mr_regular.bind(self).()
|
117
|
+
end
|
118
|
+
end # define_method :keep_file_regex
|
119
|
+
|
53
120
|
end # Cleaner
|
54
121
|
end # Jekyll
|
@@ -14,9 +14,6 @@ module Jekyll::DistorteD; end
|
|
14
14
|
module Jekyll::DistorteD::StaticState
|
15
15
|
|
16
16
|
|
17
|
-
ATTRIBUTES = Set[:title]
|
18
|
-
|
19
|
-
|
20
17
|
# Returns the to-be-written path of a single standard StaticFile.
|
21
18
|
# The value returned by this method is only the 'main' or 'original'
|
22
19
|
# (even if modified somehow) file and does not include the
|
@@ -36,7 +33,7 @@ module Jekyll::DistorteD::StaticState
|
|
36
33
|
# that should be kept when regenerating the site.
|
37
34
|
# This makes DistorteD fast!
|
38
35
|
def destinations(dest_root)
|
39
|
-
|
36
|
+
changes&.flat_map { |change| change.paths(dest_root) }
|
40
37
|
end
|
41
38
|
|
42
39
|
# HACK HACK HACK
|
@@ -47,7 +44,7 @@ module Jekyll::DistorteD::StaticState
|
|
47
44
|
# Assume modified for the sake of freshness :)
|
48
45
|
modified = true
|
49
46
|
|
50
|
-
site_dest =
|
47
|
+
site_dest = the_setting_sun(:jekyll, :destination).to_s
|
51
48
|
if Dir.exist?(site_dest)
|
52
49
|
if Dir.exist?(File.join(site_dest, @relative_dest))
|
53
50
|
extant_files = Dir.entries(File.join(site_dest, @relative_dest)).to_set
|
@@ -85,7 +82,6 @@ module Jekyll::DistorteD::StaticState
|
|
85
82
|
#
|
86
83
|
# Returns false if the file was not modified since last time (no-op).
|
87
84
|
def write(dest_root)
|
88
|
-
plug
|
89
85
|
return false if File.exist?(path) && !modified?
|
90
86
|
|
91
87
|
# Create any directories to the depth of the intended destination.
|
@@ -94,24 +90,26 @@ module Jekyll::DistorteD::StaticState
|
|
94
90
|
# This will be a Set of Hashes each describing the name, type,
|
95
91
|
# dimensions, attributes, etc of each output variation we want.
|
96
92
|
# Full-size outputs will have the special tag `:full`.
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
93
|
+
changes&.each { |change|
|
94
|
+
if self.respond_to?(change.type.distorted_file_method)
|
95
|
+
Jekyll.logger.debug("DistorteD::#{change.type.distorted_file_method}", change.name)
|
96
|
+
# WISHLIST: Remove the empty final positional Hash argument once we require a Ruby version
|
97
|
+
# that will not perform the implicit Change-to-Hash conversion due to Change's
|
98
|
+
# implementation of :to_hash. Ruby 2.7 will complain but still do the conversion,
|
99
|
+
# breaking downstream callers that want a Struct they can call arbitrary key methods on.
|
100
|
+
# https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/
|
101
|
+
self.send(change.type.distorted_file_method, dest_root, change, **{})
|
102
|
+
elsif extname == ".#{change.type.preferred_extension}"
|
105
103
|
Jekyll.logger.debug(@name, <<~RAWCOPY
|
106
|
-
No #{type.
|
107
|
-
but the intended output type #{type.to_s} is the same
|
104
|
+
No #{change.type.distorted_file_method} method is defined,
|
105
|
+
but the intended output type #{change.type.to_s} is the same
|
108
106
|
as the input type, so I will fall back to copying the raw file.
|
109
107
|
RAWCOPY
|
110
108
|
)
|
111
|
-
copy_file(
|
109
|
+
copy_file(change.paths(dest_root).first)
|
112
110
|
else
|
113
|
-
Jekyll.logger.error(@name, "Missing
|
114
|
-
raise MediaTypeOutputNotImplementedError.new(
|
111
|
+
Jekyll.logger.error(@name, "Missing write method #{change.type.distorted_file_method}")
|
112
|
+
raise MediaTypeOutputNotImplementedError.new(change.path(dest_root), type_mars, self.class.name)
|
115
113
|
end
|
116
114
|
}
|
117
115
|
end # write
|
@@ -128,11 +126,6 @@ module Jekyll::DistorteD::StaticState
|
|
128
126
|
|
129
127
|
# Basic file properties
|
130
128
|
|
131
|
-
# Filename without the dot-and-extension.
|
132
|
-
def basename
|
133
|
-
File.basename(@name, '.*')
|
134
|
-
end
|
135
|
-
|
136
129
|
# Returns the extname /!\ including the dot /!\
|
137
130
|
def extname
|
138
131
|
File.extname(@name)
|
@@ -155,46 +148,12 @@ module Jekyll::DistorteD::StaticState
|
|
155
148
|
end
|
156
149
|
end
|
157
150
|
|
158
|
-
# Returns a Hash keyed by MIME::Type objects with value as a Set of Hashes
|
159
|
-
# describing the media's output variations to be generated for each Type.
|
160
|
-
def variations
|
161
|
-
changes(abstract(:changes)).map{ |t|
|
162
|
-
[t, outer_limits(abstract(:outer_limits)).map{ |d|
|
163
|
-
|
164
|
-
# Don't change the filename of full-size variations
|
165
|
-
tag = d&.dig(:tag) != :full ? '-'.concat(d&.dig(:tag).to_s) : ''.freeze
|
166
|
-
# Use the original extname for LastResort
|
167
|
-
ext = t == CHECKING::YOU::OUT('application/x.distorted.last-resort') ? File.extname(@name) : t.preferred_extension
|
168
|
-
# Handle LastResort for files that might be a bare name with no extension
|
169
|
-
dot = '.'.freeze unless ext.nil? || ext&.empty?
|
170
|
-
|
171
|
-
d.merge({
|
172
|
-
# e.g. 'SomeImage-medium.jpg` but just `SomeImage.jpg` and not `SomeImage-full.jpg`
|
173
|
-
# for the full-resolution outputs.
|
174
|
-
# The default `.jpeg` preferred_extension is monkey-patched to `.jpg` because lol
|
175
|
-
:name => "#{basename}#{tag}#{dot}#{ext}",
|
176
|
-
})
|
177
|
-
|
178
|
-
}]
|
179
|
-
}.to_h
|
180
|
-
end
|
181
|
-
|
182
|
-
# Returns a flat Set of Hashes that each describe one variant of
|
183
|
-
# media file output that should exist for a given input file.
|
184
|
-
def files
|
185
|
-
filez = Set[]
|
186
|
-
variations.each_pair{ |t,v|
|
187
|
-
# Merge the type in to each variation Hash since we will no longer
|
188
|
-
# have it as the key to this Set in its container Hash.
|
189
|
-
v.each{ |d| filez.add(d.merge({:type => t})) }
|
190
|
-
}
|
191
|
-
filez
|
192
|
-
end
|
193
151
|
|
194
152
|
# Returns a Set of just the String filenames we want for this media.
|
195
153
|
# This will be used by `modified?` among others.
|
196
154
|
def wanted_files
|
197
|
-
|
155
|
+
# Cooltrainer::Change#names returns an Array[String], so we must concat every Change into one.
|
156
|
+
changes.map(&:names).reduce(&:concat).to_set
|
198
157
|
end
|
199
158
|
|
200
159
|
|