From 891d6218990e453384d27aea428617f9f441ea0f Mon Sep 17 00:00:00 2001 From: Ben Balter Date: Wed, 23 Aug 2017 11:07:52 -0400 Subject: [PATCH] move author logic to its own drop --- Gemfile | 2 - lib/jekyll-seo-tag.rb | 3 +- lib/jekyll-seo-tag/author_drop.rb | 88 +++++++++++++++++++++++++++++++ lib/jekyll-seo-tag/drop.rb | 53 +------------------ spec/jekyll_seo_tag/drop_spec.rb | 18 +++++-- 5 files changed, 106 insertions(+), 58 deletions(-) create mode 100644 lib/jekyll-seo-tag/author_drop.rb diff --git a/Gemfile b/Gemfile index ab00218..2ee12d7 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,4 @@ source "https://rubygems.org" -require "json" -require "open-uri" gemspec diff --git a/lib/jekyll-seo-tag.rb b/lib/jekyll-seo-tag.rb index 6d3e4b8..44ec08b 100644 --- a/lib/jekyll-seo-tag.rb +++ b/lib/jekyll-seo-tag.rb @@ -3,7 +3,8 @@ require "jekyll-seo-tag/version" module Jekyll class SeoTag < Liquid::Tag - autoload :JSONLD, "jekyll-seo-tag/json_ld" + autoload :JSONLD, "jekyll-seo-tag/json_ld" + autoload :AuthorDrop, "jekyll-seo-tag/author_drop" autoload :Drop, "jekyll-seo-tag/drop" autoload :Filters, "jekyll-seo-tag/filters" diff --git a/lib/jekyll-seo-tag/author_drop.rb b/lib/jekyll-seo-tag/author_drop.rb new file mode 100644 index 0000000..7827307 --- /dev/null +++ b/lib/jekyll-seo-tag/author_drop.rb @@ -0,0 +1,88 @@ +module Jekyll + class SeoTag + class AuthorDrop < Jekyll::Drops::Drop + # A drop representing the current page's author + # + # Author name will be pulled from: + # + # 1. The page's `author` key + # 2. The first author in the page's `authors` key + # 3. The `author` key in the site config + # + # If the result from the name search is a string, we'll also check + # for additional author metadata in `site.data.authors` + def initialize(page: nil, site: nil) + raise ArugementError unless page && site + @mutations = {} + @page = page + @site = site + end + + # Public methods to delegate to keys of the author hash + # Ensures keys will be present when `to_h` is called, even with nil values + DELEGATED_METHODS = %i[name picture].freeze + + DELEGATED_METHODS.each do |meth| + define_method meth do + author_hash[meth.to_s] + end + end + + # AuthorDrop#to_s should return name, allowing the author drop to safely + # replace `page.author`, if necessary, and remain backwards compatible + alias_method :to_s, :name + + def twitter + return @twitter if defined? @twitter + twitter = author_hash["twitter"] || author_hash["name"] + @twitter = twitter.is_a?(String) ? twitter.sub(%r!^@!, "") : nil + end + + private + + attr_reader :page + attr_reader :site + + # Finds the page author in the page.author, page.authors, or site.author + # + # Returns a string or hash representing the author + def resolved_author + return @resolved_author if defined? @resolved_author + sources = [page["author"]] + sources << page["authors"].first if page["authors"].is_a?(Array) + sources << site["author"] + @resolved_author = sources.find { |s| !s.to_s.empty? } + end + + # If resolved_author is a string, attempts to find coresponding author + # metadata in `site.data.authors` + # + # Returns a hash representing additional metadata or an empty hash + def site_data_hash + @site_data_hash ||= begin + return {} unless resolved_author.is_a?(String) + return {} unless site.data["authors"].is_a?(Hash) + author_hash = site.data["authors"][resolved_author] + author_hash.is_a?(Hash) ? author_hash : {} + end + end + + # Returns the normalized author hash representing the page author, + # including site-wide metadata if the author is provided as a string, + # or an empty hash, if the author cannot be resolved + def author_hash + if resolved_author.is_a? Hash + resolved_author + elsif resolved_author.is_a? String + { "name" => resolved_author }.merge(site_data_hash) + else + {} + end + end + + # Since author_hash is aliased to fallback_data, any values in the hash + # will be exposed via the drop, allowing support for arbitrary metadata + alias_method :fallback_data, :author_hash + end + end +end diff --git a/lib/jekyll-seo-tag/drop.rb b/lib/jekyll-seo-tag/drop.rb index 1973c65..d6a1649 100644 --- a/lib/jekyll-seo-tag/drop.rb +++ b/lib/jekyll-seo-tag/drop.rb @@ -72,29 +72,9 @@ module Jekyll end end - # Returns a nil or a hash representing the author - # Author name will be pulled from: - # - # 1. The `author` key, if the key is a string - # 2. The first author in the `authors` key - # 3. The `author` key in the site config - # - # If the result from the name search is a string, we'll also check - # to see if the author exists in `site.data.authors` + # A drop representing the page author def author - @author ||= begin - return if author_string_or_hash.to_s.empty? - - author = if author_string_or_hash.is_a?(String) - author_hash(author_string_or_hash) - else - author_string_or_hash - end - - author["twitter"] ||= author["name"] - author["twitter"].delete! "@" if author["twitter"] - author.to_liquid - end + @author ||= AuthorDrop.new(:page => page, :site => site) end def date_modified @@ -231,35 +211,6 @@ module Jekyll string unless string.empty? end - def author_string_or_hash - @author_string_or_hash ||= begin - author = page["author"] - author = page["authors"][0] if author.to_s.empty? && page["authors"] - author = site["author"] if author.to_s.empty? - author - end - end - - # Given a string representing the current document's author, return - # a normalized hash representing that author. Will try to pull from - # site.authors if present and in the proper format. - def author_hash(author_string) - site_author_hash(author_string) || { "name" => author_string } - end - - # Given a string representing the current document's author, attempt - # to retrieve additional metadata from site.data.authors, if present - # - # Returns the author hash - def site_author_hash(author_string) - return unless site.data["authors"] && site.data["authors"].is_a?(Hash) - author_hash = site.data["authors"][author_string] - return unless author_hash.is_a?(Hash) - author_hash["name"] ||= author_string - author_hash["twitter"] ||= author_string - author_hash - end - def seo_name @seo_name ||= format_string(page_seo["name"]) if page_seo["name"] end diff --git a/spec/jekyll_seo_tag/drop_spec.rb b/spec/jekyll_seo_tag/drop_spec.rb index fe54161..27fd406 100644 --- a/spec/jekyll_seo_tag/drop_spec.rb +++ b/spec/jekyll_seo_tag/drop_spec.rb @@ -223,6 +223,16 @@ RSpec.describe Jekyll::SeoTag::Drop do end context "author" do + let(:name) { "foo" } + let(:twitter) { "foo" } + let(:picture) { nil } + let(:expected_hash) do + { + "name" => name, + "twitter" => twitter, + "picture" => picture, + } + end let(:data) { {} } let(:config) { { "author" => "site_author" } } let(:site) do @@ -236,7 +246,7 @@ RSpec.describe Jekyll::SeoTag::Drop do let(:page_meta) { { "author" => "foo" } } it "doesn't error" do - expect(subject.author).to eql({ "name" => "foo", "twitter" => "foo" }) + expect(subject.author.to_h).to eql(expected_hash) end end @@ -245,7 +255,7 @@ RSpec.describe Jekyll::SeoTag::Drop do let(:page_meta) { { "author" => "foo" } } it "doesn't error" do - expect(subject.author).to eql({ "name" => "foo", "twitter" => "foo" }) + expect(subject.author.to_h).to eql(expected_hash) end end @@ -279,8 +289,8 @@ RSpec.describe Jekyll::SeoTag::Drop do "#{author_type}_author".sub("nil_", "site_").sub("empty_string_", "site_") end - it "returns a hash" do - expect(subject.author).to be_a(Hash) + it "returns a Drop" do + expect(subject.author).to be_a(Jekyll::SeoTag::AuthorDrop) end it "returns the name" do