lib/oembed/provider.rb in ruby-oembed-0.7.6 vs lib/oembed/provider.rb in ruby-oembed-0.8.1

- old
+ new

@@ -1,68 +1,145 @@ module OEmbed + # An OEmbed::Provider has information about an individual oEmbed enpoint. class Provider - attr_accessor :format, :name, :url, :urls, :endpoint + + # 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 + - def initialize(endpoint, format = OEmbed::Formatters::DEFAULT) + # 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"). + # + # If give, the format should be the name of the default format for all request + # to this Provider (e.g. 'json'). Defaults to OEmbed::Formatter.default + # + # 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}", :xml) + def initialize(endpoint, format = OEmbed::Formatter.default) + 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) + @endpoint = endpoint @urls = [] - # Try to use the best available format - @format = OEmbed::Formatters.verify?(format) + @format = 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 - @urls << url - return + 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 - full, scheme, domain, path = *url.match(%r{([^:]*)://?([^/?]*)(.*)}) - domain = Regexp.escape(domain).gsub("\\*", "(.*?)").gsub("(.*?)\\.", "([^\\.]+\\.)?") - path = Regexp.escape(path).gsub("\\*", "(.*?)") - @urls << Regexp.new("^#{Regexp.escape(scheme)}://#{domain}#{path}") + @urls << url end - def build(url, options = {}) + # 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: + # :format:: overrides this Provider's default request format. + # :url:: will be ignored, replaced by the url param. + 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. + def include?(url) + @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 = options.merge({:url => url}) + + query = query.merge({:url=>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 format_in_url? - format = endpoint["{format}"] = (query[:format] || @format).to_s + if endpoint.include?("{format}") + endpoint["{format}"] = this_format query.delete(:format) - else - format = query[:format] ||= @format end - query_string = "?" + query.inject("") do |memo, (key, value)| + query = "?" + query.inject("") do |memo, (key, value)| "#{key}=#{value}&#{memo}" end.chop - URI.parse(endpoint + query_string).instance_eval do - @format = format; def format; @format; end + URI.parse(endpoint + query).instance_eval do + @format = this_format + def format + @format + end self end end - def raw(url, options = {}) - uri = build(url, options) + # @deprecated *Note*: This method will be made private in the future. + def raw(url, query = {}) + uri = build(url, query) found = false max_redirects = 4 until found host, port = uri.host, uri.port if uri.host && uri.port - res = Net::HTTP.start(uri.host, uri.port) {|http| http.get(uri.request_uri) } + res = Net::HTTP.start(uri.host, uri.port) {|http| http.get(uri.request_uri) } res.header['location'] ? uri = URI.parse(res.header['location']) : found = true if max_redirects == 0 found = true else max_redirects = max_redirects - 1 end end case res when Net::HTTPNotImplemented - raise OEmbed::UnknownFormat, uri.format + raise OEmbed::UnknownFormat, format when Net::HTTPNotFound raise OEmbed::NotFound, url when Net::HTTPOK res.body else @@ -72,26 +149,13 @@ # Convert known errors into OEmbed::UnknownResponse for easy catching # up the line. This is important if given a URL that doesn't support # OEmbed. The following are known errors: # * Net::* errors like Net::HTTPBadResponse # * JSON::JSONError errors like JSON::ParserError - if $!.is_a?(JSON::JSONError) || $!.class.to_s =~ /\ANet::/ + if defined?(::JSON) && $!.is_a?(::JSON::JSONError) || $!.class.to_s =~ /\ANet::/ raise OEmbed::UnknownResponse, res && res.respond_to?(:code) ? res.code : 'Error' else raise $! end - end - - def get(url, options = {}) - options[:format] ||= @format if @format - OEmbed::Response.create_for(raw(url, options), self, url, options[:format]) - end - - def format_in_url? - @endpoint.include?("{format}") - end - - def include?(url) - @urls.empty? || !!@urls.detect{ |u| u =~ url } end end end