michael-ken 0.0.3 → 0.1.0
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/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
|