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
@@ -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