1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
require 'cgi'
require 'oembed/http_helper'
module OEmbed
# An OEmbed::Provider has information about an individual oEmbed enpoint.
class Provider
include OEmbed::HttpHelper
# The String that is the http URI of the Provider's oEmbed endpoint.
# This URL may also contain a {{format}} portion. In actual requests to
# this Provider, this string will be replaced with a string representing
# the request format (e.g. "json").
attr_accessor :endpoint
# The name of the default format for all request to this Provider (e.g. 'json').
attr_accessor :format
# An Array of all URL schemes supported by this Provider.
attr_accessor :urls
# The human-readable name of the Provider.
#
# @deprecated *Note*: This accessor currently isn't used anywhere in the codebase.
attr_accessor :name
# @deprecated *Note*: Added in a fork of the gem, a while back. I really would like
# to get rid of it, though. --Marcos
attr_accessor :url
# Construct a new OEmbed::Provider instance, pointing at a specific oEmbed
# endpoint.
#
# The endpoint should be a String representing the http URI of the Provider's
# oEmbed endpoint. The endpoint String may also contain a {format} portion.
# In actual requests to this Provider, this string will be replaced with a String
# representing the request format (e.g. "json").
#
# The `format:` option should be the name of the default format for all request
# to this Provider (e.g. 'json'). Defaults to OEmbed::Formatter.default
#
# # @deprecated *Note*: The `positional_format` is deprecated. Please used the named argument instead.
#
# The `required_query_params:` option should be a Hash
# representing query params that will be appended to the endpoint on each request
# and the optional name of an environment variable (i.e. ENV) whose value will be used
#
# For example:
# # If requests should be sent to:
# # "http://my.service.com/oembed?format=#{OEmbed::Formatter.default}"
# @provider = OEmbed::Provider.new("http://my.service.com/oembed")
#
# # If requests should be sent to:
# # "http://my.service.com/oembed.xml"
# @xml_provider = OEmbed::Provider.new("http://my.service.com/oembed.{format}", format: :xml)
#
# # If the endpoint requires an `access_token` be specified:
# @provider_with_auth = OEmbed::Provider.new("http://my.service.com/oembed", required_query_params: { access_token: 'MY_SERVICE_ACCESS_TOKEN' })
# # You can optionally override the value from `ENV['MY_SERVICE_ACCESS_TOKEN']`
# @provider_with_auth.access_token = @my_access_token
def initialize(endpoint, positional_format = OEmbed::Formatter.default, format: nil, required_query_params: {})
endpoint_uri = URI.parse(endpoint.gsub(/[\{\}]/,'')) rescue nil
raise ArgumentError, "The given endpoint isn't a valid http(s) URI: #{endpoint.to_s}" unless endpoint_uri.is_a?(URI::HTTP)
@required_query_params = {}
required_query_params.each do |param, default_env_var|
param = param.to_sym
@required_query_params[param] = nil
set_required_query_params(param, ENV[default_env_var]) if default_env_var
# Define a getter and a setter for each required_query_param
define_singleton_method("#{param}") { @required_query_params[param] } unless respond_to?("#{param}")
define_singleton_method("#{param}=") { |val| set_required_query_params(param, val) } unless respond_to?("#{param}=")
end
required_query_params_set?(reset_cache: true)
@endpoint = endpoint
@urls = []
@format = format || positional_format
end
# Adds the given url scheme to this Provider instance.
# The url scheme can be either a String, containing wildcards specified
# with an asterisk, (see http://oembed.com/#section2.1 for details),
# or a Regexp.
#
# For example:
# @provider << "http://my.service.com/video/*"
# @provider << "http://*.service.com/photo/*/slideshow"
# @provider << %r{^http://my.service.com/((help)|(faq))/\d+[#\?].*}
def <<(url)
if !url.is_a?(Regexp)
full, scheme, domain, path = *url.match(%r{([^:]*)://?([^/?]*)(.*)})
domain = Regexp.escape(domain).gsub("\\*", "(.*?)").gsub("(.*?)\\.", "([^\\.]+\\.)?")
path = Regexp.escape(path).gsub("\\*", "(.*?)")
url = Regexp.new("^#{Regexp.escape(scheme)}://#{domain}#{path}")
end
@urls << url
end
# Given the name of a required_query_param and a value
# store that value internally, so that it can be sent along
# with requests to this provider's endpoint.
# Raises an ArgumentError if the given param is not listed with required_query_params
# during instantiation.
def set_required_query_params(param, val)
raise ArgumentError.new("This provider does NOT have a required_query_param named #{param.inspect}") unless @required_query_params.has_key?(param)
@required_query_params[param] = val.nil? ? nil : ::CGI.escape(val.to_s)
required_query_params_set?(reset_cache: true)
@required_query_params[param]
end
# Returns true if all of this provider's required_query_params have a value
def required_query_params_set?(reset_cache: false)
return @all_required_query_params_set unless reset_cache || @all_required_query_params_set.nil?
@all_required_query_params_set = !@required_query_params.values.include?(nil)
end
# Send a request to the Provider endpoint to get information about the
# given url and return the appropriate OEmbed::Response.
#
# The query parameter should be a Hash of values which will be
# sent as query parameters in this request to the Provider endpoint. The
# following special cases apply to the query Hash:
# :timeout:: specifies the timeout (in seconds) for the http request.
# :format:: overrides this Provider's default request format.
# :url:: will be ignored, replaced by the url param.
# :max_redirects:: the number of times this request will follow 3XX redirects before throwing an error. Default: 4
def get(url, query = {})
query[:format] ||= @format
OEmbed::Response.create_for(raw(url, query), self, url, query[:format].to_s)
end
# Determine whether the given url is supported by this Provider by matching
# against the Provider's URL schemes.
# It will always return false of a provider has required_query_params that are not set.
def include?(url)
return false unless required_query_params_set?
@urls.empty? || !!@urls.detect{ |u| u =~ url }
end
# @deprecated *Note*: This method will be made private in the future.
def build(url, query = {})
raise OEmbed::NotFound, url unless include?(url)
query.delete(:timeout)
query.delete(:max_redirects)
query = query.merge(@required_query_params)
query = query.merge({:url => ::CGI.escape(url)})
# TODO: move this code exclusively into the get method, once build is private.
this_format = (query[:format] ||= @format.to_s).to_s
endpoint = @endpoint.clone
if endpoint.include?("{format}")
endpoint["{format}"] = this_format
query.delete(:format)
end
base = endpoint.include?('?') ? '&' : '?'
query = base + query.inject("") do |memo, (key, value)|
"#{key}=#{value}&#{memo}"
end.chop
URI.parse(endpoint + query).instance_eval do
@format = this_format
def format
@format
end
self
end
end
# @deprecated *Note*: This method will be made private in the future.
def raw(url, query = {})
uri = build(url, query)
http_get(uri, query)
rescue OEmbed::UnknownFormat
# raise with format to be backward compatible
raise OEmbed::UnknownFormat, format
end
end
end
|