diff --git a/app/liquid_tags/gist_tag.rb b/app/liquid_tags/gist_tag.rb index 69f82454b..2fa89c5af 100644 --- a/app/liquid_tags/gist_tag.rb +++ b/app/liquid_tags/gist_tag.rb @@ -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) diff --git a/app/liquid_tags/unified_embed.rb b/app/liquid_tags/unified_embed.rb index ed6a32f24..32079b824 100644 --- a/app/liquid_tags/unified_embed.rb +++ b/app/liquid_tags/unified_embed.rb @@ -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 diff --git a/app/liquid_tags/unified_embed/registry.rb b/app/liquid_tags/unified_embed/registry.rb new file mode 100644 index 000000000..87bf64513 --- /dev/null +++ b/app/liquid_tags/unified_embed/registry.rb @@ -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 diff --git a/app/liquid_tags/unified_embed/tag.rb b/app/liquid_tags/unified_embed/tag.rb new file mode 100644 index 000000000..34372b8bf --- /dev/null +++ b/app/liquid_tags/unified_embed/tag.rb @@ -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) + %(#{link}) + end + end +end + +Liquid::Template.register_tag("embed", UnifiedEmbed::Tag) diff --git a/app/liquid_tags/unified_embed_tag.rb b/app/liquid_tags/unified_embed_tag.rb deleted file mode 100644 index 37a975bf8..000000000 --- a/app/liquid_tags/unified_embed_tag.rb +++ /dev/null @@ -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) - %(#{link}) - end -end - -Liquid::Template.register_tag("embed", UnifiedEmbedTag) diff --git a/spec/liquid_tags/unified_embed_spec.rb b/spec/liquid_tags/unified_embed/registry_spec.rb similarity index 99% rename from spec/liquid_tags/unified_embed_spec.rb rename to spec/liquid_tags/unified_embed/registry_spec.rb index 8d4a2cb07..e43ef7c50 100644 --- a/spec/liquid_tags/unified_embed_spec.rb +++ b/spec/liquid_tags/unified_embed/registry_spec.rb @@ -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) } diff --git a/spec/liquid_tags/unified_embed_tag_spec.rb b/spec/liquid_tags/unified_embed/tag_spec.rb similarity index 91% rename from spec/liquid_tags/unified_embed_tag_spec.rb rename to spec/liquid_tags/unified_embed/tag_spec.rb index 2a1ef80e2..cb97084b9 100644 --- a/spec/liquid_tags/unified_embed_tag_spec.rb +++ b/spec/liquid_tags/unified_embed/tag_spec.rb @@ -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"