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.
- checksums.yaml +4 -4
- data/.envrc +4 -0
- data/.github/workflows/code-checks.yml +33 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +29 -76
- data/.ruby-version +1 -1
- data/docs/.envrc +2 -0
- data/docs/devs/contributing/code.md +14 -4
- data/docs/devs/contributing/docs.md +24 -6
- data/docs/devs/contributing/setup.md +21 -1
- data/docs/devs/contributing/testing.md +19 -37
- data/docs/devs/releases.md +45 -4
- data/docs/index.md +43 -18
- data/docs/logo.png +0 -0
- data/docs/logo.svg +880 -0
- data/docs/users/configuration/disable.md +1 -1
- data/docs/users/configuration/ignore_missing.md +1 -1
- data/docs/users/configuration/kramdown_fix.md +1 -1
- data/docs/users/configuration/suppress_warnings.md +1 -1
- data/docs/users/configuration/urls.md +69 -0
- data/docs/users/deployment.md +49 -0
- data/docs/users/getting_started.md +55 -0
- data/docs/users/installation.md +18 -38
- data/docs/users/liquid_tag/argument_reference/crop.md +21 -36
- data/docs/users/liquid_tag/examples.md +13 -25
- data/docs/users/liquid_tag/index.md +1 -1
- data/docs/users/notes/{migration.md → migration_1.md} +1 -1
- data/docs/users/notes/migration_2.md +99 -0
- data/docs/users/presets/cropping.md +21 -22
- data/docs/users/presets/default.md +11 -2
- data/docs/users/presets/examples.md +77 -45
- data/docs/users/presets/fallback_image.md +1 -1
- data/docs/users/presets/html_attributes.md +1 -1
- data/docs/users/presets/image_formats.md +3 -3
- data/docs/users/presets/image_quality.md +71 -56
- data/docs/users/presets/index.md +19 -45
- data/docs/users/presets/link_source.md +1 -1
- data/docs/users/presets/media_queries.md +1 -1
- data/docs/users/presets/nomarkdown_override.md +1 -1
- data/docs/users/presets/pixel_ratio_srcsets.md +1 -1
- data/docs/users/presets/width_height_attributes.md +1 -1
- data/docs/users/presets/width_srcsets.md +61 -23
- data/docs/users/presets/writing_presets.md +65 -0
- data/docs/users/tutorial.md +97 -0
- data/jekyll_picture_tag.gemspec +38 -23
- data/lib/jekyll_picture_tag.rb +8 -5
- data/lib/jekyll_picture_tag/cache.rb +64 -3
- data/lib/jekyll_picture_tag/defaults/global.rb +18 -0
- data/lib/jekyll_picture_tag/defaults/presets.rb +57 -0
- data/lib/jekyll_picture_tag/images.rb +1 -0
- data/lib/jekyll_picture_tag/images/generated_image.rb +25 -60
- data/lib/jekyll_picture_tag/images/image_file.rb +105 -0
- data/lib/jekyll_picture_tag/images/img_uri.rb +3 -10
- data/lib/jekyll_picture_tag/images/source_image.rb +44 -9
- data/lib/jekyll_picture_tag/instructions.rb +70 -6
- data/lib/jekyll_picture_tag/instructions/children/config.rb +128 -0
- data/lib/jekyll_picture_tag/instructions/children/context.rb +24 -0
- data/lib/jekyll_picture_tag/instructions/children/params.rb +90 -0
- data/lib/jekyll_picture_tag/instructions/children/parsers.rb +48 -0
- data/lib/jekyll_picture_tag/instructions/children/preset.rb +182 -0
- data/lib/jekyll_picture_tag/instructions/parents/conditional_instruction.rb +69 -0
- data/lib/jekyll_picture_tag/instructions/parents/env_instruction.rb +29 -0
- data/lib/jekyll_picture_tag/output_formats/basic.rb +5 -17
- data/lib/jekyll_picture_tag/parsers.rb +6 -0
- data/lib/jekyll_picture_tag/{instructions → parsers}/arg_splitter.rb +1 -1
- data/lib/jekyll_picture_tag/parsers/configuration.rb +28 -0
- data/lib/jekyll_picture_tag/{instructions → parsers}/html_attributes.rb +1 -1
- data/lib/jekyll_picture_tag/parsers/image_backend.rb +33 -0
- data/lib/jekyll_picture_tag/parsers/preset.rb +43 -0
- data/lib/jekyll_picture_tag/{instructions → parsers}/tag_parser.rb +15 -12
- data/lib/jekyll_picture_tag/router.rb +35 -93
- data/lib/jekyll_picture_tag/srcsets/basic.rb +4 -10
- data/lib/jekyll_picture_tag/utils.rb +10 -20
- data/lib/jekyll_picture_tag/version.rb +1 -1
- data/readme.md +48 -9
- metadata +161 -80
- data/.travis.yml +0 -8
- data/Dockerfile +0 -9
- data/docs/users/configuration/cdn.md +0 -35
- data/docs/users/configuration/relative_urls.md +0 -15
- data/docs/users/notes/input_checking.md +0 -6
- data/jekyll-picture-tag.gemspec +0 -52
- data/lib/jekyll-picture-tag.rb +0 -25
- data/lib/jekyll_picture_tag/cache/base.rb +0 -59
- data/lib/jekyll_picture_tag/cache/generated.rb +0 -20
- data/lib/jekyll_picture_tag/cache/source.rb +0 -19
- data/lib/jekyll_picture_tag/defaults/global.yml +0 -11
- data/lib/jekyll_picture_tag/defaults/presets.yml +0 -11
- data/lib/jekyll_picture_tag/instructions/configuration.rb +0 -121
- data/lib/jekyll_picture_tag/instructions/preset.rb +0 -122
- 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
|
-
|
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
|
-
|
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
|
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
|
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.
|
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}.
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|