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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/distorted-jekyll.rb +0 -4
  4. data/lib/distorted-jekyll/{template/13th-style.css → 13th-style.css} +0 -0
  5. data/lib/distorted-jekyll/13th-style.rb +1 -1
  6. data/lib/distorted-jekyll/_config_default.yml +3 -21
  7. data/lib/distorted-jekyll/invoker.rb +194 -219
  8. data/lib/distorted-jekyll/liquid_liquid.rb +255 -0
  9. data/lib/distorted-jekyll/liquid_liquid/anchor.liquid +5 -0
  10. data/lib/distorted-jekyll/liquid_liquid/anchor_inline.liquid +1 -0
  11. data/lib/distorted-jekyll/liquid_liquid/embed.liquid +1 -0
  12. data/lib/distorted-jekyll/liquid_liquid/img.liquid +1 -0
  13. data/lib/distorted-jekyll/liquid_liquid/object.liquid +5 -0
  14. data/lib/distorted-jekyll/liquid_liquid/picture.liquid +15 -0
  15. data/lib/distorted-jekyll/liquid_liquid/picture.rb +48 -0
  16. data/lib/distorted-jekyll/liquid_liquid/picture_source.liquid +1 -0
  17. data/lib/distorted-jekyll/liquid_liquid/root.liquid +5 -0
  18. data/lib/distorted-jekyll/liquid_liquid/video.liquid +5 -0
  19. data/lib/distorted-jekyll/liquid_liquid/video_source.liquid +1 -0
  20. data/lib/distorted-jekyll/md_injection.rb +30 -25
  21. data/lib/distorted-jekyll/media_molecule.rb +20 -0
  22. data/lib/distorted-jekyll/media_molecule/font.rb +21 -0
  23. data/lib/distorted-jekyll/media_molecule/image.rb +15 -0
  24. data/lib/distorted-jekyll/media_molecule/never_let_you_down.rb +28 -0
  25. data/lib/distorted-jekyll/media_molecule/pdf.rb +108 -0
  26. data/lib/distorted-jekyll/media_molecule/svg.rb +20 -0
  27. data/lib/distorted-jekyll/media_molecule/text.rb +23 -0
  28. data/lib/distorted-jekyll/media_molecule/video.rb +45 -0
  29. data/lib/distorted-jekyll/monkey_business/jekyll/cleaner.rb +68 -1
  30. data/lib/distorted-jekyll/static_state.rb +19 -60
  31. data/lib/distorted-jekyll/the_setting_sun.rb +179 -0
  32. metadata +26 -21
  33. data/lib/distorted-jekyll/floor.rb +0 -266
  34. data/lib/distorted-jekyll/molecule/font.rb +0 -62
  35. data/lib/distorted-jekyll/molecule/image.rb +0 -94
  36. data/lib/distorted-jekyll/molecule/lastresort.rb +0 -51
  37. data/lib/distorted-jekyll/molecule/pdf.rb +0 -79
  38. data/lib/distorted-jekyll/molecule/svg.rb +0 -47
  39. data/lib/distorted-jekyll/molecule/text.rb +0 -62
  40. data/lib/distorted-jekyll/molecule/video.rb +0 -85
  41. data/lib/distorted-jekyll/template/error_code.liquid +0 -3
  42. data/lib/distorted-jekyll/template/font.liquid +0 -32
  43. data/lib/distorted-jekyll/template/image.liquid +0 -32
  44. data/lib/distorted-jekyll/template/lastresort.liquid +0 -20
  45. data/lib/distorted-jekyll/template/pdf.liquid +0 -14
  46. data/lib/distorted-jekyll/template/svg.liquid +0 -32
  47. data/lib/distorted-jekyll/template/text.liquid +0 -32
  48. data/lib/distorted-jekyll/template/video.liquid +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5eaf5c713e61dd6cea6fee1d1cb031b7ec25331e8cdeb9aa79f1ea96c61d94a8
4
- data.tar.gz: '068625124864b575107596d9b91761171eecdb76cdaf8f34a66fa37f4d34b69e'
3
+ metadata.gz: a5fe548492f52f0b4bb138fbd93ca2fb127f53baf7a3657d73aefc904c22a51b
4
+ data.tar.gz: 2444637db29356d306f2e1e31a41b0fe9c758574d77bc40d691f51c0ab514af5
5
5
  SHA512:
6
- metadata.gz: 4b55e4771aadea12c70adf95995432b4cff70cf0548b06ae76dcb8a55f11de273531a3f2def6be56de9b7043a9f752a6f7d7cc1f66299475409ac269be80dc77
7
- data.tar.gz: 4c152988ef67bd5717429578de241169b13f7ffee38cb255979184e2b3cdf4d551d1b50d9c4472ca98bbb9695d8892f09b0b2d9d0e2a02deeeb1dd54d8415f66
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-Ruby` Gem if used in this way.
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
 
@@ -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'
@@ -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__), 'template'.freeze, '13th-style.css'.freeze)
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
- - tag: full
10
- crop: none
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
- last_resort: true
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/floor'
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
- module Jekyll
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
- # Any any attr value will get a to_sym if shorter than this
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
- # If we didn't get one of the two above options there is nothing we
109
- # can do but bail.
110
- unless @name
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
- # Returns a Set of DD MIME::Types descriving our file,
117
- # optionally falling through to a plain file copy.
118
- def type_mars
119
- @type_mars ||= begin
120
- mime = CHECKING::YOU::OUT(@name)
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
- # Return any arguments given by the user to our Liquid tag.
131
- # This method name is generic across all DD entrypoints so it can be
132
- # referenced from lower layers in the pile.
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
- # Decides which MediaMolecule is most appropriate for our file and returns it.
138
- def media_molecule
139
- available_molecules = TYPE_MOLECULES.keys.to_set & type_mars
140
- # TODO: Handle multiple molecules for the same file
141
- case available_molecules.length
142
- when 0
143
- raise MediaTypeNotImplementedError.new(@name)
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
- def plug
150
- unless self.singleton_class.instance_variable_defined?(:@media_molecule)
151
- self.singleton_class.instance_variable_set(:@media_molecule, media_molecule)
152
- self.singleton_class.prepend(media_molecule)
153
- Jekyll.logger.info(@name, "Plugging #{media_molecule}")
154
- end
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
- # Called by Jekyll::Renderer
158
- # https://github.com/jekyll/jekyll/blob/HEAD/lib/jekyll/renderer.rb
159
- # https://jekyllrb.com/tutorials/orderofinterpretation/
160
- def render(context)
161
- plug
162
- render_to_output_buffer(context, '')
163
- end
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
- # A future Liquid version (5.0?) will call this function directly
166
- # instead of calling render()
167
- def render_to_output_buffer(context, output)
168
- plug
169
- # Get Jekyll Site object back from tag rendering context registers so we
170
- # can get configuration data and path information from it and
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
- # Generic Liquid template loader that will be used in every MediaMolecule.
211
- # Callers will call `render(**{:template => vars})` on the Object returned
212
- # by this method.
213
- def parse_template(site: nil, name: nil)
214
- site = site || @site || Jekyll.sites.first
215
- begin
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