basuco 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +24 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.textile +318 -0
- data/Rakefile +1 -0
- data/basuco.gemspec +23 -0
- data/examples/artist.rb +20 -0
- data/examples/artist_links.rb +38 -0
- data/lib/basuco.rb +30 -0
- data/lib/basuco/api.rb +26 -0
- data/lib/basuco/attribute.rb +67 -0
- data/lib/basuco/collection.rb +7 -0
- data/lib/basuco/property.rb +91 -0
- data/lib/basuco/request.rb +86 -0
- data/lib/basuco/resource.rb +215 -0
- data/lib/basuco/search.rb +100 -0
- data/lib/basuco/topic.rb +192 -0
- data/lib/basuco/trans.rb +36 -0
- data/lib/basuco/type.rb +53 -0
- data/lib/basuco/util.rb +17 -0
- data/lib/basuco/version.rb +3 -0
- data/lib/basuco/view.rb +54 -0
- data/rails/init.rb +2 -0
- data/stats.sh +30 -0
- data/tasks/ken.rb +4 -0
- data/tasks/spec.rb +25 -0
- data/test/fixtures/music_artist.json +103 -0
- data/test/fixtures/the_police.json +937 -0
- data/test/fixtures/the_police_topic.json +751 -0
- data/test/integration/ken_test.rb +75 -0
- data/test/test_helper.rb +59 -0
- data/test/unit/attribute_test.rb +49 -0
- data/test/unit/property_test.rb +27 -0
- data/test/unit/resource_test.rb +58 -0
- data/test/unit/session_test.rb +44 -0
- data/test/unit/topic_test.rb +92 -0
- data/test/unit/type_test.rb +35 -0
- data/test/unit/view_test.rb +48 -0
- metadata +94 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
module Basuco
|
2
|
+
class << self
|
3
|
+
attr_accessor :search
|
4
|
+
end
|
5
|
+
|
6
|
+
# partially taken from chris eppstein's freebase api
|
7
|
+
# http://github.com/chriseppstein/freebase/tree
|
8
|
+
class Search
|
9
|
+
include Request
|
10
|
+
public
|
11
|
+
# @param host<String> the API host
|
12
|
+
# @param username<String> freebase username
|
13
|
+
# @param password<String> user password
|
14
|
+
def initialize(options = {:host => 'http://www.freebase.com', :username => 'un', :password => 'pw'})
|
15
|
+
@host = options[:host]
|
16
|
+
@username = options[:username]
|
17
|
+
@password = options[:password]
|
18
|
+
Basuco.search = self
|
19
|
+
|
20
|
+
# TODO: check connection
|
21
|
+
end
|
22
|
+
|
23
|
+
# Perform a mqlread and return the results
|
24
|
+
# Specify :cursor => true to batch the results of a query, sending multiple requests if necessary.
|
25
|
+
# TODO: should support multiple queries
|
26
|
+
# you should be able to pass an array of queries
|
27
|
+
def mqlread(query, options = {})
|
28
|
+
cursor = options[:cursor]
|
29
|
+
if cursor
|
30
|
+
query_result = []
|
31
|
+
while cursor
|
32
|
+
response = get_query_response(query, cursor)
|
33
|
+
query_result += response['result']
|
34
|
+
cursor = response['cursor']
|
35
|
+
end
|
36
|
+
else
|
37
|
+
response = get_query_response(query, cursor)
|
38
|
+
cursor = response['cursor']
|
39
|
+
query_result = response['result']
|
40
|
+
end
|
41
|
+
query_result
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# Executes an Mql Query against the Freebase API and returns the result as
|
46
|
+
# a <tt>Collection</tt> of <tt>Resources</tt>.
|
47
|
+
def all(options = {})
|
48
|
+
#assert_kind_of 'options', options, Hash
|
49
|
+
query = { :name => nil }.merge!(options).merge!(:id => nil)
|
50
|
+
result = mqlread([ query ], :cursor => !options[:limit])
|
51
|
+
Basuco::Collection.new(result.map { |r| Basuco::Resource.new(r) })
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# Executes an Mql Query against the Freebase API and returns the result wrapped
|
56
|
+
# in a <tt>Resource</tt> Object.
|
57
|
+
#
|
58
|
+
# == Examples
|
59
|
+
#
|
60
|
+
# Basuco.get('/en/the_police') => #<Resource id="/en/the_police" name="The Police">
|
61
|
+
# @api public
|
62
|
+
def get(id)
|
63
|
+
Basuco::Resource.get(id)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def topic(id, options = {})
|
68
|
+
options.merge!({:id => id})
|
69
|
+
|
70
|
+
response = http_request topic_service_url+"/standard", options
|
71
|
+
--debugger
|
72
|
+
result = JSON.parse response
|
73
|
+
inner = result[id]
|
74
|
+
handle_read_error(inner)
|
75
|
+
inner['result']
|
76
|
+
end
|
77
|
+
|
78
|
+
def search(query, options = {})
|
79
|
+
options.merge!({:query => query})
|
80
|
+
|
81
|
+
response = http_request search_service_url, options
|
82
|
+
result = JSON.parse response
|
83
|
+
|
84
|
+
handle_read_error(result)
|
85
|
+
|
86
|
+
result['result']
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
#yes or no
|
91
|
+
def status?
|
92
|
+
response = http_request status_service_url
|
93
|
+
result = JSON.parse response
|
94
|
+
return result["code"] == "/api/status/ok"
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
end # class Search
|
100
|
+
end # module Basuco
|
data/lib/basuco/topic.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
module Basuco
|
2
|
+
class Topic
|
3
|
+
|
4
|
+
attr_reader :data
|
5
|
+
|
6
|
+
# initializes a topic using a json result
|
7
|
+
def initialize(data)
|
8
|
+
# assert_kind_of 'data', data, Hash
|
9
|
+
@data = data
|
10
|
+
@schema_loaded, @attributes_loaded = false, false
|
11
|
+
end
|
12
|
+
|
13
|
+
# Retrieves a topic using the Topic API by Freebase
|
14
|
+
# returns a <tt>Topic</tt> Object.
|
15
|
+
#
|
16
|
+
# == Examples
|
17
|
+
#
|
18
|
+
# Basuco::Topic.get('/en/the_police') => #<Topic id="/en/the_police" name="The Police">
|
19
|
+
# @api public
|
20
|
+
def self.get(id)
|
21
|
+
#assert_kind_of 'id', id, String
|
22
|
+
result = Basuco.search.topic(id)
|
23
|
+
raise TopicNotFound unless result
|
24
|
+
Basuco::Topic.new(result)
|
25
|
+
end
|
26
|
+
|
27
|
+
# topic id
|
28
|
+
# @api public
|
29
|
+
def id
|
30
|
+
@data["id"] || ""
|
31
|
+
end
|
32
|
+
|
33
|
+
# topic aliases
|
34
|
+
def aliases
|
35
|
+
@data["alias"]
|
36
|
+
end
|
37
|
+
|
38
|
+
# topic freebase url
|
39
|
+
def url
|
40
|
+
@data["url"]
|
41
|
+
end
|
42
|
+
|
43
|
+
# topic name
|
44
|
+
# @api public
|
45
|
+
def name
|
46
|
+
text
|
47
|
+
end
|
48
|
+
|
49
|
+
# topic description
|
50
|
+
# @api public
|
51
|
+
def description
|
52
|
+
@data["description"]
|
53
|
+
end
|
54
|
+
|
55
|
+
# topic text
|
56
|
+
# @api public
|
57
|
+
def text
|
58
|
+
@data["text"]
|
59
|
+
end
|
60
|
+
|
61
|
+
# topic thumbnail
|
62
|
+
def thumbnail
|
63
|
+
@data["thumbnail"]
|
64
|
+
end
|
65
|
+
|
66
|
+
# @api public
|
67
|
+
def to_s
|
68
|
+
name || id || ""
|
69
|
+
end
|
70
|
+
|
71
|
+
# @api public
|
72
|
+
def inspect
|
73
|
+
result = "#<Topic id=\"#{id}\" name=\"#{name || "nil"}\">"
|
74
|
+
end
|
75
|
+
|
76
|
+
# topic webpages
|
77
|
+
# currently returned as an array of hashes containing the keys "text" and "url"
|
78
|
+
# that hashes may be wrapped into a Webpage class later
|
79
|
+
# @api public
|
80
|
+
def webpages
|
81
|
+
@data["webpage"]
|
82
|
+
end
|
83
|
+
|
84
|
+
# returns all assigned types
|
85
|
+
# @api public
|
86
|
+
def types
|
87
|
+
load_schema! unless schema_loaded?
|
88
|
+
@types
|
89
|
+
end
|
90
|
+
|
91
|
+
# returns individual type based on the requested type id
|
92
|
+
# @api public
|
93
|
+
def type(type)
|
94
|
+
types.each { |t| return t if t.id =~ /^#{Regexp.escape(type)}$/}
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
98
|
+
# returns all the properties from all assigned types
|
99
|
+
# @api public
|
100
|
+
def properties
|
101
|
+
@properties = Basuco::Collection.new
|
102
|
+
types.each do |type|
|
103
|
+
@properties.concat(type.properties)
|
104
|
+
end
|
105
|
+
@properties
|
106
|
+
end
|
107
|
+
|
108
|
+
# returns all attributes for every type the topic is an instance of
|
109
|
+
# @api public
|
110
|
+
def attributes
|
111
|
+
load_attributes! unless attributes_loaded?
|
112
|
+
@attributes.values
|
113
|
+
end
|
114
|
+
|
115
|
+
# returns all available views based on the assigned types
|
116
|
+
# @api public
|
117
|
+
def views
|
118
|
+
@views ||= Basuco::Collection.new(types.map { |type| Basuco::View.new(self, type) })
|
119
|
+
end
|
120
|
+
|
121
|
+
# returns individual view based on the requested type id
|
122
|
+
# @api public
|
123
|
+
def view(type)
|
124
|
+
views.each { |v| return v if v.type.id =~ /^#{Regexp.escape(type)}$/}
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
# search for an attribute by name and return it
|
130
|
+
# @api public
|
131
|
+
def attribute(name)
|
132
|
+
attributes.each { |a| return a if a.property.id == name }
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
|
136
|
+
# returns true if type information is already loaded
|
137
|
+
# @api public
|
138
|
+
def schema_loaded?
|
139
|
+
@schema_loaded
|
140
|
+
end
|
141
|
+
|
142
|
+
# returns true if attributes are already loaded
|
143
|
+
# @api public
|
144
|
+
def attributes_loaded?
|
145
|
+
@attributes_loaded
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
# loads the full set of attributes using reflection
|
151
|
+
# information is extracted from master, value and reverse attributes
|
152
|
+
# @api private
|
153
|
+
def load_attributes!
|
154
|
+
load_schema! unless schema_loaded?
|
155
|
+
|
156
|
+
@attributes = {}
|
157
|
+
@data["properties"].each do |id, data|
|
158
|
+
# skip mediator properties for now
|
159
|
+
if !data["properties"]
|
160
|
+
values = []
|
161
|
+
data["values"].each do |value|
|
162
|
+
values << { "id" => value["id"], "name" => value["text"], "value" => value["text"] }
|
163
|
+
end
|
164
|
+
@attributes[id] = Basuco::Attribute.create(values, properties.select { |p| p.id == id }.first)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
@attributes_loaded = true
|
169
|
+
end
|
170
|
+
|
171
|
+
# loads the topic's metainfo
|
172
|
+
# @api private
|
173
|
+
def load_schema!
|
174
|
+
result = {}
|
175
|
+
|
176
|
+
@data["type"].each do |type|
|
177
|
+
result[type["id"]] = { "id" => type["id"], "name" => type["text"], "properties" => [] }
|
178
|
+
end
|
179
|
+
@data["properties"].each do |id, data|
|
180
|
+
result[id.gsub(/\/\w*$/, "")]["properties"] << {
|
181
|
+
"expected_type" => data["expected_type"]["id"],
|
182
|
+
"id" => id,
|
183
|
+
"name" => data["text"],
|
184
|
+
"unique" => true
|
185
|
+
}
|
186
|
+
end
|
187
|
+
@types = Basuco::Collection.new(result.values.map { |type| Basuco::Type.new(type) })
|
188
|
+
@schema_loaded = true
|
189
|
+
end
|
190
|
+
|
191
|
+
end # class Topic
|
192
|
+
end # module Basuco
|
data/lib/basuco/trans.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Basuco
|
2
|
+
class << self
|
3
|
+
attr_accessor :trans
|
4
|
+
end
|
5
|
+
|
6
|
+
# partially taken from chris eppstein's freebase api
|
7
|
+
# http://github.com/chriseppstein/freebase/tree
|
8
|
+
class Trans
|
9
|
+
include Request
|
10
|
+
|
11
|
+
def initialize(options = {:host => 'http://www.freebase.com', :username => 'un', :password => 'pw'})
|
12
|
+
@host = options[:host]
|
13
|
+
@username = options[:username]
|
14
|
+
@password = options[:password]
|
15
|
+
Basuco.trans = self
|
16
|
+
end
|
17
|
+
|
18
|
+
#yes or no
|
19
|
+
def status?
|
20
|
+
response = http_request status_service_url
|
21
|
+
result = JSON.parse response
|
22
|
+
return result["blob"] == "OK"
|
23
|
+
end
|
24
|
+
|
25
|
+
def raw_content(id, options = {})
|
26
|
+
response = http_request raw_service_url+id, options
|
27
|
+
response
|
28
|
+
end
|
29
|
+
|
30
|
+
def blurb_content(id, options = {})
|
31
|
+
response = http_request blurb_service_url+id, options
|
32
|
+
response
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/basuco/type.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Basuco
|
2
|
+
class Type
|
3
|
+
|
4
|
+
# include Extlib::Assertions
|
5
|
+
|
6
|
+
# initializes a resource using a json result
|
7
|
+
def initialize(data)
|
8
|
+
# assert_kind_of 'data', data, Hash
|
9
|
+
@data = data
|
10
|
+
end
|
11
|
+
|
12
|
+
# access property info
|
13
|
+
# @api public
|
14
|
+
def properties
|
15
|
+
@properties ||= Basuco::Collection.new(@data["properties"].map { |property| Basuco::Property.new(property, self) })
|
16
|
+
end
|
17
|
+
|
18
|
+
# type id
|
19
|
+
# @api public
|
20
|
+
def id
|
21
|
+
@data["id"]
|
22
|
+
end
|
23
|
+
|
24
|
+
# type name
|
25
|
+
# @api public
|
26
|
+
def name
|
27
|
+
@data["name"]
|
28
|
+
end
|
29
|
+
|
30
|
+
# @api public
|
31
|
+
def to_s
|
32
|
+
name || id || ""
|
33
|
+
end
|
34
|
+
|
35
|
+
# @api public
|
36
|
+
def inspect
|
37
|
+
result = "#<Type id=\"#{id}\" name=\"#{name || "nil"}\">"
|
38
|
+
end
|
39
|
+
|
40
|
+
# delegate to property_get
|
41
|
+
def method_missing sym
|
42
|
+
property_get(sym.to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
# @api private
|
47
|
+
# search for a property by name and return it
|
48
|
+
def property_get(name)
|
49
|
+
properties.each { |p| return p if p.id =~ /\/#{name}$/ }
|
50
|
+
raise PropertyNotFound
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/basuco/util.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Basuco
|
2
|
+
module Util
|
3
|
+
# magic hash conversion
|
4
|
+
def convert_hash(source)
|
5
|
+
source.inject({}) do |result, item|
|
6
|
+
if result[item["link"]]
|
7
|
+
result[item["link"]] << { "id" => item["id"], "name" => item["name"], "value" => item["value"] }
|
8
|
+
else
|
9
|
+
result[item["link"]] = []
|
10
|
+
result[item["link"]] << { "id" => item["id"], "name" => item["name"], "value" => item["value"] }
|
11
|
+
end
|
12
|
+
result
|
13
|
+
end
|
14
|
+
end
|
15
|
+
module_function :convert_hash
|
16
|
+
end
|
17
|
+
end
|
data/lib/basuco/view.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# provides an interface to view a subject (resource or topic) as a specific type
|
2
|
+
# provides an interface for working with attributes, properties
|
3
|
+
module Basuco
|
4
|
+
class View
|
5
|
+
|
6
|
+
|
7
|
+
# initializes a subject (resource or topic) by json result
|
8
|
+
def initialize(subject, type)
|
9
|
+
# assert_kind_of 'type', type, Basuco::Type
|
10
|
+
@subject, @type = subject, type
|
11
|
+
end
|
12
|
+
|
13
|
+
# @api public
|
14
|
+
def to_s
|
15
|
+
@type.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
# return correspondent type
|
19
|
+
# @api public
|
20
|
+
def type
|
21
|
+
@type
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api public
|
25
|
+
def inspect
|
26
|
+
result = "#<View type=\"#{type.id || "nil"}\">"
|
27
|
+
end
|
28
|
+
|
29
|
+
# returns attributes which are member of the view's type
|
30
|
+
# @api public
|
31
|
+
def attributes
|
32
|
+
@subject.attributes.select { |a| a.property.type == @type}
|
33
|
+
end
|
34
|
+
|
35
|
+
# search for an attribute by name and return it
|
36
|
+
# @api public
|
37
|
+
def attribute(name)
|
38
|
+
attributes.each { |a| return a if a.property.id =~ /\/#{name}$/ }
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# returns properties which are member of the view's type
|
43
|
+
# @api public
|
44
|
+
def properties
|
45
|
+
@subject.properties.select { |p| p.type == @type}
|
46
|
+
end
|
47
|
+
|
48
|
+
# delegate to attribute
|
49
|
+
def method_missing sym
|
50
|
+
attribute(sym.to_s)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|