jekyll_picture_tag 1.13.0 → 2.0.2

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