michael-ken 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +70 -22
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/examples/artist.rb +2 -2
- data/examples/artist_links.rb +28 -0
- data/ken.gemspec +82 -0
- data/lib/ken/attribute.rb +16 -7
- data/lib/ken/collection.rb +0 -8
- data/lib/ken/property.rb +5 -0
- data/lib/ken/resource.rb +17 -14
- data/lib/ken/session.rb +34 -11
- data/lib/ken/type.rb +17 -2
- data/lib/ken/view.rb +13 -0
- data/lib/ken.rb +4 -10
- data/rails/init.rb +2 -0
- data/tasks/ken.rb +4 -0
- data/tasks/spec.rb +1 -1
- data/{spec → test}/fixtures/music_artist.json +1 -0
- data/test/fixtures/the_police.json +940 -0
- data/{spec/integration/ken_spec.rb → test/integration/ken_test.rb} +33 -28
- data/test/test_helper.rb +51 -0
- data/test/unit/attribute_test.rb +48 -0
- data/test/unit/property_test.rb +27 -0
- data/test/unit/resource_test.rb +48 -0
- data/test/unit/session_test.rb +27 -0
- data/test/unit/type_test.rb +35 -0
- data/test/unit/view_test.rb +48 -0
- metadata +38 -26
- data/lib/ken/version.rb +0 -3
- data/spec/fixtures/the_police.json +0 -891
- data/spec/spec.opts +0 -3
- data/spec/spec_helper.rb +0 -40
- data/spec/unit/attribute_spec.rb +0 -50
- data/spec/unit/property_spec.rb +0 -30
- data/spec/unit/resource_spec.rb +0 -63
- data/spec/unit/session_spec.rb +0 -18
- data/spec/unit/type_spec.rb +0 -21
- data/spec/unit/view_spec.rb +0 -28
data/README.textile
CHANGED
@@ -5,18 +5,15 @@ h2. Introduction
|
|
5
5
|
Ken is a Data Layer for Knowledge Representation.
|
6
6
|
|
7
7
|
It's being built to access the Metaweb Services supplied by Freebase.com.
|
8
|
-
|
8
|
+
The project’s goals are the provision of a concise API for querying and writing.
|
9
9
|
Therefore it wraps the Metaweb Architecture to smart Ruby Objects.
|
10
10
|
|
11
11
|
You can navigate the Freebase Graph using a rubyish syntax.
|
12
|
-
|
13
|
-
If things go right, you should be able to use this library as a Data Layer (instead of or in addition to
|
14
|
-
ActiveRecord/DataMapper) for your Web Framework of choice (Merb, Rails).
|
15
|
-
|
12
|
+
Also you can use this library as a Data Layer (instead of or in addition to ActiveRecord/DataMapper) for your Web Framework of choice (Merb, Rails).
|
16
13
|
|
17
14
|
h2. Installation
|
18
15
|
|
19
|
-
Use GitHub RubyGems
|
16
|
+
Use GitHub RubyGems:
|
20
17
|
|
21
18
|
<pre>
|
22
19
|
<code>
|
@@ -25,8 +22,15 @@ Use GitHub RubyGems.
|
|
25
22
|
</code>
|
26
23
|
</pre>
|
27
24
|
|
28
|
-
|
29
|
-
|
25
|
+
In your Ruby files add:
|
26
|
+
<pre>
|
27
|
+
<code>
|
28
|
+
require 'rubygems'
|
29
|
+
gem 'michael-ken'
|
30
|
+
require 'ken'
|
31
|
+
</code>
|
32
|
+
</pre>
|
33
|
+
|
30
34
|
|
31
35
|
h2. Getting started
|
32
36
|
|
@@ -159,7 +163,7 @@ So asking for values in a nested level does not make sense. Use nested statement
|
|
159
163
|
lowering the top level result.
|
160
164
|
|
161
165
|
However you can instead navigate the normal way to figure out that values.
|
162
|
-
_But won't that require another query to triggered?
|
166
|
+
_But won't that require another query to triggered? Certainly._
|
163
167
|
|
164
168
|
Let's look at a nested query:
|
165
169
|
|
@@ -182,32 +186,76 @@ Let's look at a nested query:
|
|
182
186
|
</code>
|
183
187
|
</pre>
|
184
188
|
|
185
|
-
|
189
|
+
h3. Access properties attributes directly
|
190
|
+
|
191
|
+
Ken is primarily designed for inspecting resources in a generic way, what's ideal for domain independent browsing applications.
|
192
|
+
However, there are legitimate situations where you already know what you want to access.
|
193
|
+
|
194
|
+
That's why I now added direct Property/Attribute access, but only on a Type/View level:
|
195
|
+
|
196
|
+
<pre>
|
197
|
+
<code>
|
198
|
+
resource = Ken.get('/en/new_order')
|
199
|
+
type = resource.types[1] # => #<Type id="/music/artist" name="Musical Artist">
|
200
|
+
# because we know _/music/artist_ has a _genre_ property we can access that directly
|
201
|
+
type.genre # => #<Property id="/music/artist/genre" expected_type="/music/genre" unique="false" object_type="true">
|
202
|
+
</code>
|
203
|
+
</pre>
|
204
|
+
The same works for views:
|
205
|
+
<pre>
|
206
|
+
<code>
|
207
|
+
resource = Ken.get('/en/new_order')
|
208
|
+
view = resource.views[1] # => #<View type="/music/artist">
|
209
|
+
# because we know _/music/artist_ has a _genre_ property we can access attribute directly as well
|
210
|
+
view.genre # => #<Attribute property="/music/artist/genre">
|
211
|
+
</code>
|
212
|
+
</pre>
|
213
|
+
|
214
|
+
If you rather want to query based on Types and access Properties/Attributes directly you can consider using
|
215
|
+
Chris "Eppsteins Freebase Library":http://github.com/chriseppstein/freebase/tree/master as an alternative.
|
216
|
+
|
186
217
|
|
187
|
-
|
188
|
-
there is no support for accessing Properties or Attributes directly when you know them already.
|
189
|
-
That keeps me focussed and prevents me from adding too much sugar.
|
218
|
+
h3. Low Level API
|
190
219
|
|
191
|
-
|
220
|
+
Sometimes you may want to do specific queries instead of inspecting Resources as a whole.
|
221
|
+
In such a case you would want to use Ken's low level API.
|
222
|
+
|
223
|
+
_mqlread_ works like the regular _mqlread service_, except that you are able to pass ruby hashes instead of JSON.
|
224
|
+
And you don't have to deal with HTTP, parameter encoding and parsing JSON.
|
225
|
+
|
226
|
+
<pre>
|
227
|
+
<code>
|
228
|
+
artists = Ken.session.mqlread([{
|
229
|
+
:type => "/music/artist",
|
230
|
+
:id => nil,
|
231
|
+
:"/common/topic/webpage" => [{:uri => nil}],
|
232
|
+
:home_page => [{:uri => nil}],
|
233
|
+
:limit => 2
|
234
|
+
}])
|
235
|
+
|
236
|
+
# => [
|
237
|
+
{"type"=>"/music/artist", "home_page"=>[{"uri"=>"http://www.massiveattack.co.uk/"}], "id"=>"/en/massive_attack", "/common/topic/webpage"=>[{"uri"=>"http://musicmoz.org/Bands_and_Artists/M/Massive_Attack/"}, {"uri"=>"http://www.discogs.com/artist/Massive+Attack"}, {"uri"=>"http://www.massiveattackarea.com/"}, {"uri"=>"http://www.massiveattack.co.uk/"}, {"uri"=>"http://www.massiveattack.com/"}]},
|
238
|
+
{"type"=>"/music/artist", "home_page"=>[{"uri"=>"http://www.apartment26.com/"}], "id"=>"/en/apartment_26", "/common/topic/webpage"=>[{"uri"=>"http://www.discogs.com/artist/Apartment+26"}, {"uri"=>"http://musicmoz.org/Bands_and_Artists/A/Apartment_26/"}, {"uri"=>"http://www.apartment26.com/"}]}
|
239
|
+
]
|
240
|
+
</code>
|
241
|
+
</pre>
|
192
242
|
|
193
|
-
|
194
|
-
Chris "Eppsteins Freebase Library":http://github.com/chriseppstein/freebase/tree/master instead.
|
243
|
+
h2. Project Status
|
195
244
|
|
196
245
|
h3. Features
|
197
246
|
|
198
247
|
* Fetching of single Resources
|
199
248
|
* Fetching of multiple Resources by specifying a query
|
249
|
+
* Accessing Properties/Attributes directly (on a type/view level)
|
200
250
|
* Type inspection
|
201
|
-
*
|
251
|
+
* Attribute inspection
|
252
|
+
* Low Level API (mqlread)
|
253
|
+
* Rails and Merb support
|
202
254
|
* Views on Resources to group Attributes based on the Resource's types
|
203
|
-
* Some specs
|
204
255
|
|
205
256
|
h3. Roadmap
|
206
257
|
|
207
|
-
#
|
208
|
-
# Better Type Support
|
209
|
-
# API for Set Based Browsing (see http://mqlx.com/~david/parallax/)
|
210
|
-
# Accessing Properties/Attributes directly (e.g. resource.genres )
|
258
|
+
# More tests
|
211
259
|
# Write-Support
|
212
260
|
|
213
261
|
Initial thoughts, obviously not up-to-date and not conforming to the current version, are available at "http://wiki.github.com/michael/ken":http://wiki.github.com/michael/ken.
|
data/Rakefile
CHANGED
@@ -9,6 +9,7 @@ begin
|
|
9
9
|
gem.email = "ma[at]zive[dot]at"
|
10
10
|
gem.homepage = "http://github.com/michael/ken"
|
11
11
|
gem.authors = ["michael"]
|
12
|
+
gem.add_dependency('extlib')
|
12
13
|
# gem.files = FileList["[A-Z]*.*"]
|
13
14
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
15
|
end
|
@@ -37,7 +38,6 @@ rescue LoadError
|
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
|
-
|
41
41
|
task :default => :test
|
42
42
|
|
43
43
|
require 'rake/rdoctask'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/examples/artist.rb
CHANGED
@@ -10,10 +10,10 @@ resource = Ken.get('/en/the_police')
|
|
10
10
|
|
11
11
|
resource.views.each do |view|
|
12
12
|
puts view
|
13
|
-
puts "
|
13
|
+
puts "="*20
|
14
14
|
view.attributes.each do |a|
|
15
15
|
puts a.property
|
16
|
-
puts "
|
16
|
+
puts "-"*20
|
17
17
|
puts a
|
18
18
|
puts # newline
|
19
19
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
# displays all links related to music/artists
|
5
|
+
# low level api through Ken.session.mqlread is used here
|
6
|
+
|
7
|
+
EXAMPLES_ROOT = Pathname(__FILE__).dirname.expand_path
|
8
|
+
require EXAMPLES_ROOT.parent + 'lib/ken'
|
9
|
+
|
10
|
+
Ken::Session.new('http://www.freebase.com', 'ma', '*****')
|
11
|
+
|
12
|
+
artists = Ken.session.mqlread([{
|
13
|
+
:type => "/music/artist",
|
14
|
+
:id => nil,
|
15
|
+
:"/common/topic/webpage" => [{:uri => nil}],
|
16
|
+
:home_page => [{:uri => nil}],
|
17
|
+
:limit => 50
|
18
|
+
}])
|
19
|
+
|
20
|
+
artists.each do |artist|
|
21
|
+
artist["/common/topic/webpage"].each do |webpage|
|
22
|
+
puts webpage["uri"]
|
23
|
+
end
|
24
|
+
|
25
|
+
artist["home_page"].each do |homepage|
|
26
|
+
puts homepage["uri"]
|
27
|
+
end
|
28
|
+
end
|
data/ken.gemspec
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{ken}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["michael"]
|
9
|
+
s.date = %q{2009-07-08}
|
10
|
+
s.email = %q{ma[at]zive[dot]at}
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"LICENSE",
|
13
|
+
"README.textile",
|
14
|
+
"README.txt"
|
15
|
+
]
|
16
|
+
s.files = [
|
17
|
+
".gitignore",
|
18
|
+
"History.txt",
|
19
|
+
"LICENSE",
|
20
|
+
"README.textile",
|
21
|
+
"README.txt",
|
22
|
+
"Rakefile",
|
23
|
+
"TODO",
|
24
|
+
"VERSION",
|
25
|
+
"examples/artist.rb",
|
26
|
+
"examples/artist_links.rb",
|
27
|
+
"ken.gemspec",
|
28
|
+
"lib/ken.rb",
|
29
|
+
"lib/ken/attribute.rb",
|
30
|
+
"lib/ken/collection.rb",
|
31
|
+
"lib/ken/logger.rb",
|
32
|
+
"lib/ken/property.rb",
|
33
|
+
"lib/ken/resource.rb",
|
34
|
+
"lib/ken/session.rb",
|
35
|
+
"lib/ken/type.rb",
|
36
|
+
"lib/ken/util.rb",
|
37
|
+
"lib/ken/view.rb",
|
38
|
+
"rails/init.rb",
|
39
|
+
"tasks/ken.rb",
|
40
|
+
"tasks/spec.rb",
|
41
|
+
"test/fixtures/music_artist.json",
|
42
|
+
"test/fixtures/the_police.json",
|
43
|
+
"test/integration/ken_test.rb",
|
44
|
+
"test/test_helper.rb",
|
45
|
+
"test/unit/attribute_test.rb",
|
46
|
+
"test/unit/property_test.rb",
|
47
|
+
"test/unit/resource_test.rb",
|
48
|
+
"test/unit/session_test.rb",
|
49
|
+
"test/unit/type_test.rb",
|
50
|
+
"test/unit/view_test.rb"
|
51
|
+
]
|
52
|
+
s.homepage = %q{http://github.com/michael/ken}
|
53
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
54
|
+
s.require_paths = ["lib"]
|
55
|
+
s.rubygems_version = %q{1.3.4}
|
56
|
+
s.summary = %q{Ruby API for Accessing the Freebase}
|
57
|
+
s.test_files = [
|
58
|
+
"test/integration/ken_test.rb",
|
59
|
+
"test/test_helper.rb",
|
60
|
+
"test/unit/attribute_test.rb",
|
61
|
+
"test/unit/property_test.rb",
|
62
|
+
"test/unit/resource_test.rb",
|
63
|
+
"test/unit/session_test.rb",
|
64
|
+
"test/unit/type_test.rb",
|
65
|
+
"test/unit/view_test.rb",
|
66
|
+
"examples/artist.rb",
|
67
|
+
"examples/artist_links.rb"
|
68
|
+
]
|
69
|
+
|
70
|
+
if s.respond_to? :specification_version then
|
71
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
72
|
+
s.specification_version = 3
|
73
|
+
|
74
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
75
|
+
s.add_runtime_dependency(%q<extlib>, [">= 0"])
|
76
|
+
else
|
77
|
+
s.add_dependency(%q<extlib>, [">= 0"])
|
78
|
+
end
|
79
|
+
else
|
80
|
+
s.add_dependency(%q<extlib>, [">= 0"])
|
81
|
+
end
|
82
|
+
end
|
data/lib/ken/attribute.rb
CHANGED
@@ -2,7 +2,6 @@ module Ken
|
|
2
2
|
class Attribute
|
3
3
|
|
4
4
|
include Extlib::Assertions
|
5
|
-
|
6
5
|
attr_reader :property
|
7
6
|
|
8
7
|
# initializes a resource by json result
|
@@ -12,6 +11,8 @@ module Ken
|
|
12
11
|
@data, @property = data, property
|
13
12
|
end
|
14
13
|
|
14
|
+
# factory method for creating an attribute instance
|
15
|
+
# @api semipublic
|
15
16
|
def self.create(data, property)
|
16
17
|
Ken::Attribute.new(data, property)
|
17
18
|
end
|
@@ -26,26 +27,34 @@ module Ken
|
|
26
27
|
result = "#<Attribute property=\"#{property.id || "nil"}\">"
|
27
28
|
end
|
28
29
|
|
29
|
-
# returns
|
30
|
+
# returns a collection of values
|
31
|
+
# in case of a unique property the array holds just one value
|
32
|
+
# @api public
|
30
33
|
def values
|
31
34
|
subject
|
32
35
|
end
|
33
36
|
|
37
|
+
# unique properties can have at least one value
|
38
|
+
# @api public
|
34
39
|
def unique?
|
35
40
|
@property.unique?
|
36
41
|
end
|
37
42
|
|
43
|
+
# object type properties always link to resources
|
44
|
+
# @api public
|
38
45
|
def object_type?
|
39
46
|
@property.object_type?
|
40
47
|
end
|
41
48
|
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
|
49
|
+
# returns true if the property is a value type
|
50
|
+
# value type properties refer to simple values like /type/text
|
51
|
+
# @api public
|
52
|
+
def value_type?
|
53
|
+
@property.value_type?
|
54
|
+
end
|
46
55
|
|
47
|
-
# initializes the subject if used the first time
|
48
56
|
private
|
57
|
+
# initializes the subject if used for the first time
|
49
58
|
def subject
|
50
59
|
@subject ||= Ken::Collection.new(@data.map { |r| object_type? ? Ken::Resource.new(r) : r["value"] })
|
51
60
|
end
|
data/lib/ken/collection.rb
CHANGED
@@ -1,13 +1,5 @@
|
|
1
1
|
module Ken
|
2
2
|
class Collection < Array
|
3
|
-
# def initialize(data = [])
|
4
|
-
# @data = data
|
5
|
-
# end
|
6
|
-
|
7
|
-
# def method_missing(name, *args, &blk)
|
8
|
-
# @data.send name, *args, &blk
|
9
|
-
# end
|
10
|
-
|
11
3
|
# add a linebreak after each entry
|
12
4
|
def to_s
|
13
5
|
self.inject("") { |m,i| "#{m}#{i.to_s}\n"}
|
data/lib/ken/property.rb
CHANGED
@@ -22,11 +22,13 @@ module Ken
|
|
22
22
|
@data, @type = data, type
|
23
23
|
end
|
24
24
|
|
25
|
+
# property id
|
25
26
|
# @api public
|
26
27
|
def id
|
27
28
|
@data["id"]
|
28
29
|
end
|
29
30
|
|
31
|
+
# property name
|
30
32
|
# @api public
|
31
33
|
def name
|
32
34
|
@data["name"]
|
@@ -50,11 +52,13 @@ module Ken
|
|
50
52
|
@type
|
51
53
|
end
|
52
54
|
|
55
|
+
# reverse property, which represent incoming links
|
53
56
|
# @api public
|
54
57
|
def reverse_property
|
55
58
|
@data["reverse_property"]
|
56
59
|
end
|
57
60
|
|
61
|
+
# master property, which represent an outgoing link (or primitive value)
|
58
62
|
# @api public
|
59
63
|
def master_property
|
60
64
|
@data["master_property"]
|
@@ -78,6 +82,7 @@ module Ken
|
|
78
82
|
VALUE_TYPES.include?(expected_type)
|
79
83
|
end
|
80
84
|
|
85
|
+
# type, which attribute values of that property are expected to have
|
81
86
|
# @api public
|
82
87
|
def expected_type
|
83
88
|
@data["expected_type"]
|
data/lib/ken/resource.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Ken
|
2
2
|
class Resource
|
3
|
-
|
4
3
|
include Extlib::Assertions
|
5
4
|
|
6
5
|
FETCH_SCHEMA_QUERY = {
|
@@ -19,6 +18,7 @@ module Ken
|
|
19
18
|
}
|
20
19
|
|
21
20
|
FETCH_ATTRIBUTES_QUERY = {
|
21
|
+
# :id => id # needs to be merg!d in instance method
|
22
22
|
:"/type/reflect/any_master" => [
|
23
23
|
{
|
24
24
|
:id => nil,
|
@@ -37,27 +37,27 @@ module Ken
|
|
37
37
|
{
|
38
38
|
:link => nil,
|
39
39
|
:value => nil
|
40
|
+
# TODO: support multiple language
|
40
41
|
# :lang => "/lang/en",
|
41
42
|
# :type => "/type/text"
|
42
43
|
}
|
43
|
-
]
|
44
|
-
|
45
|
-
# :id => id # needs to be merg!d in instance method
|
44
|
+
]
|
46
45
|
}
|
47
46
|
|
48
|
-
|
49
|
-
# initializes a resource by json result
|
47
|
+
# initializes a resource using a json result
|
50
48
|
def initialize(data)
|
51
49
|
assert_kind_of 'data', data, Hash
|
52
50
|
# intialize lazy if there is no type supplied
|
53
51
|
@schema_loaded, @attributes_loaded, @data = false, false, data
|
54
52
|
end
|
55
53
|
|
54
|
+
# resource id
|
56
55
|
# @api public
|
57
56
|
def id
|
58
57
|
@data["id"] || ""
|
59
58
|
end
|
60
59
|
|
60
|
+
# resource name
|
61
61
|
# @api public
|
62
62
|
def name
|
63
63
|
@data["name"] || ""
|
@@ -80,7 +80,7 @@ module Ken
|
|
80
80
|
@types
|
81
81
|
end
|
82
82
|
|
83
|
-
# returns all available
|
83
|
+
# returns all available views based on the assigned types
|
84
84
|
# @api public
|
85
85
|
def views
|
86
86
|
@views ||= Ken::Collection.new(types.map { |type| Ken::View.new(self, type) })
|
@@ -96,7 +96,7 @@ module Ken
|
|
96
96
|
@properties
|
97
97
|
end
|
98
98
|
|
99
|
-
# returns all attributes for every type the resource is an instance
|
99
|
+
# returns all attributes for every type the resource is an instance of
|
100
100
|
# @api public
|
101
101
|
def attributes
|
102
102
|
load_attributes! unless attributes_loaded?
|
@@ -116,15 +116,18 @@ module Ken
|
|
116
116
|
end
|
117
117
|
|
118
118
|
private
|
119
|
+
# executes the fetch attributes query in order to load the full set if attributes
|
120
|
+
# more info at http://lists.freebase.com/pipermail/developers/2007-December/001022.html
|
121
|
+
# @api private
|
119
122
|
def fetch_attributes
|
120
|
-
# fetching all objects regardless of the type
|
121
|
-
# check this http://lists.freebase.com/pipermail/developers/2007-December/001022.html
|
122
123
|
Ken.session.mqlread(FETCH_ATTRIBUTES_QUERY.merge!(:id => id))
|
123
124
|
end
|
124
125
|
|
126
|
+
# loads the full set of attributes using reflection
|
127
|
+
# information is extracted from master, value and reverse attributes
|
128
|
+
# @api private
|
125
129
|
def load_attributes!
|
126
130
|
data = @data["ken:attribute"] || fetch_attributes
|
127
|
-
|
128
131
|
# master & value attributes
|
129
132
|
raw_attributes = Ken::Util.convert_hash(data["/type/reflect/any_master"])
|
130
133
|
raw_attributes.merge!(Ken::Util.convert_hash(data["/type/reflect/any_value"]))
|
@@ -146,18 +149,18 @@ module Ken
|
|
146
149
|
@attributes_loaded = true
|
147
150
|
end
|
148
151
|
|
152
|
+
# executes the fetch schema query in order to load all schema information
|
153
|
+
# @api private
|
149
154
|
def fetch_schema
|
150
155
|
Ken.session.mqlread(FETCH_SCHEMA_QUERY.merge!(:id => id))["ken:type"]
|
151
156
|
end
|
152
157
|
|
153
|
-
# loads the
|
158
|
+
# loads the resource's metainfo
|
154
159
|
# @api private
|
155
160
|
def load_schema!
|
156
161
|
@data["ken:type"] ||= fetch_schema
|
157
162
|
@types = Ken::Collection.new(@data["ken:type"].map { |type| Ken::Type.new(type) })
|
158
163
|
@schema_loaded = true
|
159
164
|
end
|
160
|
-
|
161
|
-
|
162
165
|
end # class Resource
|
163
166
|
end # module Ken
|
data/lib/ken/session.rb
CHANGED
@@ -16,10 +16,13 @@ module Ken
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
class AttributeNotFound < StandardError; end
|
20
|
+
class PropertyNotFound < StandardError; end
|
21
|
+
class ResourceNotFound < StandardError; end
|
22
|
+
|
19
23
|
# partially taken from chris eppstein's freebase api
|
20
24
|
# http://github.com/chriseppstein/freebase/tree
|
21
25
|
class Session
|
22
|
-
# include Singleton
|
23
26
|
|
24
27
|
public
|
25
28
|
# Initialize a new Ken Session
|
@@ -48,7 +51,6 @@ module Ken
|
|
48
51
|
|
49
52
|
# get the service url for the specified service.
|
50
53
|
def service_url(svc)
|
51
|
-
#"http://#{Configuration.instance[:host]}#{SERVICES[svc]}"
|
52
54
|
"#{@host}#{SERVICES[svc]}"
|
53
55
|
end
|
54
56
|
|
@@ -67,30 +69,51 @@ module Ken
|
|
67
69
|
end
|
68
70
|
end # handle_read_error
|
69
71
|
|
72
|
+
|
70
73
|
# perform a mqlread and return the results
|
71
74
|
# TODO: should support multiple queries
|
72
75
|
# you should be able to pass an array of queries
|
76
|
+
# Specify :cursor => true to batch the results of a query, sending multiple requests if necessary.
|
73
77
|
def mqlread(query, options = {})
|
74
78
|
Ken.logger.info ">>> Sending Query: #{query.to_json}"
|
75
|
-
|
79
|
+
cursor = options[:cursor]
|
80
|
+
if cursor
|
81
|
+
query_result = []
|
82
|
+
while cursor
|
83
|
+
response = get_query_response(query, cursor)
|
84
|
+
query_result += response['result']
|
85
|
+
cursor = response['cursor']
|
86
|
+
end
|
87
|
+
else
|
88
|
+
response = get_query_response(query, cursor)
|
89
|
+
cursor = response['cursor']
|
90
|
+
query_result = response['result']
|
91
|
+
end
|
92
|
+
query_result
|
93
|
+
end
|
94
|
+
|
95
|
+
protected
|
96
|
+
# returns parsed json response from freebase mqlread service
|
97
|
+
def get_query_response(query, cursor=nil)
|
76
98
|
envelope = { :qname => {:query => query }}
|
99
|
+
envelope[:qname][:cursor] = cursor if cursor
|
77
100
|
|
78
101
|
response = http_request mqlread_service_url, :queries => envelope.to_json
|
102
|
+
|
79
103
|
result = JSON.parse response
|
104
|
+
|
80
105
|
inner = result['qname']
|
81
106
|
handle_read_error(inner)
|
82
|
-
|
83
107
|
Ken.logger.info "<<< Received Response: #{inner['result'].inspect}"
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
protected
|
108
|
+
inner
|
109
|
+
end
|
110
|
+
|
111
|
+
# encode parameters
|
90
112
|
def params_to_string(parameters)
|
91
113
|
parameters.keys.map {|k| "#{URI.encode(k.to_s)}=#{URI.encode(parameters[k])}" }.join('&')
|
92
114
|
end
|
93
|
-
|
115
|
+
|
116
|
+
# does the dirty work
|
94
117
|
def http_request(url, parameters = {})
|
95
118
|
params = params_to_string(parameters)
|
96
119
|
url << '?'+params unless params !~ /\S/
|
data/lib/ken/type.rb
CHANGED
@@ -3,7 +3,7 @@ module Ken
|
|
3
3
|
|
4
4
|
include Extlib::Assertions
|
5
5
|
|
6
|
-
# initializes a resource
|
6
|
+
# initializes a resource using a json result
|
7
7
|
def initialize(data)
|
8
8
|
assert_kind_of 'data', data, Hash
|
9
9
|
@data = data
|
@@ -12,14 +12,16 @@ module Ken
|
|
12
12
|
# access property info
|
13
13
|
# @api public
|
14
14
|
def properties
|
15
|
-
Ken::Collection.new(@data["properties"].map { |property| Ken::Property.new(property, self) })
|
15
|
+
@properties ||= Ken::Collection.new(@data["properties"].map { |property| Ken::Property.new(property, self) })
|
16
16
|
end
|
17
17
|
|
18
|
+
# type id
|
18
19
|
# @api public
|
19
20
|
def id
|
20
21
|
@data["id"]
|
21
22
|
end
|
22
23
|
|
24
|
+
# type name
|
23
25
|
# @api public
|
24
26
|
def name
|
25
27
|
@data["name"]
|
@@ -34,5 +36,18 @@ module Ken
|
|
34
36
|
def inspect
|
35
37
|
result = "#<Type id=\"#{id}\" name=\"#{name || "nil"}\">"
|
36
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
|
37
52
|
end
|
38
53
|
end
|
data/lib/ken/view.rb
CHANGED
@@ -17,6 +17,7 @@ module Ken
|
|
17
17
|
@type.to_s
|
18
18
|
end
|
19
19
|
|
20
|
+
# return correspondent type
|
20
21
|
# @api public
|
21
22
|
def type
|
22
23
|
@type
|
@@ -39,5 +40,17 @@ module Ken
|
|
39
40
|
@resource.properties.select { |p| p.type == @type}
|
40
41
|
end
|
41
42
|
|
43
|
+
# delegate to attribute_get
|
44
|
+
def method_missing sym
|
45
|
+
attribute_get(sym.to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
# search for an attribute by name and return it
|
50
|
+
# @api private
|
51
|
+
def attribute_get(name)
|
52
|
+
attributes.each { |a| return a if a.property.id =~ /\/#{name}$/ }
|
53
|
+
raise AttributeNotFound
|
54
|
+
end
|
42
55
|
end
|
43
56
|
end
|