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
@@ -5,7 +5,7 @@ module PictureTag
5
5
  class SourceImage
6
6
  attr_reader :shortname, :missing, :media_preset
7
7
 
8
- include MiniMagick
8
+ # include MiniMagick
9
9
 
10
10
  def initialize(relative_filename, media_preset = nil)
11
11
  # /home/dave/my_blog/assets/images/somefolder/myimage.jpg
@@ -21,12 +21,38 @@ module PictureTag
21
21
  @digest ||= cache[:digest] || ''
22
22
  end
23
23
 
24
+ def crop
25
+ PictureTag.crop(media_preset)
26
+ end
27
+
28
+ def crop?
29
+ !crop.nil?
30
+ end
31
+
32
+ def keep
33
+ PictureTag.keep(media_preset)
34
+ end
35
+
36
+ def dimensions
37
+ [width, height]
38
+ end
39
+
24
40
  def width
25
- @width ||= cache[:width] || 999_999
41
+ return raw_width unless crop?
42
+
43
+ [raw_width, (raw_height * cropped_aspect)].min.round
26
44
  end
27
45
 
28
46
  def height
29
- @height ||= cache[:height] || 999_999
47
+ return raw_height unless crop?
48
+
49
+ [raw_height, (raw_width / cropped_aspect)].min.round
50
+ end
51
+
52
+ def cropped_aspect
53
+ return Utils.aspect_float(raw_width, raw_height) unless crop?
54
+
55
+ Utils.aspect_float(*crop.split(':').map(&:to_f))
30
56
  end
31
57
 
32
58
  # /home/dave/my_blog/assets/images/somefolder/myimage.jpg
@@ -44,13 +70,23 @@ module PictureTag
44
70
  # /home/dave/my_blog/assets/images/somefolder/myimage.jpg
45
71
  # ^^^
46
72
  def ext
47
- @ext ||= File.extname(name)[1..-1].downcase
73
+ @ext ||= File.extname(name)[1..].downcase
48
74
  end
49
75
 
50
76
  private
51
77
 
78
+ # pre-crop
79
+ def raw_width
80
+ @raw_width ||= @missing ? 999_999 : image.width
81
+ end
82
+
83
+ # pre-crop
84
+ def raw_height
85
+ @raw_height ||= @missing ? 999_999 : image.height
86
+ end
87
+
52
88
  def cache
53
- @cache ||= Cache::Source.new(@shortname)
89
+ @cache ||= Cache.new(@shortname)
54
90
  end
55
91
 
56
92
  def missing?
@@ -75,14 +111,12 @@ module PictureTag
75
111
 
76
112
  def update_cache
77
113
  cache[:digest] = source_digest
78
- cache[:width] = image.width
79
- cache[:height] = image.height
80
114
 
81
115
  cache.write
82
116
  end
83
117
 
84
118
  def image
85
- @image ||= Image.open(name)
119
+ @image ||= Vips::Image.new_from_file(name)
86
120
  end
87
121
 
88
122
  def source_digest
@@ -90,7 +124,8 @@ module PictureTag
90
124
  end
91
125
 
92
126
  def missing_image_warning
93
- "JPT Could not find #{name}. Your site will have broken images. Continuing."
127
+ "JPT Could not find #{name}. " \
128
+ 'Your site will have broken images. Continuing.'
94
129
  end
95
130
 
96
131
  def missing_image_error
@@ -1,6 +1,70 @@
1
- require_relative './instructions/set'
2
- require_relative './instructions/configuration'
3
- require_relative './instructions/html_attributes'
4
- require_relative './instructions/preset'
5
- require_relative './instructions/tag_parser'
6
- require_relative './instructions/arg_splitter'
1
+ module PictureTag
2
+ # Instructions obtain, validate, and typecast/coerce input values. These
3
+ # inputs are either taken directly from jekyll's inputs, or handled by parsers
4
+ # first.
5
+ #
6
+ # Logic which affects only a single setting belongs in Instructions, while
7
+ # logic which affects multiple settings belongs in Parsers.
8
+ #
9
+ # Since instruction classes are so small, we define several per file in the
10
+ # instructions directory to save on boilerplate. All fall under the
11
+ # Instructions module namespace.
12
+ module Instructions
13
+ # Generic instruction, meant to be inherited. Children of this class must
14
+ # override the source method, and likely want to override valid?, coerce,
15
+ # and error_message as applicable.
16
+ class Instruction
17
+ # Memoized value of the given instruction. This is the public API.
18
+ def value
19
+ return @value if defined?(@value)
20
+
21
+ raise ArgumentError, error_message unless valid?
22
+
23
+ @value = coerced
24
+ end
25
+
26
+ private
27
+
28
+ # Source(s) of truth - where does this setting come from? Logic does not
29
+ # belong here. If information comes from muliple places, return an array
30
+ # or a hash.
31
+ def source
32
+ raise NotImplementedError
33
+ end
34
+
35
+ # Determine whether or not the input(s) are valid.
36
+ def valid?
37
+ true
38
+ end
39
+
40
+ # Convert input(s) to output.
41
+ def coerce
42
+ source
43
+ end
44
+
45
+ # Message returned if validation fails. Override this with something more
46
+ # helpful.
47
+ def error_message
48
+ "JPT - #{setting_name} received an invalid argument: #{source}"
49
+ end
50
+
51
+ def coerced
52
+ return @coerced if defined?(@coerced)
53
+
54
+ @coerced = coerce
55
+ end
56
+
57
+ def setting_name
58
+ Utils.snakeize(self.class.to_s.split('::').last)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # Load Parents
65
+ Dir[File.dirname(__FILE__) + '/instructions/parents/*.rb']
66
+ .sort.each { |file| require file }
67
+
68
+ # Load children:
69
+ Dir[File.dirname(__FILE__) + '/instructions/children/*.rb']
70
+ .sort.each { |file| require file }
@@ -0,0 +1,128 @@
1
+ module PictureTag
2
+ module Instructions
3
+ # PictureTag configuration in _config.yml
4
+ class Pconfig < Instruction
5
+ def source
6
+ PictureTag.config['picture']
7
+ end
8
+ end
9
+
10
+ # https://example.com/my-base-path/assets/generated-images/image.jpg
11
+ # ^^^^^^^^^^^^^
12
+ # | domain | baseurl | directory | filename
13
+ class Baseurl < Instruction
14
+ def source
15
+ {
16
+ ignore: PictureTag.pconfig['ignore_baseurl'],
17
+ key: PictureTag.pconfig['baseurl_key']
18
+ }
19
+ end
20
+
21
+ def coerce
22
+ return '' if source[:ignore]
23
+
24
+ PictureTag.config[source[:key]] || ''
25
+ end
26
+ end
27
+
28
+ # Whether to use relative or absolute URLs for images.
29
+ class RelativeUrl < EnvInstruction
30
+ def source
31
+ PictureTag.pconfig['relative_url']
32
+ end
33
+ end
34
+
35
+ # Image source directory
36
+ class SourceDir < Instruction
37
+ private
38
+
39
+ def source
40
+ [
41
+ PictureTag.site.source,
42
+ PictureTag.pconfig['source']
43
+ ]
44
+ end
45
+
46
+ def coerce
47
+ File.join(*source.map(&:to_s))
48
+ end
49
+
50
+ def setting_name
51
+ 'source directory'
52
+ end
53
+ end
54
+
55
+ # Image output directory
56
+ class DestDir < Instruction
57
+ private
58
+
59
+ def source
60
+ [
61
+ PictureTag.site.config['destination'],
62
+ PictureTag.pconfig['output']
63
+ ]
64
+ end
65
+
66
+ def coerce
67
+ File.join(*source.map(&:to_s))
68
+ end
69
+ end
70
+
71
+ # Whether to continue if a source image is missing
72
+ class ContinueOnMissing < EnvInstruction
73
+ def source
74
+ PictureTag.pconfig['ignore_missing_images']
75
+ end
76
+ end
77
+
78
+ # Whether to use a CDN
79
+ class Cdn < EnvInstruction
80
+ def source
81
+ {
82
+ url: PictureTag.pconfig['cdn_url'],
83
+ setting: PictureTag.pconfig['cdn_environments']
84
+ }
85
+ end
86
+
87
+ def coerce
88
+ source[:url] && super
89
+ end
90
+ end
91
+
92
+ # CDN URL
93
+ class CdnUrl < Instruction
94
+ def source
95
+ PictureTag.pconfig['cdn_url']
96
+ end
97
+
98
+ def valid?
99
+ require 'uri'
100
+ uri = URI(source)
101
+
102
+ # If the URI library can't parse it, it's not valid.
103
+ uri.scheme && uri.host
104
+ end
105
+
106
+ def error_message
107
+ <<~HEREDOC
108
+ cdn_url must be a valid URI in the following format: https://example.com/
109
+ current setting: #{source}
110
+ HEREDOC
111
+ end
112
+ end
113
+
114
+ # Disable JPT?
115
+ class Disabled < EnvInstruction
116
+ def source
117
+ PictureTag.pconfig['disabled']
118
+ end
119
+ end
120
+
121
+ # Fast build?
122
+ class FastBuild < EnvInstruction
123
+ def source
124
+ PictureTag.pconfig['fast_build']
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,24 @@
1
+ module PictureTag
2
+ module Instructions
3
+ # Jekyll site info
4
+ class Site < Instruction
5
+ def source
6
+ PictureTag.context.registers[:site]
7
+ end
8
+ end
9
+
10
+ # Current page in jekyll site
11
+ class Page < Instruction
12
+ def source
13
+ PictureTag.context.registers[:page]
14
+ end
15
+ end
16
+
17
+ # Digs into jekyll context, returns current environment
18
+ class JekyllEnv < Instruction
19
+ def source
20
+ PictureTag.context.environments.first['jekyll']['environment']
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,90 @@
1
+ module PictureTag
2
+ module Instructions
3
+ # Builds instances of all source images.
4
+ class SourceImages < Instruction
5
+ def source
6
+ {
7
+ source_names: PictureTag.params.source_names,
8
+ media_presets: PictureTag.params.media_presets
9
+ }
10
+ end
11
+
12
+ def coerce
13
+ sources = [PictureTag::SourceImage.new(source[:source_names].shift)]
14
+
15
+ while source[:source_names].any?
16
+ sources << PictureTag::SourceImage.new(
17
+ source[:source_names].shift, source[:media_presets].shift
18
+ )
19
+ end
20
+
21
+ sources
22
+ end
23
+ end
24
+
25
+ # Which crop to use for a given media query. Can be given either in params
26
+ # or preset.
27
+ class Crop < ConditionalInstruction
28
+ def source
29
+ super.merge(
30
+ { params: PictureTag.params.crop }
31
+ )
32
+ end
33
+
34
+ def coerce(media = nil)
35
+ raise ArgumentError unless valid?
36
+
37
+ source[:params][media] || value_hash[media]
38
+ end
39
+
40
+ def setting_basename
41
+ 'crop'
42
+ end
43
+
44
+ def setting_prefix
45
+ 'media'
46
+ end
47
+
48
+ def acceptable_types
49
+ super + [String]
50
+ end
51
+ end
52
+
53
+ # Which vips interestingness setting to use for a given media query. Can be
54
+ # given either in params or preset.
55
+ class Keep < ConditionalInstruction
56
+ def source
57
+ super.merge(
58
+ { params: PictureTag.params.keep }
59
+ )
60
+ end
61
+
62
+ def coerce(media = nil)
63
+ raise ArgumentError unless valid?
64
+
65
+ lookup[source[:params][media] || super(media)]
66
+ end
67
+
68
+ def lookup
69
+ {
70
+ 'center' => :centre,
71
+ 'centre' => :centre,
72
+ 'attention' => :attention,
73
+ 'entropy' => :entropy
74
+ }
75
+ end
76
+
77
+ def setting_basename
78
+ 'keep'
79
+ end
80
+
81
+ def setting_prefix
82
+ 'media'
83
+ end
84
+
85
+ def acceptable_types
86
+ super + [String]
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,48 @@
1
+ module PictureTag
2
+ module Instructions
3
+ # PictureTag configuration
4
+ class Config < Instruction
5
+ def source
6
+ PictureTag::Parsers::Configuration.new
7
+ end
8
+ end
9
+
10
+ # Tag parameters
11
+ class Params < Instruction
12
+ def source
13
+ PictureTag::Parsers::TagParser.new PictureTag.raw_params
14
+ end
15
+ end
16
+
17
+ # Currently selected preset
18
+ class Preset < Instruction
19
+ def source
20
+ PictureTag::Parsers::Preset.new PictureTag.params.preset_name
21
+ end
22
+ end
23
+
24
+ # Handles non-image arguments to liquid tag and preset.
25
+ class HtmlAttributes < Instruction
26
+ def source
27
+ PictureTag::Parsers::HTMLAttributeSet.new PictureTag.params.leftovers
28
+ end
29
+ end
30
+
31
+ # TODO: rename to MediaQueries
32
+ # Returns user-defined media queries.
33
+ class MediaPresets < Instruction
34
+ def source
35
+ STOCK_MEDIA_QUERIES.merge(
36
+ PictureTag.site.data.dig('picture', 'media_queries') || {}
37
+ )
38
+ end
39
+ end
40
+
41
+ # Main job is to determine which backend should handle which image formats.
42
+ class Backend < Instruction
43
+ def source
44
+ PictureTag::Parsers::ImageBackend.new
45
+ end
46
+ end
47
+ end
48
+ end