distorted-jekyll 0.5.6 → 0.5.7

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +661 -0
  3. data/README.md +6 -10
  4. data/lib/distorted-jekyll.rb +79 -0
  5. data/lib/distorted-jekyll/13th-style.rb +58 -0
  6. data/lib/distorted-jekyll/_config_default.yml +79 -0
  7. data/lib/distorted-jekyll/blocks.rb +16 -0
  8. data/lib/distorted-jekyll/error_code.rb +24 -0
  9. data/lib/distorted-jekyll/floor.rb +148 -0
  10. data/lib/distorted-jekyll/injection_of_love.rb +305 -0
  11. data/lib/distorted-jekyll/invoker.rb +400 -0
  12. data/lib/distorted-jekyll/molecule/abstract.rb +238 -0
  13. data/lib/distorted-jekyll/molecule/font.rb +29 -0
  14. data/lib/distorted-jekyll/molecule/image.rb +105 -0
  15. data/lib/distorted-jekyll/molecule/last-resort.rb +54 -0
  16. data/lib/distorted-jekyll/molecule/pdf.rb +88 -0
  17. data/lib/distorted-jekyll/molecule/svg.rb +59 -0
  18. data/lib/distorted-jekyll/molecule/text.rb +74 -0
  19. data/lib/distorted-jekyll/molecule/video.rb +43 -0
  20. data/lib/distorted-jekyll/monkey_business/jekyll/cleaner.rb +54 -0
  21. data/lib/distorted-jekyll/static/font.rb +42 -0
  22. data/lib/distorted-jekyll/static/image.rb +55 -0
  23. data/lib/distorted-jekyll/static/lastresort.rb +28 -0
  24. data/lib/distorted-jekyll/static/pdf.rb +53 -0
  25. data/lib/distorted-jekyll/static/state.rb +141 -0
  26. data/lib/distorted-jekyll/static/svg.rb +52 -0
  27. data/lib/distorted-jekyll/static/text.rb +57 -0
  28. data/lib/distorted-jekyll/static/video.rb +90 -0
  29. data/lib/distorted-jekyll/template/13th-style.css +78 -0
  30. data/lib/distorted-jekyll/template/error_code.liquid +3 -0
  31. data/lib/distorted-jekyll/template/font.liquid +32 -0
  32. data/lib/distorted-jekyll/template/image.liquid +32 -0
  33. data/lib/distorted-jekyll/template/lastresort.liquid +20 -0
  34. data/lib/distorted-jekyll/template/pdf.liquid +14 -0
  35. data/lib/distorted-jekyll/template/svg.liquid +32 -0
  36. data/lib/distorted-jekyll/template/text.liquid +32 -0
  37. data/lib/distorted-jekyll/template/video.liquid +11 -0
  38. metadata +41 -6
data/README.md CHANGED
@@ -1,19 +1,16 @@
1
- # Cooltrainer::DistorteD
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
- DD 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.
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
- Or, for a DD grid:
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
159
  The `DistorteD-Jekyll` Gem will automatically use its local sibling `DistorteD-Ruby` Gem if used in this way.
164
160
 
165
161
  ## License
166
162
 
167
- DistorteD is available as open source under the terms of the [GNU Affero General Public License version 3](https://opensource.org/licenses/AGPL-3.0).
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,79 @@
1
+ require 'distorted-jekyll/13th-style'
2
+ require 'distorted-jekyll/blocks'
3
+ require 'distorted-jekyll/injection_of_love'
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 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
+ # Monkey-patch Jekyll::Cleaner to not nuke DistorteD-generated variations
48
+ # for our media files. This makes DistorteD fast!
49
+ require 'distorted-jekyll/monkey_business/jekyll/cleaner'
50
+
51
+ # Register DistorteD's entrypoint class with Liquid.
52
+ # `Invoker` will mix in the proper handler module for the given media.
53
+ Liquid::Template.register_tag('distorted', Jekyll::DistorteD::Invoker)
54
+
55
+ # Register a block version for arranging multiple pieces of media.
56
+ Liquid::Template.register_tag('distort', Jekyll::DistorteD::BLOCKS)
57
+
58
+ # Register a tag for basic DistorteD CSS.
59
+ Liquid::Template.register_tag('13th_style', Jekyll::DistorteD::ThirteenthStyle)
60
+
61
+ # Transform Markdown image syntax ![alt](url.jpg "title")
62
+ # to instances of our liquid tag {% distorted %}
63
+ # Available hooks can be seen here:
64
+ # https://github.com/jekyll/jekyll/blob/master/lib/jekyll/hooks.rb
65
+ # `:documents` does not seem to include `_pages` but does include `_posts`.
66
+ Jekyll::Hooks.register(:pages, :pre_render, &md_injection)
67
+ Jekyll::Hooks.register(:posts, :pre_render, &md_injection)
68
+
69
+ else
70
+ # Example of how this looks with the outdated Ruby 2.5 on my Mint 19 laptop:
71
+ #
72
+ # Bundler::GemRequireError: There was an error while trying to load the gem 'distorted-jekyll'.
73
+ # Gem Load Error is: Please use DistorteD with Ruby 2.7.0 or later: https://www.brightbox.com/docs/ruby/ubuntu/#installation
74
+ if FATAL_FURY
75
+ raise RuntimeError.new("#{UPDATE_RUBY}: #{update_ruby}")
76
+ else
77
+ Jekyll.logger.info('DistorteD', "#{UPDATE_RUBY}: #{update_ruby}")
78
+ end
79
+ end
@@ -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__), 'template'.freeze, '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,79 @@
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
+ - 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
+
24
+ standard_text: &standard_text
25
+ ? image/png
26
+ ? image/webp
27
+
28
+ distorted:
29
+
30
+ # Liquid template caching is default as of Jekyll 4.0,
31
+ # but it can be disabled if needed.
32
+ # I just can't think of when it would be needed :)
33
+ cache_templates: true
34
+
35
+ # Should unrecognized media-types fall back to a bare
36
+ # <img> tag around the original media file?
37
+ # If not, the site build will fail when an unrecognized
38
+ # file is encountered.
39
+ last_resort: true
40
+
41
+ # Configure DistorteD format changes by media_type, then by sub_type.
42
+ # The list of target formats is plain text, media_type/sub_type.
43
+ # These are mostly based on IANA's official Media Types list:
44
+ # https://www.iana.org/assignments/media-types/media-types.xhtml
45
+ # but with some custom additions like using 'gif-sequence' for
46
+ # animated GIF and leaving 'image/gif' to refer to single-frame GIFs.
47
+ changes:
48
+ image:
49
+ jpeg:
50
+ ? image/jpeg
51
+ ? image/webp
52
+ png:
53
+ ? image/png
54
+ ? image/webp
55
+ gif:
56
+ ? image/gif
57
+ ? image/png
58
+ ? image/webp
59
+ gif-sequence:
60
+ ? image/gif-sequence
61
+ svg:
62
+ ? image/svg+xml
63
+ ? image/png
64
+ ? image/webp
65
+ text:
66
+ plain: *standard_text
67
+ x-nfo: *standard_text
68
+ font:
69
+ ttf:
70
+ ? image/png
71
+ ? image/webp
72
+
73
+ outer_limits:
74
+ image:
75
+ jpeg: *standard_image
76
+ png: *standard_image
77
+ webp: *standard_image
78
+ font:
79
+ ttf: *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,24 @@
1
+ require 'distorted/error_code'
2
+
3
+
4
+ module Jekyll
5
+ module DistorteD
6
+ class OutOfDateLibraryError < LoadError
7
+ end
8
+
9
+ # The built-in NotImplementedError is for "when a feature is not implemented
10
+ # on the current platform", so make our own more appropriate ones.
11
+ class MediaTypeNotImplementedError < StandardDistorteDError
12
+ attr_reader :media_type, :name
13
+ def initialize(name)
14
+ super("No supported media type for #{name}")
15
+ end
16
+ end
17
+ class MediaTypeNotFoundError < StandardDistorteDError
18
+ attr_reader :media_type, :name
19
+ def initialize(name)
20
+ super("Failed to detect media type for #{name}")
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,148 @@
1
+ require 'distorted/monkey_business/hash'
2
+ require 'yaml'
3
+ require 'jekyll'
4
+ require 'set'
5
+
6
+
7
+ module Jekyll
8
+ module DistorteD
9
+ class Floor
10
+
11
+ # Top-level config key (once stringified) for Jekyll and Default YAML.
12
+ CONFIG_ROOT = :distorted
13
+
14
+ # Filename for default config YAML. Should be a sibling of this file.
15
+ # Don't move this file or the YAML defaults without changing this.
16
+ DEFAULT_CONFIG_FILE_NAME = '_config_default.yml'.freeze
17
+ DEFAULT_CONFIG_PATH = File.join(File.dirname(__FILE__), DEFAULT_CONFIG_FILE_NAME).freeze
18
+
19
+ # Separator character for pretty-printing config hierarchy.
20
+ PP_SEPARATOR = "\u21e2 ".encode('utf-8').freeze
21
+
22
+ # Path separator is almost always '/' internally, but support
23
+ # ALT_SEPARATOR platforms too.
24
+ # On Lunix - Ruby 2.7:
25
+ # irb(main):003:0> File::ALT_SEPARATOR
26
+ # => nil
27
+ # irb(main):004:0> File::SEPARATOR
28
+ # => "/"
29
+ PATH_SEPARATOR = (File::ALT_SEPARATOR || File::SEPARATOR).freeze
30
+
31
+
32
+ # Generic main config-loading function that will search, in order:
33
+ # - The memoized pre-transformed config data store in-memory.
34
+ # - Jekyll's Site config, for a passed-in site or for the default site.
35
+ # - DistorteD's Gem-internal default config YAML.
36
+ #
37
+ # Optionally provide a class to be used as a fallback for missing keys.
38
+ def self.config(*keys, **kw)
39
+ # Symbolize for our internal representation of the config path.
40
+ # The Jekyll config and default config are both YAML, so we want string
41
+ # keys for them. Go ahead and prepend the top-level search key here too.
42
+ memo_keys = keys.compact.map(&:to_sym).to_set
43
+ search_keys = keys.compact.map(&:to_s).map(&:freeze)
44
+ # Pretty print the config path for logging.
45
+ log_key = search_keys.join(PP_SEPARATOR.to_s).freeze
46
+ # Initialize memoization class variable as a Hash that will return nil
47
+ # for any key access that doesn't already contain something.
48
+ @@memories ||= Hash.new { |h,k| h[k] = h.class.new(&h.default_proc) }
49
+ # Try to load a memoized config if we can, to skip any filesystem
50
+ # access and data transformation steps.
51
+ config = @@memories&.dig(*memo_keys)
52
+ unless config.nil?
53
+ if config.is_a?(TrueClass) || config.is_a?(FalseClass)
54
+ return config
55
+ elsif config.is_a?(Enumerable)
56
+ unless config.empty?
57
+ # Can't check this at the top level because True/FalseClass
58
+ # don't respond to this message.
59
+ return config
60
+ end
61
+ end
62
+ end
63
+
64
+ # The key isn't memoized. Look for it first in Jekyll's Site config.
65
+ # Is it even possible to have more than one Site? Support being passed
66
+ # a `site` object just in case, but taking the first one should be fine.
67
+ site = kw[:site] || Jekyll.sites.first
68
+ # Get the config, or nil if the queried config path doesn't exist.
69
+ loaded_config = site.config.dig(*search_keys)
70
+ Jekyll.logger.debug(['_config', log_key].join(PP_SEPARATOR.to_s).concat(':'.freeze), loaded_config || 'No data'.freeze)
71
+ if loaded_config.nil?
72
+ # The wanted config key didn't exist in the Site config, so let's
73
+ # try our defaults!
74
+ # This file will always be small enough for a one-shot read.
75
+ default_config = YAML.load(File.read(DEFAULT_CONFIG_PATH))
76
+ loaded_config = default_config.dig(*search_keys)
77
+ Jekyll.logger.debug(['Default', log_key].join(PP_SEPARATOR.to_s).concat(':'.freeze), loaded_config || 'No data'.freeze)
78
+ end
79
+ # Was the desired config key found in the Gem defaults?
80
+ if loaded_config.nil?
81
+ # Nope.
82
+ return nil
83
+ else
84
+ # Symbolize any output keys and values, and convert Arrays and Ruby::YAML
85
+ # Sets-as-Hashes to Ruby stdlib Sets.
86
+ # Returning a Set instead of an Array should be fine since none of our
87
+ # configs can (read: should) contain duplicate values for any reason.
88
+ loaded_config = symbolic(set_me_free(loaded_config))
89
+ end
90
+ # Memoize any of our own config, but just return anything outside our tree.
91
+ if keys.first == CONFIG_ROOT
92
+ @@memories.bury(*memo_keys, loaded_config)
93
+ Jekyll.logger.debug(log_key, "Memoizing config: #{@@memories.dig(*memo_keys)}")
94
+ # And return a config to the caller. Don't return the `new`ly fetched
95
+ # data directly to ensure consistency between this first fetch and
96
+ # subsequent memoized fetches, and to let callers take advantage of
97
+ # the memo Hash's `default_proc` setup.
98
+ return @@memories.dig(*memo_keys)
99
+ else
100
+ return loaded_config
101
+ end
102
+ end
103
+
104
+ # AFAICT Ruby::YAML will not give me a Ruby Set[] for a YAML Set,
105
+ # just a Hash with all-nil-values which is what it is internally.
106
+ # distorted⇢ image Trying Jekyll _config key: {"(max-width: 400px)"=>nil, "(min-width: 800px)"=>nil, "(min-width: 1500px)"=>nil}
107
+ # It is possible with some sugar in the YAML files, but I don't
108
+ # want to ask anyone to do that :)
109
+ # https://rhnh.net/2011/01/31/yaml-tutorial/
110
+ def self.set_me_free(dunno)
111
+ if dunno.class == Array
112
+ return dunno&.to_set.map{|d| set_me_free(d)}
113
+ elsif dunno.class == Hash
114
+ if dunno&.values.all?{|v| v.nil?}
115
+ return dunno&.keys.to_set
116
+ else
117
+ return dunno&.transform_values!{|v| set_me_free(v)}
118
+ end
119
+ end
120
+ return dunno
121
+ end
122
+
123
+ # Transform arbitrary configuration data structure keys from
124
+ # strings to symbols before memoization.
125
+ # https://stackoverflow.com/a/8189435
126
+ def self.symbolic(dunno)
127
+ # Check message-handling responses to gauge emptiness since classes that
128
+ # don't respond to `:empty?` might not respond to `:method_exists?` either.
129
+ if dunno.nil?
130
+ return dunno
131
+ elsif dunno.class == Hash
132
+ return dunno.transform_keys!(&:to_sym).transform_values!{|v| symbolic(v)}
133
+ elsif dunno.class == Array
134
+ return dunno.map{|r| symbolic(r)}
135
+ elsif dunno.respond_to?(:to_sym)
136
+ # Plain types
137
+ return dunno.to_sym
138
+ elsif dunno.respond_to?(:to_str)
139
+ # Freeze string config values.
140
+ # Specifically :to_str, not :to_s. Usually implemented by actual Strings.
141
+ return dunno.to_str.freeze
142
+ end
143
+ return dunno
144
+ end
145
+
146
+ end
147
+ end
148
+ end