# This class provides methods for getting structured metadata properties for # Open Graph usage # @note A wrapper around the metainspector gem class OpenGraph delegate :meta, :meta_tags, :images, to: :page delegate :favicon, to: :images attr_reader :page, :tags DEFAULT_METHODS = %i[description title url].freeze CACHE_EXPIRY_IN_MINUTES = 15 def initialize(url) html = fetch_html(url) @page = MetaInspector.new(url, document: html) @tags = meta_tags end # the optional "use_best" argument is used to determine if we should use the # "best" option availabile for said method this works by picking the most # available (first?) title, description, or author (respectively), if defined # with the og:* or twitter:* metatags. example page.title(true) would look in # said areas. the default only looks at the head or default metatag. DEFAULT_METHODS.each do |method_name| define_method(method_name) do |use_best = false| if use_best page.public_send("best_#{method_name}".to_sym) else page.public_send(method_name) end end end def meta_for(data) page.meta[data] end def image page.images.best end def properties return {} if tags["property"].blank? tags["property"] end def main_properties # QUESTION: If we don't have `og:url` could we infer the url based on what was passed? %w[og:title og:url] end def main_properties_present? (main_properties - properties.keys).empty? end def preferred_desc properties["og:description"].first || page.description end # this method groups like-properties, making it a little easier to determine # the high level properties available for use. All "fb", "og", etc properties # will be grouped by their respective key def grouped_properties return {} if properties.blank? group(properties) end def grouped_meta return {} if meta.blank? group(meta) end def twitter return {} unless grouped_meta.key?("twitter") grouped_meta["twitter"] end private def fetch_html(url) Rails.cache.fetch("#{url}_open_graph_html", expires_in: CACHE_EXPIRY_IN_MINUTES.minutes) do response = HTTParty.get(url, headers: { "User-Agent" => "#{Settings::Community.community_name} (#{URL.url})" }) response&.body end end def group(data) data.each_with_object({}) do |(key, value), hash| group_key = key.split(":").first hash[group_key] ||= {} hash[group_key][key] = value end end end