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
@@ -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
- wanted_files.map{|f| File.join(dest_root, @relative_dest, f)}
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 = Jekyll::DistorteD::Floor::config(:destination).to_s
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
- files.each { |variation|
98
- type = variation&.dig(:type)
99
- filename = File.join(dest_root, @relative_dest, variation&.dig(:name) || @name)
100
-
101
- if self.respond_to?(type.distorted_method)
102
- Jekyll.logger.debug("DistorteD::#{type.distorted_method}", filename)
103
- self.send(type.distorted_method, filename, **variation)
104
- elsif extname == ".#{type.preferred_extension}"
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.distorted_method} method is defined,
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(filename)
109
+ copy_file(change.paths(dest_root).first)
112
110
  else
113
- Jekyll.logger.error(@name, "Missing rendering method #{type.distorted_method}")
114
- raise MediaTypeOutputNotImplementedError.new(filename, type, self.class.name)
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
- files.map{|f| f[:name]}.to_set
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