break image logic into its own drop

This commit is contained in:
Ben Balter 2017-08-23 12:11:30 -04:00
parent 2e90fc7238
commit 1081a0ebd9
No known key found for this signature in database
GPG Key ID: DBB67C246AD356C4
8 changed files with 187 additions and 112 deletions

View File

@ -5,6 +5,8 @@ module Jekyll
class SeoTag < Liquid::Tag
autoload :JSONLD, "jekyll-seo-tag/json_ld"
autoload :AuthorDrop, "jekyll-seo-tag/author_drop"
autoload :ImageDrop, "jekyll-seo-tag/image_drop"
autoload :UrlHelper, "jekyll-seo-tag/url_helper"
autoload :Drop, "jekyll-seo-tag/drop"
autoload :Filters, "jekyll-seo-tag/filters"

View File

@ -2,6 +2,7 @@ module Jekyll
class SeoTag
class Drop < Jekyll::Drops::Drop
include Jekyll::SeoTag::JSONLD
include Jekyll::SeoTag::UrlHelper
TITLE_SEPARATOR = " | ".freeze
FORMAT_STRING_METHODS = %i[
@ -129,33 +130,8 @@ module Jekyll
end
end
# Returns nil or a hash representing the page image
# The image hash will always contain a path, pulled from:
#
# 1. The `image` key if it's a string
# 2. The `image.path` key if it's a hash
# 3. The `image.facebook` key
# 4. The `image.twitter` key
#
# The resulting path is always an absolute URL
def image
return @image if defined?(@image)
image = page["image"]
return @image = nil unless image
image = { "path" => image } if image.is_a?(String)
image["path"] ||= image["facebook"] || image["twitter"]
return @image = nil unless image["path"]
# absolute_url? will return nil for an invalid URL
if absolute_url?(image["path"]) == false
image["path"] = filters.absolute_url image["path"]
end
image["path"] = filters.uri_escape image["path"]
@image = image.to_liquid
@image ||= ImageDrop.new(:page => page, :context => @context)
end
def page_lang
@ -196,13 +172,6 @@ module Jekyll
@fallback_data ||= {}
end
def absolute_url?(string)
return unless string
Addressable::URI.parse(string).absolute?
rescue Addressable::URI::InvalidURIError
nil
end
def format_string(string)
string = FORMAT_STRING_METHODS.reduce(string) do |memo, method|
filters.public_send(method, memo)

View File

@ -0,0 +1,67 @@
module Jekyll
class SeoTag
# Returns nil or a hash representing the page image
# The image hash will always contain a path, pulled from:
#
# 1. The `image` key if it's a string
# 2. The `image.path` key if it's a hash
# 3. The `image.facebook` key
# 4. The `image.twitter` key
#
# The resulting path is always an absolute URL
class ImageDrop < Jekyll::Drops::Drop
include Jekyll::SeoTag::UrlHelper
# Initialize a new ImageDrop
#
# page - The page hash (e.g., Page#to_liquid)
def initialize(page: nil, context: nil)
raise ArgumentError unless page && context
@mutations = {}
@page = page
@context = context
end
def path
@path ||= filters.uri_escape(absolute_url) if absolute_url
end
alias_method :to_s, :path
private
attr_accessor :page
attr_accessor :context
def image_hash
@image_hash ||= if page["image"].is_a?(Hash)
page["image"]
elsif page["image"].is_a?(String)
{ "path" => page["image"] }
else
{ "path" => nil }
end
end
alias_method :fallback_data, :image_hash
def raw_path
@raw_path ||= begin
image_hash["path"] || image_hash["facebook"] || image_hash["twitter"]
end
end
def absolute_url
return unless raw_path
return @absolute_url if defined? @absolute_url
@absolute_url = if raw_path.is_a?(String) && absolute_url?(raw_path) == false
filters.absolute_url raw_path
else
raw_path
end
end
def filters
@filters ||= Jekyll::SeoTag::Filters.new(context)
end
end
end
end

View File

@ -46,9 +46,9 @@ module Jekyll
def json_image
return unless image
return image["path"] if image.length == 1
return image["path"] if image.keys.length == 1
hash = image.dup
hash = image.to_h
hash["url"] = hash.delete("path")
hash["@type"] = "imageObject"
hash

View File

@ -0,0 +1,14 @@
module Jekyll
class SeoTag
module UrlHelper
private
def absolute_url?(string)
return unless string
Addressable::URI.parse(string).absolute?
rescue Addressable::URI::InvalidURIError
nil
end
end
end
end

View File

@ -27,7 +27,7 @@
<meta property="og:site_name" content="{{ seo_tag.site_title }}" />
{% endif %}
{% if seo_tag.image %}
{% if seo_tag.image.path %}
<meta property="og:image" content="{{ seo_tag.image.path }}" />
{% if seo_tag.image.height %}
<meta property="og:image:height" content="{{ seo_tag.image.height }}" />
@ -50,7 +50,7 @@
{% endif %}
{% if site.twitter %}
{% if seo_tag.image %}
{% if seo_tag.image.path %}
<meta name="twitter:card" content="summary_large_image" />
{% else %}
<meta name="twitter:card" content="summary" />

View File

@ -402,85 +402,15 @@ RSpec.describe Jekyll::SeoTag::Drop do
end
context "image" do
let(:image) { "foo.png" }
let(:page_meta) { { "image" => image } }
context "with image as a string" do
let(:image) { "image.png" }
it "returns a hash" do
expect(subject.image).to be_a(Hash)
end
it "returns the image" do
expect(subject.image["path"]).to eql("/image.png")
end
context "with site.url" do
let(:config) { { "url" => "http://example.com" } }
it "makes the path absolute" do
expect(subject.image["path"]).to eql("http://example.com/image.png")
end
end
context "with a URL-escaped path" do
let(:image) { "some image.png" }
it "URL-escapes the image" do
expect(subject.image["path"]).to eql("/some%20image.png")
end
end
it "returns a Drop" do
expect(subject.image).to be_a(Jekyll::SeoTag::ImageDrop)
end
context "with image as a hash" do
context "with a path" do
let(:image) { { "path" => "image.png" } }
it "returns the image" do
expect(subject.image["path"]).to eql("/image.png")
end
end
context "with facebook" do
let(:image) { { "facebook" => "image.png" } }
it "returns the image" do
expect(subject.image["path"]).to eql("/image.png")
end
end
context "with twitter" do
let(:image) { { "twitter" => "image.png" } }
it "returns the image" do
expect(subject.image["path"]).to eql("/image.png")
end
end
context "with some random hash" do
let(:image) { { "foo" => "bar" } }
it "returns nil" do
expect(subject.image).to be_nil
end
end
context "with an invalid path" do
let(:image) { ":" }
it "returns nil" do
expect(subject.image["path"]).to eql(":")
end
end
context "with height and width" do
let(:image) { { "path" => "image.png", "height" => 5, "width" => 10 } }
it "returns the height and width" do
expect(subject.image["height"]).to eql(5)
expect(subject.image["width"]).to eql(10)
end
end
it "returns the image" do
expect(subject.image["path"]).to eql("/foo.png")
end
end

View File

@ -0,0 +1,93 @@
RSpec.describe Jekyll::SeoTag::ImageDrop do
let(:config) { { "title" => "site title" } }
let(:image) { nil }
let(:page_meta) { { "image" => image } }
let(:page) { make_page(page_meta) }
let(:site) { make_site(config) }
let(:context) { make_context(:page => page, :site => site) }
let(:text) { "" }
subject { described_class.new(:page => page.to_liquid, :context => context) }
before do
Jekyll.logger.log_level = :error
end
context "with image as a string" do
let(:image) { "image.png" }
it "returns the image" do
expect(subject["path"]).to eql("/image.png")
end
context "with site.url" do
let(:config) { { "url" => "http://example.com" } }
it "makes the path absolute" do
expect(subject["path"]).to eql("http://example.com/image.png")
end
end
context "with a URL-escaped path" do
let(:image) { "some image.png" }
it "URL-escapes the image" do
expect(subject["path"]).to eql("/some%20image.png")
end
end
end
context "with image as a hash" do
context "with a path" do
let(:image) { { "path" => "image.png" } }
it "returns the image" do
expect(subject["path"]).to eql("/image.png")
end
end
context "with facebook" do
let(:image) { { "facebook" => "image.png" } }
it "returns the image" do
expect(subject["path"]).to eql("/image.png")
end
end
context "with twitter" do
let(:image) { { "twitter" => "image.png" } }
it "returns the image" do
expect(subject["path"]).to eql("/image.png")
end
end
context "with some random hash" do
let(:image) { { "foo" => "bar" } }
it "returns nil" do
expect(subject["path"]).to be_nil
end
it "returns arbitrary values" do
expect(subject["foo"]).to eql("bar")
end
end
context "with an invalid path" do
let(:image) { ":" }
it "returns the path" do
expect(subject["path"]).to eql(":")
end
end
context "with height and width" do
let(:image) { { "path" => "image.png", "height" => 5, "width" => 10 } }
it "returns the height and width" do
expect(subject["height"]).to eql(5)
expect(subject["width"]).to eql(10)
end
end
end
end