evri 0.03
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/Manifest.txt +15 -0
- data/README.txt +48 -0
- data/Rakefile +22 -0
- data/TODO +13 -0
- data/evri-api.gemspec +39 -0
- data/lib/evri/entity.rb +240 -0
- data/lib/evri/media.rb +47 -0
- data/lib/evri/relation.rb +22 -0
- data/lib/evri/zeitgeist.rb +31 -0
- data/lib/evri.rb +144 -0
- data/test/test_entity.rb +144 -0
- data/test/test_evri.rb +35 -0
- data/test/test_media.rb +10 -0
- data/test/test_zeitgeist.rb +19 -0
- metadata +103 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
TODO
|
6
|
+
evri-api.gemspec
|
7
|
+
lib/evri.rb
|
8
|
+
lib/evri/entity.rb
|
9
|
+
lib/evri/media.rb
|
10
|
+
lib/evri/relation.rb
|
11
|
+
lib/evri/zeitgeist.rb
|
12
|
+
test/test_entity.rb
|
13
|
+
test/test_evri.rb
|
14
|
+
test/test_media.rb
|
15
|
+
test/test_zeitgeist.rb
|
data/README.txt
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= evri-api
|
2
|
+
|
3
|
+
* http://github.com/joevandyk/evri-api
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
A beautiful API that wraps the RESTful services provided by evri.com.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
None yet.
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
None yet.
|
16
|
+
|
17
|
+
== REQUIREMENTS:
|
18
|
+
|
19
|
+
none yet.
|
20
|
+
|
21
|
+
== INSTALL:
|
22
|
+
|
23
|
+
gem install evri-api
|
24
|
+
|
25
|
+
== LICENSE:
|
26
|
+
|
27
|
+
(The MIT License)
|
28
|
+
|
29
|
+
Copyright (c) 2008 Joe Van Dyk
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
32
|
+
a copy of this software and associated documentation files (the
|
33
|
+
'Software'), to deal in the Software without restriction, including
|
34
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
35
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
36
|
+
permit persons to whom the Software is furnished to do so, subject to
|
37
|
+
the following conditions:
|
38
|
+
|
39
|
+
The above copyright notice and this permission notice shall be
|
40
|
+
included in all copies or substantial portions of the Software.
|
41
|
+
|
42
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
43
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
44
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
45
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
46
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
47
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
48
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/evri.rb'
|
6
|
+
|
7
|
+
Hoe.new('evri', Evri::VERSION) do |p|
|
8
|
+
p.rubyforge_name = 'evri-api'
|
9
|
+
p.developer('Joe Van Dyk', 'joe@@fixieconsulting.com')
|
10
|
+
p.extra_deps = [:json]
|
11
|
+
p.extra_dev_deps = [:mocha]
|
12
|
+
end
|
13
|
+
|
14
|
+
task :github do
|
15
|
+
data = `rake check_manifest | sed 1d`
|
16
|
+
IO.popen("patch -p0 Manifest.txt", "w") do |p|
|
17
|
+
p << data
|
18
|
+
end
|
19
|
+
`rake debug_gem | sed 1d > evri-api.gemspec`
|
20
|
+
end
|
21
|
+
|
22
|
+
# vim: syntax=Ruby
|
data/TODO
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
STUFF LEFT TO DO
|
2
|
+
|
3
|
+
* Handle Query Tokens
|
4
|
+
- QTs are initially generated here:
|
5
|
+
- query token for whole medias/entity document
|
6
|
+
- query token for each entity in the returned results of ^
|
7
|
+
- query token for each pair of entities in above ^
|
8
|
+
- Then pass them into the calls that can take them (and handle the returned QTs)
|
9
|
+
|
10
|
+
* Refactor code to make Resource and JSON handling flexible (after API is done)
|
11
|
+
|
12
|
+
* Ability to batch requests (via threading)
|
13
|
+
- see how facebooker does it
|
data/evri-api.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = %q{evri}
|
3
|
+
s.version = "0.03"
|
4
|
+
|
5
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
6
|
+
s.authors = ["Joe Van Dyk"]
|
7
|
+
s.date = %q{2008-10-27}
|
8
|
+
s.description = %q{A beautiful API that wraps the RESTful services provided by evri.com.}
|
9
|
+
s.email = ["joe@@fixieconsulting.com"]
|
10
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
|
11
|
+
s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "TODO", "evri-api.gemspec", "lib/evri.rb", "lib/evri/entity.rb", "lib/evri/media.rb", "lib/evri/relation.rb", "lib/evri/zeitgeist.rb", "test/test_entity.rb", "test/test_evri.rb", "test/test_media.rb", "test/test_zeitgeist.rb"]
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.homepage = %q{http://github.com/joevandyk/evri-api}
|
14
|
+
s.rdoc_options = ["--main", "README.txt"]
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.rubyforge_project = %q{evri}
|
17
|
+
s.rubygems_version = %q{1.2.0}
|
18
|
+
s.summary = %q{A beautiful API that wraps the RESTful services provided by evri.com.}
|
19
|
+
s.test_files = ["test/test_media.rb", "test/test_zeitgeist.rb", "test/test_entity.rb", "test/test_evri.rb"]
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 2
|
24
|
+
|
25
|
+
if current_version >= 3 then
|
26
|
+
s.add_runtime_dependency(%q<json>, [">= 0"])
|
27
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
28
|
+
s.add_development_dependency(%q<hoe>, [">= 1.7.0"])
|
29
|
+
else
|
30
|
+
s.add_dependency(%q<json>, [">= 0"])
|
31
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
32
|
+
s.add_dependency(%q<hoe>, [">= 1.7.0"])
|
33
|
+
end
|
34
|
+
else
|
35
|
+
s.add_dependency(%q<json>, [">= 0"])
|
36
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
37
|
+
s.add_dependency(%q<hoe>, [">= 1.7.0"])
|
38
|
+
end
|
39
|
+
end
|
data/lib/evri/entity.rb
ADDED
@@ -0,0 +1,240 @@
|
|
1
|
+
module Evri
|
2
|
+
# Represents an Evri Entity.
|
3
|
+
class Entity
|
4
|
+
attr_reader :properties
|
5
|
+
|
6
|
+
# Finds a specific entity, given an ID.
|
7
|
+
def self.find id
|
8
|
+
@entended_properties = true
|
9
|
+
@results ||= {}
|
10
|
+
id = "/" + id unless id =~ /\A\//
|
11
|
+
return @results[id] if @results[id]
|
12
|
+
@results[id] = create_from_json do
|
13
|
+
Evri.query(:type => :uri, :query => id)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Searches for an exact match.
|
18
|
+
def self.search name
|
19
|
+
create_from_jsons do
|
20
|
+
Evri.query(:type => :search, :query => name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Searches for a partial match. i.e. 'ob' will return 'Obama'.
|
25
|
+
def self.search_by_prefix prefix
|
26
|
+
create_from_jsons do
|
27
|
+
Evri.query(:type => :prefix, :query => prefix)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Given a :uri and :text options, return the related entities.
|
32
|
+
def self.from_media options={}
|
33
|
+
uri, text = options[:uri], options[:text]
|
34
|
+
raise ArgumentError, "Must specify URI via the :uri option" unless uri
|
35
|
+
raise ArgumentError, "Must specify some text via the :text option" unless text
|
36
|
+
|
37
|
+
json = Evri.parse_json(Evri.query(:type => :from_media, :uri => uri, :text => text))
|
38
|
+
json["graph"]["entities"].map do |e, entity_json|
|
39
|
+
Entity.new entity_json
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns where the information for this entity came from.
|
44
|
+
def source_url
|
45
|
+
@source_url
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns information about an entity.
|
49
|
+
# i.e obama.info(:birth_date)
|
50
|
+
def info option=nil
|
51
|
+
@properties ||= {}
|
52
|
+
if defined?(@extended_properties)
|
53
|
+
if option
|
54
|
+
@properties[option]
|
55
|
+
else
|
56
|
+
@properties
|
57
|
+
end
|
58
|
+
else
|
59
|
+
@properties = Entity.find(uri).properties
|
60
|
+
@extended_properties = true
|
61
|
+
info(option)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
"#{name} (#{ id })"
|
67
|
+
end
|
68
|
+
|
69
|
+
def inspect
|
70
|
+
"#<Evri::Entity:#{to_s}>"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the name of the entity
|
74
|
+
def name
|
75
|
+
@parsed_json["name"]["$"] || @parsed_json["name"]
|
76
|
+
end
|
77
|
+
|
78
|
+
def initialize json
|
79
|
+
@parsed_json = json
|
80
|
+
@relations = []
|
81
|
+
@source_url = Evri.source_url
|
82
|
+
if json["properties"]
|
83
|
+
set_properties json["properties"]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def == b
|
88
|
+
self.id == b.id
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns the Evri URI of the entity
|
92
|
+
def href
|
93
|
+
@parsed_json["@href"]
|
94
|
+
end
|
95
|
+
|
96
|
+
alias id href
|
97
|
+
alias uri href
|
98
|
+
|
99
|
+
# TODO
|
100
|
+
def target_href
|
101
|
+
@parsed_json["@targetHref"]
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns relationships for the entity
|
105
|
+
def relations options={}
|
106
|
+
from_domains = nil
|
107
|
+
if options[:from]
|
108
|
+
if options[:from].class == String
|
109
|
+
from_domains = options[:from]
|
110
|
+
else
|
111
|
+
from_domains = options[:from].join(',')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
json = Evri.query(:type => :relations, :query => id, :from_domains => from_domains)
|
115
|
+
Evri.parse_json(json)["relations"].each do |type, relation_json|
|
116
|
+
next if type != 'relation'
|
117
|
+
relation_json.each do |r|
|
118
|
+
@relations << Relation.new(r)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
@relations
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns which entities are related to the current entity
|
125
|
+
def related_by options={}
|
126
|
+
media, verb, verb_value, entity, uri = nil, nil, nil, nil, nil
|
127
|
+
options.each do |key, value|
|
128
|
+
if key == :entity
|
129
|
+
entity = value
|
130
|
+
elsif key == :uri
|
131
|
+
uri = value
|
132
|
+
elsif key == :type
|
133
|
+
media = value
|
134
|
+
else
|
135
|
+
verb = key
|
136
|
+
verb_value = value
|
137
|
+
end
|
138
|
+
end
|
139
|
+
@entities = []
|
140
|
+
json = Evri.query(:type => :related_by, :media => media, :query => id, :uri => uri, :entity => entity, :verb => verb, :value => verb_value)
|
141
|
+
Evri.parse_json(json)["relations"].each do |type, relation_json|
|
142
|
+
next if type != 'relation'
|
143
|
+
relation_json["targets"].each do |type, r|
|
144
|
+
r.each do |entity|
|
145
|
+
if entity.class == Hash
|
146
|
+
if entity["@href"]
|
147
|
+
@entities << Entity.new(entity)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
@entities
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns images relating to the current entity
|
157
|
+
def images options={}
|
158
|
+
medias = []
|
159
|
+
if options[:entities]
|
160
|
+
entity_uris = options[:entities].map do |e|
|
161
|
+
if e.class == Evri::Entity
|
162
|
+
e.uri
|
163
|
+
else
|
164
|
+
e
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
json = Evri.parse_json(Evri.query(:type => :related_medias, :query => href, :entities => entity_uris, :media => 'image'))
|
170
|
+
# TODO handle images here
|
171
|
+
if json["mediaResult"]["imageList"]
|
172
|
+
json["mediaResult"]["imageList"]["image"].each do |media_json|
|
173
|
+
medias << Image.new(media_json)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
medias
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns articles relating to the current entity
|
180
|
+
def articles options={}
|
181
|
+
medias = []
|
182
|
+
if options[:entities]
|
183
|
+
entity_uris = options[:entities].map do |e|
|
184
|
+
if e.class == Evri::Entity
|
185
|
+
e.uri
|
186
|
+
else
|
187
|
+
e
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
type = options[:type] ? options[:type] : nil
|
193
|
+
json = Evri.parse_json(Evri.query(:type => :related_medias, :query => href, :entities => entity_uris, :media => type))
|
194
|
+
if json["mediaResult"]["articleList"]
|
195
|
+
json["mediaResult"]["articleList"]["article"].each do |media_json|
|
196
|
+
medias << Article.new(media_json)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
medias
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def self.create_from_json json=nil, &block
|
206
|
+
begin
|
207
|
+
json = block.call if block
|
208
|
+
Entity.new Evri.parse_json(json)["entity"]
|
209
|
+
rescue Evri::Error => e
|
210
|
+
raise Evri::EntityNotFound.new(e.message)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def self.create_from_jsons jsons=nil, &block
|
215
|
+
jsons = block.call if block
|
216
|
+
result = []
|
217
|
+
Evri.parse_json(jsons)["entities"].each do |type, entity|
|
218
|
+
if entity.class == Array
|
219
|
+
entity.each do |e|
|
220
|
+
result << Entity.new(e)
|
221
|
+
end
|
222
|
+
else
|
223
|
+
result << Entity.new(entity)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
result
|
227
|
+
end
|
228
|
+
|
229
|
+
def set_properties json
|
230
|
+
@properties = {}
|
231
|
+
json["property"].each do |prop|
|
232
|
+
if prop.class == Array
|
233
|
+
@properties[prop.first.to_sym] = prop.last["$"]
|
234
|
+
else
|
235
|
+
@properties[prop["name"]["$"].to_sym] = prop["value"]["$"]
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
data/lib/evri/media.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Evri
|
2
|
+
class Media
|
3
|
+
end
|
4
|
+
|
5
|
+
# Represents an Article.
|
6
|
+
class Article < Media
|
7
|
+
attr_accessor :title, :href, :uri, :author, :published_at, :content
|
8
|
+
def initialize json
|
9
|
+
@title = json["title"]["$"]
|
10
|
+
@author = json["author"]["$"]
|
11
|
+
@content = json["content"] ? json["content"]["$"] : "No content provided"
|
12
|
+
@published_at = json["published"]["$"]
|
13
|
+
@href = Evri.api_host + json["link"]["@href"]
|
14
|
+
@uri = json["link"]["@hostName"] + json["link"]["@path"]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Represents an Image.
|
19
|
+
class Image < Media
|
20
|
+
attr_accessor :size, :title, :article_href, :mime_type, :thumbnail, :date, :content, :width, :url, :click_url, :height
|
21
|
+
def initialize json
|
22
|
+
@title = json["title"]
|
23
|
+
@width = json["@width"]
|
24
|
+
@height = json["@height"]
|
25
|
+
@url = json["@url"]
|
26
|
+
@size = json["size"]
|
27
|
+
@title = json["title"]
|
28
|
+
@article_href = json["articleHref"]["$"]
|
29
|
+
@mime_type = json["mimeType"]
|
30
|
+
@date = Date.parse(json["date"]["$"])
|
31
|
+
@content = json["content"]["$"]
|
32
|
+
@click_url = json["clickUrl"]["$"]
|
33
|
+
@thumbnail = Thumbnail.new(json["thumbnail"])
|
34
|
+
end
|
35
|
+
|
36
|
+
# Represents an Thumbnail for an Image.
|
37
|
+
class Thumbnail
|
38
|
+
attr_accessor :size, :width, :height, :url
|
39
|
+
def initialize json
|
40
|
+
@size = json["size"]
|
41
|
+
@width = json["@width"]
|
42
|
+
@height = json["@height"]
|
43
|
+
@url = json["@url"]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Evri
|
2
|
+
# Represents a relation.
|
3
|
+
class Relation
|
4
|
+
attr_accessor :name, :href, :type
|
5
|
+
def initialize json
|
6
|
+
@name = json["name"]["$"]
|
7
|
+
@href = json["@href"]
|
8
|
+
@type = json["type"]["$"]
|
9
|
+
@entities = []
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the entities in this relation.
|
13
|
+
def entities
|
14
|
+
return @entities unless @entities.empty?
|
15
|
+
json = JSON.parse(Evri.query(:type => :uri, :query => @href))
|
16
|
+
json["relations"]["relation"]["targets"]["entity"].each do |entity_json|
|
17
|
+
@entities << Entity.new(entity_json)
|
18
|
+
end
|
19
|
+
@entities
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Evri
|
2
|
+
# Returns information about a specific trend.
|
3
|
+
#
|
4
|
+
# To access the information, combine a TREND with a TYPE to form a class method.
|
5
|
+
#
|
6
|
+
# i.e.
|
7
|
+
#
|
8
|
+
# * Zeitgeist.all_chemical
|
9
|
+
# * Zeitgeist.falling_person
|
10
|
+
# * Zeitgeist.popular_organization
|
11
|
+
#
|
12
|
+
# etc
|
13
|
+
class Zeitgeist
|
14
|
+
|
15
|
+
TYPES = %w( animal backterium chemical concept disorder event location organization person plant product virus )
|
16
|
+
TRENDS = %w( all popular rising falling )
|
17
|
+
|
18
|
+
TRENDS.each do |t|
|
19
|
+
TYPES.each do |et|
|
20
|
+
eval %(
|
21
|
+
def self.#{t}_#{et}
|
22
|
+
json = Evri.parse_json(Evri.query :type => :zeitgeist, :query => "#{et}/#{t}")
|
23
|
+
json["zeitgeist"]["#{t}"]["entities"]["entity"].map do |entity_json|
|
24
|
+
Entity.new(entity_json)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/evri.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
$: << File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'net/http'
|
5
|
+
require 'cgi'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'rubygems'
|
9
|
+
rescue LoadError
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'json'
|
14
|
+
|
15
|
+
for file in Dir[File.join(File.dirname(__FILE__), 'evri', '*')]
|
16
|
+
require file
|
17
|
+
end
|
18
|
+
|
19
|
+
module Evri
|
20
|
+
|
21
|
+
# A general error.
|
22
|
+
class Error < Exception; end
|
23
|
+
|
24
|
+
# Raised whenever the entity you are searching for cannot be found.
|
25
|
+
class EntityNotFound < Error; end
|
26
|
+
|
27
|
+
VERSION = "0.03"
|
28
|
+
@@api_host = "api.evri.com"
|
29
|
+
@@source_host = @@api_host
|
30
|
+
@@source_url = nil
|
31
|
+
|
32
|
+
# Sets the hostname for the Evri API.
|
33
|
+
def self.api_host= host
|
34
|
+
@@api_host = validate_host(host)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the hostname for the Evri API.
|
38
|
+
def self.api_host
|
39
|
+
@@api_host
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the source host for the Evri API
|
43
|
+
def self.source_host
|
44
|
+
@@source_host
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the source host for the Evri API
|
48
|
+
def self.source_host= host
|
49
|
+
@@source_host = validate_host(host)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the source url for the Evri API
|
53
|
+
def self.source_url
|
54
|
+
@@source_url
|
55
|
+
end
|
56
|
+
|
57
|
+
# Parses JSON data
|
58
|
+
def self.parse_json json
|
59
|
+
begin
|
60
|
+
JSON.parse(json)
|
61
|
+
rescue JSON::ParserError => e
|
62
|
+
# puts "Error!"
|
63
|
+
# puts e.message
|
64
|
+
# puts json
|
65
|
+
raise Error.new(e)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# TODO Rewrite
|
70
|
+
def self.query options={}
|
71
|
+
query = ""
|
72
|
+
case options[:type]
|
73
|
+
when :uri
|
74
|
+
path = options[:query]
|
75
|
+
when :relations
|
76
|
+
path = options[:query] + "/relations"
|
77
|
+
if options[:from_domains]
|
78
|
+
query = "includeDomain=#{options[:from_domains]}"
|
79
|
+
end
|
80
|
+
when :related_by
|
81
|
+
if options[:uri]
|
82
|
+
path = options[:query] + "/related/entities"
|
83
|
+
query = "uri=#{escape(options[:uri])}"
|
84
|
+
else
|
85
|
+
path = options[:query] + "/relations"
|
86
|
+
if options[:verb]
|
87
|
+
path += "/#{options[:verb]}/#{escape(options[:value])}"
|
88
|
+
if options[:entity]
|
89
|
+
path += options[:entity].href
|
90
|
+
end
|
91
|
+
end
|
92
|
+
if options[:media]
|
93
|
+
query += "media=#{options[:media]}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
when :zeitgeist
|
97
|
+
path = "/zeitgeist/entities/" + options[:query]
|
98
|
+
when :related_medias
|
99
|
+
path = options[:query] + "/media/related"
|
100
|
+
if options[:entities]
|
101
|
+
query = options[:entities].map do |e|
|
102
|
+
"entityURI=#{e}&"
|
103
|
+
end
|
104
|
+
query = query.join
|
105
|
+
end
|
106
|
+
query += "type=#{options[:media]}" if options[:media]
|
107
|
+
when :from_media
|
108
|
+
path = "/media/entities"
|
109
|
+
query = "uri=#{escape(options[:uri])}&text=#{escape(options[:text])}"
|
110
|
+
when :search
|
111
|
+
path = "/entities/find"
|
112
|
+
query = "name=#{escape(options[:query])}"
|
113
|
+
when :prefix
|
114
|
+
path = "/entities/find"
|
115
|
+
query = "prefix=#{escape(options[:query])}"
|
116
|
+
else
|
117
|
+
raise ArgumentError, "unexpected type #{ options[:type] }"
|
118
|
+
end
|
119
|
+
query = nil if query.empty?
|
120
|
+
uri = URI::HTTP.build :host => self.api_host, :path => path + '.json', :query => query
|
121
|
+
# puts "getting #{ uri }"
|
122
|
+
response = Net::HTTP.get_response(uri)
|
123
|
+
raise Error.new("unexpected http response: #{ response.code }") unless response.code == "200"
|
124
|
+
@@source_url = @@source_host + uri.request_uri
|
125
|
+
response.body
|
126
|
+
end
|
127
|
+
|
128
|
+
# Escapes CGI text
|
129
|
+
def self.escape text
|
130
|
+
text ? CGI.escape(text.to_s) : ''
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def self.validate_host host
|
136
|
+
host = host.to_s
|
137
|
+
begin
|
138
|
+
URI::HTTP.build(:host => host)
|
139
|
+
host
|
140
|
+
rescue URI::InvalidComponentError
|
141
|
+
raise ArgumentError.new("Invalid host name specified (#{host}). Use something like 'api.evri.com'")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/test/test_entity.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'lib/evri'
|
3
|
+
require 'mocha'
|
4
|
+
|
5
|
+
class TestEntity < Test::Unit::TestCase
|
6
|
+
BARACK_ID = "/person/barack-obama-0x16f69"
|
7
|
+
MICHELLE_ID = "person/michelle-obama-0x4c6e8" # Works without leading slash
|
8
|
+
MCCAIN_ID = "/person/john-mccain-0x2a2a7"
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@obama = Evri::Entity.find BARACK_ID
|
12
|
+
@michelle = Evri::Entity.find MICHELLE_ID
|
13
|
+
@mccain = Evri::Entity.find MCCAIN_ID
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_failed_http_get_raises_good_error
|
17
|
+
bad_response = stub(:code => "500")
|
18
|
+
Net::HTTP.expects(:get_response).returns(bad_response)
|
19
|
+
assert_raises(Evri::Error) { @obama.relations }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Ensure that when we search for Barack, we get the @obama and @michelle objects returned
|
23
|
+
def test_class_find
|
24
|
+
results = Evri::Entity.search "obama"
|
25
|
+
[@obama, @michelle].each do |person|
|
26
|
+
assert results.include?(person), "Expected search to contain #{ person }"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_find_raises_for_missing_ids
|
31
|
+
assert_raises(Evri::EntityNotFound) { Evri::Entity.find "crap" }
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_failed_search_returns_empty_array
|
35
|
+
assert Evri::Entity.search("asdfasdfasdfasdfjlskdjflkasdjfkljasdklfjkladsjfklajsdl;kfjlaksdjfadklsjfkladsjlfkjasdfkljas#").empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_class_find_with_prefix
|
39
|
+
results = Evri::Entity.search_by_prefix "ob"
|
40
|
+
assert results.include?(@obama), "Expected search to contain #{ @obama }"
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_name
|
44
|
+
assert_equal @obama.name, "Barack Obama"
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_id
|
48
|
+
assert_equal @obama.id, BARACK_ID
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_properties
|
52
|
+
vandyk_parks = Evri::Entity.find "/person/van-dyke-parks-0x16b10"
|
53
|
+
assert vandyk_parks.info(:occupation).include?("Composer")
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_properties_from_search
|
57
|
+
vandyk_parks = Evri::Entity.search_by_prefix("van dyke parks").first
|
58
|
+
assert vandyk_parks.info(:occupation).include?("Composer")
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_properties_from_related_by
|
62
|
+
loaded_entity = @obama.related_by(:verb => :kill).first
|
63
|
+
assert loaded_entity.info(:name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_relations
|
67
|
+
assert @obama.relations.find { |r| r.name == "US Politician" }
|
68
|
+
assert @obama.relations.find { |r| r.href == "#{@obama.href}/relations/facet/politician" }
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_related_by_from_uri
|
72
|
+
# Sends right data, but bug in API prevents it from knowing about the URI
|
73
|
+
# assert !@obama.related_by(:uri => "www.boston.com/news/nation/articles/2008/10/23/obama_takes_campaign_break_to_visit_ill_grandmother/?rss_id=Boston.com+--+Latest+news")
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_relations_from_domains
|
77
|
+
assert !@obama.relations(:from => "time.com").empty?
|
78
|
+
assert !@obama.relations(:from => ["time.com", "nytimes.com"]).empty?
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_relations_with_type
|
82
|
+
# TODO tests
|
83
|
+
@obama.related_by(:verb => :kill, :type => :image)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_relations_related_to
|
87
|
+
# TODO tests
|
88
|
+
@obama.related_by(:facet => :musical_artist)
|
89
|
+
@obama.related_by(:qt => "joined-by-0x78")
|
90
|
+
@mccain.related_by(:verb => :kill, :entity => @obama)
|
91
|
+
@obama.related_by(:facet => :politician, :entity => @mccain)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_source_url
|
95
|
+
dude = @obama.related_by(:facet => :politician).first
|
96
|
+
assert_equal "#{Evri.source_host}#{@obama.uri}/relations/facet/politician.json", dude.source_url
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_media_related_to_entities_about_an_entity
|
100
|
+
# articles takes either entities or entity URIs
|
101
|
+
# NOTE: the plural of 'media' is 'media'
|
102
|
+
medias = @obama.articles(:entities => [@mccain, "/person/bill-ayers-0x27a65"])
|
103
|
+
assert medias.size > 1
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_medias_with_type_of_image
|
107
|
+
image = @obama.images.first
|
108
|
+
%w( size title article_href mime_type thumbnail date content width url click_url height).each do |field|
|
109
|
+
assert image.send(field), "Expected #{ image.inspect }'s #{ field } field to be larger than zero"
|
110
|
+
end
|
111
|
+
%w( size width height url).each do |field|
|
112
|
+
assert image.thumbnail.send(field), "Expected #{ image.thumbnail.inspect }'s #{ field } field to be larger than zero"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_relations_loading
|
117
|
+
politician_relation = @obama.relations.find { |r| r.name == "US Politician" }
|
118
|
+
assert politician_relation.entities.include?(@mccain)
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_relations_with_verb
|
122
|
+
entities = @obama.related_by(:verb => :attack)
|
123
|
+
assert entities.first.target_href.include?("attack")
|
124
|
+
|
125
|
+
assert @obama.related_by(:qt => "joined-by-0x78").find { |e| e.name == "Joe Biden" }
|
126
|
+
|
127
|
+
assert @obama.related_by(:facet => :politician).include?(@mccain)
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_media
|
131
|
+
media = @obama.articles.first
|
132
|
+
# check for media.title, media.uri, etc
|
133
|
+
%w( title author published_at content href uri).each do |field|
|
134
|
+
assert media.send(field).size > 0, "Expected #{ media.inspect }'s #{ field } field to be larger than zero"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_find_by_media
|
139
|
+
uri = "http://www.reuters.com/article/industryNews/idUSTRE4981RO20081009"
|
140
|
+
dreamworks = Evri::Entity.find "/organization/dreamworks-0x3c510"
|
141
|
+
entities = Evri::Entity.from_media(:uri => uri, :text => "Dreamworks")
|
142
|
+
assert entities.include?(dreamworks)
|
143
|
+
end
|
144
|
+
end
|
data/test/test_evri.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'lib/evri'
|
3
|
+
require 'mocha'
|
4
|
+
|
5
|
+
class TestModuleEvri < Test::Unit::TestCase
|
6
|
+
def test_setting_invalid_new_api_host
|
7
|
+
remember_original_settings(:api_host) do
|
8
|
+
assert_raise(ArgumentError) { Evri.api_host = "http://slashdot.org" }
|
9
|
+
assert_raise(ArgumentError) { Evri.api_host = "Joe's Cat" }
|
10
|
+
assert_raise(ArgumentError) { Evri.api_host = 3 }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_setting_new_api_host
|
15
|
+
remember_original_settings(:api_host) do
|
16
|
+
Evri.api_host = "new.api.evri.com"
|
17
|
+
assert_equal Evri.api_host, "new.api.evri.com"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_source_host
|
22
|
+
remember_original_settings(:source_host) do
|
23
|
+
Evri.source_host = "new.api.evri.com"
|
24
|
+
assert_equal Evri.source_host, "new.api.evri.com"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def remember_original_settings method
|
31
|
+
original = Evri.send method
|
32
|
+
yield ensure Evri.send"#{method}=", original
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
data/test/test_media.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'lib/evri'
|
3
|
+
|
4
|
+
class TestZeitgeist < Test::Unit::TestCase
|
5
|
+
BARACK_ID = "person/barack-obama-0x16f69"
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@obama = Evri::Entity.find BARACK_ID
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_popular_person
|
12
|
+
assert Evri::Zeitgeist.popular_person.include?(@obama)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_falling_product
|
16
|
+
# just check to see that the first falling product's name is not blank.
|
17
|
+
assert Evri::Zeitgeist.falling_product.first.name.size > 0
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: evri
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.03"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joe Van Dyk
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-10-31 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: :json
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: :mocha
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.8.2
|
44
|
+
version:
|
45
|
+
description: A beautiful API that wraps the RESTful services provided by evri.com.
|
46
|
+
email:
|
47
|
+
- joe@@fixieconsulting.com
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- History.txt
|
54
|
+
- Manifest.txt
|
55
|
+
- README.txt
|
56
|
+
files:
|
57
|
+
- History.txt
|
58
|
+
- Manifest.txt
|
59
|
+
- README.txt
|
60
|
+
- Rakefile
|
61
|
+
- TODO
|
62
|
+
- evri-api.gemspec
|
63
|
+
- lib/evri.rb
|
64
|
+
- lib/evri/entity.rb
|
65
|
+
- lib/evri/media.rb
|
66
|
+
- lib/evri/relation.rb
|
67
|
+
- lib/evri/zeitgeist.rb
|
68
|
+
- test/test_entity.rb
|
69
|
+
- test/test_evri.rb
|
70
|
+
- test/test_media.rb
|
71
|
+
- test/test_zeitgeist.rb
|
72
|
+
has_rdoc: true
|
73
|
+
homepage: http://github.com/joevandyk/evri-api
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options:
|
76
|
+
- --main
|
77
|
+
- README.txt
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: "0"
|
85
|
+
version:
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0"
|
91
|
+
version:
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project: evri-api
|
95
|
+
rubygems_version: 1.2.0
|
96
|
+
signing_key:
|
97
|
+
specification_version: 2
|
98
|
+
summary: A beautiful API that wraps the RESTful services provided by evri.com.
|
99
|
+
test_files:
|
100
|
+
- test/test_entity.rb
|
101
|
+
- test/test_evri.rb
|
102
|
+
- test/test_zeitgeist.rb
|
103
|
+
- test/test_media.rb
|