distorted-jekyll 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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/md_injection'
4
+ require 'distorted-jekyll/invoker'
5
+
6
+
7
+ FATAL_FURY = true
8
+ UPDATE_RUBY = "Please use DistorteD with Ruby 2.7.0 or later"
9
+ def update_ruby
10
+ if defined? RUBY_PLATFORM
11
+ if (/freebsd/ =~ RUBY_PLATFORM) != nil
12
+ return 'pkg install lang/ruby27'
13
+ elsif (/darwin/ =~ RUBY_PLATFORM) != nil
14
+ return 'brew upgrade ruby'
15
+ elsif (/win/ =~ RUBY_PLATFORM) != nil
16
+ return 'https://rubyinstaller.org/'
17
+ elsif (/linux/ =~ RUBY_PLATFORM) != nil
18
+ if File.exists?('/etc/lsb-release')
19
+ lsb = File.read('/etc/lsb-release')
20
+ if (/Ubuntu|LinuxMint/ =~ lsb) != nil
21
+ return 'https://www.brightbox.com/docs/ruby/ubuntu/#installation'
22
+ end
23
+ end
24
+ end
25
+ end
26
+ return 'https://github.com/rbenv/ruby-build'
27
+ end
28
+
29
+
30
+ # I want to be able to use:
31
+ # - Array#dig and Hash#dig (Ruby 2.3): https://bugs.ruby-lang.org/issues/11643
32
+ # - Lonely operator (Ruby 2.3): https://bugs.ruby-lang.org/issues/11537
33
+ # - Hash#transform_keys (Ruby 2.5): https://bugs.ruby-lang.org/issues/13583
34
+ # - Enumerable#filter_map (Ruby 2.7): https://bugs.ruby-lang.org/issues/5663
35
+ # https://blog.saeloun.com/2019/05/25/ruby-2-7-enumerable-filter-map.html
36
+ # - 'Real' kwargs in preparation for Ruby 3: https://bugs.ruby-lang.org/issues/14183
37
+ # https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/
38
+ if [
39
+ Hash.method_defined?(:dig), # 2.3
40
+ Hash.method_defined?(:transform_keys), # 2.5
41
+ Enumerable.method_defined?(:filter_map), # 2.7
42
+ ].all?
43
+ # Monkey-patch 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,81 @@
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
+
25
+ distorted:
26
+
27
+ # Liquid template caching is default as of Jekyll 4.0,
28
+ # but it can be disabled if needed.
29
+ # I just can't think of when it would be needed :)
30
+ cache_templates: true
31
+
32
+ # Should unrecognized media-types fall back to a bare
33
+ # <img> tag around the original media file?
34
+ # If not, the site build will fail when an unrecognized
35
+ # file is encountered.
36
+ last_resort: true
37
+
38
+ # Configure DistorteD format changes by media_type, then by sub_type.
39
+ # The list of target formats is plain text, media_type/sub_type.
40
+ # These are mostly based on IANA's official Media Types list:
41
+ # https://www.iana.org/assignments/media-types/media-types.xhtml
42
+ # but with some custom additions like using 'gif-sequence' for
43
+ # animated GIF and leaving 'image/gif' to refer to single-frame GIFs.
44
+ changes:
45
+ image:
46
+ jpeg:
47
+ ? image/jpeg
48
+ ? image/webp
49
+ png:
50
+ ? image/png
51
+ ? image/webp
52
+ gif:
53
+ ? image/gif
54
+ ? image/png
55
+ ? image/webp
56
+ gif-sequence:
57
+ ? image/gif-sequence
58
+ svg:
59
+ ? image/svg+xml
60
+ ? image/png
61
+ ? image/webp
62
+ text:
63
+ plain:
64
+ ? text/plain
65
+ ? image/png
66
+ ? image/webp
67
+ x-nfo:
68
+ ? text/x-nfo
69
+ ? image/png
70
+ ? image/webp
71
+ font:
72
+ ttf:
73
+ ? font/ttf
74
+ ? image/png
75
+ ? image/webp
76
+
77
+ outer_limits:
78
+ image:
79
+ jpeg: *standard_image
80
+ png: *standard_image
81
+ webp: *standard_image
@@ -0,0 +1,16 @@
1
+
2
+ module Jekyll
3
+ module DistorteD
4
+ class BLOCKS < Liquid::Block
5
+
6
+ def initialize(tag_name, arguments, liquid_options)
7
+ super
8
+ end
9
+
10
+ def render(context)
11
+ "<div class=\"distorted-block\">#{super}</div>"
12
+ end
13
+
14
+ end # BLOCKS
15
+ end # DistorteD
16
+ end # Jekyll
@@ -0,0 +1,266 @@
1
+ require 'yaml'
2
+ require 'jekyll'
3
+ require 'set'
4
+
5
+ require 'distorted/monkey_business/hash'
6
+ require 'distorted/checking_you_out'
7
+
8
+
9
+ module Jekyll
10
+ module DistorteD
11
+ module Floor
12
+
13
+ ATTRIBUTES = Set[:lower_world, :changes, :outer_limits]
14
+
15
+ # Top-level config key (once stringified) for Jekyll and Default YAML.
16
+ CONFIG_ROOT = :distorted
17
+
18
+ # Filename for default config YAML. Should be a sibling of this file.
19
+ # Don't move this file or the YAML defaults without changing this.
20
+ DEFAULT_CONFIG_FILE_NAME = '_config_default.yml'.freeze
21
+ DEFAULT_CONFIG_PATH = File.join(File.dirname(__FILE__), DEFAULT_CONFIG_FILE_NAME).freeze
22
+
23
+ # Separator character for pretty-printing config hierarchy.
24
+ PP_SEPARATOR = "\u21e2 ".encode('utf-8').freeze
25
+
26
+ # Path separator is almost always '/' internally, but support
27
+ # ALT_SEPARATOR platforms too.
28
+ # On Lunix - Ruby 2.7:
29
+ # irb(main):003:0> File::ALT_SEPARATOR
30
+ # => nil
31
+ # irb(main):004:0> File::SEPARATOR
32
+ # => "/"
33
+ PATH_SEPARATOR = (File::ALT_SEPARATOR || File::SEPARATOR).freeze
34
+
35
+
36
+ # Generic main config-loading function that will search, in order:
37
+ # - The memoized pre-transformed config data store in-memory.
38
+ # - Jekyll's Site config, for a passed-in site or for the default site.
39
+ # - DistorteD's Gem-internal default config YAML.
40
+ #
41
+ # Optionally provide a class to be used as a fallback for missing keys.
42
+ def self.config(*keys, **kw)
43
+ # Symbolize for our internal representation of the config path.
44
+ # The Jekyll config and default config are both YAML, so we want string
45
+ # keys for them. Go ahead and prepend the top-level search key here too.
46
+ memo_keys = keys.compact.map(&:to_sym).to_set
47
+ search_keys = keys.compact.map(&:to_s).map(&:freeze)
48
+ # Pretty print the config path for logging.
49
+ log_key = search_keys.join(PP_SEPARATOR.to_s).freeze
50
+ # Initialize memoization class variable as a Hash that will return nil
51
+ # for any key access that doesn't already contain something.
52
+ @@memories ||= Hash.new { |h,k| h[k] = h.class.new(&h.default_proc) }
53
+ # Try to load a memoized config if we can, to skip any filesystem
54
+ # access and data transformation steps.
55
+ config = @@memories&.dig(*memo_keys)
56
+ unless config.nil?
57
+ if config.is_a?(TrueClass) || config.is_a?(FalseClass)
58
+ return config
59
+ elsif config.is_a?(Enumerable)
60
+ unless config.empty?
61
+ # Can't check this at the top level because True/FalseClass
62
+ # don't respond to this message.
63
+ return config
64
+ end
65
+ end
66
+ end
67
+
68
+ # The key isn't memoized. Look for it first in Jekyll's Site config.
69
+ # Is it even possible to have more than one Site? Support being passed
70
+ # a `site` object just in case, but taking the first one should be fine.
71
+ site = kw[:site] || Jekyll.sites.first
72
+ # Get the config, or nil if the queried config path doesn't exist.
73
+ loaded_config = site.config.dig(*search_keys)
74
+ if loaded_config.nil?
75
+ # The wanted config key didn't exist in the Site config, so let's
76
+ # try our defaults!
77
+ # This file will always be small enough for a one-shot read.
78
+ default_config = YAML.load(File.read(DEFAULT_CONFIG_PATH))
79
+ loaded_config = default_config.dig(*search_keys)
80
+ unless loaded_config.nil?
81
+ Jekyll.logger.debug(['Default', log_key].join(PP_SEPARATOR.to_s).concat(':'.freeze), loaded_config)
82
+ end
83
+ else # else Jekyll _config is not nil
84
+ Jekyll.logger.debug(['_config', log_key].join(PP_SEPARATOR.to_s).concat(':'.freeze), loaded_config)
85
+ end
86
+ # Was the desired config key found in the Gem defaults?
87
+ if loaded_config.nil?
88
+ # Nope.
89
+ return nil
90
+ else
91
+ # Symbolize any output keys and values, and convert Arrays and Ruby::YAML
92
+ # Sets-as-Hashes to Ruby stdlib Sets.
93
+ # Returning a Set instead of an Array should be fine since none of our
94
+ # configs can (read: should) contain duplicate values for any reason.
95
+ loaded_config = symbolic(set_me_free(loaded_config))
96
+ end
97
+ # Memoize any of our own config, but just return anything outside our tree.
98
+ if keys.first == CONFIG_ROOT
99
+ @@memories.bury(*memo_keys, loaded_config)
100
+ Jekyll.logger.debug(log_key, "Memoizing config: #{@@memories.dig(*memo_keys)}")
101
+ # And return a config to the caller. Don't return the `new`ly fetched
102
+ # data directly to ensure consistency between this first fetch and
103
+ # subsequent memoized fetches, and to let callers take advantage of
104
+ # the memo Hash's `default_proc` setup.
105
+ return @@memories.dig(*memo_keys)
106
+ else
107
+ return loaded_config
108
+ end
109
+ end
110
+
111
+ # AFAICT Ruby::YAML will not give me a Ruby Set[] for a YAML Set,
112
+ # just a Hash with all-nil-values which is what it is internally.
113
+ # distorted⇢ image Trying Jekyll _config key: {"(max-width: 400px)"=>nil, "(min-width: 800px)"=>nil, "(min-width: 1500px)"=>nil}
114
+ # It is possible with some sugar in the YAML files, but I don't
115
+ # want to ask anyone to do that :)
116
+ # https://rhnh.net/2011/01/31/yaml-tutorial/
117
+ def self.set_me_free(dunno)
118
+ if dunno.class == Array
119
+ return dunno&.to_set.map{|d| set_me_free(d)}
120
+ elsif dunno.class == Hash
121
+ if dunno&.values.all?{|v| v.nil?}
122
+ return dunno&.keys.to_set
123
+ else
124
+ return dunno&.transform_values!{|v| set_me_free(v)}
125
+ end
126
+ end
127
+ return dunno
128
+ end
129
+
130
+ # Transform arbitrary configuration data structure keys from
131
+ # strings to symbols before memoization.
132
+ # https://stackoverflow.com/a/8189435
133
+ def self.symbolic(dunno)
134
+ # Check message-handling responses to gauge emptiness since classes that
135
+ # don't respond to `:empty?` might not respond to `:method_exists?` either.
136
+ if dunno.nil?
137
+ return dunno
138
+ elsif dunno.class == Hash
139
+ return dunno.transform_keys!(&:to_sym).transform_values!{|v| symbolic(v)}
140
+ elsif dunno.class == Array
141
+ return dunno.map{|r| symbolic(r)}
142
+ elsif dunno.respond_to?(:to_sym)
143
+ # Plain types
144
+ return dunno.to_sym
145
+ elsif dunno.respond_to?(:to_str)
146
+ # Freeze string config values.
147
+ # Specifically :to_str, not :to_s. Usually implemented by actual Strings.
148
+ return dunno.to_str.freeze
149
+ end
150
+ return dunno
151
+ end
152
+
153
+ # Returns a Set of Arrays of search keys to try in config()
154
+ def search_keys(*keys)
155
+ # It's likely that we will get a default argument of [nil]
156
+ # here due to the output of abstract(:whatever) for unset attrs.
157
+ keys = keys.compact
158
+ # If a search key path was given, construct one based
159
+ # on the MIME::Type union Set between the source media
160
+ # and the plugged MediaMolecule.
161
+ if keys.empty? or keys.all?{|k| k.nil?}
162
+ try_keys = type_mars.map{ |t|
163
+ # Use only the first part of complex sub_types like 'svg+xml'
164
+ [t.media_type, t.sub_type.split('+').first].compact
165
+ }
166
+ else
167
+ # Or use a user-provided config path.
168
+ try_keys = Set[keys]
169
+ end
170
+ end
171
+
172
+ # Loads configuration data telling us how to open certain
173
+ # types of files.
174
+ def lower_world(*keys)
175
+ # Try each set of keys until we find a match
176
+ for try in search_keys(*keys)
177
+ tried = Jekyll::DistorteD::Floor::config(
178
+ Jekyll::DistorteD::Floor::CONFIG_ROOT,
179
+ :welcome,
180
+ *try,
181
+ )
182
+ # Is the YAML config of the appropriate structure?
183
+ if tried.is_a?(Hash)
184
+ # Non-Hashes may not respond to `empty?`
185
+ unless tried.empty?
186
+ return tried
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ # Load configuration telling us what media-types to generate
193
+ # for any given media-type input.
194
+ def changes(*keys)
195
+ out = Set[]
196
+ # `changes` media-type[sub_type] config will contain information about
197
+ # what variations output format are desired for what input format,
198
+ # e.g. {:image => {:jpeg => Set['image/jpeg', 'image/webp']}}
199
+ # It is not automatically implied that the source format is also
200
+ # an output format!
201
+ for try in search_keys(*keys)
202
+ tried = Jekyll::DistorteD::Floor::config(
203
+ Jekyll::DistorteD::Floor::CONFIG_ROOT,
204
+ :changes,
205
+ *try,
206
+ )
207
+ if tried.is_a?(Enumerable) and tried.all?{|t| t.is_a?(String)} and not tried.empty?
208
+ tried.each{ |t|
209
+ # MIME::Type.new() won't give us a usable Type object:
210
+ #
211
+ # irb> MIME::Types['image/svg+xml'].first.preferred_extension
212
+ # => "svg"
213
+ # irb> MIME::Type.new('image/svg+xml').preferred_extension
214
+ # => nil
215
+ out.merge(CHECKING::YOU::IN(t))
216
+ }
217
+ end
218
+ end
219
+
220
+ # If the config didn't give us any MIME::Type changes
221
+ # then we will just output the same type we loaded.
222
+ if out.empty?
223
+ return type_mars
224
+ else
225
+ return out
226
+ end
227
+ end
228
+
229
+ # Loads configuration telling us what variations to generate for any
230
+ # given type of file, or for an arbitrary key hierarchy.
231
+ def outer_limits(*keys)
232
+ out = Set[]
233
+ # See if any config data exists for each given key hierarchy,
234
+ # but under the root DistorteD config key.
235
+ for try in search_keys(*keys)
236
+ tried = Jekyll::DistorteD::Floor::config(
237
+ Jekyll::DistorteD::Floor::CONFIG_ROOT,
238
+ :outer_limits,
239
+ *try,
240
+ )
241
+
242
+ # Is the YAML config of the appropriate structure?
243
+ # Merge a shallow copy of it with the Liquid-given attrs.
244
+ # If we don't take a copy the attrs will be memoized into the config.
245
+ if tried.is_a?(Enumerable) and tried.all?{|t| t.is_a?(Hash)} and not tried.empty?
246
+ out.merge(tried.dup.map{ |d| d.merge(@liquid_liquid) })
247
+ end
248
+ end
249
+
250
+ # We should output something if the config didn't give us anything.
251
+ # This is kind of a mess right now with redundancies in the call sites
252
+ # of things like Molecule::Image. I'll come up with a better general-
253
+ # purpose fallback solution at some point, but for now this will get
254
+ # non-Image StaticFiles working with no config :)
255
+ if out.empty?
256
+ out << {
257
+ :tag => :full,
258
+ }
259
+ end
260
+
261
+ return out
262
+ end
263
+
264
+ end
265
+ end
266
+ end