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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5fe548492f52f0b4bb138fbd93ca2fb127f53baf7a3657d73aefc904c22a51b
|
4
|
+
data.tar.gz: 2444637db29356d306f2e1e31a41b0fe9c758574d77bc40d691f51c0ab514af5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6aff9df826afc000fea96df60f7a4305195ea4518e5c96a1c38b5899e72d47860d7866cbad2d5184cd820221d8d15bc86ed946a278345773972b5bdaccaf0008
|
7
|
+
data.tar.gz: 26efca75634c2c45d9b2d01116b96f64585b1c3a00e98471d6659adeeb0d545224911af048faef4df5afc228923f4ea44ebe2eb6e055ff2217ecf73ea6ed64d2
|
data/README.md
CHANGED
@@ -156,7 +156,7 @@ Clone the DistorteD repository and modify your Jekyll `Gemfile` to refer to your
|
|
156
156
|
gem 'distorted-jekyll', :path => '~/repos/DistorteD/DistorteD-Jekyll/'[, :branch => 'NEW-SENSATION']
|
157
157
|
```
|
158
158
|
|
159
|
-
The `DistorteD-Jekyll` Gem will automatically use its local sibling `DistorteD-
|
159
|
+
The `DistorteD-Jekyll` Gem will automatically use its local sibling `DistorteD-Floor` Gem if used in this way.
|
160
160
|
|
161
161
|
## License
|
162
162
|
|
data/lib/distorted-jekyll.rb
CHANGED
@@ -40,10 +40,6 @@ if [
|
|
40
40
|
Hash.method_defined?(:transform_keys), # 2.5
|
41
41
|
Enumerable.method_defined?(:filter_map), # 2.7
|
42
42
|
].all?
|
43
|
-
# Monkey-patch preferred_extensions iff we're going to load.
|
44
|
-
# My JPEGs coming out with a '.jpeg' file extension just annoys me so much.
|
45
|
-
require 'distorted/monkey_business/mnemoniq'
|
46
|
-
|
47
43
|
# Monkey-patch Jekyll::Cleaner to not nuke DistorteD-generated variations
|
48
44
|
# for our media files. This makes DistorteD fast!
|
49
45
|
require 'distorted-jekyll/monkey_business/jekyll/cleaner'
|
File without changes
|
@@ -35,7 +35,7 @@ module Jekyll
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def render_to_output_buffer(context, output)
|
38
|
-
css_filename = File.join(File.dirname(__FILE__), '
|
38
|
+
css_filename = File.join(File.dirname(__FILE__), '13th-style.css'.freeze)
|
39
39
|
|
40
40
|
# Use IO.foreach() to call a block on each line of our template file
|
41
41
|
# without slurping the entire file into memory like File.read() / File.readlines()
|
@@ -6,34 +6,16 @@
|
|
6
6
|
# so duplicate Array values will be compacted!
|
7
7
|
|
8
8
|
standard_image: &standard_image
|
9
|
-
-
|
10
|
-
|
11
|
-
- tag: small
|
12
|
-
width: 400
|
13
|
-
height: 400
|
14
|
-
media: "(max-width: 400px)"
|
15
|
-
- tag: medium
|
16
|
-
width: 800
|
17
|
-
height: 800
|
18
|
-
media: "(min-width: 400px) and (max-width: 800px)"
|
19
|
-
- tag: large
|
20
|
-
width: 1500
|
21
|
-
height: 1500
|
22
|
-
media: "(min-width: 800px)"
|
23
|
-
|
9
|
+
- crop: none
|
10
|
+
breaks: [333, 555, 777, 1111]
|
24
11
|
|
25
12
|
distorted:
|
26
13
|
|
27
|
-
# Liquid template caching is default as of Jekyll 4.0,
|
28
|
-
# but it can be disabled if needed.
|
29
|
-
# I just can't think of when it would be needed :)
|
30
|
-
cache_templates: true
|
31
|
-
|
32
14
|
# Should unrecognized media-types fall back to a bare
|
33
15
|
# <img> tag around the original media file?
|
34
16
|
# If not, the site build will fail when an unrecognized
|
35
17
|
# file is encountered.
|
36
|
-
|
18
|
+
never_let_you_down: true
|
37
19
|
|
38
20
|
# Configure DistorteD format changes by media_type, then by sub_type.
|
39
21
|
# The list of target formats is plain text, media_type/sub_type.
|
@@ -1,259 +1,234 @@
|
|
1
1
|
# Our custom Exceptions
|
2
2
|
require 'distorted/error_code'
|
3
3
|
|
4
|
+
# Molecule loading and plugging functionality
|
5
|
+
require 'distorted/invoker'
|
6
|
+
|
4
7
|
# MIME::Typer
|
5
8
|
require 'distorted/checking_you_out'
|
6
9
|
|
7
10
|
# Configuration-loading code
|
8
|
-
require 'distorted-jekyll/
|
11
|
+
require 'distorted-jekyll/the_setting_sun'
|
9
12
|
require 'distorted-jekyll/static_state'
|
10
13
|
|
11
|
-
# Media-type drivers
|
12
|
-
require 'distorted-jekyll/molecule/font'
|
13
|
-
require 'distorted-jekyll/molecule/image'
|
14
|
-
require 'distorted-jekyll/molecule/text'
|
15
|
-
require 'distorted-jekyll/molecule/pdf'
|
16
|
-
require 'distorted-jekyll/molecule/svg'
|
17
|
-
require 'distorted-jekyll/molecule/video'
|
18
|
-
require 'distorted-jekyll/molecule/lastresort'
|
19
|
-
|
20
|
-
# Set.to_hash
|
21
|
-
require 'distorted/monkey_business/set'
|
22
|
-
|
23
14
|
# Slip in and out of phenomenon
|
24
15
|
require 'liquid/tag'
|
25
16
|
require 'liquid/tag/parser'
|
17
|
+
require 'distorted-jekyll/liquid_liquid'
|
18
|
+
|
19
|
+
require 'distorted-jekyll/media_molecule'
|
26
20
|
|
27
21
|
# Explicitly required for l/t/parser since a1cfa27c27cf4d4c308da2f75fbae88e9d5ae893
|
28
22
|
require 'shellwords'
|
29
23
|
|
30
24
|
# Set is in stdlib but is not in core.
|
31
25
|
require 'set'
|
26
|
+
# Set.to_hash
|
27
|
+
require 'distorted/monkey_business/set'
|
32
28
|
|
33
29
|
# I mean, this is why we're here, right?
|
34
30
|
require 'jekyll'
|
35
31
|
|
36
32
|
|
37
|
-
|
38
|
-
module DistorteD
|
39
|
-
class Invoker < Liquid::Tag
|
40
|
-
|
41
|
-
GEM_ROOT = File.dirname(__FILE__).freeze
|
42
|
-
|
43
|
-
# Mix in config-loading methods.
|
44
|
-
include Jekyll::DistorteD::Floor
|
45
|
-
include Jekyll::DistorteD::StaticState
|
46
|
-
|
47
|
-
# Enabled media_type drivers. These will be attempted back to front.
|
48
|
-
# TODO: Make this configurable.
|
49
|
-
MEDIA_MOLECULES = [
|
50
|
-
Jekyll::DistorteD::Molecule::LastResort,
|
51
|
-
Jekyll::DistorteD::Molecule::Font,
|
52
|
-
Jekyll::DistorteD::Molecule::Text,
|
53
|
-
Jekyll::DistorteD::Molecule::PDF,
|
54
|
-
Jekyll::DistorteD::Molecule::SVG,
|
55
|
-
Jekyll::DistorteD::Molecule::Video,
|
56
|
-
Jekyll::DistorteD::Molecule::Image,
|
57
|
-
]
|
58
|
-
# Reduce the above to a Hash of Sets of MediaMolecules-per-Type, keyed by Type.
|
59
|
-
TYPE_MOLECULES = MEDIA_MOLECULES.reduce(
|
60
|
-
Hash.new{|hash, key| hash[key] = Set[]}
|
61
|
-
) { |types, molecule|
|
62
|
-
if molecule.const_defined?(:LOWER_WORLD)
|
63
|
-
molecule.const_get(:LOWER_WORLD).each { |t|
|
64
|
-
types.update(t => Set[molecule]) { |k,o,n| o.merge(n) }
|
65
|
-
}
|
66
|
-
end
|
67
|
-
types
|
68
|
-
}
|
33
|
+
class Jekyll::DistorteD::Invoker < Liquid::Tag
|
69
34
|
|
70
|
-
|
71
|
-
# totally arbitrary length, or if the attr key is in the plugged
|
72
|
-
# Molecule's set of attrs that take only a defined set of values.
|
73
|
-
# My chosen boundary length fits all of the outer-limit tag names I use,
|
74
|
-
# like 'medium'. It fits the longest value of Vips::Interesting too,
|
75
|
-
# though `crop` will be symbolized based on the other condition.
|
76
|
-
ARBITRARY_ATTR_SYMBOL_STRING_LENGTH_BOUNDARY = 13
|
77
|
-
|
78
|
-
|
79
|
-
# 𝘏𝘖𝘞 𝘈𝘙𝘌 𝘠𝘖𝘜 𝘎𝘌𝘕𝘛𝘓𝘌𝘔𝘌𝘕 !!
|
80
|
-
def initialize(tag_name, arguments, liquid_options)
|
81
|
-
super
|
82
|
-
# Tag name as given to Liquid::Template.register_tag().
|
83
|
-
@tag_name = tag_name.to_sym
|
84
|
-
|
85
|
-
# Liquid leaves argument parsing totally up to us.
|
86
|
-
# Use the envygeeks/liquid-tag-parser library to wrangle them.
|
87
|
-
parsed_arguments = Liquid::Tag::Parser.new(arguments)
|
88
|
-
|
89
|
-
# Filename is the only non-keyword argument our tag should ever get.
|
90
|
-
# It's spe-shul and gets its own definition outside the attr loop.
|
91
|
-
if parsed_arguments.key?(:src)
|
92
|
-
@name = parsed_arguments.delete(:src)
|
93
|
-
else
|
94
|
-
@name = parsed_arguments.delete(:argv1)
|
95
|
-
end
|
96
|
-
@liquid_liquid = parsed_arguments.select{ |attr, val|
|
97
|
-
not [nil, ''.freeze].include?(val)
|
98
|
-
}.transform_keys { |attr|
|
99
|
-
attr.length <= ARBITRARY_ATTR_SYMBOL_STRING_LENGTH_BOUNDARY ? attr.to_sym : attr.freeze
|
100
|
-
}.transform_values { |val|
|
101
|
-
if val.respond_to?(:length)
|
102
|
-
val.length <= ARBITRARY_ATTR_SYMBOL_STRING_LENGTH_BOUNDARY ? val.to_sym : val.freeze
|
103
|
-
else
|
104
|
-
val
|
105
|
-
end
|
106
|
-
}
|
35
|
+
GEM_ROOT = File.dirname(__FILE__).freeze
|
107
36
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
raise "Failed to get a usable filename from #{arguments}"
|
112
|
-
end
|
37
|
+
include Jekyll::DistorteD::Setting # Config-loading methods.
|
38
|
+
include Jekyll::DistorteD::StaticState # Jekyll::StaticFile impersonation methods.
|
39
|
+
include Cooltrainer::DistorteD::Invoker # Instance-setup methods.
|
113
40
|
|
114
|
-
end
|
115
41
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
if mime.empty?
|
122
|
-
if Jekyll::DistorteD::Floor::config(Jekyll::DistorteD::Floor::CONFIG_ROOT, :last_resort)
|
123
|
-
mime = Jekyll::DistorteD::Molecule::LastResort::LOWER_WORLD
|
124
|
-
end
|
125
|
-
end
|
126
|
-
mime
|
127
|
-
end
|
128
|
-
end
|
42
|
+
# 𝘏𝘖𝘞 𝘈𝘙𝘌 𝘠𝘖𝘜 𝘎𝘌𝘕𝘛𝘓𝘌𝘔𝘌𝘕 !!
|
43
|
+
def initialize(tag_name, arguments, liquid_options)
|
44
|
+
super
|
45
|
+
# Tag name as given to Liquid::Template.register_tag().
|
46
|
+
@tag_name = tag_name.to_sym
|
129
47
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
def user_arguments
|
134
|
-
@liquid_liquid || Hash[]
|
135
|
-
end
|
48
|
+
# Liquid leaves argument parsing totally up to us.
|
49
|
+
# Use the envygeeks/liquid-tag-parser library to wrangle them.
|
50
|
+
parsed_arguments = Liquid::Tag::Parser.new(arguments)
|
136
51
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
when 1
|
145
|
-
return TYPE_MOLECULES[available_molecules.first].first
|
146
|
-
end
|
147
|
-
end
|
52
|
+
# Filename is the only non-keyword argument our tag should ever get.
|
53
|
+
# It's spe-shul and gets its own definition outside the attr loop.
|
54
|
+
if parsed_arguments.key?(:src)
|
55
|
+
@name = parsed_arguments.delete(:src)
|
56
|
+
else
|
57
|
+
@name = parsed_arguments.delete(:argv1)
|
58
|
+
end
|
148
59
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
60
|
+
# Load contextual variables for abstract()
|
61
|
+
@tag_arguments = parsed_arguments.select{ |attr, val|
|
62
|
+
not [nil, ''.freeze].include?(val)
|
63
|
+
}.transform_keys(&:to_sym).transform_values { |val|
|
64
|
+
case val
|
65
|
+
when 'true' then true
|
66
|
+
when 'false' then false
|
67
|
+
when String then (val.length <= Jekyll::DistorteD::ARBITRARY_ATTR_SYMBOL_STRING_LENGTH_BOUNDARY) ? val.to_sym : val.freeze
|
68
|
+
else val
|
155
69
|
end
|
70
|
+
}
|
156
71
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
72
|
+
# If we didn't get one of the two above options there is nothing we
|
73
|
+
# can do but bail.
|
74
|
+
unless @name
|
75
|
+
raise "Failed to get a usable filename from #{arguments}"
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
164
79
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
# then pass it along to our StaticFile subclass.
|
172
|
-
@site = context.registers[:site]
|
173
|
-
|
174
|
-
# The rendering context's `first` page will be the one that invoked us.
|
175
|
-
page_data = context.environments.first['page'.freeze]
|
176
|
-
|
177
|
-
#
|
178
|
-
# Our subclass' additional args:
|
179
|
-
# dest - The String path to the generated `url` folder of the page HTML output
|
180
|
-
@base = @site.source
|
181
|
-
|
182
|
-
# `relative_path` doesn't seem to always exist, but `path` does? idk.
|
183
|
-
# I was testing with `relative_path` only with `_posts`, but it broke
|
184
|
-
# when I invoked DD on a _page. Both have `path`.
|
185
|
-
@dir = File.dirname(page_data['path'.freeze])
|
186
|
-
|
187
|
-
# Every one of Ruby's `File.directory?` / `Pathname.directory?` /
|
188
|
-
# `FileTest.directory?` methods actually tests that path on the
|
189
|
-
# real filesystem, but we shouldn't look at the FS here because
|
190
|
-
# this function gets called when the Site.dest directory does
|
191
|
-
# not exist yet!
|
192
|
-
# Hackily look at the last character to see if the URL is a
|
193
|
-
# directory (like configured on cooltrainer) or a `.html`
|
194
|
-
# (or other extension) like the default Jekyll config.
|
195
|
-
# Get the dirname if the url is not a dir itself.
|
196
|
-
@relative_dest = page_data['url'.freeze]
|
197
|
-
unless @relative_dest[-1] == Jekyll::DistorteD::Floor::PATH_SEPARATOR
|
198
|
-
@relative_dest = File.dirname(@relative_dest)
|
199
|
-
# Append the trailing slash so we don't have to do it
|
200
|
-
# in the Liquid templates.
|
201
|
-
@relative_dest << Jekyll::DistorteD::Floor::PATH_SEPARATOR
|
202
|
-
end
|
203
|
-
|
204
|
-
# Add our new file to the list that will be handled
|
205
|
-
# by Jekyll's built-in StaticFile generator.
|
206
|
-
@site.static_files << self
|
207
|
-
output
|
80
|
+
# Returns a Set of DD MIME::Types descriving our file,
|
81
|
+
# optionally falling through to a plain file copy.
|
82
|
+
def type_mars
|
83
|
+
@type_mars ||= (CHECKING::YOU::OUT(path, so_deep: true) & lower_world.keys.to_set).tap { |gemini|
|
84
|
+
if gemini.empty? && the_setting_sun(:never_let_you_down)
|
85
|
+
gemini << CHECKING::YOU::OUT['application/x.distorted.never-let-you-down']
|
208
86
|
end
|
87
|
+
}
|
88
|
+
raise MediaTypeNotImplementedError.new(@name) if @type_mars.empty?
|
89
|
+
@type_mars
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns an Array[Change] for every intended output Type
|
93
|
+
# and every variation (e.g. resolution, bitrate) on each Type.
|
94
|
+
def changes
|
95
|
+
# The available/desired output Media Types and (variations on those Types)
|
96
|
+
# are based on the input Type and the Molecule(s) available to service those Types.
|
97
|
+
# Use an Array, since order might be important here when generating many variations
|
98
|
+
# at multiple levels of the DistorteD stack, e.g. the actual files on the Floor level
|
99
|
+
# and the templates/markup here in the Jekyll level.
|
100
|
+
@changes ||= type_mars.each_with_object(Array[]) { |lower, wanted|
|
101
|
+
# Query our configuration for Type changes, e.g. image/webp to (image/png and image/webp).
|
102
|
+
# Handle empty sub_types by compacting and splatting a sub-Array.
|
103
|
+
change_config = the_setting_sun(:changes, *(lower.settings_paths))
|
104
|
+
# If there is no config, treat it as a change to the same Type as the input,
|
105
|
+
# otherwise instantiate each "mediatype/subtype" config String to a MIME::Type.
|
106
|
+
((change_config.nil? || change_config&.empty?) ? Set[lower] : change_config.map {|t| CHECKING::YOU::OUT[t]}).each { |type|
|
107
|
+
# Query our configuration again for variations on each Type.
|
108
|
+
# For example, one single image Type may want multiple resolutions to enable responsive <picture> tags,
|
109
|
+
# or a single video Type may want multiple bitrates for adaptive streaming.
|
110
|
+
limit_breaks = the_setting_sun(:outer_limits, *(type&.settings_paths)) || Array[Hash[]]
|
111
|
+
# Which MediaMolecule Modules support this Type as an output? Probably just one.
|
112
|
+
outer_limits.keep_if { |k, v| v.has_key?(type) }.keys.each { |molecule|
|
113
|
+
# As before, if there is nothing in the config just treat it as a Change to
|
114
|
+
# the full resolution/bitrate/whatever as the input, so this will always run at least once.
|
115
|
+
limit_breaks.each { |limit_break|
|
116
|
+
# Merge each variation's config with any/all attributes given to our Liquid Tag,
|
117
|
+
# as well as any Jekyll Stuff™ like the relative destination path.
|
118
|
+
change_arguments = limit_break.merge(Hash[:dir => @relative_dest]).merge(context_arguments)
|
119
|
+
# Each Change will carry instance Compound data in Atom Structs so we can avoid modifying
|
120
|
+
# the Compound Struct with any variation-specific values since they will be reused.
|
121
|
+
atoms = Hash.new
|
122
|
+
# We will always want an Atom from every Compound even if it only carries the :default.
|
123
|
+
Cooltrainer::DistorteD::IMPLANTATION(:OUTER_LIMITS, molecule)&.dig(type)&.each_pair { |aka, compound|
|
124
|
+
next if aka != compound.element # Skip alias Compounds since they will all be handled at once.
|
125
|
+
# Look for a user-given argument matching any supported alias of a Compound,
|
126
|
+
# and check those values against the Compound for validity.
|
127
|
+
atoms.store(compound.element, Cooltrainer::Atom.new(compound.isotopes.reduce(nil) { |value, isotope|
|
128
|
+
# TODO: valid?
|
129
|
+
value || change_arguments&.delete(isotope)
|
130
|
+
}, compound.default))
|
131
|
+
}
|
132
|
+
# After looping through the Compounds and calling :delete for matched values,
|
133
|
+
# this bag will be left with only the freeform non-Compound-associated arguments, if any.
|
134
|
+
# Separate those into arguments that match Change member names, and arguments that don't.
|
135
|
+
change_member_keys, atom_keys = change_arguments.keys.partition(&Cooltrainer::Change.members.method(:include?))
|
136
|
+
# Instantiate a no-default Atom for every remaining argument that isn't a Change member.
|
137
|
+
atom_keys.each { |attribute| atoms.store(attribute, Cooltrainer::Atom.new(change_arguments.delete(attribute), nil)) }
|
138
|
+
# Instantiate each variation of each Type into a Change struct
|
139
|
+
# that will handle some of the details like output-filename generation.
|
140
|
+
wanted.append(Cooltrainer::Change.new(type, src: @name, molecule: molecule, **change_arguments, **atoms))
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
wanted
|
145
|
+
}
|
146
|
+
end
|
209
147
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
# Use a given filename, or detect one based on media-type.
|
217
|
-
if name.nil?
|
218
|
-
# e.g. Jekyll::DistorteD::Molecule::Image -> 'image.liquid'
|
219
|
-
name = "#{self.singleton_class.instance_variable_get(:@media_molecule).name.gsub(/^.*::/, '').downcase}.liquid".freeze
|
220
|
-
elsif not name.include?('.liquid'.freeze)
|
221
|
-
# Support filename arguments with and without file extension.
|
222
|
-
# The given String might already be frozen, so concatenating
|
223
|
-
# the extension might fail. Just set a new version.
|
224
|
-
name = "#{name}.liquid"
|
225
|
-
end
|
226
|
-
template = File.join(
|
227
|
-
self.singleton_class.const_get(:GEM_ROOT),
|
228
|
-
'template'.freeze,
|
229
|
-
name,
|
230
|
-
)
|
231
|
-
|
232
|
-
# Jekyll's Liquid renderer caches in 4.0+.
|
233
|
-
if Jekyll::DistorteD::Floor::config(
|
234
|
-
Jekyll::DistorteD::Floor::CONFIG_ROOT,
|
235
|
-
:cache_templates,
|
236
|
-
)
|
237
|
-
# file(path) is the caching function, with path as the cache key.
|
238
|
-
# The `template` here will be the full path, so no versions of this
|
239
|
-
# gem should ever conflict. For example, right now during dev it's:
|
240
|
-
# `/home/okeeblow/Works/DistorteD/lib/image.liquid`
|
241
|
-
Jekyll.logger.debug('DistorteD', "Parsing #{template} with caching renderer.")
|
242
|
-
site.liquid_renderer.file(template).parse(File.read(template))
|
243
|
-
else
|
244
|
-
# Re-read the template just for this piece of media.
|
245
|
-
Jekyll.logger.debug('DistorteD', "Parsing #{template} with fresh (uncached) renderer.")
|
246
|
-
Liquid::Template.parse(File.read(template))
|
247
|
-
end
|
248
|
-
|
249
|
-
rescue Liquid::SyntaxError => l
|
250
|
-
# This shouldn't ever happen unless a new version of Liquid
|
251
|
-
# breaks syntax compatibility with our templates somehow.
|
252
|
-
l.message
|
253
|
-
end
|
254
|
-
end # parse_template
|
148
|
+
# Return any arguments given by the user to our Liquid tag.
|
149
|
+
# This method name is generic across all DD entrypoints so it can be
|
150
|
+
# referenced from lower layers in the pile.
|
151
|
+
def context_arguments
|
152
|
+
@tag_arguments ||= Hash[]
|
153
|
+
end
|
255
154
|
|
155
|
+
# Returns a context-only setting from our Liquid attributes.
|
156
|
+
def abstract(key)
|
157
|
+
context_arguments.dig(key)
|
158
|
+
end
|
256
159
|
|
160
|
+
# Called by Jekyll::Renderer
|
161
|
+
# https://github.com/jekyll/jekyll/blob/HEAD/lib/jekyll/renderer.rb
|
162
|
+
# https://jekyllrb.com/tutorials/orderofinterpretation/
|
163
|
+
def render(context)
|
164
|
+
# Get Jekyll Site object back from tag rendering context registers so we
|
165
|
+
# can get configuration data and path information from it and
|
166
|
+
# then pass it along to our StaticFile subclass.
|
167
|
+
@site = context.registers[:site]
|
168
|
+
|
169
|
+
# The rendering context's `first` page will be the one that invoked us.
|
170
|
+
page_data = context.environments.first['page'.freeze]
|
171
|
+
|
172
|
+
#
|
173
|
+
# Our subclass' additional args:
|
174
|
+
# dest - The String path to the generated `url` folder of the page HTML output
|
175
|
+
@base = @site.source
|
176
|
+
|
177
|
+
# `relative_path` doesn't seem to always exist, but `path` does? idk.
|
178
|
+
# I was testing with `relative_path` only with `_posts`, but it broke
|
179
|
+
# when I invoked DD on a _page. Both have `path`.
|
180
|
+
@dir = File.dirname(page_data['path'.freeze])
|
181
|
+
|
182
|
+
# Every one of Ruby's `File.directory?` / `Pathname.directory?` /
|
183
|
+
# `FileTest.directory?` methods actually tests that path on the
|
184
|
+
# real filesystem, but we shouldn't look at the FS here because
|
185
|
+
# this function gets called when the Site.dest directory does
|
186
|
+
# not exist yet!
|
187
|
+
# Hackily look at the last character to see if the URL is a
|
188
|
+
# directory (like configured on cooltrainer) or a `.html`
|
189
|
+
# (or other extension) like the default Jekyll config.
|
190
|
+
# Get the dirname if the url is not a dir itself.
|
191
|
+
@relative_dest = page_data['url'.freeze]
|
192
|
+
unless @relative_dest[-1] == Jekyll::DistorteD::PATH_SEPARATOR
|
193
|
+
@relative_dest = File.dirname(@relative_dest)
|
194
|
+
# Append the trailing slash so we don't have to do it
|
195
|
+
# in the Liquid templates.
|
196
|
+
@relative_dest << Jekyll::DistorteD::PATH_SEPARATOR
|
257
197
|
end
|
198
|
+
|
199
|
+
# Add our new file to the list that will be handled
|
200
|
+
# by Jekyll's built-in StaticFile generator.
|
201
|
+
@site.static_files << self
|
202
|
+
render_to_output_buffer(context, '')
|
258
203
|
end
|
204
|
+
|
205
|
+
# A future Liquid version (5.0?) will call this function directly
|
206
|
+
# instead of calling render()
|
207
|
+
def render_to_output_buffer(context, output)
|
208
|
+
roots_of_my_way = Cooltrainer::ElementalCreation.new(:root).tap { |wrapper|
|
209
|
+
wrapper.dan = "distorted #{changes.reduce(Set[]) { |classes, change|
|
210
|
+
classes.add(change.molecule&.name.split('::'.freeze).last.downcase)
|
211
|
+
classes.add(change.type.sub_type.to_s.split(MIME::Type::SUB_TYPE_SEPARATORS)[0])
|
212
|
+
}.to_a.join(' ')}"
|
213
|
+
}
|
214
|
+
|
215
|
+
changes&.each { |change|
|
216
|
+
unless self.respond_to_missing?(change.type.distorted_template_method)
|
217
|
+
Jekyll.logger.error(@name, "Missing template method #{change.type.distorted_template_method}")
|
218
|
+
raise MediaTypeOutputNotImplementedError.new(@name, type_mars, self.class.name)
|
219
|
+
end
|
220
|
+
Jekyll.logger.debug("DistorteD::#{change.type.distorted_template_method}", File.join(change.dir, change.name))
|
221
|
+
|
222
|
+
# Get an ElementalCreation Struct from the MediaMolecule's render method.
|
223
|
+
# WISHLIST: Remove the empty final positional Hash argument once we require a Ruby version
|
224
|
+
# that will not perform the implicit Change-to-Hash conversion due to Change's
|
225
|
+
# implementation of :to_hash. Ruby 2.7 will complain but still do the conversion,
|
226
|
+
# breaking downstream callers that want a Struct they can call arbitrary key methods on.
|
227
|
+
# https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/
|
228
|
+
roots_of_my_way.mad_child(self.send(change.type.distorted_template_method, change, **{}))
|
229
|
+
}
|
230
|
+
|
231
|
+
output << roots_of_my_way.render
|
232
|
+
end
|
233
|
+
|
259
234
|
end
|