diff --git a/lib/jekyll-seo-tag.rb b/lib/jekyll-seo-tag.rb index 9f9b7e2..6d3e4b8 100644 --- a/lib/jekyll-seo-tag.rb +++ b/lib/jekyll-seo-tag.rb @@ -3,6 +3,7 @@ require "jekyll-seo-tag/version" module Jekyll class SeoTag < Liquid::Tag + autoload :JSONLD, "jekyll-seo-tag/json_ld" autoload :Drop, "jekyll-seo-tag/drop" autoload :Filters, "jekyll-seo-tag/filters" diff --git a/lib/jekyll-seo-tag/drop.rb b/lib/jekyll-seo-tag/drop.rb index 34cec8d..8e07d71 100644 --- a/lib/jekyll-seo-tag/drop.rb +++ b/lib/jekyll-seo-tag/drop.rb @@ -1,6 +1,8 @@ module Jekyll class SeoTag class Drop < Jekyll::Drops::Drop + include Jekyll::SeoTag::JSONLD + TITLE_SEPARATOR = " | ".freeze FORMAT_STRING_METHODS = %i[ markdownify strip_html normalize_whitespace escape_once @@ -91,10 +93,12 @@ module Jekyll def date_modified @date_modified ||= begin - if page["seo"] && page["seo"]["date_modified"] - return page["seo"]["date_modified"] - end - page["last_modified_at"] || page["date"] + date = if page["seo"] && page["seo"]["date_modified"] + page["seo"]["date_modified"] + else + page["last_modified_at"] || page["date"] + end + filters.date_to_xmlschema(date) if date end end @@ -164,6 +168,10 @@ module Jekyll @page_lang ||= page["lang"] || site["lang"] || "en_US" end + def canonical_url + @canonical_url ||= filters.absolute_url(page["url"]).gsub(%r!/index\.html$!, "/") + end + private def filters diff --git a/lib/jekyll-seo-tag/json_ld.rb b/lib/jekyll-seo-tag/json_ld.rb new file mode 100644 index 0000000..f4eb9fd --- /dev/null +++ b/lib/jekyll-seo-tag/json_ld.rb @@ -0,0 +1,76 @@ +module Jekyll + class SeoTag + module JSONLD + + # A hash of instance methods => key in resulting JSON-LD hash + METHODS_KEYS = { + :json_context => "@context", + :type => "@type", + :name => "name", + :page_title => "headline", + :json_author => "author", + :image_path => "image", + :date_modified => "dateModified", + :description => "description", + :publisher => "publisher", + :main_entity => "mainEntityOfPage", + :links => "sameAs", + :canonical_url => "url", + }.freeze + + def json_ld + @json_ld ||= begin + output = {} + METHODS_KEYS.each do |method, key| + value = send(method) + output[key] = value unless value.nil? + end + output + end + end + + private + + def json_context + "http://schema.org" + end + + def json_author + return unless author + { + "@type" => "Person", + "name" => author["name"], + } + end + + def image_path + image["path"] if image + end + + def date + filters.date_to_xmlschema(page["date"]) if page["date"] + end + + def publisher + return unless logo + output = { + "@type" => "Organization", + "logo" => { + "@type" => "ImageObject", + "url" => logo, + }, + } + output["name"] = author["name"] if author + output + end + + def main_entity + return unless %w(BlogPosting CreativeWork).include?(type) + { + "@type" => "WebPage", + "@id" => canonical_url, + } + end + end + end +end diff --git a/lib/template.html b/lib/template.html index d05ce92..8a6a7aa 100755 --- a/lib/template.html +++ b/lib/template.html @@ -19,8 +19,8 @@ {% endif %} {% if site.url %} - - + + {% endif %} {% if seo_tag.site_title %} @@ -99,70 +99,7 @@ diff --git a/spec/jekyll_seo_tag/drop_spec.rb b/spec/jekyll_seo_tag/drop_spec.rb index 34ef79c..2c8008b 100644 --- a/spec/jekyll_seo_tag/drop_spec.rb +++ b/spec/jekyll_seo_tag/drop_spec.rb @@ -244,26 +244,26 @@ RSpec.describe Jekyll::SeoTag::Drop do context "date modified" do context "with seo.date_modified" do - let(:page_meta) { { "seo" => { "date_modified" => "tuesday" } } } + let(:page_meta) { { "seo" => { "date_modified" => "2017-01-01" } } } it "uses seo.date_modified" do - expect(subject.date_modified).to eql("tuesday") + expect(subject.date_modified).to eql("2017-01-01T00:00:00-05:00") end end context "with page.last_modified_at" do - let(:page_meta) { { "last_modified_at" => "tuesday" } } + let(:page_meta) { { "last_modified_at" => "2017-01-01" } } it "uses page.last_modified_at" do - expect(subject.date_modified).to eql("tuesday") + expect(subject.date_modified).to eql("2017-01-01T00:00:00-05:00") end end context "date" do - let(:page_meta) { { "date" => "tuesday" } } + let(:page_meta) { { "date" => "2017-01-01" } } it "uses page.date" do - expect(subject.date_modified).to eql("tuesday") + expect(subject.date_modified).to eql("2017-01-01T00:00:00-05:00") end end end diff --git a/spec/jekyll_seo_tag/json_ld_spec.rb b/spec/jekyll_seo_tag/json_ld_spec.rb new file mode 100644 index 0000000..68e043f --- /dev/null +++ b/spec/jekyll_seo_tag/json_ld_spec.rb @@ -0,0 +1,106 @@ +RSpec.describe Jekyll::SeoTag::JSONLD do + let(:metadata) do + { + "title" => "title", + "author" => "author", + "image" => "image", + "date" => "2017-01-01", + "description" => "description", + "seo" => { + "name" => "seo name", + "date_modified" => "2017-01-01", + "links" => %w(a b), + }, + } + end + let(:config) do + { + "logo" => "logo", + } + end + let(:page) { make_page(metadata) } + let(:site) { make_site(config) } + let(:context) { make_context(:page => page, :site => site) } + subject { Jekyll::SeoTag::Drop.new("", context).json_ld } + + it "returns the context" do + expect(subject).to have_key("@context") + expect(subject["@context"]).to eql("http://schema.org") + end + + it "returns the type" do + expect(subject).to have_key("@type") + expect(subject["@type"]).to eql("BlogPosting") + end + + it "returns the name" do + expect(subject).to have_key("name") + expect(subject["name"]).to eql("seo name") + end + + it "returns the headline" do + expect(subject).to have_key("headline") + expect(subject["headline"]).to eql("title") + end + + it "returns the author" do + expect(subject).to have_key("author") + expect(subject["author"]).to be_a(Hash) + expect(subject["author"]).to have_key("@type") + expect(subject["author"]["@type"]).to eql("Person") + expect(subject["author"]).to have_key("name") + expect(subject["author"]["name"]).to eql("author") + end + + it "returns the image" do + expect(subject).to have_key("image") + expect(subject["image"]).to eql("/image") + end + + it "returns the dateModified" do + expect(subject).to have_key("dateModified") + expect(subject["dateModified"]).to eql("2017-01-01T00:00:00-05:00") + end + + it "returns the description" do + expect(subject).to have_key("description") + expect(subject["description"]).to eql("description") + end + + it "returns the publisher" do + expect(subject).to have_key("publisher") + + publisher = subject["publisher"] + expect(publisher).to be_a(Hash) + + expect(publisher).to have_key("@type") + expect(publisher["@type"]).to eql("Organization") + expect(publisher).to have_key("logo") + + logo = publisher["logo"] + expect(logo).to have_key("@type") + expect(logo["@type"]).to eql("ImageObject") + expect(logo).to have_key("url") + expect(logo["url"]).to eql("/logo") + end + + it "returns the main entity of page" do + expect(subject).to have_key("mainEntityOfPage") + expect(subject["mainEntityOfPage"]).to be_a(Hash) + expect(subject["mainEntityOfPage"]).to have_key("@type") + expect(subject["mainEntityOfPage"]["@type"]).to eql("WebPage") + expect(subject["mainEntityOfPage"]).to have_key("@id") + expect(subject["mainEntityOfPage"]["@id"]).to eql("/page.html") + end + + it "returns sameAs" do + expect(subject).to have_key("sameAs") + expect(subject["sameAs"]).to be_a(Array) + expect(subject["sameAs"]).to eql(%w(a b)) + end + + it "returns the url" do + expect(subject).to have_key("url") + expect(subject["url"]).to eql("/page.html") + end +end diff --git a/spec/jekyll_seo_tag_spec.rb b/spec/jekyll_seo_tag_spec.rb index 9f5d052..bcc5d76 100755 --- a/spec/jekyll_seo_tag_spec.rb +++ b/spec/jekyll_seo_tag_spec.rb @@ -331,10 +331,8 @@ EOS end it "minifies JSON-LD" do - expected = <<-EOS -{"@context": "http://schema.org", -"@type": "BlogPosting", -"headline": "post", + expected = <<-EOS.strip +{"@context":"http://schema.org","@type":"BlogPosting","headline":"post", EOS expect(output).to match(expected) end