ken 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,91 @@
1
+ module Ken
2
+ class Property
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
+
18
+ # initializes a resource by json result
19
+ def initialize(data, type)
20
+ assert_kind_of 'data', data, Hash
21
+ assert_kind_of 'type', type, Ken::Type
22
+ @data, @type = data, type
23
+ end
24
+
25
+ # property id
26
+ # @api public
27
+ def id
28
+ @data["id"]
29
+ end
30
+
31
+ # property name
32
+ # @api public
33
+ def name
34
+ @data["name"]
35
+ end
36
+
37
+ # @api public
38
+ def to_s
39
+ name || id || ""
40
+ end
41
+
42
+ # @api public
43
+ def inspect
44
+ result = "#<Property id=\"#{id}\" expected_type=\"#{expected_type || "nil"}\" unique=\"#{unique?}\" object_type=\"#{object_type?}\">"
45
+ end
46
+
47
+ # returns the type of which the property is a part of
48
+ # every property always has exactly one type.
49
+ # that's why /type/property/schema is a unique property
50
+ # @api public
51
+ def type
52
+ @type
53
+ end
54
+
55
+ # reverse property, which represent incoming links
56
+ # @api public
57
+ def reverse_property
58
+ @data["reverse_property"]
59
+ end
60
+
61
+ # master property, which represent an outgoing link (or primitive value)
62
+ # @api public
63
+ def master_property
64
+ @data["master_property"]
65
+ end
66
+
67
+ # returns true if the property is unique
68
+ # @api public
69
+ def unique?
70
+ return @data["unique"]==true
71
+ end
72
+
73
+ # returns true if the property is an object type
74
+ # @api public
75
+ def object_type?
76
+ !value_type?
77
+ end
78
+
79
+ # returns true if the property is a value type
80
+ # @api public
81
+ def value_type?
82
+ VALUE_TYPES.include?(expected_type)
83
+ end
84
+
85
+ # type, which attribute values of that property are expected to have
86
+ # @api public
87
+ def expected_type
88
+ @data["expected_type"]
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,151 @@
1
+ module Ken
2
+ class Resource
3
+ include Extlib::Assertions
4
+
5
+ attr_reader :data
6
+
7
+ # initializes a resource using a json result
8
+ def initialize(data)
9
+ assert_kind_of 'data', data, Hash
10
+ @schema_loaded, @attributes_loaded, @data = false, false, data
11
+ @data_fechted = data["/type/reflect/any_master"] != nil
12
+ end
13
+
14
+ # resource id
15
+ # @api public
16
+ def id
17
+ @data["id"] || ""
18
+ end
19
+
20
+ # resource guid
21
+ # @api public
22
+ def guid
23
+ @data['guid'] || ""
24
+ end
25
+
26
+ # resource name
27
+ # @api public
28
+ def name
29
+ @data["name"] || ""
30
+ end
31
+
32
+ # @api public
33
+ def to_s
34
+ name || id || ""
35
+ end
36
+
37
+ # @api public
38
+ def inspect
39
+ result = "#<Resource id=\"#{id}\" name=\"#{name || "nil"}\">"
40
+ end
41
+
42
+ # returns all assigned types
43
+ # @api public
44
+ def types
45
+ load_schema! unless schema_loaded?
46
+ @types
47
+ end
48
+
49
+ # returns all available views based on the assigned types
50
+ # @api public
51
+ def views
52
+ @views ||= Ken::Collection.new(types.map { |type| Ken::View.new(self, type) })
53
+ end
54
+
55
+ # returns individual view based on the requested type id
56
+ # @api public
57
+ def view(type)
58
+ views.each { |v| return v if v.type.id =~ /^#{Regexp.escape(type)}$/}
59
+ nil
60
+ end
61
+
62
+ # returns individual type based on the requested type id
63
+ # @api public
64
+ def type(type)
65
+ types.each { |t| return t if t.id =~ /^#{Regexp.escape(type)}$/}
66
+ nil
67
+ end
68
+
69
+ # returns all the properties from all assigned types
70
+ # @api public
71
+ def properties
72
+ @properties = Ken::Collection.new
73
+ types.each do |type|
74
+ @properties.concat(type.properties)
75
+ end
76
+ @properties
77
+ end
78
+
79
+ # returns all attributes for every type the resource is an instance of
80
+ # @api public
81
+ def attributes
82
+ load_attributes! unless attributes_loaded?
83
+ @attributes.values
84
+ end
85
+
86
+ # search for an attribute by name and return it
87
+ # @api public
88
+ def attribute(name)
89
+ attributes.each { |a| return a if a.property.id == name }
90
+ nil
91
+ end
92
+
93
+ # returns true if type information is already loaded
94
+ # @api public
95
+ def schema_loaded?
96
+ @schema_loaded
97
+ end
98
+
99
+ # returns true if attributes are already loaded
100
+ # @api public
101
+ def attributes_loaded?
102
+ @attributes_loaded
103
+ end
104
+ # returns true if json data is already loaded
105
+ # @api public
106
+ def data_fetched?
107
+ @data_fetched
108
+ end
109
+
110
+ private
111
+ # executes the fetch data query in order to load the full set of types, properties and attributes
112
+ # more info at http://lists.freebase.com/pipermail/developers/2007-December/001022.html
113
+ # @api private
114
+ def fetch_data
115
+ return @data if @data["/type/reflect/any_master"]
116
+ @data = Ken.session.mqlread(FETCH_DATA_QUERY.merge!(:id => id))
117
+ end
118
+
119
+ # loads the full set of attributes using reflection
120
+ # information is extracted from master, value and reverse attributes
121
+ # @api private
122
+ def load_attributes!
123
+ fetch_data unless data_fetched?
124
+ # master & value attributes
125
+ raw_attributes = Ken::Util.convert_hash(@data["/type/reflect/any_master"])
126
+ raw_attributes.merge!(Ken::Util.convert_hash(@data["/type/reflect/any_value"]))
127
+ @attributes = {}
128
+ raw_attributes.each_pair do |a, d|
129
+ properties.select { |p| p.id == a}.each do |p|
130
+ @attributes[p.id] = Ken::Attribute.create(d, p)
131
+ end
132
+ end
133
+ # reverse properties
134
+ raw_attributes = Ken::Util.convert_hash(@data["/type/reflect/any_reverse"])
135
+ raw_attributes.each_pair do |a, d|
136
+ properties.select { |p| p.master_property == a}.each do |p|
137
+ @attributes[p.id] = Ken::Attribute.create(d, p)
138
+ end
139
+ end
140
+ @attributes_loaded = true
141
+ end
142
+
143
+ # loads the resource's metainfo
144
+ # @api private
145
+ def load_schema!
146
+ fetch_data unless data_fetched?
147
+ @types = Ken::Collection.new(@data["ken:type"].map { |type| Ken::Type.new(type) })
148
+ @schema_loaded = true
149
+ end
150
+ end # class Resource
151
+ end # module Ken
@@ -0,0 +1,129 @@
1
+ module Ken
2
+ class << self
3
+ attr_accessor :session
4
+ end
5
+
6
+ # A class for returing errors from the freebase api.
7
+ # For more infomation see the freebase documentation:
8
+ class ReadError < ArgumentError
9
+ attr_accessor :code, :msg
10
+ def initialize(code,msg)
11
+ self.code = code
12
+ self.msg = msg
13
+ end
14
+ def message
15
+ "#{code}: #{msg}"
16
+ end
17
+ end
18
+
19
+ class AttributeNotFound < StandardError ; end
20
+ class PropertyNotFound < StandardError ; end
21
+ class ResourceNotFound < StandardError ; end
22
+ class ViewNotFound < StandardError ; end
23
+
24
+ # partially taken from chris eppstein's freebase api
25
+ # http://github.com/chriseppstein/freebase/tree
26
+ class Session
27
+ public
28
+ # Initialize a new Ken Session
29
+ # Ken::Session.new(host{String, IO}, username{String}, password{String})
30
+ #
31
+ # @param host<String> the API host
32
+ # @param username<String> freebase username
33
+ # @param password<String> user password
34
+ def initialize(host, username, password)
35
+ @host = host
36
+ @username = username
37
+ @password = password
38
+
39
+ Ken.session = self
40
+
41
+ # TODO: check connection
42
+ Ken.logger.info("connection established.")
43
+ end
44
+
45
+ SERVICES = {
46
+ :mqlread => '/api/service/mqlread',
47
+ :mqlwrite => '/api/service/mqlwrite',
48
+ :login => '/api/account/login',
49
+ :upload => '/api/service/upload'
50
+ }
51
+
52
+ # get the service url for the specified service.
53
+ def service_url(svc)
54
+ "#{@host}#{SERVICES[svc]}"
55
+ end
56
+
57
+ SERVICES.each_key do |k|
58
+ define_method("#{k}_service_url") do
59
+ service_url(k)
60
+ end
61
+ end
62
+
63
+ # raise an error if the inner response envelope is encoded as an error
64
+ def handle_read_error(inner)
65
+ unless inner['code'][0, '/api/status/ok'.length] == '/api/status/ok'
66
+ Ken.logger.error "Read Error #{inner.inspect}"
67
+ error = inner['messages'][0]
68
+ raise ReadError.new(error['code'], error['message'])
69
+ end
70
+ end # handle_read_error
71
+
72
+ # Perform a mqlread and return the results
73
+ # Specify :cursor => true to batch the results of a query, sending multiple requests if necessary.
74
+ # TODO: should support multiple queries
75
+ # you should be able to pass an array of queries
76
+ def mqlread(query, options = {})
77
+ Ken.logger.info ">>> Sending Query: #{query.to_json}"
78
+ cursor = options[:cursor]
79
+ if cursor
80
+ query_result = []
81
+ while cursor
82
+ response = get_query_response(query, cursor)
83
+ query_result += response['result']
84
+ cursor = response['cursor']
85
+ end
86
+ else
87
+ response = get_query_response(query, cursor)
88
+ cursor = response['cursor']
89
+ query_result = response['result']
90
+ end
91
+ query_result
92
+ end
93
+
94
+ protected
95
+ # returns parsed json response from freebase mqlread service
96
+ def get_query_response(query, cursor=nil)
97
+ envelope = { :qname => {:query => query }}
98
+ envelope[:qname][:cursor] = cursor if cursor
99
+
100
+ response = http_request mqlread_service_url, :queries => envelope.to_json
101
+
102
+ result = JSON.parse response
103
+
104
+ inner = result['qname']
105
+ handle_read_error(inner)
106
+ Ken.logger.info "<<< Received Response: #{inner['result'].inspect}"
107
+ inner
108
+ end
109
+
110
+ # encode parameters
111
+ def params_to_string(parameters)
112
+ parameters.keys.map {|k| "#{URI.encode(k.to_s)}=#{URI.encode(parameters[k])}" }.join('&')
113
+ end
114
+
115
+ # does the dirty work
116
+ def http_request(url, parameters = {})
117
+ params = params_to_string(parameters)
118
+ url << '?'+params unless params !~ /\S/
119
+
120
+ return Net::HTTP.get_response(::URI.parse(url)).body
121
+
122
+ fname = "#{MD5.md5(params)}.mql"
123
+ open(fname,"w") do |f|
124
+ f << response
125
+ end
126
+ Ken.logger.info("Wrote response to #{fname}")
127
+ end
128
+ end # class Session
129
+ end # module Ken
@@ -0,0 +1,53 @@
1
+ module Ken
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 ||= Ken::Collection.new(@data["properties"].map { |property| Ken::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
@@ -0,0 +1,18 @@
1
+ module Ken
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
18
+
@@ -0,0 +1,56 @@
1
+ # provides an interface to view a resource as a specific type
2
+ # provides an interface for working with attributes, properties
3
+ module Ken
4
+ class View
5
+
6
+ include Extlib::Assertions
7
+
8
+ # initializes a resource by json result
9
+ def initialize(resource, type)
10
+ assert_kind_of 'resource', resource, Ken::Resource
11
+ assert_kind_of 'type', type, Ken::Type
12
+ @resource, @type = resource, type
13
+ end
14
+
15
+ # @api public
16
+ def to_s
17
+ @type.to_s
18
+ end
19
+
20
+ # return correspondent type
21
+ # @api public
22
+ def type
23
+ @type
24
+ end
25
+
26
+ # @api public
27
+ def inspect
28
+ result = "#<View type=\"#{type.id || "nil"}\">"
29
+ end
30
+
31
+ # returns attributes which are member of the view's type
32
+ # @api public
33
+ def attributes
34
+ @resource.attributes.select { |a| a.property.type == @type}
35
+ end
36
+
37
+ # search for an attribute by name and return it
38
+ # @api public
39
+ def attribute(name)
40
+ attributes.each { |a| return a if a.property.id =~ /\/#{name}$/ }
41
+ nil
42
+ end
43
+
44
+ # returns properties which are member of the view's type
45
+ # @api public
46
+ def properties
47
+ @resource.properties.select { |p| p.type == @type}
48
+ end
49
+
50
+ # delegate to attribute
51
+ def method_missing sym
52
+ attribute(sym.to_s)
53
+ end
54
+
55
+ end
56
+ end