ruby-oembed 0.7.6 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -1
- data/CHANGELOG.rdoc +49 -0
- data/Gemfile.lock +13 -8
- data/README.rdoc +79 -0
- data/Rakefile +21 -6
- data/lib/oembed/errors.rb +23 -4
- data/lib/oembed/formatter/json/backends/activesupportjson.rb +28 -0
- data/lib/oembed/formatter/json/backends/jsongem.rb +31 -0
- data/lib/oembed/formatter/json/backends/yaml.rb +85 -0
- data/lib/oembed/formatter/json.rb +69 -0
- data/lib/oembed/formatter/xml/backends/rexml.rb +44 -0
- data/lib/oembed/formatter/xml/backends/xmlsimple.rb +39 -0
- data/lib/oembed/formatter/xml.rb +76 -0
- data/lib/oembed/formatter.rb +97 -0
- data/lib/oembed/provider.rb +102 -38
- data/lib/oembed/provider_discovery.rb +19 -7
- data/lib/oembed/providers/embedly_urls.yml +487 -0
- data/lib/oembed/providers/oohembed_urls.yml +17 -0
- data/lib/oembed/providers.rb +68 -11
- data/lib/oembed/response/link.rb +14 -0
- data/lib/oembed/response/photo.rb +12 -0
- data/lib/oembed/response/rich.rb +11 -0
- data/lib/oembed/response/video.rb +10 -0
- data/lib/oembed/response.rb +58 -10
- data/lib/oembed/version.rb +18 -0
- data/lib/oembed.rb +2 -1
- data/lib/tasks/oembed.rake +45 -0
- data/lib/tasks/rspec.rake +5 -0
- data/ruby-oembed.gemspec +38 -17
- data/spec/formatter/json/.DS_Store +0 -0
- data/spec/formatter/json/jsongem_backend_spec.rb +34 -0
- data/spec/formatter/json/yaml_backend_spec.rb +30 -0
- data/spec/formatter/xml/rexml_backend_spec.rb +30 -0
- data/spec/formatter/xml/xmlsimple_backend_spec.rb +34 -0
- data/spec/formatter_spec.rb +35 -0
- data/spec/provider_spec.rb +189 -24
- data/spec/providers_spec.rb +20 -1
- data/spec/response_spec.rb +129 -48
- data/spec/spec_helper.rb +5 -6
- metadata +45 -38
- data/CHANGELOG.md +0 -29
- data/README.md +0 -50
- data/VERSION +0 -1
- data/lib/oembed/embedly_urls.json +0 -227
- data/lib/oembed/formatters.rb +0 -40
- data/rails/init.rb +0 -3
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'oembed/formatter/json'
|
2
|
+
require 'oembed/formatter/xml'
|
3
|
+
|
4
|
+
module OEmbed
|
5
|
+
# Takes the raw response from an oEmbed server and turns it into a nice Hash of data.
|
6
|
+
module Formatter
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Returns the default format for OEmbed::Provider requests as a String.
|
10
|
+
def default
|
11
|
+
# Listed in order of preference.
|
12
|
+
%w{json xml}.detect { |type| supported?(type) rescue false }
|
13
|
+
end
|
14
|
+
|
15
|
+
# Given the name of a format we want to know about (e.g. 'json'), returns
|
16
|
+
# true if there is a valid backend. If there is no backend, raises
|
17
|
+
# OEmbed::FormatNotSupported.
|
18
|
+
def supported?(format)
|
19
|
+
case format.to_s
|
20
|
+
when 'json'
|
21
|
+
JSON.supported?
|
22
|
+
when 'xml'
|
23
|
+
XML.supported?
|
24
|
+
else
|
25
|
+
raise OEmbed::FormatNotSupported, format
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Convert the given value into a nice Hash of values. The format should
|
30
|
+
# be the name of the response format (e.g. 'json'). The value should be
|
31
|
+
# a String or IO containing the response from an oEmbed server.
|
32
|
+
#
|
33
|
+
# For example:
|
34
|
+
# value = '{"version": "1.0", "type": "link", "title": "Some Cool News Article"}'
|
35
|
+
# OEmbed::Formatter.decode('json', value)
|
36
|
+
# #=> {"version": "1.0", "type": "link", "title": "Some Cool News Article"}
|
37
|
+
def decode(format, value)
|
38
|
+
supported?(format)
|
39
|
+
|
40
|
+
case format.to_s
|
41
|
+
when 'json'
|
42
|
+
begin
|
43
|
+
JSON.decode(value)
|
44
|
+
rescue JSON.backend::ParseError
|
45
|
+
raise OEmbed::ParseError, $!.message
|
46
|
+
end
|
47
|
+
when 'xml'
|
48
|
+
begin
|
49
|
+
XML.decode(value)
|
50
|
+
rescue XML.backend::ParseError
|
51
|
+
raise OEmbed::ParseError, $!.message
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Test the given backend to make sure it parses known values correctly.
|
57
|
+
# The backend_module should be either a JSON or XML backend.
|
58
|
+
def test_backend(backend_module)
|
59
|
+
expected = {
|
60
|
+
"version"=>1.0,
|
61
|
+
"string"=>"test",
|
62
|
+
"int"=>42,
|
63
|
+
"html"=>"<i>Cool's</i>\n the \"word\"!",
|
64
|
+
}
|
65
|
+
|
66
|
+
given_value = case backend_module.to_s
|
67
|
+
when /OEmbed::Formatter::JSON::Backends::/
|
68
|
+
<<-JSON
|
69
|
+
{"version":"1.0", "string":"test", "int":42,"html":"<i>Cool's</i>\\n the \\"word\\"\\u0021"}
|
70
|
+
JSON
|
71
|
+
when /OEmbed::Formatter::XML::Backends::/
|
72
|
+
<<-XML
|
73
|
+
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
74
|
+
<oembed>
|
75
|
+
<version>1.0</version>
|
76
|
+
<string>test</string>
|
77
|
+
<int>42</int>
|
78
|
+
<html><i>Cool's</i>\n the "word"!</html>
|
79
|
+
</oembed>
|
80
|
+
XML
|
81
|
+
else
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
actual = backend_module.decode(given_value)
|
86
|
+
|
87
|
+
# For the test to be true the actual output Hash should have the
|
88
|
+
# exact same list of keys _and_ the values should be the same
|
89
|
+
# if we ignoring typecasting.
|
90
|
+
actual.keys.sort == expected.keys.sort &&
|
91
|
+
!actual.detect { |key, value| value.to_s != expected[key].to_s }
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
data/lib/oembed/provider.rb
CHANGED
@@ -1,55 +1,132 @@
|
|
1
1
|
module OEmbed
|
2
|
+
# An OEmbed::Provider has information about an individual oEmbed enpoint.
|
2
3
|
class Provider
|
3
|
-
|
4
|
+
|
5
|
+
# The String that is the http URI of the Provider's oEmbed endpoint.
|
6
|
+
# This URL may also contain a {{format}} portion. In actual requests to
|
7
|
+
# this Provider, this string will be replaced with a string representing
|
8
|
+
# the request format (e.g. "json").
|
9
|
+
attr_accessor :endpoint
|
10
|
+
|
11
|
+
# The name of the default format for all request to this Provider (e.g. 'json').
|
12
|
+
attr_accessor :format
|
13
|
+
|
14
|
+
# An Array of all URL schemes supported by this Provider.
|
15
|
+
attr_accessor :urls
|
16
|
+
|
17
|
+
# The human-readable name of the Provider.
|
18
|
+
#
|
19
|
+
# @deprecated *Note*: This accessor currently isn't used anywhere in the codebase.
|
20
|
+
attr_accessor :name
|
21
|
+
|
22
|
+
# @deprecated *Note*: Added in a fork of the gem, a while back. I really would like
|
23
|
+
# to get rid of it, though. --Marcos
|
24
|
+
attr_accessor :url
|
25
|
+
|
4
26
|
|
5
|
-
|
27
|
+
# Construct a new OEmbed::Provider instance, pointing at a specific oEmbed
|
28
|
+
# endpoint.
|
29
|
+
#
|
30
|
+
# The endpoint should be a String representing the http URI of the Provider's
|
31
|
+
# oEmbed endpoint. The endpoint String may also contain a {format} portion.
|
32
|
+
# In actual requests to this Provider, this string will be replaced with a String
|
33
|
+
# representing the request format (e.g. "json").
|
34
|
+
#
|
35
|
+
# If give, the format should be the name of the default format for all request
|
36
|
+
# to this Provider (e.g. 'json'). Defaults to OEmbed::Formatter.default
|
37
|
+
#
|
38
|
+
# For example:
|
39
|
+
# # If requests should be sent to:
|
40
|
+
# # "http://my.service.com/oembed?format=#{OEmbed::Formatter.default}"
|
41
|
+
# @provider = OEmbed::Provider.new("http://my.service.com/oembed")
|
42
|
+
#
|
43
|
+
# # If requests should be sent to:
|
44
|
+
# # "http://my.service.com/oembed.xml"
|
45
|
+
# @xml_provider = OEmbed::Provider.new("http://my.service.com/oembed.{format}", :xml)
|
46
|
+
def initialize(endpoint, format = OEmbed::Formatter.default)
|
47
|
+
endpoint_uri = URI.parse(endpoint.gsub(/[\{\}]/,'')) rescue nil
|
48
|
+
raise ArgumentError, "The given endpoint isn't a valid http(s) URI: #{endpoint.to_s}" unless endpoint_uri.is_a?(URI::HTTP)
|
49
|
+
|
6
50
|
@endpoint = endpoint
|
7
51
|
@urls = []
|
8
|
-
|
9
|
-
@format = OEmbed::Formatters.verify?(format)
|
52
|
+
@format = format
|
10
53
|
end
|
11
54
|
|
55
|
+
# Adds the given url scheme to this Provider instance.
|
56
|
+
# The url scheme can be either a String, containing wildcards specified
|
57
|
+
# with an asterisk, (see http://oembed.com/#section2.1 for details),
|
58
|
+
# or a Regexp.
|
59
|
+
#
|
60
|
+
# For example:
|
61
|
+
# @provider << "http://my.service.com/video/*"
|
62
|
+
# @provider << "http://*.service.com/photo/*/slideshow"
|
63
|
+
# @provider << %r{^http://my.service.com/((help)|(faq))/\d+[#\?].*}
|
12
64
|
def <<(url)
|
13
|
-
if url.is_a?
|
14
|
-
|
15
|
-
|
65
|
+
if !url.is_a?(Regexp)
|
66
|
+
full, scheme, domain, path = *url.match(%r{([^:]*)://?([^/?]*)(.*)})
|
67
|
+
domain = Regexp.escape(domain).gsub("\\*", "(.*?)").gsub("(.*?)\\.", "([^\\.]+\\.)?")
|
68
|
+
path = Regexp.escape(path).gsub("\\*", "(.*?)")
|
69
|
+
url = Regexp.new("^#{Regexp.escape(scheme)}://#{domain}#{path}")
|
16
70
|
end
|
17
|
-
|
18
|
-
domain = Regexp.escape(domain).gsub("\\*", "(.*?)").gsub("(.*?)\\.", "([^\\.]+\\.)?")
|
19
|
-
path = Regexp.escape(path).gsub("\\*", "(.*?)")
|
20
|
-
@urls << Regexp.new("^#{Regexp.escape(scheme)}://#{domain}#{path}")
|
71
|
+
@urls << url
|
21
72
|
end
|
22
73
|
|
23
|
-
|
74
|
+
# Send a request to the Provider endpoint to get information about the
|
75
|
+
# given url and return the appropriate OEmbed::Response.
|
76
|
+
#
|
77
|
+
# The query parameter should be a Hash of values which will be
|
78
|
+
# sent as query parameters in this request to the Provider endpoint. The
|
79
|
+
# following special cases apply to the query Hash:
|
80
|
+
# :format:: overrides this Provider's default request format.
|
81
|
+
# :url:: will be ignored, replaced by the url param.
|
82
|
+
def get(url, query = {})
|
83
|
+
query[:format] ||= @format
|
84
|
+
OEmbed::Response.create_for(raw(url, query), self, url, query[:format].to_s)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Determine whether the given url is supported by this Provider by matching
|
88
|
+
# against the Provider's URL schemes.
|
89
|
+
def include?(url)
|
90
|
+
@urls.empty? || !!@urls.detect{ |u| u =~ url }
|
91
|
+
end
|
92
|
+
|
93
|
+
# @deprecated *Note*: This method will be made private in the future.
|
94
|
+
def build(url, query = {})
|
24
95
|
raise OEmbed::NotFound, url unless include?(url)
|
25
|
-
|
96
|
+
|
97
|
+
query = query.merge({:url=>url})
|
98
|
+
# TODO: move this code exclusively into the get method, once build is private.
|
99
|
+
this_format = (query[:format] ||= @format.to_s).to_s
|
100
|
+
|
26
101
|
endpoint = @endpoint.clone
|
27
102
|
|
28
|
-
if
|
29
|
-
|
103
|
+
if endpoint.include?("{format}")
|
104
|
+
endpoint["{format}"] = this_format
|
30
105
|
query.delete(:format)
|
31
|
-
else
|
32
|
-
format = query[:format] ||= @format
|
33
106
|
end
|
34
107
|
|
35
|
-
|
108
|
+
query = "?" + query.inject("") do |memo, (key, value)|
|
36
109
|
"#{key}=#{value}&#{memo}"
|
37
110
|
end.chop
|
38
111
|
|
39
|
-
URI.parse(endpoint +
|
40
|
-
@format =
|
112
|
+
URI.parse(endpoint + query).instance_eval do
|
113
|
+
@format = this_format
|
114
|
+
def format
|
115
|
+
@format
|
116
|
+
end
|
41
117
|
self
|
42
118
|
end
|
43
119
|
end
|
44
120
|
|
45
|
-
|
46
|
-
|
121
|
+
# @deprecated *Note*: This method will be made private in the future.
|
122
|
+
def raw(url, query = {})
|
123
|
+
uri = build(url, query)
|
47
124
|
|
48
125
|
found = false
|
49
126
|
max_redirects = 4
|
50
127
|
until found
|
51
128
|
host, port = uri.host, uri.port if uri.host && uri.port
|
52
|
-
res = Net::HTTP.start(uri.host, uri.port) {|http|
|
129
|
+
res = Net::HTTP.start(uri.host, uri.port) {|http| http.get(uri.request_uri) }
|
53
130
|
res.header['location'] ? uri = URI.parse(res.header['location']) : found = true
|
54
131
|
if max_redirects == 0
|
55
132
|
found = true
|
@@ -60,7 +137,7 @@ module OEmbed
|
|
60
137
|
|
61
138
|
case res
|
62
139
|
when Net::HTTPNotImplemented
|
63
|
-
raise OEmbed::UnknownFormat,
|
140
|
+
raise OEmbed::UnknownFormat, format
|
64
141
|
when Net::HTTPNotFound
|
65
142
|
raise OEmbed::NotFound, url
|
66
143
|
when Net::HTTPOK
|
@@ -74,24 +151,11 @@ module OEmbed
|
|
74
151
|
# OEmbed. The following are known errors:
|
75
152
|
# * Net::* errors like Net::HTTPBadResponse
|
76
153
|
# * JSON::JSONError errors like JSON::ParserError
|
77
|
-
if $!.is_a?(JSON::JSONError) || $!.class.to_s =~ /\ANet::/
|
154
|
+
if defined?(::JSON) && $!.is_a?(::JSON::JSONError) || $!.class.to_s =~ /\ANet::/
|
78
155
|
raise OEmbed::UnknownResponse, res && res.respond_to?(:code) ? res.code : 'Error'
|
79
156
|
else
|
80
157
|
raise $!
|
81
158
|
end
|
82
159
|
end
|
83
|
-
|
84
|
-
def get(url, options = {})
|
85
|
-
options[:format] ||= @format if @format
|
86
|
-
OEmbed::Response.create_for(raw(url, options), self, url, options[:format])
|
87
|
-
end
|
88
|
-
|
89
|
-
def format_in_url?
|
90
|
-
@endpoint.include?("{format}")
|
91
|
-
end
|
92
|
-
|
93
|
-
def include?(url)
|
94
|
-
@urls.empty? || !!@urls.detect{ |u| u =~ url }
|
95
|
-
end
|
96
160
|
end
|
97
161
|
end
|
@@ -1,16 +1,29 @@
|
|
1
1
|
module OEmbed
|
2
|
+
# Uses {oEmbed Discover}[http://oembed.com/#section4] to generate a new Provider
|
3
|
+
# instance about a URL for which a Provider didn't previously exist.
|
2
4
|
class ProviderDiscovery
|
3
5
|
class << self
|
4
|
-
|
5
|
-
|
6
|
+
|
7
|
+
# Discover the Provider for the given url, then call Provider#raw on that provider.
|
8
|
+
# The query parameter will be passed to both discover_provider and Provider#raw
|
9
|
+
# @deprecated *Note*: This method will be made private in the future.
|
10
|
+
def raw(url, query={})
|
11
|
+
provider = discover_provider(url, query)
|
6
12
|
provider.raw(url, options)
|
7
13
|
end
|
8
14
|
|
9
|
-
|
10
|
-
|
11
|
-
|
15
|
+
# Discover the Provider for the given url, then call Provider#get on that provider.
|
16
|
+
# The query parameter will be passed to both discover_provider and Provider#get
|
17
|
+
def get(url, query={})
|
18
|
+
provider = discover_provider(url, query)
|
19
|
+
provider.get(url, query)
|
12
20
|
end
|
13
21
|
|
22
|
+
# Returns a new Provider instance based on information from oEmbed discovery
|
23
|
+
# performed on the given url.
|
24
|
+
#
|
25
|
+
# The options Hash recognizes the following keys:
|
26
|
+
# :format:: If given only discover endpoints for the given format. If not format is given, use the first available format found.
|
14
27
|
def discover_provider(url, options = {})
|
15
28
|
uri = URI.parse(url)
|
16
29
|
|
@@ -43,8 +56,7 @@ module OEmbed
|
|
43
56
|
raise OEmbed::NotFound, url
|
44
57
|
end
|
45
58
|
|
46
|
-
|
47
|
-
Provider.new(provider_endpoint, format || OEmbed::Formatters::DEFAULT)
|
59
|
+
Provider.new(provider_endpoint, format || OEmbed::Formatter.default)
|
48
60
|
else
|
49
61
|
raise OEmbed::UnknownResponse, res.code
|
50
62
|
end
|