distorted-jekyll 0.5.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
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