netshade-oembed_links 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +68 -0
- data/Rakefile +14 -0
- data/lib/oembed_links/fetchers/net_http.rb +19 -0
- data/lib/oembed_links/formatters/json.rb +18 -0
- data/lib/oembed_links/formatters/xml.rb +36 -0
- data/lib/oembed_links/response.rb +102 -0
- data/lib/oembed_links.rb +307 -0
- data/oembed_links.gemspec +15 -0
- data/oembed_links_example.yml +40 -0
- data/rails/init.rb +7 -0
- data/spec/oembed_links_spec.rb +128 -0
- data/spec/oembed_links_test.yml +26 -0
- data/spec/spec_helper.rb +62 -0
- metadata +88 -0
data/README
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
This is the oembed_links gem. It allows you to easily parse text and
|
2
|
+
query configured providers for embedding information on the links
|
3
|
+
inside the text. A sample configuration file for configuring the
|
4
|
+
library has been included (oembed_links_example.yml), though you
|
5
|
+
may also configure the library programmatically (see rdocs).
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
To get started quickly (in irb):
|
10
|
+
|
11
|
+
require 'oembed_links'
|
12
|
+
OEmbed.register({:method => "NetHTTP"},
|
13
|
+
{:flickr => "http://www.flickr.com/services/oembed/",
|
14
|
+
:vimeo => "http://www.vimeo.com/api/oembed.{format}"},
|
15
|
+
{:flickr => { :format => "xml", :schemes => ["http://www.flickr.com/photos/*"]},
|
16
|
+
:vimeo => { :format => "json", :schemes => ["http://www.vimeo.com/*"]}})
|
17
|
+
|
18
|
+
# Simple transformation
|
19
|
+
OEmbed.transform("This is my flickr URL http://www.flickr.com/photos/bees/2341623661/ and all I did was show the URL straight to the picture")
|
20
|
+
|
21
|
+
# More complex transformation
|
22
|
+
OEmbed.transform("This is my flickr URL http://www.flickr.com/photos/bees/2341623661/ and this is a vimeo URL http://www.vimeo.com/757219 wow neat") do |r, url|
|
23
|
+
r.audio? { |a| "It's unlikely flickr or vimeo will give me audio" }
|
24
|
+
r.photo? { |p| "<img src='#{p["url"]}' alt='Sweet, a photo named #{p["title"]}' />" }
|
25
|
+
r.from?(:vimeo) { |v| "<div class='vimeo'>#{v['html']}</div>" }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Transformation to drive affiliate links to Amazon to our department and help us buy some laptops (hint)
|
29
|
+
OEmbed.register_provider(:oohembed,
|
30
|
+
"http://oohembed.com/oohembed/",
|
31
|
+
"json",
|
32
|
+
"http://*.amazon.(com|co.uk|de|ca|jp)/*/(gp/product|o/ASIN|obidos/ASIN|dp)/*",
|
33
|
+
"http://*.amazon.(com|co.uk|de|ca|jp)/(gp/product|o/ASIN|obidos/ASIN|dp)/*")
|
34
|
+
OEmbed.transform("Here is a link to amazon http://www.amazon.com/Complete-Aubrey-Maturin-Novels/dp/039306011X/ref=pd_bbs_sr_2 wow") do |res, url|
|
35
|
+
res.matches?(/amazon/) { |d|
|
36
|
+
unless url =~ /(&|\?)tag=[^&]+/i
|
37
|
+
url += ((url.index("?")) ? "&" : "?")
|
38
|
+
url += "tag=wwwindystarco-20"
|
39
|
+
end
|
40
|
+
<<-EOHTML
|
41
|
+
<div style="text-align:center;">
|
42
|
+
<a href='#{url}' target='_blank'>
|
43
|
+
<img src='#{d['thumbnail_url']}' border='0' /><br />
|
44
|
+
#{d['title']} #{"<br />by #{d['author']}" if d['author']}
|
45
|
+
</a>
|
46
|
+
</div>
|
47
|
+
EOHTML
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
To get started quickly in Rails:
|
53
|
+
|
54
|
+
Copy the included oembed_links_example.yml file to RAILS_ROOT/config/oembed_links.yml,
|
55
|
+
add a dependency to the gem in your environment.rb ( config.gem "oembed_links" )
|
56
|
+
and start your server. That's it.
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
See the rdocs for much more complete examples. The specs directory has some examples of programmatic
|
62
|
+
use, but the test to code ratio is slim atm.
|
63
|
+
|
64
|
+
Thanks to the Indianapolis Star I&D department for open sourcing this; most notably Chris Vannoy for giving the okay.
|
65
|
+
|
66
|
+
|
67
|
+
CZ - chris.zelenak!at!!indystar.com
|
68
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
Gem::manage_gems
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
CLEAN.include("pkg")
|
6
|
+
|
7
|
+
spec = eval(File.read("oembed_links.gemspec")) # I'm going to hell, etc. etc.
|
8
|
+
|
9
|
+
task :default => [:clean, :repackage]
|
10
|
+
|
11
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
12
|
+
pkg.need_tar = true
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'libxml'
|
2
|
+
|
3
|
+
class OEmbed
|
4
|
+
module Formatters
|
5
|
+
class LibXML
|
6
|
+
|
7
|
+
def name
|
8
|
+
"xml"
|
9
|
+
end
|
10
|
+
|
11
|
+
# This is an extremely naive XML doc to hash
|
12
|
+
# formatter. Cases like arrays represented in
|
13
|
+
# XML will not work; only strings, ints and
|
14
|
+
# floats will be converted.
|
15
|
+
def format(txt)
|
16
|
+
parser = LibXML::XML::Parser.string(txt)
|
17
|
+
doc = parser.parse
|
18
|
+
h = { }
|
19
|
+
doc.root.children.each do |node|
|
20
|
+
unless node.name.strip.empty?
|
21
|
+
c = node.content
|
22
|
+
if c =~ /^[0-9]+$/
|
23
|
+
c = c.to_i
|
24
|
+
elsif c=~ /^[0-9]+\.[0-9]+$/
|
25
|
+
c = c.to_f
|
26
|
+
end
|
27
|
+
h[node.name.strip] = c
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return h
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
OEmbed.register_formatter(OEmbed::Formatters::LibXML)
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# The class used to represent data returned by the server.
|
2
|
+
#
|
3
|
+
class OEmbed
|
4
|
+
class Response
|
5
|
+
|
6
|
+
def initialize(provider, url, response_object)
|
7
|
+
@provider = provider
|
8
|
+
@url = url
|
9
|
+
@response = response_object
|
10
|
+
@rendered_via_provider = @rendered_via_regex = @rendered_via_type = false
|
11
|
+
@rendered = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@response["html"] || @response["url"]
|
16
|
+
end
|
17
|
+
|
18
|
+
# If no content has been explicitly rendered for this Response,
|
19
|
+
# the default representation of the data will be returned.
|
20
|
+
def rendered_content
|
21
|
+
@rendered || self.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
# Test if this response has been returned from
|
25
|
+
# the given provider_name.
|
26
|
+
def from?(provider_name, &block)
|
27
|
+
if @provider.to_s === provider_name.to_s
|
28
|
+
if can_render_type?(:provider)
|
29
|
+
@rendered_via_provider = true
|
30
|
+
return render_content(&block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Test if this response came from a URL
|
36
|
+
# that matches the given regex.
|
37
|
+
def matches?(regex, &block)
|
38
|
+
if @url =~ regex
|
39
|
+
if can_render_type?(:regex)
|
40
|
+
@rendered_via_regex = true
|
41
|
+
render_content(&block)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Lowest priority renderer, which will execute
|
47
|
+
# a block regardless of conditions so long as
|
48
|
+
# no content has yet been rendered for this response.
|
49
|
+
def any?(&block)
|
50
|
+
if can_render_type?
|
51
|
+
return render_content(&block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Provides the mechanism to allow .audio?, .video?
|
56
|
+
# and other .type? checking methods. The value of the
|
57
|
+
# method name will be compared against the "type" field
|
58
|
+
# from the returned server data.
|
59
|
+
def method_missing(msym, *args, &block)
|
60
|
+
mname = msym.to_s
|
61
|
+
if mname[mname.size - 1, mname.size] == "?"
|
62
|
+
ts = mname[0..mname.size - 2]
|
63
|
+
if @response["type"] == ts
|
64
|
+
if can_render_type?(:type)
|
65
|
+
@rendered_via_type = true
|
66
|
+
return render_content(&block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
raise NoMethodError.new("No such method #{msym.to_s}", msym, *args)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Needlessly stupid priority for rendering.
|
77
|
+
def can_render_type?(type = nil)
|
78
|
+
if type == :provider
|
79
|
+
!@rendered_via_provider
|
80
|
+
elsif type == :regex
|
81
|
+
!@rendered_via_provider && !@rendered_via_regex
|
82
|
+
elsif type == :type
|
83
|
+
!@rendered_via_provider && !@rendered_via_regex && !@rendered_via_type
|
84
|
+
else
|
85
|
+
!has_rendered?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def has_rendered?
|
90
|
+
!@rendered.nil?
|
91
|
+
end
|
92
|
+
|
93
|
+
def render_content(&block)
|
94
|
+
if block_given?
|
95
|
+
@rendered = yield(@response)
|
96
|
+
else
|
97
|
+
@rendered = self.to_s
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
data/lib/oembed_links.rb
ADDED
@@ -0,0 +1,307 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'uri'
|
3
|
+
require 'yaml'
|
4
|
+
require 'oembed_links/response'
|
5
|
+
|
6
|
+
# The OEmbed class is the interface to class registration functions
|
7
|
+
# such as register_provider, register_formatter, etc., as well as
|
8
|
+
# the seat of the main .transform() function. If you are using OEmbed
|
9
|
+
# inside a Rails application, the process of initialization will be
|
10
|
+
# handled for you by the init.rb file. Create a file called
|
11
|
+
# oembed_links.yml inside your RAILS_ROOT/config directory, giving it
|
12
|
+
# a format like:
|
13
|
+
#
|
14
|
+
# :config:
|
15
|
+
# :method: "NetHTTP"
|
16
|
+
#
|
17
|
+
# :providers:
|
18
|
+
# :provider_1: "http://provider1.com/oembed.{format}"
|
19
|
+
# :provider_2: "http://provider2.com/oembed.{format}"
|
20
|
+
#
|
21
|
+
# :provider_1:
|
22
|
+
# :format: "json"
|
23
|
+
# :schemes:
|
24
|
+
# - "http://provider1.com/links/*/user/*"
|
25
|
+
# - "http://provider1.com/photos/*/user/*"
|
26
|
+
#
|
27
|
+
# :provider_2:
|
28
|
+
# :format: "xml"
|
29
|
+
# :schemes:
|
30
|
+
# - "http://provider2.com/videos/*"
|
31
|
+
#
|
32
|
+
#
|
33
|
+
# If you are not using the library in a Rails app, you can still create
|
34
|
+
# a YAML file like above and register it using OEmbed.register_yaml_file("/path/to/file.yml")
|
35
|
+
#
|
36
|
+
# You may also programmatically register information into the app using the
|
37
|
+
# register function, which takes a hash of configuration options, a
|
38
|
+
# provider hash, and the provider scheme hash.
|
39
|
+
#
|
40
|
+
# You may also register providers ad hoc using the OEmbed.register_provider
|
41
|
+
# function.
|
42
|
+
#
|
43
|
+
# To transform text, use the OEmbed.transform function, like:
|
44
|
+
#
|
45
|
+
# OEmbed.transform("This is my text and here's a picture http://www.flickr.com/path/to/a/photo")
|
46
|
+
#
|
47
|
+
# OEmbed.transform("Same text http://youtube.com/videos/somevideo") do |r, url|
|
48
|
+
# r.from?(:youtube) { |vid| vid["html"] }
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# See the OEmbed.transform function for more details.
|
52
|
+
#
|
53
|
+
#
|
54
|
+
# The OEmbed library by default uses net/http, libxml and json libraries for
|
55
|
+
# fetching network data, parsing xml and parsing json, respectively. These
|
56
|
+
# libraries are loaded by default.
|
57
|
+
#
|
58
|
+
# If you want to write your own mechanism for fetching HTTP data, parsing XML
|
59
|
+
# data, JSON data, or some other format, see the OEmbed::Fetchers::NetHTTP and
|
60
|
+
# OEmbed::Formatters::JSON for simple examples in terms of API. Once your new
|
61
|
+
# class mirrors these classes, you can register it with OEmbed by using
|
62
|
+
# OEmbed.register_formatter(class) or OEmbed.register_fetcher(class).
|
63
|
+
#
|
64
|
+
# NOTE that the default formatters and fetcher are EXTREMELY naive. There is no
|
65
|
+
# attempt at error handling, connection timeouts, or the like. If you need richer
|
66
|
+
# functionality you should subclass the existing formatters / fetchers and register them.
|
67
|
+
#
|
68
|
+
class OEmbed
|
69
|
+
|
70
|
+
# Configure OEmbed with all necessary information - library configuration,
|
71
|
+
# oembed provider urls, and the supported schemes and formats of said providers.
|
72
|
+
#
|
73
|
+
# The configuration hash should follow the form:
|
74
|
+
# { :method => "NameOfFetcher" }
|
75
|
+
# Note that the name of the fetcher is NOT the classname, but the arbitrarily
|
76
|
+
# chosen name provided by that class' .name() method. By default, it will
|
77
|
+
# be NetHTTP.
|
78
|
+
#
|
79
|
+
# The provider hash will be a hash where the keys are the symbolic names of the
|
80
|
+
# providers, eg. :vimeo, and the values are the URL strings used to query those providers.
|
81
|
+
# You may use the substring {format} inside these URLs to indicate that the
|
82
|
+
# given provider URL requires the format desired to be inserted at that point.
|
83
|
+
# Whatever format you have configured that provider for in the provider_scheme_hash
|
84
|
+
# will be inserted when they are queried.
|
85
|
+
#
|
86
|
+
# The provider_scheme_hash is a hash with two keys - the first key is the format
|
87
|
+
# key, which will either be the string "json" or the string "xml". The other
|
88
|
+
# key will be the schemes key, which contains an array of supported URL schemes
|
89
|
+
# by the provider.
|
90
|
+
#
|
91
|
+
# It is assumed that all hashes passed in use symbols for keys. Do not use string
|
92
|
+
# keys. This decision is totally arbitrary and without any technical merit.
|
93
|
+
#
|
94
|
+
# It is assumed that all provider names are symbols. Same rules as above.
|
95
|
+
#
|
96
|
+
def self.register(config_hash = { }, provider_hash = { }, provider_scheme_hash = { })
|
97
|
+
@fetch_method = (config_hash[:method] || "NetHTTP")
|
98
|
+
@config = config_hash
|
99
|
+
provider_hash.each do |provider, url|
|
100
|
+
config = provider_scheme_hash[provider]
|
101
|
+
raise "No Schemes were provided for #{provider.to_s}" if config.nil? ||
|
102
|
+
config[:schemes].nil? ||
|
103
|
+
config[:schemes].empty?
|
104
|
+
self.register_provider(provider, url, config[:format] || "json", *config[:schemes])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# The configuration hash passed into register() or parsed from the YAML file
|
109
|
+
def self.config
|
110
|
+
@config
|
111
|
+
end
|
112
|
+
|
113
|
+
# Register a provider with OEmbed. The provider name should be a symbol,
|
114
|
+
# like :flickr. The URL should be a string representing the endpoint
|
115
|
+
# for that provider, and may include the {format} substring to indicate
|
116
|
+
# how that provider should be notified of the desired format.
|
117
|
+
# format is either the string "json", or the string "xml".
|
118
|
+
# The list of schemes is an array of strings representing the different
|
119
|
+
# URL schemes supported by the provider. These strings follow the form:
|
120
|
+
#
|
121
|
+
# http://*.somedomain.*/*/*
|
122
|
+
#
|
123
|
+
# All schemes should use * to indicate variable text matched until the
|
124
|
+
# next non-* character or end of line.
|
125
|
+
def self.register_provider(provider, url, format = "json", *schemes)
|
126
|
+
@schemes ||= [ ]
|
127
|
+
@urls ||= { }
|
128
|
+
@formats ||= { }
|
129
|
+
|
130
|
+
@formats[provider] = format
|
131
|
+
@urls[provider] = url.gsub(/\{format\}/i, format)
|
132
|
+
schemes.each do |scheme|
|
133
|
+
sanitized_scheme = scheme.gsub(/([\.\?])/) { |str| "\\#{$1}" }.gsub(/\*/, '.+?')
|
134
|
+
@schemes << [Regexp.new("^" + sanitized_scheme + "$"), provider]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Loads the YAML file at the specified path and registers
|
139
|
+
# its information with OEmbed.
|
140
|
+
def self.register_yaml_file(file)
|
141
|
+
y = YAML.load(File.read(file))
|
142
|
+
self.register(y.delete(:config),
|
143
|
+
y.delete(:providers),
|
144
|
+
y)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Clear all registration information; really only valuable in testing
|
148
|
+
def self.clear_registrations
|
149
|
+
@schemes = []
|
150
|
+
@urls = { }
|
151
|
+
@formats = { }
|
152
|
+
@formatters = { }
|
153
|
+
@fetchers = { }
|
154
|
+
self.register_formatter(OEmbed::Formatters::LibXML)
|
155
|
+
self.register_formatter(OEmbed::Formatters::JSON)
|
156
|
+
self.register_fetcher(OEmbed::Fetchers::NetHTTP)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Register a new formatter. klass is the class object of the desired formatter.
|
160
|
+
# A new instance of klass will be created and stored. This instance MUST
|
161
|
+
# respond to the methods "name" and "format".
|
162
|
+
def self.register_formatter(klass)
|
163
|
+
@formatters ||= { }
|
164
|
+
inst = klass.new
|
165
|
+
@formatters[inst.name] = inst
|
166
|
+
end
|
167
|
+
|
168
|
+
# Register a new fetcher. klass is the class object of the desired fetcher.
|
169
|
+
# A new instance of klass will be created and stored. This instance MUST
|
170
|
+
# respond to the methods "name" and "fetch".
|
171
|
+
def self.register_fetcher(klass)
|
172
|
+
@fetchers ||= { }
|
173
|
+
inst = klass.new
|
174
|
+
@fetchers[inst.name] = inst
|
175
|
+
end
|
176
|
+
|
177
|
+
# Get the OEmbed::Response object for a given URL and provider. If you wish
|
178
|
+
# to pass extra attributes to the provider, provide a hash as the last attribute
|
179
|
+
# with keys and values representing the keys and values of the added querystring
|
180
|
+
# parameters.
|
181
|
+
def self.get_url_for_provider(url, provider, *attribs)
|
182
|
+
purl = @urls[provider]
|
183
|
+
eurl = CGI.escape(url)
|
184
|
+
purl += (purl.index("?")) ? "&" : "?"
|
185
|
+
purl += "url=#{eurl}"
|
186
|
+
attrib_hash = (attribs.last.is_a?(Hash)) ? attribs.last : { }
|
187
|
+
attrib_hash.each do |k, v|
|
188
|
+
purl += "&#{CGI.escape(k)}=#{CGI.escape(v)}"
|
189
|
+
end
|
190
|
+
fetcher = @fetchers[@fetch_method] || @fetchers[@fetchers.keys.first]
|
191
|
+
formatter = @formatters[@formats[provider]]
|
192
|
+
response = fetcher.fetch(purl)
|
193
|
+
formatter.format(response)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Transform all URLs supported by configured providers by the passed-in
|
197
|
+
# block or by outputting their "html" attributes ( or "url" attributes
|
198
|
+
# if no "html" attribute exists ). You may pass in a hash to specify
|
199
|
+
# extra parameters that should be appended to the querystrings to any
|
200
|
+
# matching providers (see OEmbed.get_url_for_provider). If you pass a
|
201
|
+
# block to this method, that block will be executed for each URL
|
202
|
+
# found in txt that has a matching provider. This block will be passed
|
203
|
+
# the OEmbed::Response object representing the embedding information for that
|
204
|
+
# url.
|
205
|
+
#
|
206
|
+
# OEmbed.transform supports two additional parameters:
|
207
|
+
#
|
208
|
+
# use_strict: Optionally use Ruby's stricter URI detection regex. While
|
209
|
+
# this will be technically correct regex, not all URLs
|
210
|
+
# use correct syntax. If this is false, URLs will be detected
|
211
|
+
# by the incredibly naive approach of finding any instance of
|
212
|
+
# http:// or https://, and finding anything that is not whitespace
|
213
|
+
# after that.
|
214
|
+
#
|
215
|
+
# Example:
|
216
|
+
# OEmbed.transform("all my urls are correctly formed", true)
|
217
|
+
#
|
218
|
+
# (options hash): This hash is used to append extra querystring parameters
|
219
|
+
# to the oembed provider. For example:
|
220
|
+
# OEmbed.transform("blah", false, :max_width => 320, :max_height => 200)
|
221
|
+
#
|
222
|
+
# You may fine tune the appearance of the embedding information by using the
|
223
|
+
# following forms:
|
224
|
+
#
|
225
|
+
# OEmbed.transform(some_string) do |r, url|
|
226
|
+
# r.from?(:provider_name) { |content| content["html"] }
|
227
|
+
# r.matches?(/some_regex_against_the_url/) { |content| content["title"] }
|
228
|
+
# r.video? { |video| content["html"] }
|
229
|
+
# r.audio? { |audio| content["html"] }
|
230
|
+
# r.hedgehog? { |hog| content["title"] }
|
231
|
+
# r.photo? { |photo| "<img src='#{photo["url"]}' title='#{photo['title']} />" }
|
232
|
+
# r.any? { |anythingelse| content["title"] }
|
233
|
+
# end
|
234
|
+
#
|
235
|
+
# The priority of these conditions is as follows:
|
236
|
+
# The first matching block for provider (.from?(:provider_name)) takes precendence OVER
|
237
|
+
# The first matching block for a URL regex (.matches?(/some_regex_against_the_url/)) takes precedence OVER
|
238
|
+
# The first matching block for a type equivalent (.video?, .audio?, .hedgehog?, .photo?) takes precendence OVER
|
239
|
+
# The match anything block (.any?)
|
240
|
+
#
|
241
|
+
#
|
242
|
+
#
|
243
|
+
#
|
244
|
+
# You do not need to specify an .any? block if you do not intend to perform any special
|
245
|
+
# transformations upon its data; the OEmbed::Response object will output either its html attribute
|
246
|
+
# (if it exists) or its url attribute.
|
247
|
+
#
|
248
|
+
# The value passed to these conditional blocks is a hash representing the data returned
|
249
|
+
# by the server. The keys of all the attributes will be strings.
|
250
|
+
#
|
251
|
+
# NOTE: The type equivalent blocks (.video?, .audio?, .hedgehog?, .photo?, etc.) perform
|
252
|
+
# an equality test between the method name and the type returned by the OEmbed provider.
|
253
|
+
# You may specify any type name you wish as the method name, and its type will be checked
|
254
|
+
# appropriately (as shown by the obviously trivial .hedgehog? method name).
|
255
|
+
#
|
256
|
+
def self.transform(txt, use_strict = false, *attribs, &block)
|
257
|
+
ret = txt.dup
|
258
|
+
|
259
|
+
if use_strict
|
260
|
+
URI.extract(txt, "http") do |u|
|
261
|
+
transform_url_for_text!(u, ret, *attribs, &block)
|
262
|
+
end
|
263
|
+
else
|
264
|
+
simple_extract(txt) do |u|
|
265
|
+
transform_url_for_text!(u, ret, *attribs, &block)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
return ret
|
270
|
+
end
|
271
|
+
|
272
|
+
private
|
273
|
+
|
274
|
+
# stupid simple copy of URI.extract to allow for looser URI detection
|
275
|
+
def self.simple_extract(str, &block)
|
276
|
+
reg = /(https?:\/\/[^\s]+)/i
|
277
|
+
if block_given?
|
278
|
+
str.scan(reg) { yield $& }
|
279
|
+
nil
|
280
|
+
else
|
281
|
+
result = []
|
282
|
+
str.scan(reg) { result.push $& }
|
283
|
+
result
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# extraction of inner loop of .transform(), to allow for easier
|
288
|
+
# parameterization of OEmbed
|
289
|
+
def self.transform_url_for_text!(u, txt, *attribs, &block)
|
290
|
+
unless (vschemes = @schemes.select { |a| u =~ a[0] }).empty?
|
291
|
+
regex, provider = vschemes.first
|
292
|
+
data = get_url_for_provider(u, provider, *attribs)
|
293
|
+
response = OEmbed::Response.new(provider, u, data)
|
294
|
+
if block.nil?
|
295
|
+
txt.gsub!(u, response.to_s)
|
296
|
+
else
|
297
|
+
yield(response, u)
|
298
|
+
txt.gsub!(u, response.rendered_content)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
end
|
305
|
+
require 'oembed_links/formatters/json'
|
306
|
+
require 'oembed_links/formatters/xml'
|
307
|
+
require 'oembed_links/fetchers/net_http'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "oembed_links"
|
3
|
+
s.version = "0.0.3"
|
4
|
+
s.author = "Indianapolis Star"
|
5
|
+
s.email = "bugs at indystar dot com"
|
6
|
+
s.homepage = "http://www.indystar.com"
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.summary = "a library for using the OEmbed format (http://oembed.com/) to acquire embedding information for freetext"
|
9
|
+
s.files = ["Rakefile", "README", "oembed_links_example.yml", "oembed_links.gemspec", "lib", "lib/oembed_links.rb", "lib/oembed_links", "lib/oembed_links/formatters", "lib/oembed_links/fetchers", "lib/oembed_links/response.rb", "lib/oembed_links/formatters/json.rb", "lib/oembed_links/formatters/xml.rb", "lib/oembed_links/fetchers/net_http.rb", "rails", "rails/init.rb", "spec", "spec/spec_helper.rb", "spec/oembed_links_spec.rb", "spec/oembed_links_test.yml" ]
|
10
|
+
s.require_path = "lib"
|
11
|
+
s.has_rdoc = true
|
12
|
+
s.extra_rdoc_files = ['README']
|
13
|
+
s.add_dependency(%q<json>)
|
14
|
+
s.add_dependency(%q<libxml-ruby>)
|
15
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
:config:
|
2
|
+
:method: "NetHTTP"
|
3
|
+
|
4
|
+
:providers:
|
5
|
+
:oohembed: "http://oohembed.com/oohembed/"
|
6
|
+
:vimeo: "http://www.vimeo.com/api/oembed.{format}"
|
7
|
+
:hulu: "http://www.hulu.com/api/oembed.{format}"
|
8
|
+
:flickr: "http://www.flickr.com/services/oembed"
|
9
|
+
|
10
|
+
:oohembed:
|
11
|
+
:format: "json"
|
12
|
+
:schemes:
|
13
|
+
- "http://*.amazon.(com|co.uk|de|ca|jp)/*/(gp/product|o/ASIN|obidos/ASIN|dp)/*"
|
14
|
+
- "http://*.amazon.(com|co.uk|de|ca|jp)/(gp/product|o/ASIN|obidos/ASIN|dp)/*"
|
15
|
+
- "http://*.collegehumor.com/video:*"
|
16
|
+
- "http://*.funnyordie.com/videos/*"
|
17
|
+
- "http://video.google.com/videoplay?*"
|
18
|
+
- "http://*.imdb.com/title/tt*/"
|
19
|
+
- "http://*.metacafe.com/watch/*"
|
20
|
+
- "http://*.slideshare.net/*"
|
21
|
+
- "http://twitter.com/*/statuses/*"
|
22
|
+
- "http://*.wikipedia.org/wiki/*"
|
23
|
+
- "http://*.wordpress.com/yyyy/mm/dd/*"
|
24
|
+
- "http://*.youtube.com/watch*"
|
25
|
+
|
26
|
+
:vimeo:
|
27
|
+
:format: "json"
|
28
|
+
:schemes:
|
29
|
+
- "http://www.vimeo.com/groups/*/*"
|
30
|
+
- "http://www.vimeo.com/*"
|
31
|
+
|
32
|
+
:hulu:
|
33
|
+
:format: "xml"
|
34
|
+
:schemes:
|
35
|
+
- "http://www.hulu.com/watch/*"
|
36
|
+
|
37
|
+
:flickr:
|
38
|
+
:format: "xml"
|
39
|
+
:schemes:
|
40
|
+
- "http://*.flickr.com/*"
|
data/rails/init.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe OEmbed, "registration tasks" do
|
4
|
+
include SpecHelper
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
OEmbed.clear_registrations
|
8
|
+
clear_urls
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should default to NetHTTP if no method is specified in configuration" do
|
12
|
+
OEmbed.register()
|
13
|
+
OEmbed.instance_variable_get("@fetch_method").should == "NetHTTP"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should throw an error if you provide provider URLs but no scheme or format information" do
|
17
|
+
lambda { OEmbed.register({ }, { :fake => "http://fake" })}.should raise_error
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should throw an error if you don't provide any scheme urls for registration" do
|
21
|
+
lambda { OEmbed.register({ }, { :fake => "http://fake" }, { :fake => { :format => "json", :schemes => []}})}.should raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should default to json format if none is specified" do
|
25
|
+
OEmbed.register({ }, { :fake => "http://fake" }, { :fake => { :schemes => ["http://fake/*"]}})
|
26
|
+
OEmbed.instance_variable_get("@formats")[:fake].should == "json"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should support loading a configuration via YAML" do
|
30
|
+
OEmbed.register_yaml_file(File.join(File.dirname(__FILE__), "oembed_links_test.yml"))
|
31
|
+
OEmbed.instance_variable_get("@urls").size.should == 3
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should support ad hoc addition of providers" do
|
35
|
+
OEmbed.register_yaml_file(File.join(File.dirname(__FILE__), "oembed_links_test.yml"))
|
36
|
+
OEmbed.instance_variable_get("@urls").size.should == 3
|
37
|
+
OEmbed.register_provider(:test4, "http://test4/oembed.{format}", "xml", "http://test4.*/*", "http://test4.*/foo/*")
|
38
|
+
OEmbed.instance_variable_get("@urls").size.should == 4
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should support adding new fetchers" do
|
42
|
+
|
43
|
+
OEmbed.register_fetcher(FakeFetcher)
|
44
|
+
OEmbed.register({ :method => "fake_fetcher"},
|
45
|
+
{ :fake => "http://fake" },
|
46
|
+
{ :fake => {
|
47
|
+
:format => "json",
|
48
|
+
:schemes => "http://fake/*"
|
49
|
+
}})
|
50
|
+
OEmbed.transform("http://fake/bar/baz").should == "fakecontent"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should support adding new formatters" do
|
54
|
+
OEmbed.register_formatter(FakeFormatter)
|
55
|
+
OEmbed.register({ :method => "fake_fetcher"},
|
56
|
+
{ :fake => "http://fake" },
|
57
|
+
{ :fake => {
|
58
|
+
:format => "fake_formatter",
|
59
|
+
:schemes => "http://fake/*"
|
60
|
+
}})
|
61
|
+
url_provides("")
|
62
|
+
OEmbed.transform("http://fake/bar/baz").should == "http://fakesville"
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
describe OEmbed, "transforming functions" do
|
68
|
+
include SpecHelper
|
69
|
+
before(:each) do
|
70
|
+
OEmbed.clear_registrations
|
71
|
+
clear_urls
|
72
|
+
url_provides({
|
73
|
+
"html" => "foo",
|
74
|
+
"type" => "video"
|
75
|
+
}.to_json)
|
76
|
+
OEmbed.register_yaml_file(File.join(File.dirname(__FILE__), "oembed_links_test.yml"))
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should always give priority to provider conditional blocks" do
|
80
|
+
OEmbed.transform("http://test1.net/foo") do |r, url|
|
81
|
+
r.any? { |a| "any" }
|
82
|
+
r.video? { |v| "video" }
|
83
|
+
r.from?(:test1) { |t| "test1" }
|
84
|
+
r.matches?(/./) { |m| "regex" }
|
85
|
+
end.should == "test1"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should always give priority regex conditional blocks over all others except provider" do
|
89
|
+
OEmbed.transform("http://test1.net/foo") do |r, url|
|
90
|
+
r.any? { |a| "any" }
|
91
|
+
r.video? { |v| "video" }
|
92
|
+
r.matches?(/./) { |m| "regex" }
|
93
|
+
end.should == "regex"
|
94
|
+
OEmbed.transform("http://test1.net/foo") do |r, url|
|
95
|
+
r.matches?(/./) { |m| "regex" }
|
96
|
+
r.any? { |a| "any" }
|
97
|
+
r.video? { |v| "video" }
|
98
|
+
end.should == "regex"
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should recognize the type of content and handle the conditional block appropriately" do
|
102
|
+
OEmbed.transform("http://test1.net/foo") do |r, url|
|
103
|
+
r.any? { |a| "any" }
|
104
|
+
r.video? { |v| "video" }
|
105
|
+
end.should == "video"
|
106
|
+
url_provides({
|
107
|
+
"html" => "bar",
|
108
|
+
"type" => "hedgehog"
|
109
|
+
}.to_json)
|
110
|
+
OEmbed.transform("http://test1.net/foo") do |r, url|
|
111
|
+
r.video? { |v| "video" }
|
112
|
+
r.hedgehog? { |v| "hedgey"}
|
113
|
+
end.should == "hedgey"
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should still output the content of a url if no transforming blocks match it" do
|
117
|
+
OEmbed.transform("http://test1.net/foo") do |r, url|
|
118
|
+
r.audio? { |a| "audio" }
|
119
|
+
r.hedgehog? { |v| "hedgey"}
|
120
|
+
r.from?(:test2) { |t| "test2" }
|
121
|
+
r.matches?(/baz/) { |m| "regex" }
|
122
|
+
end.should == "foo"
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
:config:
|
2
|
+
:method: "NetHTTP"
|
3
|
+
|
4
|
+
:providers:
|
5
|
+
:test1: "http://test1/dude/{format}/"
|
6
|
+
:test2: "http://test2.{format}"
|
7
|
+
:test3: "http://test3"
|
8
|
+
|
9
|
+
|
10
|
+
:test1:
|
11
|
+
:format: "json"
|
12
|
+
:schemes:
|
13
|
+
- "http://test1.*/*"
|
14
|
+
- "http://test1/*/test/*"
|
15
|
+
|
16
|
+
:test2:
|
17
|
+
:format: "xml"
|
18
|
+
:schemes:
|
19
|
+
- "http://test2.*/*/stuff/*"
|
20
|
+
- "http://test2/foo/bar/*"
|
21
|
+
|
22
|
+
:test3:
|
23
|
+
:format: "json"
|
24
|
+
:schemes:
|
25
|
+
- "http://test3/foo/*"
|
26
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
require 'oembed_links'
|
3
|
+
|
4
|
+
module SpecHelper
|
5
|
+
|
6
|
+
def url_provides(content)
|
7
|
+
Net::HTTP.fake_content(content)
|
8
|
+
end
|
9
|
+
|
10
|
+
def clear_urls
|
11
|
+
Net::HTTP.clear_fakes
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'net/http'
|
17
|
+
module Net
|
18
|
+
class HTTP
|
19
|
+
|
20
|
+
def self.fake_content(content)
|
21
|
+
@content = content
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def self.clear_fakes
|
26
|
+
@content = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.get(uri)
|
30
|
+
return @content
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
require 'json'
|
38
|
+
class FakeFetcher
|
39
|
+
def name
|
40
|
+
"fake_fetcher"
|
41
|
+
end
|
42
|
+
|
43
|
+
def fetch(url)
|
44
|
+
{
|
45
|
+
"url" => "fakecontent"
|
46
|
+
}.to_json
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class FakeFormatter
|
51
|
+
def name
|
52
|
+
"fake_formatter"
|
53
|
+
end
|
54
|
+
|
55
|
+
def format(txt)
|
56
|
+
{
|
57
|
+
"url" => "http://fakesville"
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: netshade-oembed_links
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Indianapolis Star
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-07-08 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: libxml-ruby
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: "0"
|
32
|
+
version:
|
33
|
+
description:
|
34
|
+
email: bugs at indystar dot com
|
35
|
+
executables: []
|
36
|
+
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files:
|
40
|
+
- README
|
41
|
+
files:
|
42
|
+
- Rakefile
|
43
|
+
- README
|
44
|
+
- oembed_links_example.yml
|
45
|
+
- oembed_links.gemspec
|
46
|
+
- lib
|
47
|
+
- lib/oembed_links.rb
|
48
|
+
- lib/oembed_links
|
49
|
+
- lib/oembed_links/formatters
|
50
|
+
- lib/oembed_links/fetchers
|
51
|
+
- lib/oembed_links/response.rb
|
52
|
+
- lib/oembed_links/formatters/json.rb
|
53
|
+
- lib/oembed_links/formatters/xml.rb
|
54
|
+
- lib/oembed_links/fetchers/net_http.rb
|
55
|
+
- rails
|
56
|
+
- rails/init.rb
|
57
|
+
- spec
|
58
|
+
- spec/spec_helper.rb
|
59
|
+
- spec/oembed_links_spec.rb
|
60
|
+
- spec/oembed_links_test.yml
|
61
|
+
has_rdoc: true
|
62
|
+
homepage: http://www.indystar.com
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "0"
|
79
|
+
version:
|
80
|
+
requirements: []
|
81
|
+
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.2.0
|
84
|
+
signing_key:
|
85
|
+
specification_version: 2
|
86
|
+
summary: a library for using the OEmbed format (http://oembed.com/) to acquire embedding information for freetext
|
87
|
+
test_files: []
|
88
|
+
|