distorted-jekyll 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|