jekyll_picture_tag 1.13.0 → 2.0.2

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +4 -0
  3. data/.github/workflows/code-checks.yml +33 -0
  4. data/.gitignore +3 -0
  5. data/.rubocop.yml +29 -76
  6. data/.ruby-version +1 -1
  7. data/docs/.envrc +2 -0
  8. data/docs/devs/contributing/code.md +14 -4
  9. data/docs/devs/contributing/docs.md +24 -6
  10. data/docs/devs/contributing/setup.md +21 -1
  11. data/docs/devs/contributing/testing.md +19 -37
  12. data/docs/devs/releases.md +45 -4
  13. data/docs/index.md +43 -18
  14. data/docs/logo.png +0 -0
  15. data/docs/logo.svg +880 -0
  16. data/docs/users/configuration/disable.md +1 -1
  17. data/docs/users/configuration/ignore_missing.md +1 -1
  18. data/docs/users/configuration/kramdown_fix.md +1 -1
  19. data/docs/users/configuration/suppress_warnings.md +1 -1
  20. data/docs/users/configuration/urls.md +69 -0
  21. data/docs/users/deployment.md +49 -0
  22. data/docs/users/getting_started.md +55 -0
  23. data/docs/users/installation.md +18 -38
  24. data/docs/users/liquid_tag/argument_reference/crop.md +21 -36
  25. data/docs/users/liquid_tag/examples.md +13 -25
  26. data/docs/users/liquid_tag/index.md +1 -1
  27. data/docs/users/notes/{migration.md → migration_1.md} +1 -1
  28. data/docs/users/notes/migration_2.md +99 -0
  29. data/docs/users/presets/cropping.md +21 -22
  30. data/docs/users/presets/default.md +11 -2
  31. data/docs/users/presets/examples.md +77 -45
  32. data/docs/users/presets/fallback_image.md +1 -1
  33. data/docs/users/presets/html_attributes.md +1 -1
  34. data/docs/users/presets/image_formats.md +3 -3
  35. data/docs/users/presets/image_quality.md +71 -56
  36. data/docs/users/presets/index.md +19 -45
  37. data/docs/users/presets/link_source.md +1 -1
  38. data/docs/users/presets/media_queries.md +1 -1
  39. data/docs/users/presets/nomarkdown_override.md +1 -1
  40. data/docs/users/presets/pixel_ratio_srcsets.md +1 -1
  41. data/docs/users/presets/width_height_attributes.md +1 -1
  42. data/docs/users/presets/width_srcsets.md +61 -23
  43. data/docs/users/presets/writing_presets.md +65 -0
  44. data/docs/users/tutorial.md +97 -0
  45. data/jekyll_picture_tag.gemspec +38 -23
  46. data/lib/jekyll_picture_tag.rb +8 -5
  47. data/lib/jekyll_picture_tag/cache.rb +64 -3
  48. data/lib/jekyll_picture_tag/defaults/global.rb +18 -0
  49. data/lib/jekyll_picture_tag/defaults/presets.rb +57 -0
  50. data/lib/jekyll_picture_tag/images.rb +1 -0
  51. data/lib/jekyll_picture_tag/images/generated_image.rb +25 -60
  52. data/lib/jekyll_picture_tag/images/image_file.rb +105 -0
  53. data/lib/jekyll_picture_tag/images/img_uri.rb +3 -10
  54. data/lib/jekyll_picture_tag/images/source_image.rb +44 -9
  55. data/lib/jekyll_picture_tag/instructions.rb +70 -6
  56. data/lib/jekyll_picture_tag/instructions/children/config.rb +128 -0
  57. data/lib/jekyll_picture_tag/instructions/children/context.rb +24 -0
  58. data/lib/jekyll_picture_tag/instructions/children/params.rb +90 -0
  59. data/lib/jekyll_picture_tag/instructions/children/parsers.rb +48 -0
  60. data/lib/jekyll_picture_tag/instructions/children/preset.rb +182 -0
  61. data/lib/jekyll_picture_tag/instructions/parents/conditional_instruction.rb +69 -0
  62. data/lib/jekyll_picture_tag/instructions/parents/env_instruction.rb +29 -0
  63. data/lib/jekyll_picture_tag/output_formats/basic.rb +5 -17
  64. data/lib/jekyll_picture_tag/parsers.rb +6 -0
  65. data/lib/jekyll_picture_tag/{instructions → parsers}/arg_splitter.rb +1 -1
  66. data/lib/jekyll_picture_tag/parsers/configuration.rb +28 -0
  67. data/lib/jekyll_picture_tag/{instructions → parsers}/html_attributes.rb +1 -1
  68. data/lib/jekyll_picture_tag/parsers/image_backend.rb +33 -0
  69. data/lib/jekyll_picture_tag/parsers/preset.rb +43 -0
  70. data/lib/jekyll_picture_tag/{instructions → parsers}/tag_parser.rb +15 -12
  71. data/lib/jekyll_picture_tag/router.rb +35 -93
  72. data/lib/jekyll_picture_tag/srcsets/basic.rb +4 -10
  73. data/lib/jekyll_picture_tag/utils.rb +10 -20
  74. data/lib/jekyll_picture_tag/version.rb +1 -1
  75. data/readme.md +48 -9
  76. metadata +161 -80
  77. data/.travis.yml +0 -8
  78. data/Dockerfile +0 -9
  79. data/docs/users/configuration/cdn.md +0 -35
  80. data/docs/users/configuration/relative_urls.md +0 -15
  81. data/docs/users/notes/input_checking.md +0 -6
  82. data/jekyll-picture-tag.gemspec +0 -52
  83. data/lib/jekyll-picture-tag.rb +0 -25
  84. data/lib/jekyll_picture_tag/cache/base.rb +0 -59
  85. data/lib/jekyll_picture_tag/cache/generated.rb +0 -20
  86. data/lib/jekyll_picture_tag/cache/source.rb +0 -19
  87. data/lib/jekyll_picture_tag/defaults/global.yml +0 -11
  88. data/lib/jekyll_picture_tag/defaults/presets.yml +0 -11
  89. data/lib/jekyll_picture_tag/instructions/configuration.rb +0 -121
  90. data/lib/jekyll_picture_tag/instructions/preset.rb +0 -122
  91. data/lib/jekyll_picture_tag/instructions/set.rb +0 -75
@@ -0,0 +1,182 @@
1
+ require 'mime-types'
2
+
3
+ module PictureTag
4
+ module Instructions
5
+ # Returns an instance of the correct markup format's corresponding class
6
+ class OutputClass < Instruction
7
+ private
8
+
9
+ def source
10
+ PictureTag.preset['markup']
11
+ end
12
+
13
+ def coerce
14
+ Object.const_get(class_name)
15
+ end
16
+
17
+ def class_name
18
+ 'PictureTag::OutputFormats::' + Utils.titleize(source)
19
+ end
20
+
21
+ def valid?
22
+ source.is_a?(String) &&
23
+ !source.include?(' ') &&
24
+ Object.const_defined?(class_name)
25
+ end
26
+ end
27
+
28
+ # Image formats
29
+ class Formats < Instruction
30
+ private
31
+
32
+ def source
33
+ PictureTag.preset['formats']
34
+ end
35
+
36
+ def coerce
37
+ [source].flatten
38
+ end
39
+
40
+ def valid?
41
+ coerced.all? do |format|
42
+ types = MIME::Types.type_for(format)
43
+ format == 'original' ||
44
+ types.length == 1 && types.first.media_type == 'image'
45
+ end
46
+ end
47
+ end
48
+
49
+ # Fallback image format
50
+ class FallbackFormat < Instruction
51
+ private
52
+
53
+ def source
54
+ PictureTag.preset['fallback_format']
55
+ end
56
+
57
+ def valid?
58
+ types = MIME::Types.type_for(coerced)
59
+ coerced == 'original' ||
60
+ types.length == 1 && types.first.media_type == 'image'
61
+ end
62
+ end
63
+
64
+ # Fallback Image width
65
+ class FallbackWidth < Instruction
66
+ private
67
+
68
+ def source
69
+ PictureTag.preset['fallback_width']
70
+ end
71
+
72
+ def valid?
73
+ source.is_a? Integer
74
+ end
75
+
76
+ def error_message
77
+ <<~HEREDOC
78
+ fallback_width for preset '#{PictureTag.preset.name}' is invalid. It
79
+ should be a positive integer. You can use underscores as separators:
80
+ 1200
81
+ 1_200
82
+ HEREDOC
83
+ end
84
+ end
85
+
86
+ # Whether to add a {::nomarkdown} wrapper
87
+ class Nomarkdown < Instruction
88
+ private
89
+
90
+ def source
91
+ {
92
+ config: PictureTag.pconfig['nomarkdown'],
93
+ md_page: Utils.markdown_page?,
94
+ preset: PictureTag.preset['nomarkdown']
95
+ }
96
+ end
97
+
98
+ def valid?
99
+ source.fetch_values(:preset, :config).all? do |setting|
100
+ [true, false, nil].include? setting
101
+ end
102
+ end
103
+
104
+ def coerce
105
+ return source[:preset] unless source[:preset].nil?
106
+
107
+ source[:md_page] && source[:config]
108
+ end
109
+ end
110
+
111
+ # Returns widths for a given media query.
112
+ class Widths < ConditionalInstruction
113
+ private
114
+
115
+ def setting_name
116
+ 'widths'
117
+ end
118
+
119
+ def setting_prefix
120
+ 'media'
121
+ end
122
+
123
+ def acceptable_types
124
+ super + [Array]
125
+ end
126
+
127
+ def valid_hash?
128
+ hash = source[:hash]
129
+ return true if hash.nil?
130
+
131
+ hash.is_a?(Hash) && valid_hash_keys?(hash) && valid_hash_values?(hash)
132
+ end
133
+
134
+ def valid_hash_keys?(hash)
135
+ hash.keys.all? { |k| k.is_a? String }
136
+ end
137
+
138
+ def valid_hash_values?(hash)
139
+ hash.values.all? do |val|
140
+ val.is_a?(Array) && val.all? { |subval| subval.is_a? Integer }
141
+ end
142
+ end
143
+ end
144
+
145
+ # Returns quality for a given width.
146
+ class Quality < ConditionalInstruction
147
+ private
148
+
149
+ def setting_name
150
+ 'quality'
151
+ end
152
+
153
+ def setting_prefix
154
+ 'format'
155
+ end
156
+
157
+ def acceptable_types
158
+ super + [Integer, Hash]
159
+ end
160
+
161
+ def coerce(format = nil, width = nil)
162
+ setting = super(format)
163
+
164
+ return setting unless setting.is_a? Hash
165
+
166
+ parse_quality_hash(setting, width)
167
+ end
168
+
169
+ # Works out linearly interpolated quality settings.
170
+ def parse_quality_hash(points, width)
171
+ # The points can be given in any order.
172
+ low, high = *points.keys.map(&:to_i).sort
173
+
174
+ case width
175
+ when 0..low then points[low]
176
+ when low..high then Utils.interpolate(points.keys, points.values, width)
177
+ when high..999_999 then points[high]
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,69 @@
1
+ module PictureTag
2
+ module Instructions
3
+ # Many inputs take a common format: a generic setting which applies all of
4
+ # the time, or more specific versions of that setting for specific
5
+ # circumstances. For example, quality can be set globally, or per image
6
+ # format. This instruction class handles those cases.
7
+ #
8
+ # To use, you must at minimum define setting_basename, setting_prefix, and
9
+ # add to the acceptable_types (or write your own validation).
10
+ class ConditionalInstruction < Instruction
11
+ def value(*args)
12
+ coerce(*args)
13
+ end
14
+
15
+ private
16
+
17
+ def setting_basename
18
+ raise NotImplementedError
19
+ end
20
+
21
+ # Special condition for setting; media, crop, etc
22
+ def setting_prefix
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def acceptable_types
27
+ [NilClass]
28
+ end
29
+
30
+ def coerce(arg)
31
+ raise ArgumentError unless valid?
32
+
33
+ value_hash[arg]
34
+ end
35
+
36
+ def source
37
+ {
38
+ hash: PictureTag.preset[setting_prefix + '_' + setting_name],
39
+ default: PictureTag.preset[setting_name]
40
+ }
41
+ end
42
+
43
+ def value_hash
44
+ vals = source[:hash] || {}
45
+ vals.default = source[:default]
46
+
47
+ vals
48
+ end
49
+
50
+ def valid?
51
+ valid_hash? && valid_default?
52
+ end
53
+
54
+ def acceptable_type?(value)
55
+ acceptable_types.any? { |type| value.is_a? type }
56
+ end
57
+
58
+ def valid_hash?
59
+ source[:hash].nil? || source[:hash].values.all? do |v|
60
+ acceptable_type?(v)
61
+ end
62
+ end
63
+
64
+ def valid_default?
65
+ acceptable_type? source[:default]
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,29 @@
1
+ module PictureTag
2
+ module Instructions
3
+ # There are a few config settings which are environment dependent, and can
4
+ # either be booleans, environment names, or arrays of environment names.
5
+ # This class works it out and returns a boolean.
6
+ class EnvInstruction < Instruction
7
+ private
8
+
9
+ def coerce
10
+ get_bool(source)
11
+ end
12
+
13
+ def get_bool(value)
14
+ case value
15
+ when true, false, nil then value
16
+ when String then value == PictureTag.jekyll_env
17
+ when Array then value.include? PictureTag.jekyll_env
18
+ when Hash then get_bool(value[:setting])
19
+ else raise ArgumentError, error_message
20
+ end
21
+ end
22
+
23
+ def error_message
24
+ "JPT - #{setting_name} must be a boolean, an environment name," \
25
+ ' or an array of environment names.'
26
+ end
27
+ end
28
+ end
29
+ end
@@ -76,9 +76,7 @@ module PictureTag
76
76
  image = GeneratedImage.new(
77
77
  source_file: PictureTag.source_images.first,
78
78
  format: PictureTag.fallback_format,
79
- width: checked_fallback_width,
80
- crop: PictureTag.crop,
81
- gravity: PictureTag.gravity
79
+ width: checked_fallback_width
82
80
  )
83
81
 
84
82
  image.generate
@@ -92,9 +90,7 @@ module PictureTag
92
90
  @fallback_candidate ||= GeneratedImage.new(
93
91
  source_file: PictureTag.source_images.first,
94
92
  format: PictureTag.fallback_format,
95
- width: PictureTag.fallback_width,
96
- crop: PictureTag.crop,
97
- gravity: PictureTag.gravity
93
+ width: PictureTag.fallback_width
98
94
  )
99
95
  end
100
96
 
@@ -119,22 +115,14 @@ module PictureTag
119
115
  PictureTag.source_images.first
120
116
  end
121
117
 
122
- def source_width
123
- if PictureTag.crop
124
- fallback_candidate.source_width
125
- else
126
- source.width
127
- end
128
- end
129
-
130
118
  def checked_fallback_width
131
119
  target = PictureTag.fallback_width
132
120
 
133
- if target > source_width
121
+ if target > source.width
134
122
  Utils.warning "#{source.shortname} is smaller than the " \
135
- "requested fallback width of #{target}px. Using #{source_width}" \
123
+ "requested fallback width of #{target}px. Using #{source.width}" \
136
124
  ' px instead.'
137
- source_width
125
+ source.width
138
126
  else
139
127
  target
140
128
  end
@@ -0,0 +1,6 @@
1
+ require_relative 'parsers/arg_splitter'
2
+ require_relative 'parsers/configuration'
3
+ require_relative 'parsers/html_attributes'
4
+ require_relative 'parsers/preset'
5
+ require_relative 'parsers/tag_parser'
6
+ require_relative 'parsers/image_backend'
@@ -1,5 +1,5 @@
1
1
  module PictureTag
2
- module Instructions
2
+ module Parsers
3
3
  # This class takes in the arguments passed to the liquid tag, and splits it
4
4
  # up into 'words' (correctly handling quotes and backslash escapes.)
5
5
  #
@@ -0,0 +1,28 @@
1
+ module PictureTag
2
+ module Parsers
3
+ # Global config (big picture). loads jekyll data/config files, and the j-p-t
4
+ # defaults from included yml files.
5
+ class Configuration
6
+ # returns jekyll's configuration (picture is a subset)
7
+ def [](key)
8
+ content[key]
9
+ end
10
+
11
+ private
12
+
13
+ def content
14
+ @content ||= setting_merge(DEFAULT_CONFIG, PictureTag.site.config)
15
+ end
16
+
17
+ def setting_merge(default, jekyll)
18
+ jekyll.merge default do |_key, config_setting, default_setting|
19
+ if default_setting.respond_to? :merge
20
+ setting_merge(default_setting, config_setting)
21
+ else
22
+ config_setting
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,5 +1,5 @@
1
1
  module PictureTag
2
- module Instructions
2
+ module Parsers
3
3
  # Handles HTML attributes, sourced from configuration and the liquid tag,
4
4
  # sent to various elements.
5
5
  # Stored as a hash, with string keys.
@@ -0,0 +1,33 @@
1
+ module PictureTag
2
+ module Parsers
3
+ # Returns information regarding image handlers
4
+ class ImageBackend
5
+ def handler_for(format)
6
+ if vips_formats.include? format
7
+ :vips
8
+ elsif magick_formats.include? format
9
+ :magick
10
+ else
11
+ raise "No support for generating #{format} files in this environment."
12
+ end
13
+ end
14
+
15
+ # Returns array of formats that vips can save to
16
+ def vips_formats
17
+ @vips_formats ||= `vips -l filesave`
18
+ .scan(/\.[a-z]{1,5}/)
19
+ .uniq
20
+ .map { |format| format.strip.delete_prefix('.') }
21
+ end
22
+
23
+ # Returns an array of formats that imagemagick can handle.
24
+ def magick_formats
25
+ @magick_formats ||= `convert -version`
26
+ .split("\n")
27
+ .last
28
+ .delete_prefix('Delegates (built-in):')
29
+ .split
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ module PictureTag
2
+ module Parsers
3
+ # Handles the specific tag image set to construct.
4
+ class Preset
5
+ attr_reader :name
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+
11
+ def [](key)
12
+ content[key]
13
+ end
14
+
15
+ protected
16
+
17
+ def content
18
+ @content ||= DEFAULT_PRESET.merge settings
19
+ end
20
+
21
+ private
22
+
23
+ def settings
24
+ PictureTag.site.data.dig('picture', 'presets', name) ||
25
+ STOCK_PRESETS[name] ||
26
+ no_preset
27
+ end
28
+
29
+ def no_preset
30
+ unless name == 'default'
31
+ Utils.warning(
32
+ <<~HEREDOC
33
+ Preset "#{name}" not found in #{PictureTag.config['data_dir']}/picture.yml
34
+ under 'presets' key, or in stock presets. Using default values."
35
+ HEREDOC
36
+ )
37
+ end
38
+
39
+ {}
40
+ end
41
+ end
42
+ end
43
+ end