Skip to content

Commit

Permalink
First working version
Browse files Browse the repository at this point in the history
  • Loading branch information
Bob Corsaro committed Jan 20, 2011
1 parent d4be3ff commit dbd768b
Show file tree
Hide file tree
Showing 12 changed files with 303 additions and 7 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Gemfile.lock
embedly.gemspec
pkg/

1 change: 1 addition & 0 deletions .rvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rvm 1.9.2@embedly
19 changes: 15 additions & 4 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "embedly"
gem.summary = %Q{TODO: one-line summary of your gem}
gem.description = %Q{TODO: longer description of your gem}
gem.summary = %Q{Ruby Embedly client library}
gem.description = %Q{Ruby Embedly client library}
gem.email = "bob@embed.ly"
gem.homepage = "http://github.com/dokipen/embedly"
gem.homepage = "http://github.com/embedly/embedly-ruby"
gem.authors = ["Bob Corsaro"]
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
gem.add_dependency "typhoeus", ">= 0"
gem.add_development_dependency "cucumber", ">= 0"
gem.add_development_dependency "jeweler", ">= 0"
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
Expand All @@ -25,6 +27,15 @@ Rake::TestTask.new(:test) do |test|
test.verbose = true
end

begin
require 'cucumber/rake/task'
Cucumber::Rake::Task.new(:features)
rescue LoadError
task :features do
abort "Cucumber is not installed"
end
end

begin
require 'rcov/rcovtask'
Rcov::RcovTask.new do |test|
Expand Down
11 changes: 11 additions & 0 deletions bin/objectify
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env ruby
$:.unshift(File.expand_path('../../lib', __FILE__))
%w{embedly json}.each {|l| require l}

Embedly::VERSION
urls = ARGV
api = Embedly::API.new :endpoint => 'http://localhost:8001'
urls.each do |url|
obj = api.objectify :url => url
puts JSON.pretty_generate(obj.marshal_dump)
end
11 changes: 11 additions & 0 deletions bin/oembed
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env ruby
$:.unshift(File.expand_path('../../lib', __FILE__))
%w{embedly json}.each {|l| require l}

Embedly::VERSION
urls = ARGV
api = Embedly::API.new
urls.each do |url|
obj = api.oembed :url => url
puts JSON.pretty_generate(obj.marshal_dump)
end
11 changes: 11 additions & 0 deletions bin/preview
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env ruby
$:.unshift(File.expand_path('../../lib', __FILE__))
%w{embedly json}.each {|l| require l}

Embedly::VERSION
urls = ARGV
api = Embedly::API.new :endpoint => 'http://localhost:8001'
urls.each do |url|
obj = api.preview :url => url
puts JSON.pretty_generate(obj.marshal_dump)
end
1 change: 1 addition & 0 deletions cucumber.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default: features
61 changes: 61 additions & 0 deletions features/oembed.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
Feature: OEmbed

As an embedly user
I want to call the the embedly api
Because I want and oembed for a specific url

Scenario Outline: Get the provider_url
Given an embedly endpoint
When oembed is called with the <url> URL
Then the provider_url should be <provider_url>

Examples:
| url | provider_url |
| http://www.scribd.com/doc/13994900/Easter | http://www.scribd.com/ |
| http://www.scribd.com/doc/28452730/Easter-Cards | http://www.scribd.com/ |
| http://www.youtube.com/watch?v=Zk7dDekYej0 | http://www.youtube.com/ |
| http://tweetphoto.com/14784358 | http://plixi.com |


Scenario Outline: Get the provider_url with force flag
Given an embedly endpoint
When oembed is called with the <url> URL and force flag
Then the provider_url should be <provider_url>

Examples:
| url | provider_url |
| http://www.youtube.com/watch?v=Zk7dDekYej0 | http://www.youtube.com/ |


Scenario Outline: Get multiple provider_urls
Given an embedly endpoint
When oembed is called with the <urls> URLs
Then provider_url should be <provider_urls>

Examples:
| urls | provider_urls |
| http://www.scribd.com/doc/13994900/Easter,http://www.scribd.com/doc/28452730/Easter-Cards | http://www.scribd.com/,http://www.scribd.com/ |
| http://www.youtube.com/watch?v=Zk7dDekYej0,http://plixi.com/p/16044847 | http://www.youtube.com/,http://plixi.com |


Scenario Outline: Get the provider_url with pro
Given an embedly endpoint with 3ea4fc74fd6d11df84894040444cdc60 key
When oembed is called with the <url> URL
Then the provider_url should be <provider_url>

Examples:
| url | provider_url |
| http://blog.embed.ly/bob | http://posterous.com |
| http://blog.doki-pen.org/cassandra-rules | http://posterous.com |


Scenario Outline: Attempt to get 404 URL
Given an embedly endpoint
When oembed is called with the <url> URLs
Then type should be error
And error_code should be 404

Examples:
| url |
| http://www.youtube.com/this/is/a/bad/url |

30 changes: 30 additions & 0 deletions features/steps/api_steps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
$:.unshift(File.expand_path('../../../lib',__FILE__))
require 'embedly'

Given /an embedly endpoint( [^\s]+)?( with ([^\s]+) key)?$/ do |endpoint, _, key|
opts = {}
opts[:endpoint] = endpoint
opts[:key] = key
@api = Embedly::API.new opts
end

When /oembed is called with the (.*) URLs?( and ([^\s]+) flag)?$/ do |urls, _, flag|
urls = urls.split(',')
opts = {}
if urls.size == 1
opts[:url] = urls.first
else
opts[:urls] = urls
end
opts[flag.to_sym] = true if flag
@result = @api.oembed opts
end

Then /([^\s]+) should be ([^\s]+)/ do |key, value|
if @result.is_a?Array
@result.collect{|o| puts o.provider_url; puts key; puts o.send(key); o.send(key).to_s}.join(',').should == value
else
@result.send(key).to_s.should == value
end
end

14 changes: 14 additions & 0 deletions lib/embedly.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'logger'

module Embedly
VERSION = File.read(File.expand_path('../../VERSION', __FILE__))

def self.logger(name)
@_loggers ||= {}
@_loggers[name] ||= Logger.new(STDOUT)
@_loggers[name].level = Logger::ERROR
@_loggers[name]
end
end

require 'embedly/api'
141 changes: 141 additions & 0 deletions lib/embedly/api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
require 'net/http'
require 'json'
require 'ostruct'

# = Embedly::API =
#
# Performs api calls to embedly.
#
# You won't find methods. We are using method_missing and passing the method
# name to apicall.
#
# == Parameters ==
# :+endpoint+: Hostname of embedly server. Defaults to api.embed.ly if no key
# is provided, pro.embed.ly if key is provided.
# :+key+: Your pro.embed.ly api key.
#
# == Currently Supported Methods ==
#
# * +oembed+
# * +objectify+
# * +preview+ _pro-only_
#
# Call parameters should be passed as the opts parameter. If set, key will
# automatically be added to the query string of the call, so no need to set it.
#
# This API _would_ be future compatible, if not for the version. In order to
# add support for a new method, you will need to add a version to the
# api_version hash. Here is an example.
#
# api = Embedly::API.new
# api.api_version['new_method'] = 3
# api.new_method :arg1 => '1', :arg2 => '2'
#
class Embedly::API
attr_reader :key, :endpoint, :api_version

# = Parameters =
# :+endpoint+: Hostname of embedly server. Defaults to api.embed.ly if no key
# is provided, pro.embed.ly if key is provided.
# :+key+: Your pro.embed.ly api key.
def initialize opts={}
@key = opts[:key]
if @key
@endpoint = opts[:endpoint] || 'pro.embed.ly'
else
@endpoint = opts[:endpoint] || 'api.embed.ly'
end
@api_versions = Hash.new('1').merge!({'objectify' => '2'})
end

# Normalizes url and urls parameters and calls the endpoint. url OR urls
# must be present
#
# == Options ==
# :+url+: _(optional)_ A single url
# :+urls+: _(optional)_ An array of urls
# :+action+: The method that should be called. ex. oembed, objectify, preview
# :+version+: The api version number.
# _others_: All other parameters are used as query strings.
def apicall opts
opts[:urls] ||= []
opts[:urls] << opts[:url] if opts[:url]

raise 'must pass urls' if opts[:urls].size == 0

if opts[:urls].size == 1
params = {:url => opts[:urls].first}
else
params = {:urls => opts[:urls]}
end

params[:key] = key if key
params.merge!opts.select{|k,_| not [:url, :urls, :action, :version].index k}

path = "/#{opts[:version]}/#{opts[:action]}?#{q params}"
logger.debug { "calling http://#{endpoint}#{path}" }
http = Net::HTTP.new(endpoint)
request = Net::HTTP::Get.new(path)
response = http.request(request)

# passing url vs. urls causes different things to happen on errors (like a
# 404 for the URL). using the url parameter returns a non 200 error code
# in the response. Using urls causes an error json object to be returned,
# but the main call will still be status 200. Below, we try to canonize as
# best we can but it should really be changed server side.
if response.code.to_i == 200
logger.debug { response.body }
# [].flatten is to be sure we have an array
objs = [JSON.parse(response.body)].flatten.collect {|o| OpenStruct.new(o)}
else
objs = OpenStruct.new :type => 'error', :error_code => response.code.to_i
end

if objs.size == 1
objs.first
else
objs
end
end

private
# Performs api call based on method name
#
# == Currently supported ==
#
# * +oembed+
# * +objectify+
# * +preview+ _pro-only_
#
def method_missing(name, *args, &block)
opts = args[0]
opts[:action] = name
opts[:version] = @api_versions[name]
apicall opts
end

# Escapes url parameters
# TODO: move to utils
def escape s
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/u) do
'%'+$1.unpack('H2'*$1.bytesize).join('%').upcase
end.tr(' ', '+')
end

# Creates query string
# TODO: move to utils
def q params
params.collect do |k,v|
if v.is_a?Array
"#{k.to_s}=#{v.collect{|i|escape(i)}.join(',')}"
else
"#{k.to_s}=#{escape(v)}"
end
end.join('&')
end

def logger
@logger ||= Embedly.logger('API')
end

end
6 changes: 3 additions & 3 deletions test/test_embedly.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'helper'

class TestEmbedly < Test::Unit::TestCase
should "probably rename this file and start testing for real" do
flunk "hey buddy, you should probably rename this file and start testing for real"
end
#should "probably rename this file and start testing for real" do
# flunk "hey buddy, you should probably rename this file and start testing for real"
#end
end

0 comments on commit dbd768b

Please sign in to comment.