distorted-jekyll 0.5.4 → 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/LICENSE +661 -0
- data/README.md +7 -11
- data/lib/distorted-jekyll.rb +75 -0
- data/lib/distorted-jekyll/13th-style.css +79 -0
- data/lib/distorted-jekyll/13th-style.rb +58 -0
- data/lib/distorted-jekyll/_config_default.yml +63 -0
- data/lib/distorted-jekyll/blocks.rb +16 -0
- data/lib/distorted-jekyll/invoker.rb +234 -0
- 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 +310 -0
- 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 +121 -0
- data/lib/distorted-jekyll/static_state.rb +160 -0
- data/lib/distorted-jekyll/the_setting_sun.rb +179 -0
- metadata +37 -34
data/README.md
CHANGED
@@ -1,19 +1,16 @@
|
|
1
|
-
#
|
1
|
+
# Jekyll::DistorteD
|
2
2
|
|
3
|
-
`DistorteD` is a multimedia framework for Jekyll websites.
|
4
|
-
|
5
|
-
Right now this repo contains two Gems:
|
6
|
-
- [`DistorteD-Jekyll`](https://rubygems.org/gems/distorted-jekyll) contains anything and everything that depends on Jekyll.
|
7
|
-
- [`DistorteD-Ruby`](https://rubygems.org/gems/distorted) contains just the abstract media file format handling code. It's separate so I can use those functions in other contexts and/or easily replace the Ruby core if necessary.
|
3
|
+
`DistorteD-Jekyll` is a multimedia framework for Jekyll websites.
|
8
4
|
|
9
5
|
## Motivation
|
10
6
|
|
11
|
-
|
7
|
+
DistorteD is my solution for displaying photos, videos, and other types of media on [cooltrainer.org](https://cooltrainer.org) due to my dissatisfaction with every other solution I could find.
|
12
8
|
|
13
9
|
My previous approach was similar to what's [described here](https://eduardoboucas.com/blog/2014/12/07/including-and-managing-images-in-jekyll.html), with small/medium/large image size variations generated with [Jekyll-MiniMagick](https://github.com/MattKevan/Jekyll-MiniMagick-new).
|
14
10
|
|
15
11
|
Here are some already-existing ways to put pictures on your Jekyll site that are worth your consideration before choosing DistorteD:
|
16
12
|
|
13
|
+
- Octopress' [image_tag](https://github.com/imathis/octopress/blob/master/plugins/image_tag.rb) plugin.
|
17
14
|
- [jekyll-responsive-image](https://github.com/wildlyinaccurate/jekyll-responsive-image)
|
18
15
|
- [jekyll_picture_tag](https://rbuchberger.github.io/jekyll_picture_tag/)
|
19
16
|
- [jekyll-gallery-generator](https://github.com/ggreer/jekyll-gallery-generator)
|
@@ -95,7 +92,7 @@ will be transformed into.
|
|
95
92
|
%}
|
96
93
|
```
|
97
94
|
|
98
|
-
|
95
|
+
or, for a DD grid:
|
99
96
|
|
100
97
|
```
|
101
98
|
{% distort %}
|
@@ -157,11 +154,10 @@ Clone the DistorteD repository and modify your Jekyll `Gemfile` to refer to your
|
|
157
154
|
|
158
155
|
```
|
159
156
|
gem 'distorted-jekyll', :path => '~/repos/DistorteD/DistorteD-Jekyll/'[, :branch => 'NEW-SENSATION']
|
160
|
-
gem 'distorted', :path => '~/repos/DistorteD/DistorteD-Ruby/'[, :branch => 'NEW-SENSATION']
|
161
157
|
```
|
162
158
|
|
163
|
-
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.
|
164
160
|
|
165
161
|
## License
|
166
162
|
|
167
|
-
|
163
|
+
The gem is available as open source under the terms of the [GNU Affero General Public License version 3](https://opensource.org/licenses/AGPL-3.0).
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'distorted-jekyll/13th-style'
|
2
|
+
require 'distorted-jekyll/blocks'
|
3
|
+
require 'distorted-jekyll/md_injection'
|
4
|
+
require 'distorted-jekyll/invoker'
|
5
|
+
|
6
|
+
|
7
|
+
FATAL_FURY = true
|
8
|
+
UPDATE_RUBY = "Please use DistorteD with Ruby 2.7.0 or later"
|
9
|
+
def update_ruby
|
10
|
+
if defined? RUBY_PLATFORM
|
11
|
+
if (/freebsd/ =~ RUBY_PLATFORM) != nil
|
12
|
+
return 'pkg install lang/ruby27'
|
13
|
+
elsif (/darwin/ =~ RUBY_PLATFORM) != nil
|
14
|
+
return 'brew upgrade ruby'
|
15
|
+
elsif (/win/ =~ RUBY_PLATFORM) != nil
|
16
|
+
return 'https://rubyinstaller.org/'
|
17
|
+
elsif (/linux/ =~ RUBY_PLATFORM) != nil
|
18
|
+
if File.exists?('/etc/lsb-release')
|
19
|
+
lsb = File.read('/etc/lsb-release')
|
20
|
+
if (/Ubuntu|LinuxMint/ =~ lsb) != nil
|
21
|
+
return 'https://www.brightbox.com/docs/ruby/ubuntu/#installation'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
return 'https://github.com/rbenv/ruby-build'
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# I want to be able to use:
|
31
|
+
# - Array#dig and Hash#dig (Ruby 2.3): https://bugs.ruby-lang.org/issues/11643
|
32
|
+
# - Lonely operator (Ruby 2.3): https://bugs.ruby-lang.org/issues/11537
|
33
|
+
# - Hash#transform_keys (Ruby 2.5): https://bugs.ruby-lang.org/issues/13583
|
34
|
+
# - Enumerable#filter_map (Ruby 2.7): https://bugs.ruby-lang.org/issues/5663
|
35
|
+
# https://blog.saeloun.com/2019/05/25/ruby-2-7-enumerable-filter-map.html
|
36
|
+
# - 'Real' kwargs in preparation for Ruby 3: https://bugs.ruby-lang.org/issues/14183
|
37
|
+
# https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/
|
38
|
+
if [
|
39
|
+
Hash.method_defined?(:dig), # 2.3
|
40
|
+
Hash.method_defined?(:transform_keys), # 2.5
|
41
|
+
Enumerable.method_defined?(:filter_map), # 2.7
|
42
|
+
].all?
|
43
|
+
# Monkey-patch Jekyll::Cleaner to not nuke DistorteD-generated variations
|
44
|
+
# for our media files. This makes DistorteD fast!
|
45
|
+
require 'distorted-jekyll/monkey_business/jekyll/cleaner'
|
46
|
+
|
47
|
+
# Register DistorteD's entrypoint class with Liquid.
|
48
|
+
# `Invoker` will mix in the proper handler module for the given media.
|
49
|
+
Liquid::Template.register_tag('distorted', Jekyll::DistorteD::Invoker)
|
50
|
+
|
51
|
+
# Register a block version for arranging multiple pieces of media.
|
52
|
+
Liquid::Template.register_tag('distort', Jekyll::DistorteD::BLOCKS)
|
53
|
+
|
54
|
+
# Register a tag for basic DistorteD CSS.
|
55
|
+
Liquid::Template.register_tag('13th_style', Jekyll::DistorteD::ThirteenthStyle)
|
56
|
+
|
57
|
+
# Transform Markdown image syntax 
|
58
|
+
# to instances of our liquid tag {% distorted %}
|
59
|
+
# Available hooks can be seen here:
|
60
|
+
# https://github.com/jekyll/jekyll/blob/master/lib/jekyll/hooks.rb
|
61
|
+
# `:documents` does not seem to include `_pages` but does include `_posts`.
|
62
|
+
Jekyll::Hooks.register(:pages, :pre_render, &md_injection)
|
63
|
+
Jekyll::Hooks.register(:posts, :pre_render, &md_injection)
|
64
|
+
|
65
|
+
else
|
66
|
+
# Example of how this looks with the outdated Ruby 2.5 on my Mint 19 laptop:
|
67
|
+
#
|
68
|
+
# Bundler::GemRequireError: There was an error while trying to load the gem 'distorted-jekyll'.
|
69
|
+
# Gem Load Error is: Please use DistorteD with Ruby 2.7.0 or later: https://www.brightbox.com/docs/ruby/ubuntu/#installation
|
70
|
+
if FATAL_FURY
|
71
|
+
raise RuntimeError.new("#{UPDATE_RUBY}: #{update_ruby}")
|
72
|
+
else
|
73
|
+
Jekyll.logger.info('DistorteD', "#{UPDATE_RUBY}: #{update_ruby}")
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
div.distorted {text-align: center;}
|
2
|
+
div.distorted::after {
|
3
|
+
content: '';
|
4
|
+
display: block;
|
5
|
+
clear: left;
|
6
|
+
}
|
7
|
+
div.distorted.svg {background-color: #fbfbf8;}
|
8
|
+
div.distorted.pdf > object {min-height: 76vh;}
|
9
|
+
div.distorted > video {object-fit: contain;}
|
10
|
+
div.distorted-caption {
|
11
|
+
color: #8888888;
|
12
|
+
margin-top: 0.5em;
|
13
|
+
}
|
14
|
+
div.distorted-block {
|
15
|
+
width: 100%;
|
16
|
+
clear: both;
|
17
|
+
}
|
18
|
+
div.distorted-block > div.distorted {
|
19
|
+
display: block;
|
20
|
+
box-sizing: border-box;
|
21
|
+
float: left;
|
22
|
+
border: 4px solid transparent;
|
23
|
+
margin: 0px;
|
24
|
+
text-align: center;
|
25
|
+
width: 100%;
|
26
|
+
}
|
27
|
+
@media screen and (min-width: 40em) {
|
28
|
+
div.distorted-block > div.distorted {
|
29
|
+
width: 50%;
|
30
|
+
}
|
31
|
+
div.distorted-block > div.distorted:only-child {
|
32
|
+
width: 100%;
|
33
|
+
}
|
34
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(3),
|
35
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(3) ~ div.distorted,
|
36
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(6),
|
37
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(6) ~ div.distorted,
|
38
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(9),
|
39
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(9) ~ div.distorted {
|
40
|
+
width: 33.3333%;
|
41
|
+
}
|
42
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(5),
|
43
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(5) ~ div.distorted:nth-child(2) {
|
44
|
+
width: 50%;
|
45
|
+
}
|
46
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(5) ~ div.distorted {
|
47
|
+
width: 33.3333%;
|
48
|
+
}
|
49
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(7) ~ div.distorted:nth-child(3),
|
50
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(7) ~ div.distorted:nth-child(4),
|
51
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(7) ~ div.distorted:nth-child(5) {
|
52
|
+
width: 33.3333%;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
@media screen and (min-width: 76em) {
|
56
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(4),
|
57
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(4) ~ div.distorted,
|
58
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(8),
|
59
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(8) ~ div.distorted {
|
60
|
+
width: 25%;
|
61
|
+
}
|
62
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(5),
|
63
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(5) ~ div.distorted:nth-child(2),
|
64
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(5) ~ div.distorted,
|
65
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(10),
|
66
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(10) ~ div.distorted {
|
67
|
+
width: 20%;
|
68
|
+
}
|
69
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(7),
|
70
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(7) ~ div.distorted:nth-child(2),
|
71
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(7) ~ div.distorted:nth-child(3) {
|
72
|
+
width: 33.3333%;
|
73
|
+
}
|
74
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(7) ~ div.distorted,
|
75
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(7) ~ div.distorted:nth-child(4),
|
76
|
+
div.distorted-block > div.distorted:first-child:nth-last-child(7) ~ div.distorted:nth-child(5) {
|
77
|
+
width: 25%;
|
78
|
+
}
|
79
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
# Slip in and out of phenomenon
|
3
|
+
require 'liquid/tag'
|
4
|
+
require 'liquid/tag/parser'
|
5
|
+
|
6
|
+
# Explicitly required for l/t/parser since a1cfa27c27cf4d4c308da2f75fbae88e9d5ae893
|
7
|
+
require 'shellwords'
|
8
|
+
|
9
|
+
|
10
|
+
module Jekyll
|
11
|
+
module DistorteD
|
12
|
+
class ThirteenthStyle < Liquid::Tag
|
13
|
+
|
14
|
+
TAB_SEQUENCE = ' '.freeze # two spaces
|
15
|
+
|
16
|
+
def initialize(tag_name, arguments, liquid_options)
|
17
|
+
super
|
18
|
+
|
19
|
+
# Liquid leaves argument parsing totally up to us.
|
20
|
+
# Use the envygeeks/liquid-tag-parser library to wrangle them.
|
21
|
+
parsed_arguments = Liquid::Tag::Parser.new(arguments)
|
22
|
+
|
23
|
+
# Specify how many levels to indent printed output.
|
24
|
+
# Indentation will apply to all lines after the first,
|
25
|
+
# because the first line's output will fall at the same
|
26
|
+
# place as our Liquid tag invocation.
|
27
|
+
@tabs = parsed_arguments[:tabs] || 0
|
28
|
+
end
|
29
|
+
|
30
|
+
# This is going to go away in a future Liquid version
|
31
|
+
# and render_to_output_buffer will be the standard approach.
|
32
|
+
# I'm going ahead and using it since we are building strings here.
|
33
|
+
def render(context)
|
34
|
+
return render_to_output_buffer(context, '')
|
35
|
+
end
|
36
|
+
|
37
|
+
def render_to_output_buffer(context, output)
|
38
|
+
css_filename = File.join(File.dirname(__FILE__), '13th-style.css'.freeze)
|
39
|
+
|
40
|
+
# Use IO.foreach() to call a block on each line of our template file
|
41
|
+
# without slurping the entire file into memory like File.read() / File.readlines()
|
42
|
+
File.foreach(css_filename).with_index do |line, line_num|
|
43
|
+
# Don't indent the first line of the CSS file, because the first line
|
44
|
+
# will print starting at the position of our {% 13thStyle %} Liquid tag.
|
45
|
+
unless line_num == 0
|
46
|
+
output << TAB_SEQUENCE * @tabs
|
47
|
+
end
|
48
|
+
output << line
|
49
|
+
end
|
50
|
+
# Remove CSS comments from output so I can leave notes there
|
51
|
+
# without bloating up my output.
|
52
|
+
# Based on C-shebang-style comment regex from MRE3
|
53
|
+
return output.gsub(/\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//, '')
|
54
|
+
end
|
55
|
+
|
56
|
+
end # ThirteenthStyle
|
57
|
+
end # DistorteD
|
58
|
+
end # Jekyll
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Override any or all of this default configuration in your Jekyll site's `_config.yml`!
|
2
|
+
|
3
|
+
# Is it possible to do a Set of Hashes using the Set syntax in YAML?
|
4
|
+
# It works with the Array syntax, so that's what I'm using here,
|
5
|
+
# but keep in mind any Arrays will be converted to Sets when loaded,
|
6
|
+
# so duplicate Array values will be compacted!
|
7
|
+
|
8
|
+
standard_image: &standard_image
|
9
|
+
- crop: none
|
10
|
+
breaks: [333, 555, 777, 1111]
|
11
|
+
|
12
|
+
distorted:
|
13
|
+
|
14
|
+
# Should unrecognized media-types fall back to a bare
|
15
|
+
# <img> tag around the original media file?
|
16
|
+
# If not, the site build will fail when an unrecognized
|
17
|
+
# file is encountered.
|
18
|
+
never_let_you_down: true
|
19
|
+
|
20
|
+
# Configure DistorteD format changes by media_type, then by sub_type.
|
21
|
+
# The list of target formats is plain text, media_type/sub_type.
|
22
|
+
# These are mostly based on IANA's official Media Types list:
|
23
|
+
# https://www.iana.org/assignments/media-types/media-types.xhtml
|
24
|
+
# but with some custom additions like using 'gif-sequence' for
|
25
|
+
# animated GIF and leaving 'image/gif' to refer to single-frame GIFs.
|
26
|
+
changes:
|
27
|
+
image:
|
28
|
+
jpeg:
|
29
|
+
? image/jpeg
|
30
|
+
? image/webp
|
31
|
+
png:
|
32
|
+
? image/png
|
33
|
+
? image/webp
|
34
|
+
gif:
|
35
|
+
? image/gif
|
36
|
+
? image/png
|
37
|
+
? image/webp
|
38
|
+
gif-sequence:
|
39
|
+
? image/gif-sequence
|
40
|
+
svg:
|
41
|
+
? image/svg+xml
|
42
|
+
? image/png
|
43
|
+
? image/webp
|
44
|
+
text:
|
45
|
+
plain:
|
46
|
+
? text/plain
|
47
|
+
? image/png
|
48
|
+
? image/webp
|
49
|
+
x-nfo:
|
50
|
+
? text/x-nfo
|
51
|
+
? image/png
|
52
|
+
? image/webp
|
53
|
+
font:
|
54
|
+
ttf:
|
55
|
+
? font/ttf
|
56
|
+
? image/png
|
57
|
+
? image/webp
|
58
|
+
|
59
|
+
outer_limits:
|
60
|
+
image:
|
61
|
+
jpeg: *standard_image
|
62
|
+
png: *standard_image
|
63
|
+
webp: *standard_image
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
module Jekyll
|
3
|
+
module DistorteD
|
4
|
+
class BLOCKS < Liquid::Block
|
5
|
+
|
6
|
+
def initialize(tag_name, arguments, liquid_options)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def render(context)
|
11
|
+
"<div class=\"distorted-block\">#{super}</div>"
|
12
|
+
end
|
13
|
+
|
14
|
+
end # BLOCKS
|
15
|
+
end # DistorteD
|
16
|
+
end # Jekyll
|
@@ -0,0 +1,234 @@
|
|
1
|
+
# Our custom Exceptions
|
2
|
+
require 'distorted/error_code'
|
3
|
+
|
4
|
+
# Molecule loading and plugging functionality
|
5
|
+
require 'distorted/invoker'
|
6
|
+
|
7
|
+
# MIME::Typer
|
8
|
+
require 'distorted/checking_you_out'
|
9
|
+
|
10
|
+
# Configuration-loading code
|
11
|
+
require 'distorted-jekyll/the_setting_sun'
|
12
|
+
require 'distorted-jekyll/static_state'
|
13
|
+
|
14
|
+
# Slip in and out of phenomenon
|
15
|
+
require 'liquid/tag'
|
16
|
+
require 'liquid/tag/parser'
|
17
|
+
require 'distorted-jekyll/liquid_liquid'
|
18
|
+
|
19
|
+
require 'distorted-jekyll/media_molecule'
|
20
|
+
|
21
|
+
# Explicitly required for l/t/parser since a1cfa27c27cf4d4c308da2f75fbae88e9d5ae893
|
22
|
+
require 'shellwords'
|
23
|
+
|
24
|
+
# Set is in stdlib but is not in core.
|
25
|
+
require 'set'
|
26
|
+
# Set.to_hash
|
27
|
+
require 'distorted/monkey_business/set'
|
28
|
+
|
29
|
+
# I mean, this is why we're here, right?
|
30
|
+
require 'jekyll'
|
31
|
+
|
32
|
+
|
33
|
+
class Jekyll::DistorteD::Invoker < Liquid::Tag
|
34
|
+
|
35
|
+
GEM_ROOT = File.dirname(__FILE__).freeze
|
36
|
+
|
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.
|
40
|
+
|
41
|
+
|
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
|
47
|
+
|
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)
|
51
|
+
|
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
|
59
|
+
|
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
|
69
|
+
end
|
70
|
+
}
|
71
|
+
|
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
|
79
|
+
|
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']
|
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
|
147
|
+
|
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
|
154
|
+
|
155
|
+
# Returns a context-only setting from our Liquid attributes.
|
156
|
+
def abstract(key)
|
157
|
+
context_arguments.dig(key)
|
158
|
+
end
|
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
|
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, '')
|
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
|
+
|
234
|
+
end
|