discourse/lib/onebox/engine.rb
David Taylor 2e00d4d024
DEV: Fix flaky twitter onebox behavior (#18141)
The order in which Onebox engines are loaded is not guaranteed. Occasionally during tests, the twitter engine would be loaded before the instagram engine, and cause the Instagram Onebox spec to fail due to the lack of `Onebox.options.twitter_client`.

This commit makes the load order of Onebox engines consistent, and fixes the issue in the twitter_status_onebox.
2022-08-31 08:42:55 +08:00

214 lines
6.4 KiB
Ruby

# frozen_string_literal: true
module Onebox
module Engine
def self.included(object)
object.extend(ClassMethods)
end
def self.engines
constants.select do |constant|
constant.to_s =~ /Onebox$/
end.sort.map(&method(:const_get))
end
def self.all_iframe_origins
engines.flat_map { |e| e.iframe_origins }.uniq.compact
end
def self.origins_to_regexes(origins)
return [/.*/] if origins.include?("*")
origins.map do |origin|
escaped_origin = Regexp.escape(origin)
if origin.start_with?("*.", "https://*.", "http://*.")
escaped_origin = escaped_origin.sub("\\*", '\S*')
end
Regexp.new("\\A#{escaped_origin}", 'i')
end
end
attr_reader :url, :uri, :options, :timeout
attr :errors
def options=(opt)
return @options if opt.nil? # make sure options provided
opt = opt.to_h if opt.instance_of?(OpenStruct)
@options.merge!(opt)
@options
end
def initialize(url, timeout = nil)
@errors = {}
@options = {}
class_name = self.class.name.split("::").last.to_s
# Set the engine options extracted from global options.
self.options = Onebox.options[class_name] || {}
@url = url
@uri = URI(url)
if always_https?
@uri.scheme = 'https'
@url = @uri.to_s
end
@timeout = timeout || Onebox.options.timeout
end
# raises error if not defined in onebox engine.
# This is the output method for an engine.
def to_html
fail NoMethodError, "Engines need to implement this method"
end
# Some oneboxes create iframes or other complicated controls. If you're using
# a live editor with HTML preview, rendering those complicated controls can
# be slow or cause flickering.
#
# This method allows engines to produce a placeholder such as static image
# frame of a video.
#
# By default it just calls `to_html` unless implemented.
def placeholder_html
to_html
end
private
# raises error if not defined in onebox engine
# in each onebox, uses either Nokogiri or StandardEmbed to get raw HTML from url
def raw
fail NoMethodError, "Engines need to implement this method"
end
# raises error if not defined in onebox engine
# in each onebox, returns hash of desired onebox content
def data
fail NoMethodError, "Engines need this method defined"
end
def link
::Onebox::Helpers.uri_encode(@url)
end
def always_https?
self.class.always_https?
end
module ClassMethods
def handles_content_type?(other)
if other && class_variable_defined?(:@@matcher_content_type)
!!(other.to_s =~ class_variable_get(:@@matcher_content_type))
end
end
def ===(other)
if other.kind_of?(URI)
!!(other.to_s =~ class_variable_get(:@@matcher))
else
super
end
end
def priority
100
end
def matches_regexp(r)
class_variable_set :@@matcher, r
end
def matches_content_type(ct)
class_variable_set :@@matcher_content_type, ct
end
def requires_iframe_origins(*origins)
class_variable_set :@@iframe_origins, origins
end
def iframe_origins
class_variable_defined?(:@@iframe_origins) ? class_variable_get(:@@iframe_origins) : []
end
# calculates a name for onebox using the class name of engine
def onebox_name
name.split("::").last.downcase.gsub(/onebox/, "")
end
def always_https
@https = true
end
def always_https?
defined?(@https) ? @https : false
end
end
end
end
require_relative "helpers"
require_relative "layout_support"
require_relative "file_type_finder"
require_relative "engine/standard_embed"
require_relative "engine/html"
require_relative "engine/json"
require_relative "engine/amazon_onebox"
require_relative "engine/github_issue_onebox"
require_relative "engine/github_blob_onebox"
require_relative "engine/github_commit_onebox"
require_relative "engine/github_folder_onebox"
require_relative "engine/github_gist_onebox"
require_relative "engine/github_pull_request_onebox"
require_relative "engine/google_calendar_onebox"
require_relative "engine/google_docs_onebox"
require_relative "engine/google_maps_onebox"
require_relative "engine/google_play_app_onebox"
require_relative "engine/image_onebox"
require_relative "engine/video_onebox"
require_relative "engine/audio_onebox"
require_relative "engine/stack_exchange_onebox"
require_relative "engine/twitter_status_onebox"
require_relative "engine/wikimedia_onebox"
require_relative "engine/wikipedia_onebox"
require_relative "engine/youtube_onebox"
require_relative "engine/youku_onebox"
require_relative "engine/allowlisted_generic_onebox"
require_relative "engine/pubmed_onebox"
require_relative "engine/sound_cloud_onebox"
require_relative "engine/imgur_onebox"
require_relative "engine/pastebin_onebox"
require_relative "engine/slides_onebox"
require_relative "engine/xkcd_onebox"
require_relative "engine/animated_image_onebox"
require_relative "engine/gfycat_onebox"
require_relative "engine/typeform_onebox"
require_relative "engine/vimeo_onebox"
require_relative "engine/steam_store_onebox"
require_relative "engine/sketch_fab_onebox"
require_relative "engine/audioboom_onebox"
require_relative "engine/replit_onebox"
require_relative "engine/asciinema_onebox"
require_relative "engine/mixcloud_onebox"
require_relative "engine/band_camp_onebox"
require_relative "engine/coub_onebox"
require_relative "engine/flickr_onebox"
require_relative "engine/flickr_shortened_onebox"
require_relative "engine/five_hundred_px_onebox"
require_relative "engine/pdf_onebox"
require_relative "engine/twitch_clips_onebox"
require_relative "engine/twitch_stream_onebox"
require_relative "engine/twitch_video_onebox"
require_relative "engine/trello_onebox"
require_relative "engine/cloud_app_onebox"
require_relative "engine/wistia_onebox"
require_relative "engine/simplecast_onebox"
require_relative "engine/instagram_onebox"
require_relative "engine/gitlab_blob_onebox"
require_relative "engine/google_photos_onebox"
require_relative "engine/kaltura_onebox"
require_relative "engine/reddit_media_onebox"
require_relative "engine/google_drive_onebox"
require_relative "engine/facebook_media_onebox"
require_relative "engine/hackernews_onebox"