├── .rspec ├── lib ├── freebase_api │ ├── version.rb │ ├── ext │ │ └── hash.rb │ ├── attribute.rb │ ├── exceptions.rb │ ├── image.rb │ ├── session.rb │ └── topic.rb └── freebase-api.rb ├── .gitignore ├── spec ├── fixtures │ ├── img.jpg │ ├── search.json │ ├── search_with_output.json │ └── topic.json ├── freebase_api_spec.rb ├── support │ └── helpers.rb ├── spec_helper.rb ├── attribute_spec.rb ├── image_spec.rb ├── topic_spec.rb └── session_spec.rb ├── .travis.yml ├── Rakefile ├── Gemfile ├── freebase-api.gemspec ├── LICENSE.txt └── README.markdown /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | --format documentation 3 | --tty 4 | -------------------------------------------------------------------------------- /lib/freebase_api/version.rb: -------------------------------------------------------------------------------- 1 | module FreebaseAPI 2 | VERSION = "0.1.5" 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage/ 2 | /.env 3 | /pkg/ 4 | /doc/ 5 | /.bundle/ 6 | /Gemfile.lock 7 | -------------------------------------------------------------------------------- /spec/fixtures/img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PerfectMemory/freebase-api/HEAD/spec/fixtures/img.jpg -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - jruby-19mode 4 | - 1.9.3 5 | - 2.0.0 6 | - 2.1.0 7 | - ruby-head 8 | - jruby-head -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rspec/core/rake_task' 3 | require 'yard' 4 | 5 | YARD::Rake::YardocTask.new 6 | RSpec::Core::RakeTask.new(:spec) 7 | 8 | task :default => :spec 9 | -------------------------------------------------------------------------------- /spec/freebase_api_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe FreebaseAPI do 4 | 5 | it "should have a default logger" do 6 | FreebaseAPI.logger.should be_kind_of(Logger) 7 | end 8 | 9 | it "should have a default session" do 10 | FreebaseAPI.session.should be_kind_of(FreebaseAPI::Session) 11 | end 12 | 13 | end -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake', '~> 10.0' 4 | 5 | group :test do 6 | gem 'coveralls', :require => false 7 | gem 'rspec', '~> 2.12' 8 | gem 'yard', '~> 0.8' 9 | gem 'simplecov', '~> 0.7', :require => false 10 | end 11 | 12 | # Specify your gem's dependencies in freebase-api.gemspec 13 | gemspec 14 | -------------------------------------------------------------------------------- /lib/freebase_api/ext/hash.rb: -------------------------------------------------------------------------------- 1 | class Hash 2 | 3 | def deep_merge(other_hash) 4 | dup.deep_merge!(other_hash) 5 | end 6 | 7 | def deep_merge!(other_hash) 8 | other_hash.each_pair do |k,v| 9 | tv = self[k] 10 | self[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v 11 | end 12 | self 13 | end 14 | 15 | end -------------------------------------------------------------------------------- /spec/support/helpers.rb: -------------------------------------------------------------------------------- 1 | module FreebaseAPI 2 | # Helpers for Rspec 3 | module Helpers 4 | def load_fixture(fixture) 5 | JSON.parse(IO.read(File.join(File.dirname(__FILE__), "..", "fixtures", "#{fixture}.json"))) 6 | end 7 | 8 | def load_data(file) 9 | IO.read(File.join(File.dirname(__FILE__), "..", "fixtures", file), encoding: 'BINARY') 10 | end 11 | end 12 | end -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | require 'coveralls' 3 | 4 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ 5 | SimpleCov::Formatter::HTMLFormatter, 6 | Coveralls::SimpleCov::Formatter 7 | ] 8 | SimpleCov.start 9 | 10 | require 'rspec' 11 | require 'freebase-api' 12 | require 'support/helpers' 13 | 14 | RSpec.configure do |config| 15 | config.include FreebaseAPI::Helpers 16 | end 17 | 18 | FreebaseAPI.logger.level = Logger::FATAL -------------------------------------------------------------------------------- /lib/freebase_api/attribute.rb: -------------------------------------------------------------------------------- 1 | module FreebaseAPI 2 | # Attribute can be any Freebase data type 3 | class Attribute 4 | 5 | attr_accessor :type 6 | 7 | def initialize(data, options={}) 8 | @data = data 9 | @type = options[:type] 10 | end 11 | 12 | def value 13 | @data['value'] 14 | end 15 | 16 | def text 17 | @data['text'] 18 | end 19 | 20 | def lang 21 | @data['lang'] 22 | end 23 | 24 | def type 25 | @type 26 | end 27 | 28 | def inspect 29 | "#<#{self.class}:0x#{self.__id__.to_s(16)} value: \"#{self.value}\", type: \"#{self.type}\">" 30 | end 31 | 32 | end 33 | end -------------------------------------------------------------------------------- /lib/freebase_api/exceptions.rb: -------------------------------------------------------------------------------- 1 | module FreebaseAPI 2 | # A class for returning errors from the Freebase API 3 | class Error < StandardError 4 | attr_accessor :code, :message, :errors 5 | 6 | def initialize(params) 7 | FreebaseAPI.logger.error("#{params['message']} (#{params['code']})") 8 | self.code = params['code'] 9 | self.message = params['message'] 10 | end 11 | 12 | def to_s 13 | "#{self.message} (#{self.code})" 14 | end 15 | end 16 | 17 | class ServiceError < Error 18 | def initialize(params) 19 | super 20 | self.errors = params['errors'] 21 | end 22 | end 23 | 24 | class NetError < Error ;; end 25 | end -------------------------------------------------------------------------------- /freebase-api.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/freebase_api/version', __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.authors = ["Aymeric Brisse"] 6 | gem.email = ["aymeric.brisse@mperfect-memory.com"] 7 | gem.description = %q{A library to use the Freebase API} 8 | gem.summary = %q{Provides access to both a raw-access and an abstract-layer to the Freebase API} 9 | gem.homepage = "https://github.com/PerfectMemory/freebase-api" 10 | 11 | gem.files = `git ls-files`.split($\) 12 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 13 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 14 | gem.name = "freebase-api" 15 | gem.require_paths = ["lib"] 16 | gem.version = FreebaseAPI::VERSION 17 | 18 | gem.add_dependency('httparty', '~> 0.10') 19 | end 20 | -------------------------------------------------------------------------------- /lib/freebase_api/image.rb: -------------------------------------------------------------------------------- 1 | module FreebaseAPI 2 | class Image 3 | 4 | attr_reader :id 5 | 6 | class << self 7 | def get(id, options={}) 8 | image = Image.new(id, options) 9 | image.retrieve 10 | image 11 | end 12 | end 13 | 14 | def initialize(id, options={}) 15 | @data = nil 16 | @options = options 17 | @id = id 18 | end 19 | 20 | def retrieve 21 | @data = FreebaseAPI.session.image(self.id, @options) 22 | end 23 | 24 | def store(filename) 25 | File.open(filename, 'wb') do |f| 26 | f.write data 27 | end 28 | end 29 | 30 | def size 31 | data.size 32 | end 33 | 34 | def inspect 35 | "#<#{self.class}:0x#{self.__id__.to_s(16)} id: \"#{self.id}\">" 36 | end 37 | 38 | private 39 | 40 | def data 41 | @data || retrieve 42 | end 43 | end 44 | end -------------------------------------------------------------------------------- /lib/freebase-api.rb: -------------------------------------------------------------------------------- 1 | require 'logger' 2 | 3 | require 'freebase_api/session' 4 | require 'freebase_api/image' 5 | require 'freebase_api/version' 6 | require 'freebase_api/topic' 7 | require 'freebase_api/attribute' 8 | require 'freebase_api/exceptions' 9 | require 'freebase_api/ext/hash' 10 | 11 | # FreebaseAPI is a library to use the Freebase API 12 | # 13 | # It provides : 14 | # - a Data mapper 15 | # - a low level class to use directly the Freebase API (Topic, Search, MQLRead) 16 | # 17 | # @see http://wiki.freebase.com/wiki/Freebase_API 18 | module FreebaseAPI 19 | class << self 20 | attr_accessor :logger, :session 21 | 22 | def init_logger 23 | logger = Logger.new(STDERR) 24 | logger.level = Logger::WARN 25 | logger.progname = "FreebaseAPI" 26 | self.logger = logger 27 | end 28 | end 29 | end 30 | 31 | FreebaseAPI.init_logger 32 | FreebaseAPI.session = FreebaseAPI::Session.new -------------------------------------------------------------------------------- /spec/attribute_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe FreebaseAPI::Attribute do 4 | 5 | let(:data) { 6 | { 7 | "text" => "Github", 8 | "lang" => "en", 9 | "value" => "Github ()", 10 | "creator" => "/user/mwcl_wikipedia_en", 11 | "timestamp" => "2008-08-18T10:47:44.000Z" 12 | } 13 | } 14 | 15 | let(:attribute) { FreebaseAPI::Attribute.new(data, :type => "string") } 16 | 17 | describe "#value" do 18 | it "should return the 'value' value" do 19 | attribute.value.should == "Github ()" 20 | end 21 | end 22 | 23 | describe "#text" do 24 | it "should return the 'text' value" do 25 | attribute.text.should == "Github" 26 | end 27 | end 28 | 29 | describe "#lang" do 30 | it "should return the 'lang' value" do 31 | attribute.lang.should == "en" 32 | end 33 | end 34 | 35 | describe "#type" do 36 | it "should return the attribute type" do 37 | attribute.type.should == "string" 38 | end 39 | end 40 | 41 | 42 | end -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Perfect Memory 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 | -------------------------------------------------------------------------------- /spec/fixtures/search.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mid": "/m/01vrncs", 4 | "id": "/en/bob_dylan", 5 | "name": "Bob Dylan", 6 | "notable": { 7 | "name": "Singer-songwriter", 8 | "id": "/m/016z4k" 9 | }, 10 | "lang": "en", 11 | "score": 72.587578 12 | }, 13 | { 14 | "mid": "/m/04prwq", 15 | "id": "/en/dylan_and_cole_sprouse", 16 | "name": "Dylan and Cole Sprouse", 17 | "lang": "en", 18 | "score": 38.116348 19 | }, 20 | { 21 | "mid": "/m/02fkm", 22 | "id": "/en/dylan_thomas", 23 | "name": "Dylan Thomas", 24 | "notable": { 25 | "name": "Poet", 26 | "id": "/m/05z96" 27 | }, 28 | "lang": "en", 29 | "score": 32.650681 30 | }, 31 | { 32 | "mid": "/m/03061d", 33 | "id": "/en/dylan_mcdermott", 34 | "name": "Dylan McDermott", 35 | "notable": { 36 | "name": "Actor", 37 | "id": "/m/02hrh1q" 38 | }, 39 | "lang": "en", 40 | "score": 29.216131 41 | }, 42 | { 43 | "mid": "/m/02f70", 44 | "id": "/en/dylan", 45 | "name": "Dylan", 46 | "notable": { 47 | "name": "Programming Language", 48 | "id": "/computer/programming_language" 49 | }, 50 | "lang": "en", 51 | "score": 28.707029 52 | }, 53 | { 54 | "mid": "/m/035plv", 55 | "id": "/en/jakob_dylan", 56 | "name": "Jakob Dylan", 57 | "notable": { 58 | "name": "Singer", 59 | "id": "/m/09l65" 60 | }, 61 | "lang": "en", 62 | "score": 27.369299 63 | } 64 | ] -------------------------------------------------------------------------------- /spec/image_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'tempfile' 3 | 4 | describe FreebaseAPI::Image do 5 | 6 | let(:options) { { :maxheight => 20, :maxwidth => 20 } } 7 | let(:image) { FreebaseAPI::Image.get('/en/bob_dylan', options) } 8 | 9 | let(:data) { 10 | load_data 'img.jpg' 11 | } 12 | 13 | before { 14 | stubbed_session = mock('session') 15 | FreebaseAPI.stub(:session).and_return(stubbed_session) 16 | stubbed_session.stub(:image).and_return(data) 17 | } 18 | 19 | describe ".get" do 20 | let(:stubbed_session) { mock('session').as_null_object } 21 | 22 | before { 23 | FreebaseAPI.stub(:session).and_return(stubbed_session) 24 | } 25 | 26 | it "should make a Image API call" do 27 | stubbed_session.should_receive(:image).with('/en/bob_dylan', :maxheight => 20, :maxwidth => 20).and_return(data) 28 | image 29 | end 30 | 31 | it "should return an image" do 32 | image.should be_kind_of FreebaseAPI::Image 33 | end 34 | end 35 | 36 | describe "#id" do 37 | it "should return the topic related ID" do 38 | image.id.should == '/en/bob_dylan' 39 | end 40 | end 41 | 42 | describe "#size" do 43 | it "should return the image size" do 44 | image.size.should == data.size 45 | end 46 | end 47 | 48 | describe "#store" do 49 | it "should store the image" do 50 | Dir::Tmpname.create 'freebase-api' do |file| 51 | image.store(file) 52 | File.size(file).should == data.size 53 | File.unlink(file) 54 | end 55 | end 56 | end 57 | end -------------------------------------------------------------------------------- /spec/fixtures/search_with_output.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mid": "/m/01vrncs", 4 | "id": "/en/bob_dylan", 5 | "name": "Bob Dylan", 6 | "notable": { 7 | "name": "Folk rock Artist", 8 | "id": "/m/016jny" 9 | }, 10 | "lang": "en", 11 | "score": 82.743378, 12 | "output": { 13 | "description": { 14 | "/common/topic/description": [ 15 | "Bob Dylan is an American singer-songwriter, artist, and writer. He has been an influential figure in popular music and culture for more than five decades. Much of his most celebrated work dates from the 1960s, when he was both a chronicler and a reluctant figurehead of social unrest. A number of Dylan's early songs, such as \"Blowin' in the Wind\" and \"The Times They Are a-Changin'\", became anthems for the American civil rights and anti-war movements. Leaving behind his initial base in the culture of the folk music revival, Dylan's six-minute single \"Like a Rolling Stone\" radically altered the parameters of popular music in 1965. His mid-1960s recordings, backed by rock musicians, reached the top end of the United States music charts while also attracting denunciation and criticism from others in the folk movement.\nDylan's lyrics have incorporated a variety of political, social, philosophical, and literary influences. They defied existing pop music conventions and appealed hugely to the then burgeoning counterculture." 16 | ] 17 | } 18 | } 19 | }, 20 | { 21 | "mid": "/m/04prwq", 22 | "id": "/en/dylan_and_cole_sprouse", 23 | "name": "Dylan and Cole Sprouse", 24 | "notable": { 25 | "name": "Singer", 26 | "id": "/m/0290ngj" 27 | }, 28 | "lang": "en", 29 | "score": 39.279640, 30 | "output": { 31 | "description": { 32 | "/common/topic/description": [ 33 | "Dylan Thomas Sprouse and Cole Mitchell Sprouse are American actors. They are twins and are collectively referred to as Dylan and Cole Sprouse or the Sprouse brothers, usually abbreviated as Sprouse Bros. Their first major theatrical film role was in Big Daddy, where they starred alongside Adam Sandler. They later appeared in several television sitcoms and starred in the straight-to-DVD films, I Saw Mommy Kissing Santa Claus and Just for Kicks.\nIn 2005, they starred in the Disney Channel sitcom The Suite Life of Zack & Cody. As a result of the series' success, the media has termed them \"heartthrobs\" and \"overwhelming draws\" among preteen and teen audiences. The following year, the brothers launched a franchise known as the Sprouse Bros brand, which included a clothing line, book series and magazine. The majority of the Sprouses' franchise ended in 2008, except for their clothing line. The Suite Life of Zack & Cody was retooled in 2008 as The Suite Life on Deck, in which the brothers reprised their roles as Zack and Cody. The Suite Life on Deck went on to become the most-watched tween/children's television show in 2008 and 2009. The show ended in May 2011." 34 | ] 35 | } 36 | } 37 | }, 38 | { 39 | "mid": "/m/02fkm", 40 | "id": "/en/dylan_thomas", 41 | "name": "Dylan Thomas", 42 | "notable": { 43 | "name": "Novelist", 44 | "id": "/m/02xhgwq" 45 | }, 46 | "lang": "en", 47 | "score": 38.964607, 48 | "output": { 49 | "description": { 50 | "/common/topic/description": [ 51 | "Dylan Marlais Thomas was a Welsh poet and writer whose works include the poems \"Do not go gentle into that good night\" and \"And death shall have no dominion\", the \"play for voices\", Under Milk Wood, and stories and radio broadcasts such as A Child's Christmas in Wales and Portrait of the Artist as a Young Dog. He became popular in his lifetime and remained so after his premature death in New York. In his later life he acquired a reputation, which he encouraged, as a \"roistering, drunken and doomed poet\".\nThomas was born in Swansea, Wales, in 1914. An undistinguished pupil, he left school at 16, becoming a journalist for a short time. Although many of his works appeared in print while he was still a teenager, it was the publication of \"Light breaks where no sun shines\", in 1934, that caught the attention of the literary world. While living in London, Thomas met Caitlin Macnamara, whom he married in 1937. Their relationship was defined by alcoholism and was mutually destructive. In the early part of his marriage, Thomas and his family lived hand-to-mouth, settling in the Welsh fishing village of Laugharne." 52 | ] 53 | } 54 | } 55 | } 56 | ] -------------------------------------------------------------------------------- /lib/freebase_api/session.rb: -------------------------------------------------------------------------------- 1 | require 'httparty' 2 | require 'json' 3 | 4 | module FreebaseAPI 5 | class Session 6 | include HTTParty 7 | disable_rails_query_string_format 8 | 9 | attr_reader :env, :key, :query_options 10 | 11 | # URIs of the Freebase API 12 | API_URL = "https://www.googleapis.com/freebase/v1" 13 | SANDBOX_API_URL = "https://www.googleapis.com/freebase/v1sandbox" 14 | 15 | # Freebase default values 16 | DEFAULT_LIMIT = 10 17 | DEFAULT_LANGUAGE = :en 18 | 19 | def initialize(options={}) 20 | options = { :env => :stable, :query_options => { :limit => DEFAULT_LIMIT, :lang => DEFAULT_LANGUAGE } }.deep_merge(options) 21 | @env = options[:env] 22 | @key = options[:key] || ENV['GOOGLE_API_KEY'] 23 | @query_options = options[:query_options] 24 | @proxy_options = get_proxy_options(options[:proxy]) 25 | end 26 | 27 | # Execute a MQL read query 28 | # @see https://developers.google.com/freebase/v1/mql-overview 29 | # 30 | # @param [Hash] query the MQL query 31 | # @param [Hash] options the MQL query options 32 | # @return [Hash] the response 33 | def mqlread(query, options={}) 34 | params = { :query => query.to_json, :lang => "/lang/#{@query_options[:lang]}", :limit => @query_options[:limit] }.merge(options) 35 | response = get(surl('mqlread'), params, format: :json) 36 | response['result'] 37 | end 38 | 39 | # Execute a Search query 40 | # @see https://developers.google.com/freebase/v1/search 41 | # 42 | # @param [String] query the query to search 43 | # @param [Hash] options the options 44 | # @return [Hash] the response 45 | def search(query, options={}) 46 | params = { :query => query, :lang => @query_options[:lang], :limit => @query_options[:limit] }.merge(options) 47 | response = get(surl('search'), params, format: :json) 48 | response['result'] 49 | end 50 | 51 | # Execute a Topic query 52 | # @see https://developers.google.com/freebase/v1/topic 53 | # 54 | # @param [String] id the topic ID 55 | # @param [Hash] options the options 56 | # @return [Hash] the response 57 | def topic(id, options={}) 58 | params = { :lang => @query_options[:lang], :limit => @query_options[:limit] }.merge(options) 59 | get(surl('topic') + id, params, format: :json).parsed_response 60 | end 61 | 62 | # Execute a Image Service query 63 | # @see https://developers.google.com/freebase/v1/topic-response#references-to-image-objects 64 | # 65 | # @param [String] id the topic ID 66 | # @param [Hash] options the options 67 | # @return [Hash] the response 68 | def image(id, options={}) 69 | params = options 70 | get(surl('image') + id, params).body 71 | end 72 | 73 | private 74 | 75 | # Return the URL of a Freebase service 76 | # 77 | # @param [String] service the service 78 | # @return [String] the url of the service 79 | def surl(service) 80 | service_url = @env == :stable ? API_URL : SANDBOX_API_URL 81 | service_url = service_url + "/" + service 82 | service_url.gsub!('www', 'usercontent') if service.to_s == 'image' 83 | service_url 84 | end 85 | 86 | # Make a GET request 87 | # 88 | # @param [String] url the url to request 89 | # @param [Hash] params the params of the request 90 | # @return the request response 91 | def get(url, params={}, options={}) 92 | FreebaseAPI.logger.debug("GET #{url}") 93 | params[:key] = @key if @key 94 | options = { format: options[:format], query: params } 95 | options.merge!(@proxy_options) 96 | response = self.class.get(url, options) 97 | handle_response(response) 98 | end 99 | 100 | # Handle the response 101 | # 102 | # If success, return the response body 103 | # If failure, raise an error 104 | def handle_response(response) 105 | case response.code 106 | when 200..299 107 | response 108 | else 109 | if response.request.format == :json 110 | raise FreebaseAPI::ServiceError.new(response['error']) 111 | else 112 | raise FreebaseAPI::NetError.new('code' => response.code, 'message' => response.response.message) 113 | end 114 | end 115 | end 116 | 117 | # Get the proxy options for HTTParty 118 | # 119 | # @return [Hash] the proxy options 120 | def get_proxy_options(url=nil) 121 | options = {} 122 | if url = url || ENV['HTTPS_PROXY'] || ENV['https_proxy'] || ENV['HTTP_PROXY'] || ENV['http_proxy'] 123 | proxy_uri = URI.parse(url) 124 | options[:http_proxyaddr] = proxy_uri.host 125 | options[:http_proxyport] = proxy_uri.port 126 | options[:http_proxyuser] = proxy_uri.user 127 | options[:http_proxypass] = proxy_uri.password 128 | end 129 | options 130 | end 131 | end 132 | end -------------------------------------------------------------------------------- /spec/topic_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe FreebaseAPI::Topic do 4 | 5 | let(:options) { { :lang => :en } } 6 | let(:topic) { FreebaseAPI::Topic.get('/en/github', options) } 7 | let(:new_topic) { FreebaseAPI::Topic.new('/en/github') } 8 | 9 | let(:data) { load_fixture 'topic' } 10 | 11 | before { 12 | stubbed_session = mock('session') 13 | FreebaseAPI.stub(:session).and_return(stubbed_session) 14 | stubbed_session.stub(:topic).and_return(data) 15 | stubbed_session.stub(:image).and_return(nil) 16 | } 17 | 18 | describe ".get" do 19 | let(:stubbed_session) { mock('session').as_null_object } 20 | 21 | before { 22 | FreebaseAPI::Topic.any_instance.stub(:build) 23 | FreebaseAPI.stub(:session).and_return(stubbed_session) 24 | } 25 | 26 | it "should make a Topic API call" do 27 | stubbed_session.should_receive(:topic).with('/en/github', :lang => :en, :filter => 'commons').and_return(data) 28 | topic 29 | end 30 | 31 | it "should return a topic" do 32 | topic.should be_kind_of FreebaseAPI::Topic 33 | end 34 | end 35 | 36 | describe ".search" do 37 | let(:stubbed_session) { mock('session') } 38 | let(:topic_search) { FreebaseAPI::Topic.search('dylan') } 39 | let(:data) { load_fixture 'search' } 40 | let(:item) { topic_search.values.first } 41 | 42 | before { 43 | FreebaseAPI.stub(:session).and_return(stubbed_session) 44 | stubbed_session.stub(:search).and_return(data) 45 | } 46 | 47 | it "should make a Search API call" do 48 | stubbed_session.should_receive(:search).with('dylan', {}).and_return(data) 49 | topic_search 50 | end 51 | 52 | it "should return an hash" do 53 | topic_search.should be_kind_of Hash 54 | end 55 | 56 | it "should return ordered scores" do 57 | topic_search.keys.first.should == 72.587578 58 | topic_search.keys.last.should == 27.369299 59 | end 60 | 61 | it "should return topics" do 62 | item.should be_kind_of(FreebaseAPI::Topic) 63 | end 64 | 65 | it "should store the id" do 66 | item.id.should == '/m/01vrncs' 67 | end 68 | 69 | it "should store some properties" do 70 | item.properties.keys.should == ["/type/object/name", "/common/topic/notable_for"] 71 | end 72 | 73 | context "with output" do 74 | let(:data) { load_fixture 'search_with_output' } 75 | 76 | it "should store some properties" do 77 | item.properties.keys.should == ["/type/object/name", "/common/topic/notable_for", "/common/topic/description"] 78 | end 79 | end 80 | end 81 | 82 | describe "#id" do 83 | context "when the topic has been sync yet" do 84 | it "should return the topic ID" do 85 | new_topic.id.should == '/en/github' 86 | end 87 | end 88 | 89 | context "when the topic has been sync" do 90 | it "should return the topic ID" do 91 | topic.id.should == '/m/04g0kcw' 92 | end 93 | end 94 | end 95 | 96 | describe "#text" do 97 | it "should return the topic text" do 98 | topic.text.should == "GitHub" 99 | end 100 | end 101 | 102 | describe "#lang" do 103 | it "should return the topic language" do 104 | topic.lang.should == 'en' 105 | end 106 | end 107 | 108 | describe "#name" do 109 | it "should return the topic name" do 110 | topic.name.should == 'GitHub' 111 | end 112 | end 113 | 114 | describe "#types" do 115 | it "should return all the topic types" do 116 | topic.types.should == ["/common/topic", "/internet/website", "/base/technologyofdoing/proposal_agent"] 117 | end 118 | end 119 | 120 | describe "#description" do 121 | it "should return the topic description" do 122 | topic.description.should start_with "GitHub is a web-based hosting service for software development projects that use the Git revision control system." 123 | end 124 | end 125 | 126 | describe "#properties" do 127 | it "should return the properties values which are topics" do 128 | link = topic.properties['/internet/website/category'].first 129 | link.should be_kind_of(FreebaseAPI::Topic) 130 | link.name.should == 'Revision control' 131 | end 132 | 133 | it "should return the properties values which are data" do 134 | link = topic.properties['/common/topic/official_website'].first 135 | link.should be_kind_of(FreebaseAPI::Attribute) 136 | link.value.should == 'http://github.com/' 137 | end 138 | 139 | context "with a property exclusion constraint" do 140 | let(:topic) { FreebaseAPI::Topic.get('/en/github', :exclude => '/internet') } 141 | 142 | it "should not return these properties" do 143 | topic.properties.should_not have_key('/internet/website/category') 144 | end 145 | 146 | it "should return the properties that do not match this exclusion" do 147 | topic.properties.should have_key('/common/topic/official_website') 148 | end 149 | end 150 | end 151 | 152 | describe "#property" do 153 | it "should return the asked property" do 154 | property = topic.property('/type/object/type') 155 | property.should have(3).elements 156 | end 157 | end 158 | 159 | describe "#properties domains" do 160 | it "should return an hash containing the properties domains" do 161 | topic.properties_domains.should == { 'common' => 17, 'internet' => 1,'type' => 30 } 162 | end 163 | end 164 | 165 | describe "#image" do 166 | it "should return an Image" do 167 | topic.image.should be_kind_of(FreebaseAPI::Image) 168 | end 169 | 170 | it "should be the topic image" do 171 | topic.image.id.should == topic.id 172 | end 173 | end 174 | end 175 | -------------------------------------------------------------------------------- /spec/session_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe FreebaseAPI::Session do 4 | 5 | let(:session) { FreebaseAPI::Session.new } 6 | let(:dummy_session) { FreebaseAPI::Session.new(:key => "GOOGLE_API_KEY", :env => :sandbox, :query_options => { :lang => :fr, :limit => 1 }) } 7 | 8 | it "should include HTTParty" do 9 | FreebaseAPI::Session.should include(HTTParty) 10 | end 11 | 12 | describe "#key" do 13 | context "when key has been set manually" do 14 | it "should return the API key" do 15 | dummy_session.key.should == 'GOOGLE_API_KEY' 16 | end 17 | end 18 | 19 | context "with the env variables defined" do 20 | before { ENV['GOOGLE_API_KEY'] = 'ENV_GOOGLE_API_KEY' } 21 | it "should return the API key" do 22 | session.key.should == 'ENV_GOOGLE_API_KEY' 23 | end 24 | after { ENV['GOOGLE_API_KEY'] = nil } 25 | end 26 | end 27 | 28 | describe "#env" do 29 | it "should return the environnment" do 30 | dummy_session.env.should == :sandbox 31 | end 32 | end 33 | 34 | describe "#query_options" do 35 | it "should return the environnment" do 36 | dummy_session.query_options.should == { :lang => :fr, :limit => 1 } 37 | end 38 | end 39 | 40 | describe "#surl" do 41 | context "in a sandbox environnment" do 42 | it "should return the sandbox service URL" do 43 | dummy_session.send(:surl, 'service').should == 'https://www.googleapis.com/freebase/v1sandbox/service' 44 | end 45 | end 46 | 47 | context "in a stable environnment" do 48 | it "should return the stable service URL" do 49 | session.send(:surl, 'service').should == 'https://www.googleapis.com/freebase/v1/service' 50 | end 51 | end 52 | end 53 | 54 | describe "#mqlread" do 55 | let(:query) { 56 | { 57 | :type => '/internet/website', 58 | :id => '/en/github', 59 | :'/common/topic/official_website' => nil 60 | } 61 | } 62 | 63 | let(:request) { 64 | session.mqlread(query) 65 | } 66 | 67 | context "when the query is successful" do 68 | it "should run a MQL query and return the result" do 69 | request.should == { 70 | "/common/topic/official_website" => "http://github.com/", 71 | "id" => "/en/github", 72 | "type" => "/internet/website" 73 | } 74 | end 75 | end 76 | 77 | context "when the query has failed" do 78 | let(:session) { FreebaseAPI::Session.new :key => 'nil' } 79 | 80 | it "should raise en error" do 81 | expect { 82 | request 83 | }.to raise_error(FreebaseAPI::Error) 84 | end 85 | end 86 | end 87 | 88 | describe "#image" do 89 | let(:request) { 90 | session.image('/en/bob_dylan') 91 | } 92 | 93 | context "when the query is successful" do 94 | it "should not raise any error" do 95 | expect { 96 | request 97 | }.to_not raise_error(FreebaseAPI::Error) 98 | end 99 | 100 | it "should return some data" do 101 | request.size.should > 1000 102 | end 103 | end 104 | 105 | context "when the query has failed" do 106 | let(:session) { FreebaseAPI::Session.new :key => 'nil' } 107 | 108 | it "should raise en error" do 109 | expect { 110 | request 111 | }.to raise_error(FreebaseAPI::Error) 112 | end 113 | end 114 | end 115 | 116 | describe "#search" do 117 | let(:request) { 118 | session.search('bob dylan') 119 | } 120 | 121 | context "when the query is successful" do 122 | it "should not raise any error" do 123 | expect { 124 | request 125 | }.to_not raise_error(FreebaseAPI::Error) 126 | end 127 | 128 | it "should return some topics" do 129 | request.should_not be_empty 130 | end 131 | end 132 | 133 | context "when the query has failed" do 134 | let(:session) { FreebaseAPI::Session.new :key => 'nil' } 135 | 136 | it "should raise en error" do 137 | expect { 138 | request 139 | }.to raise_error(FreebaseAPI::Error) 140 | end 141 | end 142 | end 143 | 144 | describe "#topic" do 145 | let(:request) { 146 | session.topic('/en/github', :filter => '/common/topic/official_website') 147 | } 148 | 149 | context "when the query is successful" do 150 | it "should run a topic query and return the result" do 151 | request['property'].should have(1).elements 152 | end 153 | end 154 | 155 | context "when the query has failed" do 156 | let(:session) { FreebaseAPI::Session.new :key => 'nil' } 157 | 158 | it "should raise en error" do 159 | expect { 160 | request 161 | }.to raise_error(FreebaseAPI::Error) 162 | end 163 | end 164 | end 165 | 166 | describe "#get" do 167 | let(:session) { FreebaseAPI::Session.new } 168 | 169 | context "with a proxy env variable" do 170 | before do 171 | ENV['http_proxy'] = 'http://user:pass@site.com:2020' 172 | session.stub(:handle_response) 173 | end 174 | 175 | it "should use the proxy" do 176 | session.class.should_receive(:get) do |url, params, options| 177 | params[:http_proxyaddr].should eq "site.com" 178 | params[:http_proxyport].should eq 2020 179 | params[:http_proxyuser].should eq "user" 180 | params[:http_proxypass].should eq "pass" 181 | end 182 | session.send(:get, 'URL') 183 | end 184 | end 185 | 186 | context "with a proxy configuration" do 187 | let(:session) { FreebaseAPI::Session.new(:proxy => 'http://user:pass@site.com:2020') } 188 | 189 | before do 190 | session.stub(:handle_response) 191 | end 192 | 193 | it "should use the proxy" do 194 | session.class.should_receive(:get) do |url, params, options| 195 | params[:http_proxyaddr].should eq "site.com" 196 | params[:http_proxyport].should eq 2020 197 | params[:http_proxyuser].should eq "user" 198 | params[:http_proxypass].should eq "pass" 199 | end 200 | session.send(:get, 'URL') 201 | end 202 | end 203 | end 204 | end 205 | -------------------------------------------------------------------------------- /lib/freebase_api/topic.rb: -------------------------------------------------------------------------------- 1 | module FreebaseAPI 2 | class Topic 3 | 4 | # The maximum number of property values to return. 5 | PROPERTIES_LIMIT = 100 6 | 7 | attr_reader :properties 8 | 9 | class << self 10 | # Returns a new Topic filled with all the Freebase properties 11 | # 12 | # @param [String] id the Freebase ID 13 | # @param [Hash] options the options 14 | # @return [Topic] the topic 15 | def get(id, options={}) 16 | topic = Topic.new(id, options) 17 | topic.sync 18 | topic 19 | end 20 | 21 | # Search using a query and returns the results as a hash of topics score based 22 | # 23 | # @param [String] query the string query 24 | # @param [Hash] options the options 25 | # @return [Hash] the topics 26 | def search(query, options={}) 27 | hash = {} 28 | FreebaseAPI.session.search(query, options).each do |topic| 29 | hash[topic['score'].to_f] = Topic.new(topic['mid'], :data => construct_data(topic)) 30 | end 31 | hash 32 | end 33 | 34 | private 35 | 36 | # @private 37 | def construct_data(topic) 38 | lang = topic['lang'] 39 | data = { 40 | "id" => topic['mid'], 41 | "lang" => lang, 42 | "property" => { 43 | "/type/object/name" => build_simple_property_data("string", [{"text" => topic['name'], "lang" => lang}]) 44 | } 45 | } 46 | if topic.has_key?('notable') 47 | data["property"]["/common/topic/notable_for"] = build_simple_property_data("object", 48 | [{ 49 | "text" => topic['notable']['name'], 50 | "lang" => lang, 51 | "id" => topic['notable']['id'] 52 | }]) 53 | end 54 | merge_custom_output(topic['output'], data["property"], lang) if topic.has_key?('output') 55 | data 56 | end 57 | 58 | # @private 59 | def build_simple_property_data(type, values) 60 | values_f = values.map do |v| 61 | hash = { "lang" => v["lang"], "text" => v["text"].to_s } 62 | if v.has_key?("id") 63 | hash["id"] = v["id"] 64 | else 65 | hash["value"] = v["text"] 66 | end 67 | hash 68 | end 69 | { 70 | "valuetype" => type, 71 | "values" => values_f 72 | } 73 | end 74 | 75 | # @private 76 | def merge_custom_output(output, properties, lang) 77 | values = nil 78 | valuetype = nil 79 | output.each do |_, hash| 80 | hash.each do |property, values| 81 | if values.first.is_a?(Hash) 82 | values = values.map do |value| 83 | { 84 | "text" => value['name'], 85 | "lang" => lang, 86 | "id" => value['mid'] 87 | } 88 | end 89 | valuetype = "object" 90 | else 91 | values = values.map do |value| 92 | { 93 | "text" => "#{value}", 94 | "lang" => lang, 95 | "value" => "#{value}" 96 | } 97 | end 98 | valuetype = "string" 99 | end 100 | properties[property] = build_simple_property_data(valuetype, values) 101 | end 102 | end 103 | end 104 | end 105 | 106 | def initialize(id, options={}) 107 | @properties = {} 108 | @excluded_properties = parse_exclusion(options.delete(:exclude)) 109 | @options = { :filter => 'commons' }.merge(options) 110 | @data = { 'id' => id }.merge(options[:data] || {}) 111 | build 112 | end 113 | 114 | def id 115 | @data['id'] 116 | end 117 | 118 | def text 119 | @data['text'] 120 | end 121 | 122 | def lang 123 | @data['lang'] 124 | end 125 | 126 | def name 127 | @name ||= extract_name 128 | end 129 | 130 | def types 131 | @types ||= extract_types 132 | end 133 | 134 | def description 135 | @description ||= extract_description 136 | end 137 | 138 | def property(name) 139 | @properties[name] 140 | end 141 | 142 | def properties_domains 143 | domains = {} 144 | properties.keys.each do |prop| 145 | d = prop.split('/')[1] 146 | domains[d] ||= 0 147 | domains[d] += properties[prop].size 148 | end 149 | domains 150 | end 151 | 152 | def sync 153 | @data = FreebaseAPI.session.topic(self.id, @options) 154 | build 155 | end 156 | 157 | def image(options={}) 158 | FreebaseAPI::Image.get(self.id, options) 159 | end 160 | 161 | def inspect 162 | "#<#{self.class}:0x#{self.__id__.to_s(16)} id: \"#{self.id}\", name: \"#{self.name}\">" 163 | end 164 | 165 | private 166 | 167 | def extract_name 168 | if names = property('/type/object/name') 169 | names.first.value 170 | else 171 | text 172 | end 173 | end 174 | 175 | def extract_types 176 | if types = property('/type/object/type') 177 | types.map(&:id) 178 | else 179 | [] 180 | end 181 | end 182 | 183 | def extract_description 184 | if description = property('/common/topic/description') 185 | description.first.value 186 | end 187 | end 188 | 189 | def parse_exclusion(exclusion) 190 | if !exclusion 191 | [] 192 | elsif exclusion.is_a?(Array) 193 | exclusion 194 | else 195 | [exclusion] 196 | end 197 | end 198 | 199 | def build 200 | if @data['property'] 201 | FreebaseAPI.logger.debug("Building topic #{self.id} : #{@data['property'].size} properties") 202 | @data['property'].each do |key, value| 203 | build_property(key, value) 204 | end 205 | end 206 | invalidate_lazy_properties 207 | end 208 | 209 | def build_property(property, content) 210 | unless @excluded_properties.select { |p| property.start_with?(p) }.any? 211 | if content['valuetype'] 212 | case content['valuetype'] 213 | when 'compound', 'object' 214 | # Note : some referenced topics have empty ids, we need to exclude them 215 | @properties[property] = content['values'].reject { |s| s['id'].empty? }.map { |at| Topic.new(at['id'], :data => at) } 216 | else 217 | @properties[property] = content['values'].map { |at| Attribute.new(at, :type => content['valuetype']) } 218 | end 219 | end 220 | end 221 | end 222 | 223 | def invalidate_lazy_properties 224 | @name = nil 225 | @types = nil 226 | @description = nil 227 | end 228 | end 229 | end -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # freebase-api [![Build Status](https://travis-ci.org/PerfectMemory/freebase-api.png?branch=master)](http://travis-ci.org/mongoid/mongoid) [![Coverage Status](https://coveralls.io/repos/PerfectMemory/freebase-api/badge.png?branch=master)](https://coveralls.io/r/PerfectMemory/freebase-api) [![Code Climate](https://codeclimate.com/github/PerfectMemory/freebase-api.png)](https://codeclimate.com/github/PerfectMemory/freebase-api) [![Dependency Status](https://gemnasium.com/PerfectMemory/freebase-api.png)](https://gemnasium.com/PerfectMemory/freebase-api) 2 | 3 | freebase-api provides access to both a raw-access and an abstract-layer to the [Freebase API](http://wiki.freebase.com/wiki/Freebase_API). 4 | 5 | Currently supported APIs are : 6 | 7 | - Search ([doc](https://developers.google.com/freebase/v1/search)) 8 | - MQL Read ([doc](https://developers.google.com/freebase/v1/mqlread)) 9 | - Topic ([doc](https://developers.google.com/freebase/v1/topic)) 10 | - Image support 11 | 12 | ## Installation 13 | 14 | Add this line to your application's Gemfile: 15 | 16 | gem 'freebase-api' 17 | 18 | And then execute: 19 | 20 | $ bundle 21 | 22 | Or install it yourself with: 23 | 24 | $ gem install freebase-api 25 | 26 | ## Session 27 | 28 | If you have a [Google API key](https://code.google.com/apis/console), you can set the environment variable `GOOGLE_API_KEY`, the default session will load it automatically. 29 | 30 | You can override the default session by providing some options, like the language or the environment. 31 | 32 | ```ruby 33 | FreebaseAPI.session = FreebaseAPI::Session.new(key: 'GOOGLE_API_KEY', env: :stable) 34 | ``` 35 | 36 | Options are : 37 | 38 | * `:key` : your Google API key 39 | * `:env` : the environment (sandbox or stable) (default is :stable) 40 | * `:proxy` : an optional url for a proxy that requests will be sent through 41 | * `:query_options` : some options that will be used in each query 42 | * `:limit` : the number of items returned (default is 10) 43 | * `:lang` : the language you would like the content in (default is 'en') 44 | 45 | ## Fetch resources 46 | 47 | The DSL of `freebase-api` is based on 3 main models : 48 | 49 | * **Topic** (used to store topics, types, CVTs) 50 | * **Attribute** (used to store value typed properties such as string, number, date) 51 | * **Image** (used to store image data) 52 | 53 | To access a specific resource on Freebase, you can use the `Topic` model which provides some useful methods. The `get` method is based on the `Topic API`. 54 | 55 | ```ruby 56 | resource = FreebaseAPI::Topic.get('/en/github') # => # 57 | ``` 58 | 59 | Once the resource is fetched, you can access its properties : 60 | 61 | ```ruby 62 | # id 63 | resource.id # => "/m/04g0kcw" 64 | 65 | # Name 66 | resource.name # => "GitHub" 67 | 68 | # Description 69 | resource.description # => "GitHub is a web-based hosting service for software dev..." 70 | 71 | # Types 72 | resource.types # => ["/common/topic", "/internet/website", "/base/technologyofdoing/proposal_agent"] 73 | 74 | # Properties 75 | resource.properties.size # => 18 76 | 77 | # Access a specific Property 78 | attribute = resource.property('/common/topic/official_website').first # => # 79 | attribute.value # => "http://github.com/" 80 | 81 | types = resource.property('/type/object/type') # => # 82 | types.map(&:id) # => equivalent to resource.types 83 | 84 | types[1].name # => "Website" 85 | types[1].sync # fetch all the properties of this topic 86 | types[1].description # => "Website or (Web site) is a collection of web pages, typically common..." 87 | 88 | # Image 89 | image = property.image(maxwidth: 150, maxheight: 150) # => # 90 | image.store('/path/to/filename') # record the image 91 | ``` 92 | 93 | ## Search resources 94 | 95 | To search topics using a query, you can use `FreebaseAPI::Topic.search` that returns a Hash ordered by scores (keys). This method supports 96 | all the parameters of the official API. 97 | 98 | ``` 99 | results = FreebaseAPI::Topic.search('jackson') # => [100.55349=>#, 73.209366=># ...] 100 | best_match = results.values.first # => # 101 | best_match.name # => "Michael Jackson" 102 | best_match.sync 103 | best_match.description # => "Michael Joseph Jackson (August 29, 1958 – June 25, 2009) was an American recording artist..." 104 | ``` 105 | 106 | ```ruby 107 | FreebaseAPI::Topic.search('Cee Lo Green', filter: '(all type:/music/artist created:"The Lady Killer")') 108 | # => {868.82666=>#"/m/01w5jwb", 127 | "id"=>"/en/cee_lo_1974", 128 | "name"=>"Cee Lo Green", 129 | "notable"=>{"name"=>"Singer-songwriter", "id"=>"/m/016z4k"}, 130 | "lang"=>"en", 131 | "score"=>868.82666}] 132 | ``` 133 | 134 | ### Topic 135 | 136 | The `topic` method calls a web service that will return all known facts for a given topic including images and text blurbs. You can apply filters to the Topic API so that it only returns the property values that you're interested in. This is ideal for building topic pages and short summaries of an entity. You can consult the API reference [here](https://developers.google.com/freebase/v1/topic). 137 | 138 | ```ruby 139 | FreebaseAPI.session.topic('/en/cee_lo_1974', filter: '/people/person/profession') 140 | ``` 141 | 142 | The method returns a Hash containing all the properties. 143 | 144 | ```json 145 | { "id"=>"/m/01w5jwb", 146 | "property"=>{ 147 | "/people/person/profession"=>{"valuetype"=>"object", "values"=>[ 148 | {"text"=>"Record producer", "lang"=>"en", "id"=>"/m/0dz3r", "creator"=>"/user/mw_template_bot", "timestamp"=>"2008-10-09T08:34:08.000Z"}, 149 | {"text"=>"Singer-songwriter", "lang"=>"en", "id"=>"/m/016z4k", "creator"=>"/user/mw_template_bot", "timestamp"=>"2008-10-09T08:34:08.000Z"}, 150 | {"text"=>"Actor", "lang"=>"en", "id"=>"/m/02hrh1q", "creator"=>"/user/netflixbot", "timestamp"=>"2011-04-09T04:01:36.001Z"}, 151 | {"text"=>"Rapper", "lang"=>"en", "id"=>"/m/0hpcdn2", "creator"=>"/user/lycel", "timestamp"=>"2011-12-28T07:40:42.002Z"} 152 | ], "count"=>4.0} 153 | } 154 | } 155 | ``` 156 | 157 | ### MQL Read 158 | 159 | The `mqlread` method provides access to the Freebase database using the [Metaweb query language (MQL)](https://developers.google.com/freebase/v1/mql-overview). You can consult the API reference [here](https://developers.google.com/freebase/v1/mqlread). 160 | 161 | ```ruby 162 | FreebaseAPI.session.mqlread({ 163 | :type => '/internet/website', 164 | :id => '/en/github', 165 | :'/common/topic/official_website' => nil} 166 | ) 167 | ``` 168 | 169 | The method returns a Hash containing the MQL response. 170 | 171 | ```json 172 | {"/common/topic/official_website"=>"http://github.com/", 173 | "id"=>"/en/github", 174 | "type"=>"/internet/website"} 175 | ``` 176 | 177 | ### Image 178 | 179 | The `image` method retrieves image data for a given Topic. 180 | 181 | ```ruby 182 | FreebaseAPI.session.image('/en/github', maxwidth: 150, maxheight: 150) 183 | ``` 184 | 185 | The method returns data bytes. 186 | 187 | ``` 188 | \x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x0... 189 | ``` 190 | 191 | ## Contributing to freebase-api 192 | 193 | * Check the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet 194 | * Check the issue tracker to make sure someone already hasn't requested it and/or contributed it 195 | * Fork the project 196 | * Start a feature/bugfix branch 197 | * Commit and push until you are happy with your contribution 198 | * Make sure to add tests for it. This is important so I don't unintentionally break it in a future version. 199 | * Please try not to mess with the Rakefile, version, or history. If you want, or really need, to have your own version, that is fine, but please isolate it in its own commit so I can cherry-pick around it. 200 | 201 | ## Copyright 202 | 203 | Copyright (c) 2013 Perfect Memory. See LICENSE.txt for 204 | further details. 205 | 206 | -------------------------------------------------------------------------------- /spec/fixtures/topic.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "/m/04g0kcw", 3 | "text": "GitHub", 4 | "lang": "en", 5 | "property": { 6 | "/common/topic/alias": { 7 | "valuetype": "string", 8 | "values": [ 9 | { 10 | "text": "Github", 11 | "lang": "en", 12 | "value": "Github", 13 | "creator": "/user/mwcl_wikipedia_en", 14 | "timestamp": "2008-08-18T10:47:44.000Z" 15 | } 16 | ], 17 | "count": 1.0 18 | }, 19 | "/common/topic/article": { 20 | "valuetype": "compound", 21 | "values": [ 22 | { 23 | "text": "GitHub is a web-based hosting service for software development projects that use the Git...", 24 | "lang": "en", 25 | "id": "/m/04g0kc_", 26 | "creator": "/user/mwcl_wikipedia_en", 27 | "timestamp": "2008-08-08T06:20:46.009Z", 28 | "property": { 29 | "/common/document/source_uri": { 30 | "valuetype": "uri", 31 | "values": [ 32 | { 33 | "text": "http://wp/en/18545292", 34 | "lang": "", 35 | "value": "http://wp/en/18545292", 36 | "creator": "/user/mwcl_wikipedia_en", 37 | "timestamp": "2008-08-08T06:20:46.009Z" 38 | } 39 | ], 40 | "count": 1.0 41 | }, 42 | "/common/document/text": { 43 | "valuetype": "string", 44 | "values": [ 45 | { 46 | "text": "GitHub is a web-based hosting service for software development projects that use the Git...", 47 | "lang": "en", 48 | "value": "GitHub is a web-based hosting service for software development projects that use the Git revision control system. GitHub offers both commercial plans and free accounts for open source projects. GitHub is the most popular Git hosting site, and the most popular open source hosting site.\nThe site provides social networking functionality such as feeds, followers and the network graph to display how developers work on their versions of a repository.\nGitHub also operates a pastebin-style site called Gist, wikis for individual repositories, and web pages that can be edited through a Git repository.\nAs of January 2010, GitHub is operated under the name GitHub, Inc.\nThe software that runs GitHub was written using Ruby on Rails and Erlang by GitHub, Inc. (previously known as Logical Awesome) developers Chris Wanstrath, PJ Hyett, and Tom Preston-Werner.\nGitHub was launched in April 2008.\nIn a talk at Yahoo! headquarters on 24 February 2009, GitHub team members announced that during the first year that GitHub was online, it accumulated 46,000 public repositories, 17,000 of them in the previous month alone. At that time, about 6,200 repositories had been forked at least once and 4,600 merged.", 49 | "creator": "/user/mwcl_wikipedia_en", 50 | "timestamp": "2008-08-08T06:20:46.009Z" 51 | } 52 | ], 53 | "count": 1.0 54 | }, 55 | "/type/object/attribution": { 56 | "valuetype": "object", 57 | "values": [ 58 | { 59 | "text": "Freebase Data Team", 60 | "lang": "", 61 | "id": "/m/0gs8", 62 | "creator": "/user/mwcl_wikipedia_en", 63 | "timestamp": "2008-08-08T06:20:46.009Z" 64 | } 65 | ], 66 | "count": 1.0 67 | }, 68 | "/type/object/type": { 69 | "valuetype": "object", 70 | "values": [ 71 | { 72 | "text": "Document", 73 | "lang": "", 74 | "id": "/common/document", 75 | "creator": "/user/mwcl_wikipedia_en", 76 | "timestamp": "2008-08-08T06:20:46.009Z" 77 | } 78 | ], 79 | "count": 1.0 80 | } 81 | } 82 | } 83 | ], 84 | "count": 1.0 85 | }, 86 | "/common/topic/description": { 87 | "valuetype": "string", 88 | "values": [ 89 | { 90 | "text": "GitHub is a web-based hosting service for software development projects that use the Git...", 91 | "lang": "en", 92 | "value": "GitHub is a web-based hosting service for software development projects that use the Git revision control system. GitHub offers both paid plans for private repositories, and free accounts for open source projects. As of May 2011, GitHub was the most popular open source code repository site.\nGitHub Inc. was founded in 2008 and is based in San Francisco, California.\nIn July 2012, the company received US$100 million in Series A funding, primarily from Andreessen Horowitz.", 93 | "creator": "/user/wikirecon_bot", 94 | "project": "wikirecon", 95 | "dataset": "/m/0kj4zz_", 96 | "citation": { 97 | "provider": "Wikipedia", 98 | "statement": "Description licensed under the Creative Commons Attribution-ShareAlike License (http://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License).", 99 | "uri": "http://en.wikipedia.org/wiki/GitHub" 100 | } 101 | } 102 | ], 103 | "count": 17.0 104 | }, 105 | "/common/topic/to_ignore": { 106 | "values": [], 107 | "count": 0.0, 108 | "status": "has_value" 109 | }, 110 | "/common/topic/notable_for": { 111 | "valuetype": "object", 112 | "values": [ 113 | { 114 | "text": "Website", 115 | "lang": "en", 116 | "id": "/internet/website" 117 | } 118 | ], 119 | "count": 1.0 120 | }, 121 | "/common/topic/notable_types": { 122 | "valuetype": "object", 123 | "values": [ 124 | { 125 | "text": "Website", 126 | "lang": "en", 127 | "id": "/internet/website", 128 | "timestamp": "2013-01-17T00:03:19.118Z" 129 | }, 130 | { 131 | "text": "Proposal agent", 132 | "lang": "en", 133 | "id": "/base/technologyofdoing/proposal_agent", 134 | "timestamp": "2013-01-17T00:03:19.118Z" 135 | } 136 | ], 137 | "count": 2.0 138 | }, 139 | "/common/topic/official_website": { 140 | "valuetype": "uri", 141 | "values": [ 142 | { 143 | "text": "http://github.com/", 144 | "lang": "", 145 | "value": "http://github.com/", 146 | "creator": "/user/gardening_bot", 147 | "timestamp": "2011-10-08T05:25:46.001Z" 148 | } 149 | ], 150 | "count": 1.0 151 | }, 152 | "/common/topic/topic_equivalent_webpage": { 153 | "valuetype": "uri", 154 | "values": [ 155 | { 156 | "text": "http://en.wikipedia.org/wiki/index.html?curid=18545292", 157 | "lang": "", 158 | "value": "http://en.wikipedia.org/wiki/index.html?curid=18545292", 159 | "creator": "/user/mwcl_wikipedia_en", 160 | "timestamp": "2008-08-08T06:20:46.009Z" 161 | }, 162 | { 163 | "text": "http://pt.wikipedia.org/wiki/index.html?curid=2108722", 164 | "lang": "", 165 | "value": "http://pt.wikipedia.org/wiki/index.html?curid=2108722", 166 | "creator": "/user/wikipedia_intl", 167 | "timestamp": "2010-09-24T19:25:20.000Z" 168 | }, 169 | { 170 | "text": "http://ru.wikipedia.org/wiki/index.html?curid=1588243", 171 | "lang": "", 172 | "value": "http://ru.wikipedia.org/wiki/index.html?curid=1588243", 173 | "creator": "/user/wikipedia_intl", 174 | "timestamp": "2010-09-25T02:43:28.000Z" 175 | }, 176 | { 177 | "text": "http://fr.wikipedia.org/wiki/index.html?curid=4161604", 178 | "lang": "", 179 | "value": "http://fr.wikipedia.org/wiki/index.html?curid=4161604", 180 | "creator": "/user/wikipedia_intl", 181 | "timestamp": "2010-09-25T07:46:48.001Z" 182 | }, 183 | { 184 | "text": "http://de.wikipedia.org/wiki/index.html?curid=5239764", 185 | "lang": "", 186 | "value": "http://de.wikipedia.org/wiki/index.html?curid=5239764", 187 | "creator": "/user/wikipedia_intl", 188 | "timestamp": "2010-09-29T20:54:44.000Z" 189 | }, 190 | { 191 | "text": "http://ru.wikipedia.org/wiki/GitHub", 192 | "lang": "", 193 | "value": "http://ru.wikipedia.org/wiki/GitHub", 194 | "creator": "/user/wikipedia_intl", 195 | "timestamp": "2011-05-22T03:13:18.001Z" 196 | }, 197 | { 198 | "text": "http://fr.wikipedia.org/wiki/GitHub", 199 | "lang": "", 200 | "value": "http://fr.wikipedia.org/wiki/GitHub", 201 | "creator": "/user/wikipedia_intl", 202 | "timestamp": "2011-05-22T06:19:45.000Z" 203 | }, 204 | { 205 | "text": "http://pt.wikipedia.org/wiki/GitHub", 206 | "lang": "", 207 | "value": "http://pt.wikipedia.org/wiki/GitHub", 208 | "creator": "/user/wikipedia_intl", 209 | "timestamp": "2011-05-22T09:45:11.001Z" 210 | }, 211 | { 212 | "text": "http://de.wikipedia.org/wiki/GitHub", 213 | "lang": "", 214 | "value": "http://de.wikipedia.org/wiki/GitHub", 215 | "creator": "/user/wikipedia_intl", 216 | "timestamp": "2011-05-22T15:41:11.000Z" 217 | }, 218 | { 219 | "text": "http://en.wikipedia.org/wiki/GitHub", 220 | "lang": "", 221 | "value": "http://en.wikipedia.org/wiki/GitHub", 222 | "creator": "/user/mwcl_wikipedia_en", 223 | "timestamp": "2011-05-23T10:20:05.002Z" 224 | } 225 | ], 226 | "count": 10.0 227 | }, 228 | "/internet/website/category": { 229 | "valuetype": "object", 230 | "values": [ 231 | { 232 | "text": "Revision control", 233 | "lang": "en", 234 | "id": "/m/0fj8g", 235 | "creator": "/user/giskard", 236 | "timestamp": "2010-01-29T12:12:43.000Z" 237 | } 238 | ], 239 | "count": 1.0 240 | }, 241 | "/type/object/attribution": { 242 | "valuetype": "object", 243 | "values": [ 244 | { 245 | "text": "Freebase Data Team", 246 | "lang": "en", 247 | "id": "/m/0gs8", 248 | "creator": "/user/mwcl_wikipedia_en", 249 | "timestamp": "2008-08-08T06:20:46.008Z" 250 | } 251 | ], 252 | "count": 1.0 253 | }, 254 | "/type/object/key": { 255 | "valuetype": "key", 256 | "values": [ 257 | { 258 | "text": "/wikipedia/en_id/18545292", 259 | "lang": "", 260 | "value": "/wikipedia/en_id/18545292", 261 | "creator": "/user/mwcl_wikipedia_en", 262 | "timestamp": "2008-08-08T06:20:46.009Z" 263 | }, 264 | { 265 | "text": "/en/github", 266 | "lang": "", 267 | "value": "/en/github", 268 | "creator": "/user/topicns_loader", 269 | "timestamp": "2009-02-28T15:22:15.001Z" 270 | }, 271 | { 272 | "text": "/wikipedia/de/GitHub", 273 | "lang": "", 274 | "value": "/wikipedia/de/GitHub", 275 | "creator": "/user/wikipedia_intl", 276 | "timestamp": "2010-09-23T09:35:04.001Z" 277 | }, 278 | { 279 | "text": "/wikipedia/pt/GitHub", 280 | "lang": "", 281 | "value": "/wikipedia/pt/GitHub", 282 | "creator": "/user/wikipedia_intl", 283 | "timestamp": "2010-09-23T17:54:47.000Z" 284 | }, 285 | { 286 | "text": "/wikipedia/ru/GitHub", 287 | "lang": "", 288 | "value": "/wikipedia/ru/GitHub", 289 | "creator": "/user/wikipedia_intl", 290 | "timestamp": "2010-09-24T11:52:03.000Z" 291 | }, 292 | { 293 | "text": "/wikipedia/pt_id/2108722", 294 | "lang": "", 295 | "value": "/wikipedia/pt_id/2108722", 296 | "creator": "/user/wikipedia_intl", 297 | "timestamp": "2010-09-24T19:25:20.000Z" 298 | }, 299 | { 300 | "text": "/wikipedia/ru_id/1588243", 301 | "lang": "", 302 | "value": "/wikipedia/ru_id/1588243", 303 | "creator": "/user/wikipedia_intl", 304 | "timestamp": "2010-09-25T02:43:28.000Z" 305 | }, 306 | { 307 | "text": "/wikipedia/fr_id/4161604", 308 | "lang": "", 309 | "value": "/wikipedia/fr_id/4161604", 310 | "creator": "/user/wikipedia_intl", 311 | "timestamp": "2010-09-25T07:46:48.001Z" 312 | }, 313 | { 314 | "text": "/wikipedia/fr/GitHub", 315 | "lang": "", 316 | "value": "/wikipedia/fr/GitHub", 317 | "creator": "/user/wikipedia_intl", 318 | "timestamp": "2010-09-25T19:05:21.000Z" 319 | }, 320 | { 321 | "text": "/wikipedia/de_id/5239764", 322 | "lang": "", 323 | "value": "/wikipedia/de_id/5239764", 324 | "creator": "/user/wikipedia_intl", 325 | "timestamp": "2010-09-29T20:54:44.000Z" 326 | }, 327 | { 328 | "text": "/wikipedia/ru_title/GitHub", 329 | "lang": "", 330 | "value": "/wikipedia/ru_title/GitHub", 331 | "creator": "/user/wikipedia_intl", 332 | "timestamp": "2011-05-22T03:13:18.001Z" 333 | }, 334 | { 335 | "text": "/wikipedia/fr_title/GitHub", 336 | "lang": "", 337 | "value": "/wikipedia/fr_title/GitHub", 338 | "creator": "/user/wikipedia_intl", 339 | "timestamp": "2011-05-22T06:19:45.000Z" 340 | }, 341 | { 342 | "text": "/wikipedia/pt_title/GitHub", 343 | "lang": "", 344 | "value": "/wikipedia/pt_title/GitHub", 345 | "creator": "/user/wikipedia_intl", 346 | "timestamp": "2011-05-22T09:45:11.001Z" 347 | }, 348 | { 349 | "text": "/wikipedia/de_title/GitHub", 350 | "lang": "", 351 | "value": "/wikipedia/de_title/GitHub", 352 | "creator": "/user/wikipedia_intl", 353 | "timestamp": "2011-05-22T15:41:11.000Z" 354 | }, 355 | { 356 | "text": "/wikipedia/en_title/GitHub", 357 | "lang": "", 358 | "value": "/wikipedia/en_title/GitHub", 359 | "creator": "/user/mwcl_wikipedia_en", 360 | "timestamp": "2011-05-23T10:20:05.002Z" 361 | }, 362 | { 363 | "text": "/wikipedia/en/Github", 364 | "lang": "", 365 | "value": "/wikipedia/en/Github", 366 | "creator": "/user/mwcl_wikipedia_en", 367 | "timestamp": "2012-08-15T05:11:01.000Z" 368 | }, 369 | { 370 | "text": "/wikipedia/en/Git_hub", 371 | "lang": "", 372 | "value": "/wikipedia/en/Git_hub", 373 | "creator": "/user/mwcl_wikipedia_en", 374 | "timestamp": "2012-08-15T05:11:03.000Z" 375 | }, 376 | { 377 | "text": "/wikipedia/en/Github$002Ecom", 378 | "lang": "", 379 | "value": "/wikipedia/en/Github$002Ecom", 380 | "creator": "/user/mwcl_wikipedia_en", 381 | "timestamp": "2012-08-15T05:11:05.001Z" 382 | }, 383 | { 384 | "text": "/wikipedia/en/GitHub", 385 | "lang": "", 386 | "value": "/wikipedia/en/GitHub", 387 | "creator": "/user/mwcl_wikipedia_en", 388 | "timestamp": "2012-08-15T05:11:07.001Z" 389 | } 390 | ], 391 | "count": 19.0 392 | }, 393 | "/type/object/mid": { 394 | "valuetype": "key", 395 | "values": [ 396 | { 397 | "text": "/m/04g0kcw", 398 | "lang": "", 399 | "value": "/m/04g0kcw" 400 | } 401 | ], 402 | "count": 1.0 403 | }, 404 | "/type/object/name": { 405 | "valuetype": "string", 406 | "values": [ 407 | { 408 | "text": "GitHub", 409 | "lang": "en", 410 | "value": "GitHub", 411 | "creator": "/user/mwcl_wikipedia_en", 412 | "timestamp": "2008-08-18T10:47:44.000Z" 413 | } 414 | ], 415 | "count": 1.0 416 | }, 417 | "/type/object/type": { 418 | "valuetype": "object", 419 | "values": [ 420 | { 421 | "text": "Topic", 422 | "lang": "en", 423 | "id": "/common/topic", 424 | "creator": "/user/mwcl_wikipedia_en", 425 | "timestamp": "2008-08-08T06:20:46.009Z" 426 | }, 427 | { 428 | "text": "Website", 429 | "lang": "en", 430 | "id": "/internet/website", 431 | "creator": "/user/giskard", 432 | "timestamp": "2010-01-29T12:12:03.000Z" 433 | }, 434 | { 435 | "text": "Proposal agent", 436 | "lang": "en", 437 | "id": "/base/technologyofdoing/proposal_agent", 438 | "creator": "/user/techdoer", 439 | "timestamp": "2012-09-10T08:18:26.000Z" 440 | } 441 | ], 442 | "count": 3.0 443 | }, 444 | "/type/object/guid": { 445 | "valuetype": "string", 446 | "values": [ 447 | { 448 | "text": "#9202a8c04000641f8000000008e0457b", 449 | "lang": "", 450 | "value": "#9202a8c04000641f8000000008e0457b" 451 | } 452 | ], 453 | "count": 1.0 454 | }, 455 | "/type/object/id": { 456 | "valuetype": "key", 457 | "values": [ 458 | { 459 | "text": "/en/github", 460 | "lang": "", 461 | "value": "/en/github" 462 | } 463 | ], 464 | "count": 1.0 465 | }, 466 | "/type/object/creator": { 467 | "valuetype": "object", 468 | "values": [ 469 | { 470 | "text": "mwcl_wikipedia_en", 471 | "lang": "", 472 | "id": "/user/mwcl_wikipedia_en", 473 | "timestamp": "2008-08-08T06:20:46.008Z" 474 | } 475 | ], 476 | "count": 1.0 477 | }, 478 | "/type/object/timestamp": { 479 | "valuetype": "datetime", 480 | "values": [ 481 | { 482 | "text": "2008-08-08T06:20:46.008Z", 483 | "lang": "en", 484 | "value": "2008-08-08T06:20:46.008Z" 485 | } 486 | ], 487 | "count": 1.0 488 | }, 489 | "/type/object/permission": { 490 | "valuetype": "object", 491 | "values": [ 492 | { 493 | "text": "/boot/all_permission", 494 | "lang": "", 495 | "id": "/boot/all_permission" 496 | } 497 | ], 498 | "count": 1.0 499 | } 500 | } 501 | } --------------------------------------------------------------------------------