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.
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