michael-ken 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +23 -0
- data/README.textile +47 -7
- data/Rakefile +48 -22
- data/VERSION +1 -0
- data/lib/ken.rb +63 -24
- data/lib/ken/attribute.rb +6 -5
- data/lib/ken/property.rb +19 -16
- data/lib/ken/resource.rb +99 -94
- data/lib/ken/type.rb +3 -3
- data/lib/ken/util.rb +8 -4
- data/lib/ken/version.rb +1 -1
- data/lib/ken/view.rb +7 -7
- data/spec/fixtures/the_police.json +2 -2
- data/spec/integration/ken_spec.rb +37 -1
- data/spec/unit/resource_spec.rb +1 -1
- data/spec/unit/view_spec.rb +4 -2
- metadata +30 -36
- data/Manifest.txt +0 -34
- data/tasks/gemspec.rb +0 -23
- data/tasks/hoe.rb +0 -47
- data/tasks/install.rb +0 -13
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
test_log
|
2
|
+
pkg
|
3
|
+
pkg/*
|
4
|
+
*/pkg/*
|
5
|
+
bundle
|
6
|
+
bundle/*
|
7
|
+
doc
|
8
|
+
*.log
|
9
|
+
log
|
10
|
+
!log*.rb
|
11
|
+
*/log
|
12
|
+
log/*
|
13
|
+
*/log/*
|
14
|
+
coverage
|
15
|
+
*/coverage
|
16
|
+
lib/dm-more.rb
|
17
|
+
*.db
|
18
|
+
nbproject
|
19
|
+
.DS_Store
|
20
|
+
rspec_report.html
|
21
|
+
*.swp
|
22
|
+
_Yardoc
|
23
|
+
*/ri
|
data/README.textile
CHANGED
@@ -6,11 +6,11 @@ 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
|
Just born, the project’s goals are the provision of a concise API for querying and writing.
|
9
|
-
Therefore it wraps the Metaweb Architecture to smart Ruby Objects
|
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
12
|
|
13
|
-
|
13
|
+
If things go right, you should be able to use this library as a Data Layer (instead of or in addition to
|
14
14
|
ActiveRecord/DataMapper) for your Web Framework of choice (Merb, Rails).
|
15
15
|
|
16
16
|
|
@@ -52,7 +52,7 @@ Let's ask Ken what he knows about the British Band New Order.
|
|
52
52
|
</code>
|
53
53
|
</pre>
|
54
54
|
|
55
|
-
h3. Inspecting the
|
55
|
+
h3. Inspecting the Types
|
56
56
|
|
57
57
|
Every Resource can have multiple types.
|
58
58
|
|
@@ -67,7 +67,7 @@ Every Resource can have multiple types.
|
|
67
67
|
|
68
68
|
We can see that New Order is a member of Music Artist, Film Music Contributor, Broadcast Artist and other types.
|
69
69
|
|
70
|
-
h3. Inspecting a
|
70
|
+
h3. Inspecting a Type's properties
|
71
71
|
|
72
72
|
A type defines a set of properties to describe a Resource.
|
73
73
|
|
@@ -82,7 +82,7 @@ A type defines a set of properties to describe a Resource.
|
|
82
82
|
We get sets of Properties for each Type. The Type Musical Group has just one Property @/music/musical_group/member@
|
83
83
|
named Members Of Musical Group.
|
84
84
|
|
85
|
-
|
85
|
+
h3. Listing all Attributes
|
86
86
|
|
87
87
|
After inspecting a Resource's Types and Properties we now know what we could know. But actually we don't know nothing :)
|
88
88
|
So it's time to ask for the values of properties, the so called _Attributes_.
|
@@ -116,7 +116,7 @@ There are four kinds of Attributes.
|
|
116
116
|
In order to be able to use unique and non-unique Attributes in the same manner we always wrap the value of an Attribute
|
117
117
|
in a Collection, no matter if there's one value or there are many.
|
118
118
|
|
119
|
-
|
119
|
+
h3. Group Attributes by their Type using Views
|
120
120
|
|
121
121
|
|
122
122
|
<pre>
|
@@ -142,6 +142,46 @@ h4. Group Attributes by their Type using Views
|
|
142
142
|
</code>
|
143
143
|
</pre>
|
144
144
|
|
145
|
+
h3. Fetching multiple Resources using a query
|
146
|
+
|
147
|
+
As of now you can ask for multiple Resources by specifying a query.
|
148
|
+
|
149
|
+
<pre>
|
150
|
+
<code>
|
151
|
+
resources = Ken.all(:name => "Apple", :type => "/music/album")
|
152
|
+
# => [#<Resource id="/guid/9202a8c04000641f80000000031dae7c" name="Apple">,
|
153
|
+
#<Resource id="/guid/9202a8c04000641f8000000007ce31ec" name="Apple">]
|
154
|
+
</code>
|
155
|
+
</pre>
|
156
|
+
|
157
|
+
Keep in mind that only the top level of the query is mapped to a Collection of Resource Objects.
|
158
|
+
So asking for values in a nested level does not make sense. Use nested statements just for
|
159
|
+
lowering the top level result.
|
160
|
+
|
161
|
+
However you can instead navigate the normal way to figure out that values.
|
162
|
+
_But won't that require another query to triggered? Doubtful._
|
163
|
+
|
164
|
+
Let's look at a nested query:
|
165
|
+
|
166
|
+
<pre>
|
167
|
+
<code>
|
168
|
+
query = {
|
169
|
+
:directed_by => "George Lucas",
|
170
|
+
:starring => [
|
171
|
+
{
|
172
|
+
:actor => "Harrison Ford"
|
173
|
+
}
|
174
|
+
],
|
175
|
+
:type => "/film/film"
|
176
|
+
}
|
177
|
+
|
178
|
+
resources = Ken.all(query)
|
179
|
+
# => [#<Resource id="/en/star_wars_episode_iv_a_new_hope" name="Star Wars Episode IV: A New Hope">,
|
180
|
+
#<Resource id="/en/american_graffiti" name="American Graffiti">,
|
181
|
+
#<Resource id="/en/the_star_wars_holiday_special" name="The Star Wars Holiday Special">]
|
182
|
+
</code>
|
183
|
+
</pre>
|
184
|
+
|
145
185
|
h2. Status
|
146
186
|
|
147
187
|
Currently, the focus lies on inspecting Freebase Resources in a generic way. That's why
|
@@ -156,6 +196,7 @@ Chris "Eppsteins Freebase Library":http://github.com/chriseppstein/freebase/tree
|
|
156
196
|
h3. Features
|
157
197
|
|
158
198
|
* Fetching of single Resources
|
199
|
+
* Fetching of multiple Resources by specifying a query
|
159
200
|
* Type inspection
|
160
201
|
* Attributes inspection
|
161
202
|
* Views on Resources to group Attributes based on the Resource's types
|
@@ -164,7 +205,6 @@ h3. Features
|
|
164
205
|
h3. Roadmap
|
165
206
|
|
166
207
|
# Much more specs
|
167
|
-
# Support for querying of multiple Resources
|
168
208
|
# Better Type Support
|
169
209
|
# API for Set Based Browsing (see http://mqlx.com/~david/parallax/)
|
170
210
|
# Accessing Properties/Attributes directly (e.g. resource.genres )
|
data/Rakefile
CHANGED
@@ -1,30 +1,56 @@
|
|
1
|
-
require 'pathname'
|
2
1
|
require 'rubygems'
|
2
|
+
require 'rake'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ken"
|
8
|
+
gem.summary = %Q{Ruby API for Accessing the Freebase}
|
9
|
+
gem.email = "ma[at]zive[dot]at"
|
10
|
+
gem.homepage = "http://github.com/michael/ken"
|
11
|
+
gem.authors = ["michael"]
|
12
|
+
# gem.files = FileList["[A-Z]*.*"]
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
8
15
|
|
9
|
-
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
18
|
+
end
|
10
19
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# [ 'addressable', '~>2.0.1' ]
|
18
|
-
]
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.libs << 'lib' << 'test'
|
23
|
+
test.pattern = 'test/**/*_test.rb'
|
24
|
+
test.verbose = true
|
25
|
+
end
|
19
26
|
|
20
|
-
|
21
|
-
|
27
|
+
begin
|
28
|
+
require 'rcov/rcovtask'
|
29
|
+
Rcov::RcovTask.new do |test|
|
30
|
+
test.libs << 'test'
|
31
|
+
test.pattern = 'test/**/*_test.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
rescue LoadError
|
35
|
+
task :rcov do
|
36
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
+
end
|
38
|
+
end
|
22
39
|
|
23
|
-
PROJECT_NAME = 'ken'
|
24
|
-
PROJECT_URL = "http://github.com/michael/#{GEM_NAME}"
|
25
|
-
PROJECT_DESCRIPTION = PROJECT_SUMMARY = 'Ruby API for Accessing the Freebase'
|
26
40
|
|
27
|
-
|
28
|
-
|
29
|
-
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
if File.exist?('VERSION.yml')
|
46
|
+
config = YAML.load(File.read('VERSION.yml'))
|
47
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
48
|
+
else
|
49
|
+
version = ""
|
50
|
+
end
|
51
|
+
|
52
|
+
rdoc.rdoc_dir = 'rdoc'
|
53
|
+
rdoc.title = "ken #{version}"
|
54
|
+
rdoc.rdoc_files.include('README*')
|
55
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
30
56
|
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.3
|
data/lib/ken.rb
CHANGED
@@ -3,6 +3,7 @@ require 'rubygems'
|
|
3
3
|
require 'net/http'
|
4
4
|
require 'json'
|
5
5
|
require 'extlib'
|
6
|
+
require 'extlib/assertions'
|
6
7
|
require 'addressable/uri'
|
7
8
|
|
8
9
|
dir = Pathname(__FILE__).dirname.expand_path + 'ken'
|
@@ -25,9 +26,59 @@ Ken::Logger.new(STDOUT, :error)
|
|
25
26
|
Ken::Session.new('http://www.freebase.com', 'ma', 'xxxxx')
|
26
27
|
|
27
28
|
module Ken
|
29
|
+
|
30
|
+
extend Extlib::Assertions
|
31
|
+
|
32
|
+
|
33
|
+
# store query as a constant here.
|
34
|
+
# if the hash gets updated using
|
35
|
+
# #merge! or #update, this will mean
|
36
|
+
# that it actually stores the last
|
37
|
+
# query used. there are 2 sides to this.
|
38
|
+
# on the one hand, it isn't really a
|
39
|
+
# constant anymore (ruby doesn't complain)?
|
40
|
+
# on the other hand, there is no need to
|
41
|
+
# create a new object everytime a query is
|
42
|
+
# executed. maybe this is fine, maybe not,
|
43
|
+
# this needs to be discussed.
|
44
|
+
|
45
|
+
QUERY = query = {
|
46
|
+
# :id => id, # needs to be updated in instance mehtod
|
47
|
+
:name => nil,
|
48
|
+
:"ken:type" => [{
|
49
|
+
:id => nil,
|
50
|
+
:name => nil,
|
51
|
+
:properties => [{
|
52
|
+
:id => nil,
|
53
|
+
:name => nil,
|
54
|
+
:expected_type => nil,
|
55
|
+
:unique => nil,
|
56
|
+
:reverse_property => nil,
|
57
|
+
:master_property => nil,
|
58
|
+
}]
|
59
|
+
}]
|
60
|
+
}
|
61
|
+
|
62
|
+
# Executes an Mql Query against the Freebase API and returns the result as
|
63
|
+
# a <tt>Collection</tt> of <tt>Resources</tt>.
|
64
|
+
#
|
65
|
+
# == Examples
|
66
|
+
#
|
67
|
+
# Ken.all(:name => "Apple", :type => "/music/album")
|
68
|
+
#
|
69
|
+
# Ken.all(
|
70
|
+
# :directed_by => "George Lucas",
|
71
|
+
# :starring => [{
|
72
|
+
# :actor => "Harrison Ford"
|
73
|
+
# }],
|
74
|
+
# :type => "/film/film"
|
75
|
+
# )
|
76
|
+
# @api public
|
28
77
|
def self.all(options = {})
|
29
|
-
|
30
|
-
|
78
|
+
assert_kind_of 'options', options, Hash
|
79
|
+
query = { :name => nil }.merge!(options).merge!(:id => nil) # collection queries MUST have :id => nil, no?
|
80
|
+
result = Ken.session.mqlread([ query ])
|
81
|
+
Ken::Collection.new(result.map { |r| Ken::Resource.new(r) })
|
31
82
|
end
|
32
83
|
|
33
84
|
|
@@ -37,28 +88,16 @@ module Ken
|
|
37
88
|
# == Examples
|
38
89
|
#
|
39
90
|
# Ken.get('/en/the_police') => #<Resource id="/en/the_police" name="The Police">
|
91
|
+
# @api public
|
40
92
|
def self.get(id)
|
41
|
-
|
42
|
-
raise ArgumentError
|
43
|
-
|
44
|
-
|
45
|
-
:id => id,
|
46
|
-
:name => nil,
|
47
|
-
:type => [{
|
48
|
-
:id => nil,
|
49
|
-
:name => nil,
|
50
|
-
:properties => [{
|
51
|
-
:id => nil,
|
52
|
-
:name => nil,
|
53
|
-
:expected_type => nil,
|
54
|
-
:unique => nil,
|
55
|
-
:reverse_property => nil,
|
56
|
-
:master_property => nil,
|
57
|
-
}]
|
58
|
-
}]
|
59
|
-
}
|
60
|
-
|
61
|
-
result = Ken.session.mqlread(query)
|
62
|
-
return Ken::Resource.new(result)
|
93
|
+
assert_kind_of 'id', id, String
|
94
|
+
raise ArgumentError, "id must be in /type/object/id format" unless valid_id_attribute?(id)
|
95
|
+
result = Ken.session.mqlread(QUERY.merge!(:id => id))
|
96
|
+
Ken::Resource.new(result)
|
63
97
|
end
|
98
|
+
|
99
|
+
def self.valid_id_attribute?(id)
|
100
|
+
id =~ /\/\w+/
|
101
|
+
end
|
102
|
+
|
64
103
|
end # module Ken
|
data/lib/ken/attribute.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
module Ken
|
2
2
|
class Attribute
|
3
|
+
|
4
|
+
include Extlib::Assertions
|
5
|
+
|
3
6
|
attr_reader :property
|
4
7
|
|
5
8
|
# initializes a resource by json result
|
6
9
|
def initialize(data, property)
|
7
|
-
|
8
|
-
|
9
|
-
@data = data
|
10
|
-
@property = property # belongs to a property
|
11
|
-
self
|
10
|
+
assert_kind_of 'data', data, Array
|
11
|
+
assert_kind_of 'property', property, Ken::Property
|
12
|
+
@data, @property = data, property
|
12
13
|
end
|
13
14
|
|
14
15
|
def self.create(data, property)
|
data/lib/ken/property.rb
CHANGED
@@ -1,12 +1,25 @@
|
|
1
1
|
module Ken
|
2
2
|
class Property
|
3
3
|
|
4
|
+
include Extlib::Assertions
|
5
|
+
|
6
|
+
VALUE_TYPES = %w{
|
7
|
+
/type/id
|
8
|
+
/type/int
|
9
|
+
/type/float
|
10
|
+
/type/boolean
|
11
|
+
/type/text
|
12
|
+
/type/rawstring
|
13
|
+
/type/uri
|
14
|
+
/type/datetime
|
15
|
+
/type/key
|
16
|
+
}
|
17
|
+
|
4
18
|
# initializes a resource by json result
|
5
19
|
def initialize(data, type)
|
6
|
-
|
7
|
-
|
8
|
-
@type = type
|
9
|
-
self
|
20
|
+
assert_kind_of 'data', data, Hash
|
21
|
+
assert_kind_of 'type', type, Ken::Type
|
22
|
+
@data, @type = data, type
|
10
23
|
end
|
11
24
|
|
12
25
|
# @api public
|
@@ -56,23 +69,13 @@ module Ken
|
|
56
69
|
# returns true if the property is an object type
|
57
70
|
# @api public
|
58
71
|
def object_type?
|
59
|
-
|
60
|
-
/type/id
|
61
|
-
/type/int
|
62
|
-
/type/float
|
63
|
-
/type/boolean
|
64
|
-
/type/text
|
65
|
-
/type/rawstring
|
66
|
-
/type/uri
|
67
|
-
/type/datetime
|
68
|
-
/type/key
|
69
|
-
}.include?(expected_type)
|
72
|
+
!value_type?
|
70
73
|
end
|
71
74
|
|
72
75
|
# returns true if the property is a value type
|
73
76
|
# @api public
|
74
77
|
def value_type?
|
75
|
-
|
78
|
+
VALUE_TYPES.include?(expected_type)
|
76
79
|
end
|
77
80
|
|
78
81
|
# @api public
|
data/lib/ken/resource.rb
CHANGED
@@ -1,107 +1,56 @@
|
|
1
1
|
module Ken
|
2
2
|
class Resource
|
3
|
-
# initializes a resource by json result
|
4
|
-
def initialize(data)
|
5
|
-
return nil unless data
|
6
|
-
raise "error" unless data.kind_of?(Hash)
|
7
|
-
|
8
|
-
# intialize lazy if there is no type supplied
|
9
|
-
@schema_loaded = false
|
10
|
-
@attributes_loaded = false
|
11
|
-
@data = data
|
12
|
-
|
13
|
-
self
|
14
|
-
end
|
15
|
-
|
16
|
-
def schema_loaded?
|
17
|
-
@schema_loaded
|
18
|
-
end
|
19
|
-
|
20
|
-
def attributes_loaded?
|
21
|
-
@attributes_loaded
|
22
|
-
end
|
23
3
|
|
24
|
-
|
25
|
-
# fetching all objects regardless of the type
|
26
|
-
# check this http://lists.freebase.com/pipermail/developers/2007-December/001022.html
|
27
|
-
|
28
|
-
query = {
|
29
|
-
:"/type/reflect/any_master" => [
|
30
|
-
{
|
31
|
-
:id => nil,
|
32
|
-
:link => nil,
|
33
|
-
:name => nil
|
34
|
-
}
|
35
|
-
],
|
36
|
-
:"/type/reflect/any_reverse" => [
|
37
|
-
{
|
38
|
-
:id => nil,
|
39
|
-
:link => nil,
|
40
|
-
:name => nil
|
41
|
-
}
|
42
|
-
],
|
43
|
-
:"/type/reflect/any_value" => [
|
44
|
-
{
|
45
|
-
:link => nil,
|
46
|
-
:value => nil
|
47
|
-
# :lang => "/lang/en",
|
48
|
-
# :type => "/type/text"
|
49
|
-
}
|
50
|
-
],
|
51
|
-
:id => id
|
52
|
-
}
|
53
|
-
|
54
|
-
Ken.session.mqlread(query)
|
55
|
-
end
|
4
|
+
include Extlib::Assertions
|
56
5
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
raw_attributes.merge!(Ken::Util.convert_hash(data["/type/reflect/any_value"]))
|
63
|
-
@attributes = {}
|
64
|
-
raw_attributes.each_pair do |a, d|
|
65
|
-
properties.select { |p| p.id == a}.each do |p|
|
66
|
-
@attributes[p.id] = Ken::Attribute.create(d, p)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# reverse properties
|
71
|
-
raw_attributes = Ken::Util.convert_hash(data["/type/reflect/any_reverse"])
|
72
|
-
raw_attributes.each_pair do |a, d|
|
73
|
-
properties.select { |p| p.master_property == a}.each do |p|
|
74
|
-
@attributes[p.id] = Ken::Attribute.create(d, p)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
@attributes_loaded = true
|
79
|
-
end
|
80
|
-
|
81
|
-
def fetch_schema
|
82
|
-
query = {
|
83
|
-
:id => id,
|
6
|
+
FETCH_SCHEMA_QUERY = {
|
7
|
+
# :id => id, # needs to be merge!d in instance method
|
8
|
+
:name => nil,
|
9
|
+
:"ken:type" => [{
|
10
|
+
:id => nil,
|
84
11
|
:name => nil,
|
85
|
-
:
|
12
|
+
:properties => [{
|
86
13
|
:id => nil,
|
87
14
|
:name => nil,
|
88
|
-
:
|
89
|
-
|
90
|
-
:name => nil,
|
91
|
-
:expected_type => nil,
|
92
|
-
:unique => nil
|
93
|
-
}]
|
15
|
+
:expected_type => nil,
|
16
|
+
:unique => nil
|
94
17
|
}]
|
95
|
-
}
|
18
|
+
}]
|
19
|
+
}
|
20
|
+
|
21
|
+
FETCH_ATTRIBUTES_QUERY = {
|
22
|
+
:"/type/reflect/any_master" => [
|
23
|
+
{
|
24
|
+
:id => nil,
|
25
|
+
:link => nil,
|
26
|
+
:name => nil
|
27
|
+
}
|
28
|
+
],
|
29
|
+
:"/type/reflect/any_reverse" => [
|
30
|
+
{
|
31
|
+
:id => nil,
|
32
|
+
:link => nil,
|
33
|
+
:name => nil
|
34
|
+
}
|
35
|
+
],
|
36
|
+
:"/type/reflect/any_value" => [
|
37
|
+
{
|
38
|
+
:link => nil,
|
39
|
+
:value => nil
|
40
|
+
# :lang => "/lang/en",
|
41
|
+
# :type => "/type/text"
|
42
|
+
}
|
43
|
+
],
|
96
44
|
|
97
|
-
|
98
|
-
|
45
|
+
# :id => id # needs to be merg!d in instance method
|
46
|
+
}
|
99
47
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
48
|
+
|
49
|
+
# initializes a resource by json result
|
50
|
+
def initialize(data)
|
51
|
+
assert_kind_of 'data', data, Hash
|
52
|
+
# intialize lazy if there is no type supplied
|
53
|
+
@schema_loaded, @attributes_loaded, @data = false, false, data
|
105
54
|
end
|
106
55
|
|
107
56
|
# @api public
|
@@ -153,6 +102,62 @@ module Ken
|
|
153
102
|
load_attributes! unless attributes_loaded?
|
154
103
|
@attributes.values
|
155
104
|
end
|
105
|
+
|
106
|
+
# returns true if type information is already loaded
|
107
|
+
# @api public
|
108
|
+
def schema_loaded?
|
109
|
+
@schema_loaded
|
110
|
+
end
|
111
|
+
|
112
|
+
# returns true if attributes are already loaded
|
113
|
+
# @api public
|
114
|
+
def attributes_loaded?
|
115
|
+
@attributes_loaded
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
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
|
+
Ken.session.mqlread(FETCH_ATTRIBUTES_QUERY.merge!(:id => id))
|
123
|
+
end
|
124
|
+
|
125
|
+
def load_attributes!
|
126
|
+
data = @data["ken:attribute"] || fetch_attributes
|
127
|
+
|
128
|
+
# master & value attributes
|
129
|
+
raw_attributes = Ken::Util.convert_hash(data["/type/reflect/any_master"])
|
130
|
+
raw_attributes.merge!(Ken::Util.convert_hash(data["/type/reflect/any_value"]))
|
131
|
+
@attributes = {}
|
132
|
+
raw_attributes.each_pair do |a, d|
|
133
|
+
properties.select { |p| p.id == a}.each do |p|
|
134
|
+
@attributes[p.id] = Ken::Attribute.create(d, p)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# reverse properties
|
139
|
+
raw_attributes = Ken::Util.convert_hash(data["/type/reflect/any_reverse"])
|
140
|
+
raw_attributes.each_pair do |a, d|
|
141
|
+
properties.select { |p| p.master_property == a}.each do |p|
|
142
|
+
@attributes[p.id] = Ken::Attribute.create(d, p)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
@attributes_loaded = true
|
147
|
+
end
|
148
|
+
|
149
|
+
def fetch_schema
|
150
|
+
Ken.session.mqlread(FETCH_SCHEMA_QUERY.merge!(:id => id))["ken:type"]
|
151
|
+
end
|
152
|
+
|
153
|
+
# loads the resources metainfo
|
154
|
+
# @api private
|
155
|
+
def load_schema!
|
156
|
+
@data["ken:type"] ||= fetch_schema
|
157
|
+
@types = Ken::Collection.new(@data["ken:type"].map { |type| Ken::Type.new(type) })
|
158
|
+
@schema_loaded = true
|
159
|
+
end
|
160
|
+
|
156
161
|
|
157
162
|
end # class Resource
|
158
163
|
end # module Ken
|
data/lib/ken/type.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module Ken
|
2
2
|
class Type
|
3
3
|
|
4
|
+
include Extlib::Assertions
|
5
|
+
|
4
6
|
# initializes a resource by json result
|
5
7
|
def initialize(data)
|
6
|
-
|
7
|
-
|
8
|
+
assert_kind_of 'data', data, Hash
|
8
9
|
@data = data
|
9
|
-
self
|
10
10
|
end
|
11
11
|
|
12
12
|
# access property info
|
data/lib/ken/util.rb
CHANGED
@@ -2,11 +2,15 @@ module Ken
|
|
2
2
|
module Util
|
3
3
|
# magic hash conversion
|
4
4
|
def convert_hash(source)
|
5
|
-
result
|
6
|
-
|
7
|
-
|
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
|
8
13
|
end
|
9
|
-
result
|
10
14
|
end
|
11
15
|
module_function :convert_hash
|
12
16
|
end
|
data/lib/ken/version.rb
CHANGED
data/lib/ken/view.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# provides an interface to view a resource as a specific type
|
2
2
|
# provides an interface for working with attributes, properties
|
3
3
|
module Ken
|
4
|
-
class View
|
4
|
+
class View
|
5
|
+
|
6
|
+
include Extlib::Assertions
|
7
|
+
|
5
8
|
# initializes a resource by json result
|
6
9
|
def initialize(resource, type)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@resource = resource # belongs to a resource
|
11
|
-
@type = type # belongs to a type
|
12
|
-
self
|
10
|
+
assert_kind_of 'resource', resource, Ken::Resource
|
11
|
+
assert_kind_of 'type', type, Ken::Type
|
12
|
+
@resource, @type = resource, type
|
13
13
|
end
|
14
14
|
|
15
15
|
# @api public
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"id" : "/en/the_police",
|
3
3
|
"name" : "The Police",
|
4
|
-
"type" : [
|
4
|
+
"ken:type" : [
|
5
5
|
{
|
6
6
|
"id" : "/music/artist",
|
7
7
|
"name" : "Musical Artist",
|
@@ -189,7 +189,7 @@
|
|
189
189
|
]
|
190
190
|
}
|
191
191
|
],
|
192
|
-
"attribute" : {
|
192
|
+
"ken:attribute" : {
|
193
193
|
"/type/reflect/any_master" : [
|
194
194
|
{
|
195
195
|
"id" : "/boot/all_permission",
|
@@ -9,12 +9,48 @@ describe Ken do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
describe "Ken.get('/en/the_police')" do
|
12
|
-
it
|
12
|
+
it "should return a Ken::Resource" do
|
13
13
|
the_police = Ken.get("/en/the_police")
|
14
14
|
the_police.should be_kind_of(Ken::Resource)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
describe "Ken.all" do
|
19
|
+
it "should return a Ken::Collection of Ken::Resources" do
|
20
|
+
resources = Ken.all(:name => "Apple")
|
21
|
+
resources.should be_kind_of(Ken::Collection)
|
22
|
+
resources.first.should be_kind_of(Ken::Resource)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should work with a limit specified" do
|
26
|
+
resources = Ken.all(:name => "Apple", :limit => 3)
|
27
|
+
resources.should have(3).items
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should work with a type specified" do
|
31
|
+
resources = Ken.all(:name => "Apple", :type => "/music/album")
|
32
|
+
resources.should have_at_least(1).items
|
33
|
+
resources.each {|r| r.types.select {|t| t.id == "/music/album"}.should have(1).items }
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should understand nested queries" do
|
37
|
+
query = {
|
38
|
+
:directed_by => "George Lucas",
|
39
|
+
:starring => [
|
40
|
+
{
|
41
|
+
:actor => "Harrison Ford"
|
42
|
+
}
|
43
|
+
],
|
44
|
+
:type => "/film/film"
|
45
|
+
}
|
46
|
+
|
47
|
+
resources = Ken.all(query)
|
48
|
+
resources.should have(3).items
|
49
|
+
resources.first.name.should == "Star Wars Episode IV: A New Hope"
|
50
|
+
resources.last.name.should == "The Star Wars Holiday Special"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
18
54
|
describe "Ken::Resource" do
|
19
55
|
before :all do
|
20
56
|
@the_police = Ken.get("/en/the_police")
|
data/spec/unit/resource_spec.rb
CHANGED
@@ -5,7 +5,7 @@ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
|
|
5
5
|
describe Ken::Resource do
|
6
6
|
|
7
7
|
before :each do
|
8
|
-
Ken::Logger.new(STDOUT, :info)
|
8
|
+
# Ken::Logger.new(STDOUT, :info)
|
9
9
|
# Ken::Session.new('http://www.freebase.com', 'ma', 'xxxxx')
|
10
10
|
data = load_fixture('the_police')
|
11
11
|
@the_police = Ken::Resource.new(data)
|
data/spec/unit/view_spec.rb
CHANGED
@@ -7,20 +7,22 @@ describe Ken::View do
|
|
7
7
|
Ken::Logger.new(STDOUT, :info)
|
8
8
|
data = load_fixture('the_police')
|
9
9
|
@the_police = Ken::Resource.new(data)
|
10
|
-
|
11
10
|
@view = @the_police.views.first
|
12
11
|
end
|
13
12
|
|
14
13
|
it "should have a type" do
|
15
14
|
@view.type.should_not be_nil
|
15
|
+
@view.type.should be_kind_of(Ken::Type)
|
16
16
|
end
|
17
17
|
|
18
18
|
it "should have properties" do
|
19
19
|
@view.properties.should_not be_nil
|
20
|
+
@view.properties.each { |p| p.should be_kind_of(Ken::Property)}
|
20
21
|
end
|
21
22
|
|
22
23
|
it "should have attributes" do
|
23
|
-
@view.
|
24
|
+
@view.attributes.should_not be_nil
|
25
|
+
@view.attributes.each { |p| p.should be_kind_of(Ken::Attribute)}
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
metadata
CHANGED
@@ -1,47 +1,37 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: michael-ken
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- michael
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-05-14 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: "0"
|
24
|
-
version:
|
25
|
-
description: Ruby API for accessing Freebase
|
26
|
-
email:
|
27
|
-
- ma [a] zive [d] at
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: ma[at]zive[dot]at
|
28
18
|
executables: []
|
29
19
|
|
30
20
|
extensions: []
|
31
21
|
|
32
22
|
extra_rdoc_files:
|
33
|
-
- README.txt
|
34
23
|
- LICENSE
|
35
|
-
-
|
36
|
-
-
|
24
|
+
- README.textile
|
25
|
+
- README.txt
|
37
26
|
files:
|
27
|
+
- .gitignore
|
38
28
|
- History.txt
|
39
29
|
- LICENSE
|
40
|
-
- Manifest.txt
|
41
|
-
- README.txt
|
42
30
|
- README.textile
|
31
|
+
- README.txt
|
43
32
|
- Rakefile
|
44
33
|
- TODO
|
34
|
+
- VERSION
|
45
35
|
- examples/artist.rb
|
46
36
|
- lib/ken.rb
|
47
37
|
- lib/ken/attribute.rb
|
@@ -54,27 +44,23 @@ files:
|
|
54
44
|
- lib/ken/util.rb
|
55
45
|
- lib/ken/version.rb
|
56
46
|
- lib/ken/view.rb
|
47
|
+
- spec/fixtures/music_artist.json
|
48
|
+
- spec/fixtures/the_police.json
|
49
|
+
- spec/integration/ken_spec.rb
|
50
|
+
- spec/spec.opts
|
51
|
+
- spec/spec_helper.rb
|
57
52
|
- spec/unit/attribute_spec.rb
|
58
53
|
- spec/unit/property_spec.rb
|
59
54
|
- spec/unit/resource_spec.rb
|
60
55
|
- spec/unit/session_spec.rb
|
61
56
|
- spec/unit/type_spec.rb
|
62
57
|
- spec/unit/view_spec.rb
|
63
|
-
- spec/integration/ken_spec.rb
|
64
|
-
- spec/fixtures/music_artist.json
|
65
|
-
- spec/fixtures/the_police.json
|
66
|
-
- spec/spec.opts
|
67
|
-
- spec/spec_helper.rb
|
68
|
-
- tasks/hoe.rb
|
69
|
-
- tasks/install.rb
|
70
58
|
- tasks/spec.rb
|
71
|
-
- tasks/gemspec.rb
|
72
59
|
has_rdoc: true
|
73
60
|
homepage: http://github.com/michael/ken
|
74
61
|
post_install_message:
|
75
62
|
rdoc_options:
|
76
|
-
- --
|
77
|
-
- README.txt
|
63
|
+
- --charset=UTF-8
|
78
64
|
require_paths:
|
79
65
|
- lib
|
80
66
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -91,10 +77,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
77
|
version:
|
92
78
|
requirements: []
|
93
79
|
|
94
|
-
rubyforge_project:
|
80
|
+
rubyforge_project:
|
95
81
|
rubygems_version: 1.2.0
|
96
82
|
signing_key:
|
97
83
|
specification_version: 2
|
98
|
-
summary: Ruby API for
|
99
|
-
test_files:
|
100
|
-
|
84
|
+
summary: Ruby API for Accessing the Freebase
|
85
|
+
test_files:
|
86
|
+
- spec/integration/ken_spec.rb
|
87
|
+
- spec/spec_helper.rb
|
88
|
+
- spec/unit/attribute_spec.rb
|
89
|
+
- spec/unit/property_spec.rb
|
90
|
+
- spec/unit/resource_spec.rb
|
91
|
+
- spec/unit/session_spec.rb
|
92
|
+
- spec/unit/type_spec.rb
|
93
|
+
- spec/unit/view_spec.rb
|
94
|
+
- examples/artist.rb
|
data/Manifest.txt
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
History.txt
|
2
|
-
LICENSE
|
3
|
-
Manifest.txt
|
4
|
-
README.textile
|
5
|
-
README.txt
|
6
|
-
Rakefile
|
7
|
-
TODO
|
8
|
-
examples/artist.rb
|
9
|
-
lib/ken.rb
|
10
|
-
lib/ken/attribute.rb
|
11
|
-
lib/ken/collection.rb
|
12
|
-
lib/ken/logger.rb
|
13
|
-
lib/ken/property.rb
|
14
|
-
lib/ken/resource.rb
|
15
|
-
lib/ken/session.rb
|
16
|
-
lib/ken/type.rb
|
17
|
-
lib/ken/util.rb
|
18
|
-
lib/ken/version.rb
|
19
|
-
lib/ken/view.rb
|
20
|
-
spec/fixtures/music_artist.json
|
21
|
-
spec/fixtures/the_police.json
|
22
|
-
spec/integration/ken_spec.rb
|
23
|
-
spec/spec.opts
|
24
|
-
spec/spec_helper.rb
|
25
|
-
spec/unit/attribute_spec.rb
|
26
|
-
spec/unit/property_spec.rb
|
27
|
-
spec/unit/resource_spec.rb
|
28
|
-
spec/unit/session_spec.rb
|
29
|
-
spec/unit/type_spec.rb
|
30
|
-
spec/unit/view_spec.rb
|
31
|
-
tasks/gemspec.rb
|
32
|
-
tasks/hoe.rb
|
33
|
-
tasks/install.rb
|
34
|
-
tasks/spec.rb
|
data/tasks/gemspec.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
desc "Generate gemspec"
|
2
|
-
task :gemspec do |x|
|
3
|
-
# Clean up extraneous files before checking manifest
|
4
|
-
%x[rake clean]
|
5
|
-
|
6
|
-
# Check the manifest before generating the gemspec
|
7
|
-
manifest = %x[rake check_manifest]
|
8
|
-
manifest.gsub!("(in /usr/local/projects/dm/dm-core)\n", "")
|
9
|
-
|
10
|
-
unless manifest.empty?
|
11
|
-
print "\n", "#"*68, "\n"
|
12
|
-
print <<-EOS
|
13
|
-
Manifest.txt is not up-to-date. Please review the changes below.
|
14
|
-
If the changes are correct, run 'rake check_manifest | patch'
|
15
|
-
and then run this command again.
|
16
|
-
EOS
|
17
|
-
print "#"*68, "\n\n"
|
18
|
-
puts manifest
|
19
|
-
else
|
20
|
-
%x[rake debug_gem > #{GEM_NAME}.gemspec]
|
21
|
-
puts "Successfully created gemspec for #{GEM_NAME}!"
|
22
|
-
end
|
23
|
-
end
|
data/tasks/hoe.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
require 'hoe'
|
2
|
-
|
3
|
-
@config_file = "~/.rubyforge/user-config.yml"
|
4
|
-
@config = nil
|
5
|
-
RUBYFORGE_USERNAME = "unknown"
|
6
|
-
def rubyforge_username
|
7
|
-
unless @config
|
8
|
-
begin
|
9
|
-
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
10
|
-
rescue
|
11
|
-
puts <<-EOS
|
12
|
-
ERROR: No rubyforge config file found: #{@config_file}
|
13
|
-
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
14
|
-
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
15
|
-
EOS
|
16
|
-
exit
|
17
|
-
end
|
18
|
-
end
|
19
|
-
RUBYFORGE_USERNAME.replace @config["username"]
|
20
|
-
end
|
21
|
-
|
22
|
-
# Remove hoe dependency
|
23
|
-
class Hoe
|
24
|
-
def extra_dev_deps
|
25
|
-
@extra_dev_deps.reject! { |dep| dep[0] == "hoe" }
|
26
|
-
@extra_dev_deps
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
hoe = Hoe.new(GEM_NAME, GEM_VERSION) do |p|
|
31
|
-
|
32
|
-
p.developer(AUTHOR, EMAIL)
|
33
|
-
|
34
|
-
p.description = PROJECT_DESCRIPTION
|
35
|
-
p.summary = PROJECT_SUMMARY
|
36
|
-
p.url = PROJECT_URL
|
37
|
-
|
38
|
-
p.rubyforge_name = PROJECT_NAME if PROJECT_NAME
|
39
|
-
|
40
|
-
p.clean_globs |= GEM_CLEAN
|
41
|
-
p.spec_extras = GEM_EXTRAS if GEM_EXTRAS
|
42
|
-
|
43
|
-
GEM_DEPENDENCIES.each do |dep|
|
44
|
-
p.extra_deps << dep
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
data/tasks/install.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
def sudo_gem(cmd)
|
2
|
-
sh "#{SUDO} #{RUBY} -S gem #{cmd}", :verbose => false
|
3
|
-
end
|
4
|
-
|
5
|
-
desc "Install #{GEM_NAME} #{GEM_VERSION}"
|
6
|
-
task :install => [ :package ] do
|
7
|
-
sudo_gem "install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources"
|
8
|
-
end
|
9
|
-
|
10
|
-
desc "Uninstall #{GEM_NAME} #{GEM_VERSION}"
|
11
|
-
task :uninstall => [ :clobber ] do
|
12
|
-
sudo_gem "uninstall #{GEM_NAME} -v#{GEM_VERSION} -Ix"
|
13
|
-
end
|