Renaming/rearranging constants for clarity (#15911)

* Renaming/rearranging constants for clarity

As I'm writing about the Unified Embed project and creating
documentation for the upcoming Forem Fest, I realized that the naming
convention created confusion.

This commit is an effort to tidy up that confusion.

* Normalizing implementation pattern
This commit is contained in:
Jeremy Friesen 2022-01-03 09:30:11 -05:00 committed by GitHub
parent f886c758bd
commit 4a03b4aaa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 103 additions and 88 deletions

View file

@ -2,6 +2,7 @@ class GistTag < LiquidTagBase
PARTIAL = "liquids/gist".freeze
VALID_LINK_REGEXP =
%r{\Ahttps://gist\.github\.com/([a-zA-Z0-9](-?[a-zA-Z0-9]){0,38})/([a-zA-Z0-9]){1,32}(/[a-zA-Z0-9]+)?\Z}
REGISTRY_REGEXP = %r{https?://gist.github.com}
def initialize(_tag_name, link, _parse_context)
super
@ -61,4 +62,4 @@ end
Liquid::Template.register_tag("gist", GistTag)
UnifiedEmbed.register(GistTag, regexp: %r{https?://gist.github.com})
UnifiedEmbed.register(GistTag, regexp: GistTag::REGISTRY_REGEXP)

View file

@ -1,46 +1,10 @@
# The purpose of this singleton class is to provide a registry for the
# numerous "link/embedded" type liquid tags.
#
# Each of those liquid tags must self-register their lookup regular
# expression.
class UnifiedEmbed
include Singleton
def self.current
instance
end
# @api public
# A namespacing module to help organize the concepts of embedding.
module UnifiedEmbed
# A convenience method for registering tags as part of the
# UnifiedEmbed ecosystem.
#
# @param klass [Class] the LiquidTag class that we use when we match
# the given :regexp
# @param regexp [Regexp] the regular expression that when matched
# means we use the associated :klass
def self.register(klass, regexp:)
instance.register(klass, regexp: regexp)
end
# @api public
#
# @param link [String] the string that includes the URI for the
# embed and possibly additional attributes, depending on how
# the registered liquid tag parses this string.
# @return [Class] a descendant class of LiquidTagBase
def self.find_liquid_tag_for(link:)
instance.find_liquid_tag_for(link: link)
end
def initialize
@registry = []
end
def register(klass, regexp:)
@registry << [regexp, klass]
end
def find_liquid_tag_for(link:)
link = ActionController::Base.helpers.strip_tags(link).strip if link.include?("href=")
_regexp, klass = @registry.detect { |regexp, _tag_class| regexp.match?(link) }
klass
# @see UnifiedEmbed::Registry
def self.register(...)
UnifiedEmbed::Registry.register(...)
end
end

View file

@ -0,0 +1,48 @@
module UnifiedEmbed
# The purpose of this singleton class is to provide a registry for the
# numerous "link/embedded" type liquid tags.
#
# Each of those liquid tags must self-register their lookup regular
# expression.
class Registry
include Singleton
def self.current
instance
end
# @api public
#
# @param klass [Class] the LiquidTag class that we use when we match
# the given :regexp
# @param regexp [Regexp] the regular expression that when matched
# means we use the associated :klass
def self.register(klass, regexp:)
instance.register(klass, regexp: regexp)
end
# @api public
#
# @param link [String] the string that includes the URI for the
# embed and possibly additional attributes, depending on how
# the registered liquid tag parses this string.
# @return [Class] a descendant class of LiquidTagBase
def self.find_liquid_tag_for(link:)
instance.find_liquid_tag_for(link: link)
end
def initialize
@registry = []
end
def register(klass, regexp:)
@registry << [regexp, klass]
end
def find_liquid_tag_for(link:)
link = ActionController::Base.helpers.strip_tags(link).strip if link.include?("href=")
_regexp, klass = @registry.detect { |regexp, _tag_class| regexp.match?(link) }
klass
end
end
end

View file

@ -0,0 +1,44 @@
module UnifiedEmbed
# This liquid tag is present to facilitate a unified user experience
# for declaring that they want a URL to have "embedded" behavior.
#
# What do we mean by embedded behavior? A more contextually rich
# rendering of the URL, instead of a simple "A-tag".
#
# @see https://github.com/forem/forem/issues/15099 for details on the
# purpose of this class.
class Tag < LiquidTagBase
# You will not get a UnifiedEmbedTag instance, as we are instead
# using this class as a lookup (e.g., Factory pattern?) for the
# LiquidTagBase instance that is applicable for the given :link.
#
# @param tag_name [String] in the UI, this was liquid tag name
# (e.g., `{% tag_name link %}`)
# @param link [String] the URL and additional options for that
# particular service.
# @param parse_context [Liquid::ParseContext]
#
# @return [LiquidTagBase]
def self.new(tag_name, link, parse_context)
klass = UnifiedEmbed::Registry.find_liquid_tag_for(link: link)
if klass
# Why the __send__? Because a LiquidTagBase class "privatizes"
# the `.new` method. And we want to instantiate the specific
# liquid tag for the given link.
klass.__send__(:new, tag_name, link, parse_context)
else
# If we don't know how to handle the embed, let's just give the
# user an A-tag.
super
end
end
def render(_context)
link, _options = strip_tags(@markup)
%(<a href="#{link}">#{link}</a>)
end
end
end
Liquid::Template.register_tag("embed", UnifiedEmbed::Tag)

View file

@ -1,42 +0,0 @@
# This liquid tag is present to facilitate a unified user experience
# for declaring that they want a URL to have "embedded" behavior.
#
# What do we mean by embedded behavior? A more contextually rich
# rendering of the URL, instead of a simple "A-tag".
#
# @see https://github.com/forem/forem/issues/15099 for details on the
# purpose of this class.
class UnifiedEmbedTag < LiquidTagBase
# You will not get a UnifiedEmbedTag instance, as we are instead
# using this class as a lookup (e.g., Factory pattern?) for the
# LiquidTagBase instance that is applicable for the given :link.
#
# @param tag_name [String] in the UI, this was liquid tag name
# (e.g., `{% tag_name link %}`)
# @param link [String] the URL and additional options for that
# particular service.
# @param parse_context [Liquid::ParseContext]
#
# @return [LiquidTagBase]
def self.new(tag_name, link, parse_context)
klass = UnifiedEmbed.find_liquid_tag_for(link: link)
if klass
# Why the __send__? Because a LiquidTagBase class "privatizes"
# the `.new` method. And we want to instantiate the specific
# liquid tag for the given link.
klass.__send__(:new, tag_name, link, parse_context)
else
# If we don't know how to handle the embed, let's just give the
# user an A-tag.
super
end
end
def render(_context)
link, _options = strip_tags(@markup)
%(<a href="#{link}">#{link}</a>)
end
end
Liquid::Template.register_tag("embed", UnifiedEmbedTag)

View file

@ -1,6 +1,6 @@
require "rails_helper"
RSpec.describe UnifiedEmbed do
RSpec.describe UnifiedEmbed::Registry do
subject(:unified_embed) { described_class }
let(:article) { create(:article) }

View file

@ -1,6 +1,6 @@
require "rails_helper"
RSpec.describe UnifiedEmbedTag, type: :liquid_tag do
RSpec.describe UnifiedEmbed::Tag, type: :liquid_tag do
it "delegates parsing to the link-matching class" do
link = "https://gist.github.com/jeremyf/662585f5c4d22184a6ae133a71bf891a"