Implement Unified Embeds for Glitch URLs (#16378)

* modify GlitchTag for Unified Embed

* complete implementation and add specs

* escape dots in regex; change capture group name

* address PR review comments

* rubocop fixes

* update comment

* pr review changes

* nudge Travis

* nudge Travis
This commit is contained in:
Arit Amana 2022-02-01 12:01:26 -05:00 committed by GitHub
parent 932a3b3575
commit 2a56c19d86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 34 deletions

View file

@ -2,9 +2,13 @@ class GlitchTag < LiquidTagBase
attr_accessor :uri
PARTIAL = "liquids/glitch".freeze
ID_REGEXP = /\A[a-zA-Z0-9\-]{1,110}\z/
TILDE_PREFIX_REGEXP = /\A~/
OPTION_REGEXP = /(app|code|no-files|preview-first|no-attribution|file=\w(\.\w)?)/
REGISTRY_REGEXP = %r{https://(?:(?<subdomain>[\w\-]{1,110})\.)?glitch(?:\.me|\.com)(?:/edit/#!/)?(?<slug>[\w\-]{1,110})?(?<params>\?.*)?}
ID_REGEXP = /\A(?:^~)?(?<slug>[\w\-]{1,110})\Z/
REGEXP_OPTIONS = [REGISTRY_REGEXP, ID_REGEXP].freeze
# last part of PATH_REGEX handles line & character numbers that may appear at path end
PATH_REGEX = %r{path=(?<path>[\w/\-.]*)[\d:]*}
OPTION_REGEXP = %r{(app|code|no-files|preview-first|no-attribution|file=([\w/\-.]+)?)}
OPTIONS_TO_QUERY_PAIR = {
"app" => %w[previewSize 100],
"code" => %w[previewSize 0],
@ -13,10 +17,12 @@ class GlitchTag < LiquidTagBase
"no-attribution" => %w[attributionHidden true]
}.freeze
def initialize(_tag_name, id, _parse_context)
def initialize(_tag_name, input, _parse_context)
super
@query = parse_options(id)
@id = parse_id(id)
unescaped_input = CGI.unescape_html(input)
stripped_input = strip_tags(unescaped_input)
@id, @query = parsed_input(stripped_input)
end
def render(_context)
@ -31,49 +37,71 @@ class GlitchTag < LiquidTagBase
private
def valid_id?(input)
(input =~ ID_REGEXP)&.zero?
def parsed_input(input)
id, *options = input.split
match = pattern_match_for(id, REGEXP_OPTIONS)
raise StandardError, I18n.t("liquid_tags.glitch_tag.invalid_glitch_id") unless match
[get_slug(match), parse_options(options, match)]
end
def parse_id(input)
id = input.split.first
id.sub!(TILDE_PREFIX_REGEXP, "")
raise StandardError, I18n.t("liquid_tags.glitch_tag.invalid_glitch_id") unless valid_id?(id)
id
def get_slug(match)
if match_has_named_capture_group?(match, "subdomain")
match[:subdomain]
else
match[:slug]
end
end
def valid_option(option)
option.match(OPTION_REGEXP)
def match_has_named_capture_group?(match, group_name)
match.names.include?(group_name)
end
def parse_options(options, match)
# 'app' and 'code' should cancel each other out
options -= %w[app code] if options.include?("app") && options.include?("code")
# check for file= in options, then path= in params; fallback is file=index.html
file_option = options.detect { |option| option.start_with?("file=") }
options += ["file=#{path_within_params(match)}"] unless file_option
validated_options = options.select { |option| valid_option?(option) }
raise StandardError, I18n.t("liquid_tags.glitch_tag.invalid_options") if validated_options.empty?
build_options(validated_options)
end
def path_within_params(match)
return "index.html" unless match_has_named_capture_group?(match, "params")
return "index.html" if match[:params].blank?
path_match = pattern_match_for(match[:params], [PATH_REGEX])
return "index.html" if path_match.blank?
path_match[:path]
end
def valid_option?(option)
option.match?(OPTION_REGEXP)
end
def build_options(options)
# Convert options to query param pairs
params = options.filter_map { |option| OPTIONS_TO_QUERY_PAIR[option] }
# Deal with the file option if present or use default
file_option = options.detect { |option| option.start_with?("file=") }
path = file_option ? (file_option.sub! "file=", "") : "index.html"
# by this point, there is always a 'file='
path = options
.detect { |option| option.start_with?("file=") }
.delete_prefix("file=")
params.push ["path", path]
# Encode the resulting pairs as a query string
URI.encode_www_form(params)
end
def parse_options(input)
_, *options = input.split
# 'app' and 'code' should cancel each other out
options -= %w[app code] if (options & %w[app code]) == %w[app code]
# Validation
validated_options = options.filter_map { |option| valid_option(option) }
unless options.empty? || !validated_options.empty?
raise StandardError, I18n.t("liquid_tags.glitch_tag.invalid_options")
end
build_options(options)
end
end
Liquid::Template.register_tag("glitch", GlitchTag)
UnifiedEmbed.register(GlitchTag, regexp: GlitchTag::REGISTRY_REGEXP)

View file

@ -22,6 +22,12 @@ RSpec.describe UnifiedEmbed::Registry do
"https://app.codesandbox.io/embed/exciting-knuth-hywlv?file=/index.html&runonclick=0&view=editor",
]
valid_glitch_url_formats = [
"https://zircon-quixotic-attraction.glitch.me",
"https://glitch.com/edit/#!/zircon-quixotic-attraction",
"https://glitch.com/edit/#!/zircon-quixotic-attraction?path=script.js:1:0",
]
valid_medium_url_formats = [
"https://medium.com/@edisonywh/my-ruby-journey-hooking-things-up-91d757e1c59c",
"https://themobilist.medium.com/is-universal-basic-mobility-the-route-to-a-sustainable-c-b18e1e2d014c",
@ -106,6 +112,13 @@ RSpec.describe UnifiedEmbed::Registry do
.to eq(GistTag)
end
it "returns GlitchTag for a valid glitch url", :aggregate_failures do
valid_glitch_url_formats.each do |url|
expect(described_class.find_liquid_tag_for(link: url))
.to eq(GlitchTag)
end
end
it "returns AsciinemaTag for an asciinema url" do
expect(described_class.find_liquid_tag_for(link: "https://asciinema.org/a/330532"))
.to eq(AsciinemaTag)