├── .gitignore ├── ChangeLog ├── Gemfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── VERSION ├── bin ├── embedly_extract ├── embedly_objectify ├── embedly_oembed └── embedly_preview ├── cucumber.yml ├── embedly.gemspec ├── features ├── command_line.feature ├── extract.feature ├── oembed.feature └── steps │ ├── api_steps.rb │ └── env.rb ├── lib ├── embedly.rb └── embedly │ ├── api.rb │ ├── command_line.rb │ ├── configuration.rb │ ├── exceptions.rb │ ├── model.rb │ ├── request.rb │ └── request │ ├── base.rb │ ├── faraday.rb │ ├── net_http.rb │ └── typhoeus.rb └── spec ├── embedly ├── api_spec.rb ├── command_line_spec.rb └── configuration_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | pkg/ 3 | rdoc/ 4 | doc/ 5 | .yardoc/ 6 | .ruby-gemset 7 | .ruby-version 8 | 9 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | *embedly-1.9.1 (27 Jan 2014) 2 | 17 Jan 2014: Bob Corsaro 3 | Don't require faraday to be installed 4 | 5 | *embedly-1.9.0 (21 Jan 2014) 6 | 15 Jan 2014: Roman Shterenzon 7 | Adds a Faraday request adapter 8 | Remove Typhoeus as dependency, if it is installed manually, it will be 9 | used by default 10 | 11 | *embedly-1.8.0 (17 Jun 2013) 12 | 13 | 17 Jun 2013: Felipe Elias Philipp 14 | Abstracts http client to allow for custom requesters 15 | 16 | 17 Jun 2013: Nitesh 17 | Gets key from Embedly::Configuration 18 | 19 | *embedly-1.7.0 (17 Jun 2013) 20 | 21 | 17 Jun 2013: Anton Dieterle 22 | Adds Net::HTTP::Proxy support 23 | 24 | *embedly-1.6.0 (2 Apr 2013) 25 | 26 | 2 Apr 2013: Bob Corsaro 27 | Adds extract endpoint 28 | 29 | *embedly-1.5.6 (23 May 2012) 30 | 31 | 23 May 2012: Bob Corsaro 32 | Fixes bug where EmbedlyObject fails with nil parameter 33 | 34 | *embedly-1.5.5 (11 May 2012) 35 | 36 | 11 May 2012: Bob Corsaro 37 | Remove Typhoeus as dependency for jruby 38 | 39 | *embedly-1.5.0 (22 Nov 2011) 40 | 41 | 22 Nov 2011: Russ Bradberry 42 | Adds URL length check 43 | 44 | *embedly-1.4.1 (17 Nov 2011) 45 | 46 | 17 Nov 2011: Bob Corsaro 47 | Adds attributions 48 | 49 | *embedly-1.4.0 (17 Nov 2011) 50 | 51 | 17 Nov 2011: Bob Corsaro 52 | Adds timeout support to cli 53 | 54 | 18 Oct 2011: Arun Thampi 55 | Adds better error messages 56 | 57 | *embedly-1.3.0 (12 Sep 2011) 58 | 59 | 12 Sep 2011: Russ Bradberry, Bob Corsaro 60 | Adds Typhoeus support 61 | 62 | 12 Sep 2011: Russ Bradberry 63 | Adds timeout support 64 | 65 | *embedly-1.2.0 (12 Sep 2011) 66 | 67 | 12 Sep 2011: Felipe Elias Philipp 68 | Adds Configuration class to make configuration Embedly simpler 69 | 70 | *embedly-1.1.0 (7 Sep 2011) 71 | 72 | 7 Sep 2011: Felipe Elias Philipp 73 | Improves CLI 74 | 75 | 7 Sep 2011: Bob Corsaro 76 | Support OAuth 77 | 78 | 7 Sep 2011: Bob Corsaro 79 | Update specs 80 | 81 | 82 | *embedly-1.0.0 (22 Jun 2011) 83 | 84 | 22 Jun 2011: Bob Corsaro 85 | Support changes to API 86 | 87 | 22 Jun 2011: Bob Corsaro 88 | Adds header, host and no-key CLI args 89 | 90 | 22 Jun 2011: Bob Corsaro 91 | Better exception handling 92 | 93 | *embedly-0.4.0 (20 May 2011) 94 | 95 | 20 May 2011: Bob Corsaro 96 | Support hostnames without tlds 97 | 98 | 20 May 2011: Bob Corsaro 99 | Bug fixes 100 | 101 | *embedly-0.3.3 (20 Feb 2011) 102 | 103 | 20 Feb 2011: Bob Corsaro 104 | Fixes namespace issue 105 | 106 | *embedly-0.3.0 (04 Feb 2011) 107 | 108 | 04 Feb 2011: Bob Corsaro 109 | Services methods always return an array, even for a single URL 110 | 111 | 04 Feb 2011: Bob Corsaro 112 | URLs are checked against the services regex for non-Pro calls 113 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "querystring" 4 | gem "oauth" 5 | gem "json" 6 | 7 | group :development do 8 | gem "typhoeus" 9 | gem "jeweler" 10 | gem "cucumber" 11 | gem "rake" 12 | gem "rspec" 13 | gem "yard" 14 | gem "aruba" 15 | gem "faraday" 16 | end 17 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Embed.ly, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # embedly 2 | 3 | embedly is the Embedly Ruby client library and commandline tool. It allows 4 | you to integrate Embedly into your Ruby applications, as well as use 5 | Embedly's API from the commandline. 6 | 7 | To find out what Embedly is all about, please visit http://embed.ly. To see 8 | our api documentation, visit http://api.embed.ly/docs. 9 | 10 | ## Installing 11 | 12 | To install the official latest stable version, please use rubygems. 13 | 14 | gem install embedly 15 | 16 | If you would like cutting edge, then you can clone and install HEAD. 17 | 18 | git clone git://github.com/embedly/embedly-ruby.git 19 | cd embedly-ruby 20 | rake install 21 | 22 | ## Requirements 23 | 24 | * querystring 25 | 26 | ## Getting Started 27 | 28 | You can find rdocs at http://rubydoc.info/github/embedly/embedly-ruby/master/frames 29 | 30 | ```ruby 31 | require 'embedly' 32 | require 'json' 33 | 34 | embedly_api = 35 | Embedly::API.new :user_agent => 'Mozilla/5.0 (compatible; mytestapp/1.0; my@email.com)' 36 | 37 | # single url 38 | obj = embedly_api.oembed :url => 'http://www.youtube.com/watch?v=sPbJ4Z5D-n4&feature=topvideos' 39 | puts obj[0].marshal_dump 40 | json_obj = JSON.pretty_generate(obj[0].marshal_dump) 41 | puts json_obj 42 | 43 | # multiple urls with opts 44 | objs = embedly_api.oembed( 45 | :urls => ['http://www.youtube.com/watch?v=sPbJ4Z5D-n4&feature=topvideos', 46 | 'http://twitpic.com/3yr7hk'], 47 | :maxwidth => 450, 48 | :wmode => 'transparent', 49 | :method => 'after' 50 | ) 51 | json_obj = JSON.pretty_generate(objs.collect{|o| o.marshal_dump}) 52 | puts json_obj 53 | 54 | # call api with key (you'll need a real key) 55 | embedly_api = Embedly::API.new :key => 'xxxxxxxxxxxxxxxxxxxxxxxxxx', 56 | :user_agent => 'Mozilla/5.0 (compatible; mytestapp/1.0; my@email.com)' 57 | url = 'http://www.guardian.co.uk/media/2011/jan/21/andy-coulson-phone-hacking-statement' 58 | obj = embedly_api.extract :url => url 59 | puts JSON.pretty_generate(obj[0].marshal_dump) 60 | ``` 61 | 62 | ## Configuration options 63 | 64 | You can configure some parameters in the api: 65 | 66 | ```ruby 67 | Embedly.configure do |config| 68 | # prints debug messages to the logger 69 | config.debug = true 70 | 71 | # use a custom logger 72 | config.logger = MyAwesomeLogger.new(STDERR) 73 | 74 | # Choose a request adatper (net_http, typhoeus or faraday) 75 | config.request_with :faraday 76 | end 77 | ``` 78 | 79 | ## Testing 80 | 81 | gem install jeweler 82 | rake spec 83 | rake features # if it complains of missing deps install them 84 | 85 | Some tests will fail due to missing api key. Set the `EMBEDLY_KEY` environmental 86 | variable with your key to get them to pass. 87 | 88 | EMBEDLY_KEY=xxxxxxxxxxxxx rake features 89 | 90 | To turn on debugging, set the `EMBEDLY_VERBOSE` environmental variable. 91 | 92 | EMBEDLY_VERBOSE=1 EMBEDLY_KEY=xxxxxxxxxxx rake features 93 | 94 | We have provided some commandline tools to test the Embedly interface. 95 | 96 | * embedly_oembed 97 | * embedly_objectify 98 | * embedly_preview 99 | 100 | Using `--help` with the commands should give you a good idea of how to use them. 101 | 102 | ## Note on Patches/Pull Requests 103 | 104 | * Fork the project. 105 | * Make your feature addition or bug fix. 106 | * Add tests for it. This is important so I don't break it in a 107 | future version unintentionally. 108 | * Commit, do not mess with rakefile, version, or history. 109 | (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) 110 | * Send me a pull request. Bonus points for topic branches. 111 | 112 | ## Copyright 113 | 114 | Copyright (c) 2011 Embed.ly, Inc. See MIT-LICENSE for details. 115 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | 4 | begin 5 | Bundler.setup(:default, :development) 6 | rescue Bundler::BundlerError => e 7 | $stderr.puts e.message 8 | $stderr.puts "Run `bundle install` to install missing gems" 9 | exit e.status_code 10 | end 11 | 12 | require 'rake' 13 | require 'jeweler' 14 | 15 | Jeweler::Tasks.new do |gem| 16 | gem.name = "embedly" 17 | gem.summary = %Q{Ruby Embedly client library} 18 | gem.description = %Q{Ruby Embedly client library} 19 | gem.email = "bob@embed.ly" 20 | gem.homepage = "http://github.com/embedly/embedly-ruby" 21 | gem.authors = [ 22 | "Bob Corsaro", 23 | "Felipe Elias Philipp", 24 | "Russ Bradberry", 25 | "Arun Thampi", 26 | "Anton Dieterle", 27 | "Nitesh", 28 | "Roman Shterenzon", 29 | ] 30 | gem.license = "MIT" 31 | 32 | # in Gemfile 33 | end 34 | Jeweler::GemcutterTasks.new 35 | 36 | require 'cucumber/rake/task' 37 | Cucumber::Rake::Task.new(:features) 38 | 39 | require "rspec/core/rake_task" 40 | RSpec::Core::RakeTask.new(:spec) do |t| 41 | t.rspec_opts = %w[--color] 42 | t.verbose = false 43 | end 44 | 45 | require 'yard' 46 | YARD::Rake::YardocTask.new do |t| 47 | t.files = FileList['lib/**/*.rb'].exclude('lib/jeweler/templates/**/*.rb') 48 | end 49 | 50 | require 'rdoc/task' 51 | Rake::RDocTask.new do |rdoc| 52 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 53 | 54 | rdoc.rdoc_dir = 'rdoc' 55 | rdoc.title = "embedly #{version}" 56 | rdoc.rdoc_files.include('README*') 57 | rdoc.rdoc_files.include('lib/**/*.rb') 58 | end 59 | 60 | task :all_specs => [:spec, :features] 61 | 62 | task :default => :all_specs 63 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.9.1 -------------------------------------------------------------------------------- /bin/embedly_extract: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift(File.expand_path('../../lib', __FILE__)) 3 | %w{embedly embedly/command_line json optparse ostruct}.each {|l| require l} 4 | 5 | api = Embedly::CommandLine.run!(:extract, ARGV) 6 | 7 | begin 8 | data = api.flatten.collect { |o| o.marshal_dump } 9 | puts JSON.pretty_generate(data) 10 | rescue Embedly::BadResponseException => e 11 | puts "#{e.response.code} :: #{e.response.message}" 12 | end 13 | -------------------------------------------------------------------------------- /bin/embedly_objectify: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift(File.expand_path('../../lib', __FILE__)) 3 | %w{embedly embedly/command_line json optparse ostruct}.each {|l| require l} 4 | 5 | api = Embedly::CommandLine.run!(:objectify, ARGV) 6 | 7 | begin 8 | data = api.flatten.collect { |o| o.marshal_dump } 9 | puts JSON.pretty_generate(data) 10 | rescue Embedly::BadResponseException => e 11 | puts "#{e.response.code} :: #{e.response.message}" 12 | end 13 | -------------------------------------------------------------------------------- /bin/embedly_oembed: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift(File.expand_path('../../lib', __FILE__)) 3 | %w{embedly embedly/command_line json optparse ostruct}.each {|l| require l} 4 | 5 | api = Embedly::CommandLine.run!(:oembed, ARGV) 6 | 7 | begin 8 | data = api.flatten.collect { |o| o.marshal_dump } 9 | puts JSON.pretty_generate(data) 10 | rescue Embedly::BadResponseException => e 11 | puts "#{e.response.code} :: #{e.response.message}" 12 | end 13 | -------------------------------------------------------------------------------- /bin/embedly_preview: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $:.unshift(File.expand_path('../../lib', __FILE__)) 3 | %w{embedly embedly/command_line json optparse ostruct}.each {|l| require l} 4 | 5 | api = Embedly::CommandLine.run!(:preview, ARGV) 6 | 7 | begin 8 | data = api.flatten.collect { |o| o.marshal_dump } 9 | puts JSON.pretty_generate(data) 10 | rescue Embedly::BadResponseException => e 11 | puts "#{e.response.code} :: #{e.response.message}" 12 | end 13 | -------------------------------------------------------------------------------- /cucumber.yml: -------------------------------------------------------------------------------- 1 | default: features 2 | -------------------------------------------------------------------------------- /embedly.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "embedly" 8 | s.version = "1.9.1" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Bob Corsaro", "Felipe Elias Philipp", "Russ Bradberry", "Arun Thampi", "Anton Dieterle", "Nitesh", "Roman Shterenzon"] 12 | s.date = "2014-01-27" 13 | s.description = "Ruby Embedly client library" 14 | s.email = "bob@embed.ly" 15 | s.executables = ["embedly_extract", "embedly_objectify", "embedly_oembed", "embedly_preview"] 16 | s.extra_rdoc_files = [ 17 | "ChangeLog", 18 | "README.md" 19 | ] 20 | s.files = [ 21 | "ChangeLog", 22 | "Gemfile", 23 | "MIT-LICENSE", 24 | "README.md", 25 | "Rakefile", 26 | "VERSION", 27 | "bin/embedly_extract", 28 | "bin/embedly_objectify", 29 | "bin/embedly_oembed", 30 | "bin/embedly_preview", 31 | "cucumber.yml", 32 | "embedly.gemspec", 33 | "features/command_line.feature", 34 | "features/objectify.feature", 35 | "features/oembed.feature", 36 | "features/steps/api_steps.rb", 37 | "features/steps/env.rb", 38 | "lib/embedly.rb", 39 | "lib/embedly/api.rb", 40 | "lib/embedly/command_line.rb", 41 | "lib/embedly/configuration.rb", 42 | "lib/embedly/exceptions.rb", 43 | "lib/embedly/model.rb", 44 | "lib/embedly/request.rb", 45 | "lib/embedly/request/base.rb", 46 | "lib/embedly/request/faraday.rb", 47 | "lib/embedly/request/net_http.rb", 48 | "lib/embedly/request/typhoeus.rb", 49 | "spec/embedly/api_spec.rb", 50 | "spec/embedly/command_line_spec.rb", 51 | "spec/embedly/configuration_spec.rb", 52 | "spec/spec_helper.rb" 53 | ] 54 | s.homepage = "http://github.com/embedly/embedly-ruby" 55 | s.licenses = ["MIT"] 56 | s.require_paths = ["lib"] 57 | s.rubygems_version = "1.8.25" 58 | s.summary = "Ruby Embedly client library" 59 | 60 | if s.respond_to? :specification_version then 61 | s.specification_version = 3 62 | 63 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 64 | s.add_runtime_dependency(%q, [">= 0"]) 65 | s.add_runtime_dependency(%q, [">= 0"]) 66 | s.add_runtime_dependency(%q, [">= 0"]) 67 | s.add_development_dependency(%q, [">= 0"]) 68 | s.add_development_dependency(%q, [">= 0"]) 69 | s.add_development_dependency(%q, [">= 0"]) 70 | s.add_development_dependency(%q, [">= 0"]) 71 | s.add_development_dependency(%q, [">= 0"]) 72 | s.add_development_dependency(%q, [">= 0"]) 73 | s.add_development_dependency(%q, [">= 0"]) 74 | s.add_development_dependency(%q, [">= 0"]) 75 | else 76 | s.add_dependency(%q, [">= 0"]) 77 | s.add_dependency(%q, [">= 0"]) 78 | s.add_dependency(%q, [">= 0"]) 79 | s.add_dependency(%q, [">= 0"]) 80 | s.add_dependency(%q, [">= 0"]) 81 | s.add_dependency(%q, [">= 0"]) 82 | s.add_dependency(%q, [">= 0"]) 83 | s.add_dependency(%q, [">= 0"]) 84 | s.add_dependency(%q, [">= 0"]) 85 | s.add_dependency(%q, [">= 0"]) 86 | s.add_dependency(%q, [">= 0"]) 87 | end 88 | else 89 | s.add_dependency(%q, [">= 0"]) 90 | s.add_dependency(%q, [">= 0"]) 91 | s.add_dependency(%q, [">= 0"]) 92 | s.add_dependency(%q, [">= 0"]) 93 | s.add_dependency(%q, [">= 0"]) 94 | s.add_dependency(%q, [">= 0"]) 95 | s.add_dependency(%q, [">= 0"]) 96 | s.add_dependency(%q, [">= 0"]) 97 | s.add_dependency(%q, [">= 0"]) 98 | s.add_dependency(%q, [">= 0"]) 99 | s.add_dependency(%q, [">= 0"]) 100 | end 101 | end 102 | 103 | -------------------------------------------------------------------------------- /features/command_line.feature: -------------------------------------------------------------------------------- 1 | Feature: Command line runner 2 | As an embedly user 3 | I want to call the the embedly api via command line 4 | 5 | Scenario: Run oembed command 6 | When I run `embedly_oembed https://placekitten.com/200/300` 7 | Then the output should contain: 8 | """ 9 | "provider_url": "https://placekitten.com" 10 | """ 11 | 12 | Scenario: Run oembed command verbosely 13 | When I run `embedly_oembed -v http://lockerz.com/s/136425091` 14 | Then the output should contain: 15 | """ 16 | DEBUG -- : calling http://api.embed.ly/1/oembed 17 | """ 18 | -------------------------------------------------------------------------------- /features/extract.feature: -------------------------------------------------------------------------------- 1 | Feature: Extract 2 | 3 | As an embedly user 4 | I want to call the the embedly api 5 | Because I want to extract a url 6 | 7 | Scenario Outline: Get the meta description with pro 8 | Given an embedly api with key 9 | When extract is called with the URL 10 | Then the description should start with 11 | And objectify api_version is 2 12 | 13 | Examples: 14 | | url | metadesc | 15 | | https://www.youtube.com/watch?v=jNQXAC9IVRw | The first video on YouTube. | 16 | -------------------------------------------------------------------------------- /features/oembed.feature: -------------------------------------------------------------------------------- 1 | Feature: OEmbed 2 | 3 | As an embedly user 4 | I want to call the the embedly api 5 | Because I want and oembed for a specific url 6 | 7 | Scenario Outline: Get the provider_url 8 | Given an embedly api with key 9 | When oembed is called with the URL 10 | Then the provider_url should be 11 | 12 | Examples: 13 | | url | provider_url | 14 | | https://www.youtube.com/watch?v=Zk7dDekYej0 | https://www.youtube.com/ | 15 | | https://placekitten.com/200/300 | https://placekitten.com | 16 | 17 | 18 | Scenario Outline: Get the types 19 | Given an embedly api with key 20 | When oembed is called with the URL 21 | Then the type should be 22 | 23 | Examples: 24 | | url | type | 25 | | https://github.com/embedly/embedly-ruby | link | 26 | | http://www.youtube.com/watch?v=Zk7dDekYej0 | video | 27 | | https://placekitten.com/200/300 | photo | 28 | 29 | 30 | Scenario Outline: Get the provider_url with force flag 31 | Given an embedly api with key 32 | When oembed is called with the URL and force flag 33 | Then the provider_url should be 34 | 35 | Examples: 36 | | url | provider_url | 37 | | https://www.youtube.com/watch?v=Zk7dDekYej0 | https://www.youtube.com/ | 38 | 39 | 40 | Scenario Outline: Get multiple provider_urls 41 | Given an embedly api with key 42 | When oembed is called with the URLs 43 | Then provider_url should be 44 | 45 | Examples: 46 | | urls | provider_urls | 47 | | https://www.youtube.com/watch?v=jNQXAC9IVRw,https://www.youtube.com/watch?v=dQw4w9WgXcQ | https://www.youtube.com/,https://www.youtube.com/ | 48 | | https://www.youtube.com/watch?v=Zk7dDekYej0,https://placekitten.com/200/300 | https://www.youtube.com/,https://placekitten.com | 49 | 50 | 51 | Scenario Outline: Attempt to get 404 URL 52 | Given an embedly api with key 53 | When oembed is called with the URL 54 | Then type should be error 55 | And error_code should be 404 56 | And type should be error 57 | 58 | Examples: 59 | | url | 60 | | http://www.youtube.com/watch/is/a/bad/url | 61 | | https://fav.me/alsfsdf | 62 | 63 | 64 | Scenario Outline: Attempt multi get 404 URLs 65 | Given an embedly api with key 66 | When oembed is called with the URLs 67 | Then error_code should be 68 | And type should be 69 | 70 | Examples: 71 | | urls | errcode | types | 72 | | https://www.youtube.com/watch/a/bassd/url,http://www.youtube.com/watch/ldf/asdlfj | 404,404 | error,error | 73 | | https://www.youtube.com/watch/zzzzasdf/kl,http://placekitten.com/g/200/300 | 404, | error,photo | 74 | | https://placekitten.com/200/300,https://www.youtube.com/watch/asdfasdf/asdf | ,404 | photo,error | 75 | 76 | Scenario Outline: Attempt to get 414 URL 77 | Given an embedly api with key 78 | When oembed is called with the URL 79 | Then type should be error 80 | And error_code should be 414 81 | And type should be error 82 | 83 | Examples: 84 | | url | 85 | | http://www.youtube.com/watch/is/a/bad/url/because/it/is/longer/than/2048/characters/this/is/much/too/long/for/a/url/and/shouldnt/even/be/sent/to/the/server/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| 86 | -------------------------------------------------------------------------------- /features/steps/api_steps.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(File.expand_path('../../../lib',__FILE__)) 2 | require 'embedly' 3 | 4 | # cache for hostnames 5 | HOSTNAMES = {} 6 | 7 | Given /an embedly api( with key)?$/ do |key_enabled| 8 | opts = {} 9 | if key_enabled 10 | raise 'Please set env variable $EMBEDLY_KEY' unless ENV['EMBEDLY_KEY'] 11 | opts[:key] = ENV["EMBEDLY_KEY"] 12 | opts[:secret] = ENV["EMBEDLY_SECRET"] 13 | end 14 | if not HOSTNAMES[opts] 15 | HOSTNAMES[opts] = Embedly::API.new opts 16 | end 17 | @api = HOSTNAMES[opts] 18 | end 19 | 20 | When /(\w+) is called with the (.*) URLs?( and ([^\s]+) flag)?$/ do |method, urls, _, flag| 21 | @result = nil 22 | begin 23 | urls = urls.split(',') 24 | opts = {} 25 | if urls.size == 1 26 | opts[:url] = urls.first 27 | else 28 | opts[:urls] = urls 29 | end 30 | opts[flag.to_sym] = true if flag 31 | @result = @api.send(method, opts) 32 | rescue 33 | @error = $! 34 | end 35 | end 36 | 37 | Then /an? (\w+) error should get thrown/ do |error| 38 | @error.class.to_s.should == error 39 | end 40 | 41 | Then /objectify api_version is (\d+)$/ do |version| 42 | @api.api_version[:objectify].should == version 43 | end 44 | 45 | Then /([^\s]+) should be (.+)$/ do |key, value| 46 | raise @error if @error 47 | @result.collect do |o| 48 | o.send(key).to_s 49 | end.join(',').should == value 50 | end 51 | 52 | Then /([^\s]+) should start with ([^\s]+)/ do |key, value| 53 | raise @error if @error 54 | v = key.split('.').inject(@result[0]){|o,c| o.send(c)}.to_s 55 | v.to_s.should match(/^#{value}/) 56 | end 57 | -------------------------------------------------------------------------------- /features/steps/env.rb: -------------------------------------------------------------------------------- 1 | require 'aruba/cucumber' 2 | require 'embedly' 3 | 4 | Before do 5 | @aruba_timeout_seconds = 15 6 | end 7 | 8 | Embedly.configure do |config| 9 | config.debug = !!ENV["EMBEDLY_VERBOSE"] 10 | end 11 | -------------------------------------------------------------------------------- /lib/embedly.rb: -------------------------------------------------------------------------------- 1 | module Embedly 2 | VERSION = File.read(File.expand_path('../../VERSION', __FILE__)).strip 3 | 4 | class << self 5 | def configure 6 | yield configuration 7 | end 8 | 9 | def configuration 10 | @configuration ||= Configuration.new 11 | end 12 | end 13 | end 14 | 15 | require 'embedly/api' 16 | -------------------------------------------------------------------------------- /lib/embedly/api.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'ostruct' 3 | require 'embedly/configuration' 4 | require 'embedly/model' 5 | require 'embedly/exceptions' 6 | require 'embedly/request' 7 | require 'querystring' 8 | require 'oauth' 9 | 10 | # Performs api calls to embedly. 11 | # 12 | # You won't find methods. We are using method_missing and passing the method 13 | # name to apicall. 14 | # 15 | # === Currently Supported Methods 16 | # 17 | # * +oembed+ 18 | # * +objectify+ 19 | # * +preview+ 20 | # * +extract+ 21 | # 22 | # All methods return ostructs, so fields can be accessed with the dot operator. ex. 23 | # 24 | # api = Embedly::API.new 25 | # obj = api.oembed :url => 'http://blog.doki-pen.org/' 26 | # puts obj[0].title, obj[0].description, obj[0].thumbnail_url 27 | # 28 | # Call parameters should be passed as the opts parameter. If set, key will 29 | # automatically be added to the query string of the call, so no need to set it. 30 | # 31 | # This API _would_ be future compatible, if not for the version. In order to 32 | # add support for a new method, you will need to add a version to the 33 | # api_version hash. Here is an example. 34 | # 35 | # api = Embedly::API.new 36 | # api.api_version[:new_method] = 3 37 | # api.new_method :arg1 => '1', :arg2 => '2' 38 | # 39 | class Embedly::API 40 | attr_reader :key, :hostname, :api_version, :headers, :secret, :proxy 41 | 42 | # === Options 43 | # 44 | # [:+hostname+] Hostname of embedly server. Defaults to api.embed.ly. 45 | # [:+key+] Your api.embed.ly key. 46 | # [:+secret+] Your api.embed.ly secret if you are using oauth. 47 | # [:+user_agent+] Your User-Agent header. Defaults to Mozilla/5.0 (compatible; embedly-ruby/VERSION;) 48 | # [:+timeout+] Request timeout (in seconds). Defaults to 180 seconds or 3 minutes 49 | # [:+headers+] Additional headers to send with requests. 50 | # [:+proxy+] Proxy settings in format {:host => '', :port => '', :user => '', :password => ''} 51 | def initialize opts={} 52 | @endpoints = [:oembed, :objectify, :preview, :extract] 53 | @key = opts[:key] || configuration.key 54 | @secret = opts[:secret] == "" ? nil : opts[:secret] 55 | @api_version = Hash.new('1') 56 | @api_version.merge!({:objectify => '2'}) 57 | @hostname = opts[:hostname] || 'https://api.embedly.com' 58 | @timeout = opts[:timeout] || 180 59 | @headers = { 60 | 'User-Agent' => opts[:user_agent] || "Mozilla/5.0 (compatible; embedly-ruby/#{Embedly::VERSION};)" 61 | }.merge(opts[:headers]||{}) 62 | @proxy = opts[:proxy] 63 | end 64 | 65 | def _do_oauth_call path 66 | consumer = OAuth::Consumer.new(key, secret, 67 | :site => site, 68 | :http_method => :get, 69 | :scheme => :query_string) 70 | # our implementation is broken for header authorization, thus the 71 | # query_string 72 | logger.debug "Calling with OAuth %s" % [path] 73 | req = consumer.create_signed_request(:get, path) 74 | signedpath = req.path 75 | 76 | logger.debug { "calling #{site}#{signedpath} with headers #{headers} using #{request}" } 77 | uri = URI.join(hostname, signedpath) 78 | request.get(uri, :headers => headers, :timeout => @timeout, :proxy => @proxy) 79 | end 80 | 81 | def _do_call path 82 | if key and secret 83 | _do_oauth_call path 84 | else 85 | uri = URI.join(hostname, path) 86 | logger.debug { "calling #{uri} with headers #{headers} using #{request}" } 87 | request.get(uri, :headers => headers, :timeout => @timeout, :proxy => @proxy) 88 | end 89 | end 90 | 91 | # Use methods oembed, objectify, preview and extract in favor of 92 | # this method. 93 | # 94 | # Normalizes url and urls parameters and calls the endpoint. url OR urls 95 | # must be present 96 | # 97 | # === Options 98 | # 99 | # [:+url+] _(optional)_ A single url 100 | # [:+urls+] _(optional)_ An array of urls 101 | # [:+action+] The method that should be called. ex. oembed, objectify, 102 | # preview, extract [:+version+] The api version number. 103 | # [_others_] All other parameters are used as query strings. 104 | def apicall opts 105 | opts[:urls] ||= [] 106 | opts[:urls] << opts[:url] if opts[:url] 107 | 108 | raise 'must pass urls' if opts[:urls].size == 0 109 | 110 | params = {:urls => opts[:urls]} 111 | 112 | # store unsupported services as errors and don't send them to embedly 113 | rejects = [] 114 | 115 | params[:urls].reject!.with_index do |url, i| 116 | if !key && url !~ services_regex 117 | rejects << [i, 118 | Embedly::EmbedlyObject.new( 119 | :type => 'error', 120 | :error_code => 401, 121 | :error_message => 'Embedly api key is required.' 122 | ) 123 | ] 124 | elsif url.length > 2048 125 | rejects << [i, 126 | Embedly::EmbedlyObject.new( 127 | :type => 'error', 128 | :error_code => 414, 129 | :error_message => 'URL too long.' 130 | ) 131 | ] 132 | end 133 | end 134 | 135 | if params[:urls].size > 0 136 | params[:key] = key if key and not secret 137 | params.merge!Hash[ 138 | opts.select{|k,_| not [:url, :urls, :action, :version].index k} 139 | ] 140 | 141 | path = "/#{opts[:version]}/#{opts[:action]}?#{QueryString.stringify(params)}" 142 | 143 | response = _do_call path 144 | 145 | if response.code.to_i == 200 146 | logger.debug { response.body } 147 | # [].flatten is to be sure we have an array 148 | objs = [JSON.parse(response.body)].flatten.collect do |o| 149 | Embedly::EmbedlyObject.new(o) 150 | end 151 | else 152 | logger.debug { response } 153 | raise Embedly::BadResponseException.new(response, path) 154 | end 155 | 156 | # re-insert rejects into response 157 | rejects.each do |i, obj| 158 | objs.insert i, obj 159 | end 160 | 161 | objs 162 | else 163 | # we only have rejects, return them without calling embedly 164 | rejects.collect{|i, obj| obj} 165 | end 166 | end 167 | 168 | # Returns structured data from the services API method. 169 | # 170 | # Response is cached per API object. 171 | # 172 | # see http://api.embed.ly/docs/service for a description of the response. 173 | def services 174 | if not @services 175 | response = _do_call '/1/services/ruby' 176 | raise 'services call failed', response if response.code.to_i != 200 177 | @services = JSON.parse(response.body) 178 | end 179 | @services 180 | end 181 | 182 | # Returns a regex suitable for checking urls against for non-key usage 183 | def services_regex 184 | r = services.collect {|p| p["regex"].join("|")}.join("|") 185 | Regexp.new r 186 | end 187 | 188 | # Performs api call based on method name 189 | # 190 | # === Currently supported 191 | # 192 | # - +oembed+ 193 | # - +objectify+ 194 | # - +preview+ 195 | # - +extract+ 196 | # 197 | def method_missing(name, *args, &block) 198 | if @endpoints.include?name 199 | opts = args[0] 200 | opts[:action] = name 201 | opts[:version] = @api_version[name] 202 | apicall opts 203 | else 204 | super 205 | end 206 | end 207 | 208 | def request 209 | configuration.current_requester.call(self) 210 | end 211 | 212 | private 213 | 214 | def uri_parse uri 215 | uri =~ %r{^((http(s?))://)?([^:/]+)(:([\d]+))?(/.*)?$} 216 | scheme = $2 || 'http' 217 | host = $4 218 | port = $6 ? $6 : ( scheme == 'https' ? 443 : 80) 219 | [scheme, host, port.to_i] 220 | end 221 | 222 | def site 223 | scheme, host, port = uri_parse hostname 224 | if (scheme == 'http' and port == 80) or (scheme == 'https' and port == 443) 225 | "#{scheme}://#{host}" 226 | else 227 | "#{scheme}://#{host}:#{port}" 228 | end 229 | end 230 | 231 | def logger 232 | configuration.logger 233 | end 234 | 235 | def configuration 236 | Embedly.configuration 237 | end 238 | end 239 | -------------------------------------------------------------------------------- /lib/embedly/command_line.rb: -------------------------------------------------------------------------------- 1 | require "optparse" 2 | 3 | module Embedly 4 | class CommandLine 5 | 6 | class Parser 7 | attr_accessor :options 8 | 9 | def initialize(args) 10 | @options, @args = default, args 11 | end 12 | 13 | def parse! 14 | parser.parse!(@args) 15 | set_urls! 16 | reject_nil! 17 | options 18 | rescue OptionParser::InvalidOption => error 19 | puts "ERROR: #{error.message}" 20 | puts parser.on_tail 21 | exit 22 | end 23 | 24 | def self.parse!(args) 25 | new(args).parse! 26 | end 27 | 28 | private 29 | 30 | def default 31 | { 32 | :key => ENV['EMBEDLY_KEY'], 33 | :secret => ENV['EMBEDLY_SECRET'], 34 | :timeout => nil, 35 | :headers => {}, 36 | :query => {} 37 | } 38 | end 39 | 40 | def reject_nil! 41 | options.reject! { |_, opt| opt.nil? } 42 | end 43 | 44 | def set_urls! 45 | raise(OptionParser::InvalidOption, "url required") if @args.empty? 46 | options[:query][:urls] = @args 47 | end 48 | 49 | def parser 50 | OptionParser.new do |parser| 51 | parser.banner = %{ 52 | Fetch JSON from the embedly service. 53 | Usage [OPTIONS] [url] .. 54 | } 55 | 56 | parser.separator "" 57 | parser.separator "Options:" 58 | 59 | parser.on('-H', '--hostname ENDPOINT', 'Embedly host. Default is api.embed.ly.') do |hostname| 60 | options[:hostname] = hostname 61 | end 62 | 63 | parser.on("--header NAME=VALUE", "HTTP header to send with requests.") do |hash| 64 | header, value = hash.split '=' 65 | options[:headers][header] = value 66 | end 67 | 68 | parser.on("-k", "--key KEY", "Embedly key [default: EMBEDLY_KEY environmental variable]") do |key| 69 | options[:key] = key 70 | end 71 | 72 | parser.on("-N", "--no-key", "Ignore EMBEDLY_KEY environmental variable") do |key| 73 | options[:key] = nil 74 | end 75 | 76 | parser.on("-s", "--secret SECRET", "Embedly secret [default: EMBEDLY_SECRET environmental variable]") do |secret| 77 | options[:secret] = secret 78 | end 79 | 80 | parser.on("--no-secret", "Ignore EMBEDLY_SECRET environmental variable") do 81 | options[:secret] = nil 82 | end 83 | 84 | parser.on("--timeout TIMEOUT", "Request timeout") do |timeout| 85 | options[:timeout] = timeout.to_i 86 | end 87 | 88 | parser.on("-o", "--option NAME=VALUE", "Set option to be passed as query param.") do |option| 89 | key, value = option.split('=') 90 | options[:query][key.to_sym] = value 91 | end 92 | 93 | parser.on("--no-typhoeus", "Don't use typhoeus.") do 94 | Embedly.configuration.request_with :net_http 95 | end 96 | 97 | parser.separator "" 98 | parser.separator "Common Options:" 99 | 100 | parser.on("-v", "--[no-]verbose", "Run verbosely") do |verbose| 101 | Embedly.configuration.debug = verbose 102 | end 103 | 104 | parser.on("-h", "--help", "Display this message") do 105 | puts parser 106 | exit 107 | end 108 | 109 | parser.separator "" 110 | parser.separator "Bob Corsaro " 111 | end 112 | end 113 | end 114 | 115 | class << self 116 | def run!(endpoint, args = []) 117 | new(args).run(endpoint) 118 | end 119 | end 120 | 121 | def initialize(args) 122 | @options, @args = {}, args 123 | end 124 | 125 | def run(endpoint = :oembed) 126 | api_options = options.dup 127 | query = api_options.delete(:query) 128 | Embedly::API.new(api_options).send(endpoint, query) 129 | end 130 | 131 | def options 132 | @options = Parser.parse!(@args.dup) 133 | @options 134 | end 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /lib/embedly/configuration.rb: -------------------------------------------------------------------------------- 1 | require 'logger' 2 | 3 | # Configure the api 4 | # 5 | # === Available settings 6 | # 7 | # * [+debug+] Prints debugging information to logger. Default +false+. Errors still will be logged 8 | # * [+logger+] Configure the logger; set this if you want to use a custom logger. 9 | # * [+request_with+] Sets the desired library to perform requests. Default is +Typhoeus+ 10 | # 11 | # === Usage 12 | # 13 | # Embedly.configure do |config| 14 | # # prints debug messages 15 | # config.debug = true 16 | # 17 | # # customize the logger 18 | # config.logger = MyAwesomeLogger.new(STDERR) 19 | # 20 | # # performs requests with net/http 21 | # config.request_with :net_http 22 | # end 23 | # 24 | class Embedly::Configuration 25 | attr_accessor :key, :requester # :nodoc: 26 | 27 | def initialize # :nodoc: 28 | self.reset 29 | end 30 | 31 | def debug? # :nodoc: 32 | self.logger.debug? 33 | end 34 | 35 | def debug=(true_or_false) # :nodoc: 36 | set_logger_level(true_or_false) 37 | end 38 | 39 | def logger # :nodoc: 40 | @logger ||= default_logger 41 | end 42 | 43 | def logger=(log) # :nodoc: 44 | @logger = log 45 | set_logger_level(self.debug?) 46 | end 47 | 48 | # Configures a new requester 49 | # 50 | # To add a new requester class, you can do the following: 51 | # 52 | # Embedly.configuration.add_requester :custom do |api| 53 | # MyRequester.new(api) 54 | # end 55 | # 56 | # The requester class should respond to +get+ method, which performs the request 57 | # for more details, see +embedly/request/base.rb+ 58 | def add_requester(name, &block) 59 | requesters[name] = block 60 | end 61 | 62 | def requesters # :nodoc: 63 | @requesters ||= {} 64 | end 65 | 66 | # Sets api to use the desired requester class 67 | # 68 | # When configuring the API, you can do the following: 69 | # 70 | # Embedly.configure do |config| 71 | # config.request_with :net_http 72 | # end 73 | # 74 | # This way, the API will use the +net_http+ class to perform requests 75 | def request_with(adapter_name) 76 | self.requester = adapter_name 77 | end 78 | 79 | # Returns the current configured request block 80 | def current_requester 81 | requesters[requester] 82 | end 83 | 84 | # reset configuration 85 | def reset 86 | self.logger = default_logger 87 | self.debug = false 88 | self.request_with :net_http 89 | end 90 | 91 | private 92 | 93 | def default_logger # :nodoc: 94 | Logger.new(STDERR) 95 | end 96 | 97 | def set_logger_level(true_or_false) # :nodoc: 98 | logger.level = true_or_false ? Logger::DEBUG : Logger::ERROR 99 | end 100 | end 101 | 102 | # Use typhoeus by default if it is installed 103 | begin 104 | require "typhoeus" 105 | Embedly.configuration.request_with :typhoeus 106 | rescue LoadError 107 | Embedly.configuration.request_with :net_http 108 | end 109 | -------------------------------------------------------------------------------- /lib/embedly/exceptions.rb: -------------------------------------------------------------------------------- 1 | class Embedly::BadResponseException < RuntimeError 2 | attr_accessor :response, :path 3 | 4 | def initialize(response, path = nil) 5 | @response ||= response 6 | @path ||= path 7 | end 8 | 9 | def message 10 | "Bad Response : #{@response.inspect} for path: #{@path.inspect}" 11 | end 12 | 13 | def inspect 14 | self.message 15 | end 16 | 17 | def to_s 18 | self.message 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/embedly/model.rb: -------------------------------------------------------------------------------- 1 | require 'ostruct' 2 | 3 | class Embedly::EmbedlyObject < OpenStruct 4 | 5 | # Resursively make ostruct 6 | def initialize(obj = nil) 7 | if obj 8 | o = obj.clone 9 | o.each do |k,v| 10 | if v.is_a?Hash 11 | o[k] = Embedly::EmbedlyObject.new v 12 | end 13 | end 14 | super o 15 | else 16 | super 17 | end 18 | end 19 | 20 | # for ruby 1.8.x, type should return @table[:type], not the 21 | # class. 22 | def type 23 | method_missing :type 24 | end 25 | 26 | def marshal_dump 27 | o = @table.clone 28 | o.each do |k,v| 29 | if v.is_a?Embedly::EmbedlyObject 30 | o[k] = v.marshal_dump 31 | end 32 | end 33 | return o 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /lib/embedly/request.rb: -------------------------------------------------------------------------------- 1 | require "embedly/request/base" 2 | require "embedly/request/net_http" 3 | require "embedly/request/typhoeus" 4 | require "embedly/request/faraday" 5 | -------------------------------------------------------------------------------- /lib/embedly/request/base.rb: -------------------------------------------------------------------------------- 1 | module Embedly 2 | module Request # :nodoc: 3 | # Interface to create custom requesters 4 | # 5 | # == The class 6 | # 7 | # If you want to define a custom requester, you should create a class that 8 | # inherits from this base class. 9 | # 10 | # The class should respond to +get+ that receives the uri object and the api 11 | # options. The method should perform a get request to Embedly api and return 12 | # a response object: 13 | # 14 | # class MyRequester < Embedly::Request::Base 15 | # def get(uri, options = {}) 16 | # # performs the request 17 | # end 18 | # end 19 | # 20 | # For more examples, see embedly/requests/*.rb files 21 | # 22 | # == Adding to configuration 23 | # 24 | # Make sure to add your class to +embedly+ requesters, like the following: 25 | # 26 | # Embedly.configuration.add_requester :my_requester do |api| 27 | # MyRequester.new(api) 28 | # end 29 | # 30 | # This way you can configure the API to use your custom request object: 31 | # 32 | # Embedly.configure do |config| 33 | # config.request_with :my_requester 34 | # end 35 | # 36 | class Base 37 | attr_accessor :api # :nodoc: 38 | 39 | # Receives the current api object 40 | def initialize(api) 41 | @api = api 42 | end 43 | 44 | # Implement this method 45 | def get(uri, options = {}) 46 | raise NotImplementedError 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/embedly/request/faraday.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'faraday' 3 | 4 | # Patch Faraday::Response to provide a status code 5 | module Faraday 6 | class Response 7 | alias_method :code, :status unless method_defined?(:code) 8 | end 9 | end 10 | 11 | module Embedly 12 | module Request 13 | class Faraday < Base 14 | def get(uri, options = {}) 15 | ::Faraday.get(uri.to_s, nil, options[:headers]) 16 | end 17 | end 18 | end 19 | end 20 | 21 | Embedly.configuration.add_requester :faraday do |api| 22 | Embedly::Request::Faraday.new(api) 23 | end 24 | rescue LoadError 25 | end 26 | -------------------------------------------------------------------------------- /lib/embedly/request/net_http.rb: -------------------------------------------------------------------------------- 1 | require "net/http" 2 | 3 | module Embedly 4 | module NetHTTP # :nodoc: 5 | class Request < Embedly::Request::Base 6 | # Perform request using net/http library 7 | def get(uri, options = {}) 8 | proxy = options['proxy'] 9 | http_class = if proxy 10 | logger.debug { 'using Net::HTTP::Proxy' } 11 | http_class = Net::HTTP::Proxy(proxy[:host], proxy[:port], proxy[:user], proxy[:password]) 12 | else 13 | Net::HTTP 14 | end 15 | 16 | http_class.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http| 17 | http.read_timeout = options[:timeout] 18 | http.get([uri.path, uri.query].join('?'), options[:headers]) 19 | end 20 | end 21 | end 22 | end 23 | end 24 | 25 | Embedly.configuration.add_requester :net_http do |api| 26 | Embedly::NetHTTP::Request.new(api) 27 | end 28 | -------------------------------------------------------------------------------- /lib/embedly/request/typhoeus.rb: -------------------------------------------------------------------------------- 1 | # TODO: a better solution is to only load the requester if it is in use, 2 | # rather than loading them all on startup. 3 | begin 4 | require "typhoeus" 5 | 6 | module Embedly 7 | module Typhoeus # :nodoc: 8 | class Request < Embedly::Request::Base 9 | # Perform request using typhoeus 10 | def get(uri, options = {}) 11 | options[:timeout] *= 1000 12 | ::Typhoeus::Request.get uri.to_s, :headers => options[:headers], :timeout => options[:timeout] 13 | end 14 | end 15 | end 16 | end 17 | 18 | Embedly.configuration.add_requester :typhoeus do |api| 19 | Embedly::Typhoeus::Request.new(api) 20 | end 21 | rescue LoadError 22 | end 23 | -------------------------------------------------------------------------------- /spec/embedly/api_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | 4 | module Embedly 5 | describe API do 6 | let(:api) { API.new :key => ENV['EMBEDLY_KEY'], :secret => ENV['EMBEDLY_SECRET'] } 7 | 8 | describe "logger" do 9 | let(:io) { StringIO.new } 10 | 11 | before do 12 | Embedly.configure do |c| 13 | c.debug = true 14 | c.logger = Logger.new(io) 15 | end 16 | end 17 | 18 | it "logs if debug is enabled" do 19 | api.oembed :url => 'http://blog.doki-pen.org/' 20 | io.string.should =~ %r{.*DEBUG -- : .*} 21 | end 22 | end 23 | 24 | describe "requesters" do 25 | describe "net/http" do 26 | before do 27 | Embedly.configure { |c| c.request_with :net_http } 28 | end 29 | 30 | it "sets the correct request adapter" do 31 | api.request.should be_a(Embedly::NetHTTP::Request) 32 | end 33 | end 34 | 35 | describe "typhoeus" do 36 | before do 37 | Embedly.configure { |c| c.request_with :typhoeus } 38 | end 39 | 40 | it "sets the correct request adapter" do 41 | api.request.should be_a(Embedly::Typhoeus::Request) 42 | end 43 | end 44 | 45 | describe "faraday" do 46 | before do 47 | Embedly.configure { |c| c.request_with :faraday } 48 | end 49 | 50 | it "sets the correct request adapter" do 51 | api.request.should be_a(Embedly::Request::Faraday) 52 | end 53 | 54 | it "calls faraday" do 55 | url = 'http://example.com' 56 | headers = {'User-Agent'=>'spec'} 57 | Faraday.should_receive(:get).with(url, nil, headers) 58 | api.request.get(URI.parse(url), headers: headers) 59 | end 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/embedly/command_line_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "embedly/command_line" 3 | 4 | module Embedly 5 | describe CommandLine do 6 | after do 7 | ENV['EMBEDLY_KEY'] = nil 8 | ENV['EMBEDLY_SECRET'] = nil 9 | end 10 | 11 | describe "::run!" do 12 | let(:arguments) { ['-k', 'MY_KEY', '--no-secret', 'http://yfrog.com/h7qqespj', '-o', 'maxwidth=10'] } 13 | let(:api) { double(API) } 14 | 15 | it "calls api with options" do 16 | API.should_receive(:new).with(:key => 'MY_KEY', :headers => {}) { api } 17 | api.should_receive(:oembed).with(:urls => ['http://yfrog.com/h7qqespj'], :maxwidth => '10') 18 | CommandLine.run!(:oembed, arguments) 19 | end 20 | 21 | it "raises an error if the arguments are empty" do 22 | $stdout = StringIO.new 23 | expect { 24 | CommandLine.run!(:oembed, []) 25 | }.to raise_error(SystemExit) 26 | end 27 | end 28 | 29 | describe "#run" do 30 | before do 31 | API.any_instance.stub(:oembed) 32 | end 33 | 34 | describe "with option --hostname" do 35 | %w[-H --hostname].each do |option| 36 | it "sets the hostname using #{option}" do 37 | command([option, "sth.embed.ly"])[:hostname].should == 'sth.embed.ly' 38 | end 39 | end 40 | end 41 | 42 | describe "with --header" do 43 | it "sets the header" do 44 | command(%w[--header Header=value])[:headers].should == { 'Header' => 'value' } 45 | end 46 | end 47 | 48 | describe "with --key" do 49 | %w[-k --key].each do |option| 50 | it "sets the key using #{option}" do 51 | command([option, "SOME_KEY"])[:key].should == 'SOME_KEY' 52 | end 53 | end 54 | 55 | it "gets the key from environment variables if no key was set" do 56 | ENV['EMBEDLY_KEY'] = 'ENVIRONMENT_KEY' 57 | 58 | command([])[:key].should == 'ENVIRONMENT_KEY' 59 | end 60 | end 61 | 62 | describe "with --secret" do 63 | %w[-s --secret].each do |option| 64 | it "sets the secret using #{option}" do 65 | command([option, "SECRET"])[:secret].should == 'SECRET' 66 | end 67 | end 68 | 69 | it "gets the secret from environment variables if no secret was set" do 70 | ENV['EMBEDLY_SECRET'] = 'ENVIRONMENT_SECRET' 71 | 72 | command([])[:secret].should == 'ENVIRONMENT_SECRET' 73 | end 74 | end 75 | 76 | describe "with --no-key" do 77 | %w[-N --no-key].each do |option| 78 | it "unsets the key using #{option}" do 79 | command([option])[:key].should be_nil 80 | end 81 | end 82 | end 83 | 84 | describe "with --no-secret" do 85 | it "unsets the secret" do 86 | command(['--no-secret'])[:secret].should be_nil 87 | end 88 | end 89 | 90 | describe "with --no-typhoeus" do 91 | it "sets the request with net/http" do 92 | command(['--no-typhoeus']) 93 | Embedly.configuration.requester.should == :net_http 94 | end 95 | end 96 | 97 | describe "with --option" do 98 | %w[-o --option].each do |option| 99 | it "sets custom option with #{option}" do 100 | command([option, "maxwidth=100"])[:query][:maxwidth].should == '100' 101 | end 102 | end 103 | end 104 | 105 | describe "with --verbose" do 106 | it "enables logging" do 107 | command(["--verbose"]) 108 | Embedly.configuration.should be_debug 109 | end 110 | 111 | it "disables logging" do 112 | command(["--no-verbose"]) 113 | Embedly.configuration.should_not be_debug 114 | end 115 | end 116 | end 117 | 118 | def command(arguments) 119 | arguments << 'testurl.com' 120 | command = CommandLine.new(arguments) 121 | command.run 122 | command.options 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /spec/embedly/configuration_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | module Embedly 4 | describe Configuration do 5 | 6 | let(:config) { Configuration.new } 7 | 8 | describe "debug and logging" do 9 | let(:logger) { Logger.new(STDERR) } 10 | 11 | it "has debugger disabled by default" do 12 | config.should_not have_debug_enabled 13 | end 14 | 15 | it "has default logger level as error" do 16 | config.logger.level.should == Logger::ERROR 17 | end 18 | 19 | it "can enable debugging" do 20 | config.debug = true 21 | config.should have_debug_enabled 22 | end 23 | 24 | it "can disable debugging" do 25 | config.debug = false 26 | config.should_not have_debug_enabled 27 | end 28 | 29 | it "can change the logger" do 30 | config.logger = logger 31 | config.logger.should === logger 32 | end 33 | 34 | it "sets the logger level for the new logger" do 35 | config.debug = true 36 | config.logger = logger 37 | config.logger.level.should == Logger::DEBUG 38 | end 39 | 40 | it "changes the logger level when enable debugging" do 41 | config.debug = true 42 | config.logger.level.should == Logger::DEBUG 43 | end 44 | end 45 | 46 | describe "setting options" do 47 | it "sets the api key" do 48 | config.key = 'my_api_key' 49 | config.key.should == 'my_api_key' 50 | end 51 | 52 | it "requests using Net::HTTP by default" do 53 | config.requester.should == :net_http 54 | end 55 | end 56 | 57 | describe "registering a requester" do 58 | it "adds a new requester" do 59 | config.add_requester :sample do |api| 60 | Embedly::NetHTTP::Request.new(api) 61 | end 62 | config.request_with :sample 63 | config.current_requester.call({}).should be_a(Embedly::NetHTTP::Request) 64 | end 65 | end 66 | end 67 | end 68 | 69 | RSpec::Matchers.define :have_debug_enabled do 70 | match { |actual| actual.debug? } 71 | end 72 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "embedly" 2 | 3 | RSpec.configure do |config| 4 | config.after do 5 | Embedly.configuration.reset 6 | end 7 | end 8 | --------------------------------------------------------------------------------