ken 0.1.2

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/TODO ADDED
File without changes
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.2
@@ -0,0 +1,21 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+
4
+ EXAMPLES_ROOT = Pathname(__FILE__).dirname.expand_path
5
+ require EXAMPLES_ROOT.parent + 'lib/ken'
6
+
7
+ Ken::Logger.new(STDOUT, :info)
8
+ Ken::Session.new('http://www.freebase.com', 'ma', 'xxxxx')
9
+
10
+ resource = Ken.get('/en/the_police')
11
+
12
+ resource.views.each do |view|
13
+ puts view
14
+ puts "="*20
15
+ view.attributes.each do |a|
16
+ puts a.property
17
+ puts "-"*20
18
+ puts a
19
+ puts # newline
20
+ end
21
+ end
@@ -0,0 +1,38 @@
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
+ puts "collecting artist links... this might take a while..."
13
+
14
+ artist_links = []
15
+
16
+ # execute query
17
+ artists = Ken.session.mqlread([{
18
+ :type => "/music/artist",
19
+ :id => nil,
20
+ :"/common/topic/webpage" => [{:uri => nil}],
21
+ :home_page => [{:uri => nil}]
22
+ }], :cursor => true)
23
+
24
+ # collect artist links
25
+ artists.each do |artist|
26
+ artist["/common/topic/webpage"].each do |webpage|
27
+ artist_links << webpage["uri"] unless artist_links.include?(webpage["uri"])
28
+ end
29
+
30
+ artist["home_page"].each do |homepage|
31
+ artist_links << homepage["uri"] unless artist_links.include?(homepage["uri"])
32
+ end
33
+ end
34
+
35
+ # print artist links
36
+ artist_links.each do |link|
37
+ puts link
38
+ end
@@ -0,0 +1,91 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ken}
8
+ s.version = "0.1.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["michael"]
12
+ s.date = %q{2009-10-16}
13
+ s.email = %q{ma[at]zive[dot]at}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.textile",
17
+ "README.txt"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "History.txt",
22
+ "LICENSE",
23
+ "README.textile",
24
+ "README.txt",
25
+ "Rakefile",
26
+ "TODO",
27
+ "VERSION",
28
+ "examples/artist.rb",
29
+ "examples/artist_links.rb",
30
+ "ken.gemspec",
31
+ "lib/ken.rb",
32
+ "lib/ken/attribute.rb",
33
+ "lib/ken/collection.rb",
34
+ "lib/ken/logger.rb",
35
+ "lib/ken/property.rb",
36
+ "lib/ken/resource.rb",
37
+ "lib/ken/session.rb",
38
+ "lib/ken/type.rb",
39
+ "lib/ken/util.rb",
40
+ "lib/ken/view.rb",
41
+ "rails/init.rb",
42
+ "tasks/ken.rb",
43
+ "tasks/spec.rb",
44
+ "test/fixtures/music_artist.json",
45
+ "test/fixtures/the_police.json",
46
+ "test/integration/ken_test.rb",
47
+ "test/test_helper.rb",
48
+ "test/unit/attribute_test.rb",
49
+ "test/unit/property_test.rb",
50
+ "test/unit/resource_test.rb",
51
+ "test/unit/session_test.rb",
52
+ "test/unit/type_test.rb",
53
+ "test/unit/view_test.rb"
54
+ ]
55
+ s.homepage = %q{http://github.com/michael/ken}
56
+ s.rdoc_options = ["--charset=UTF-8"]
57
+ s.require_paths = ["lib"]
58
+ s.rubygems_version = %q{1.3.5}
59
+ s.summary = %q{Ruby API for Accessing the Freebase}
60
+ s.test_files = [
61
+ "test/integration/ken_test.rb",
62
+ "test/test_helper.rb",
63
+ "test/unit/attribute_test.rb",
64
+ "test/unit/property_test.rb",
65
+ "test/unit/resource_test.rb",
66
+ "test/unit/session_test.rb",
67
+ "test/unit/type_test.rb",
68
+ "test/unit/view_test.rb",
69
+ "examples/artist.rb",
70
+ "examples/artist_links.rb"
71
+ ]
72
+
73
+ if s.respond_to? :specification_version then
74
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
75
+ s.specification_version = 3
76
+
77
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
78
+ s.add_runtime_dependency(%q<extlib>, [">= 0"])
79
+ s.add_runtime_dependency(%q<json>, [">= 0"])
80
+ s.add_runtime_dependency(%q<addressable>, [">= 0"])
81
+ else
82
+ s.add_dependency(%q<extlib>, [">= 0"])
83
+ s.add_dependency(%q<json>, [">= 0"])
84
+ s.add_dependency(%q<addressable>, [">= 0"])
85
+ end
86
+ else
87
+ s.add_dependency(%q<extlib>, [">= 0"])
88
+ s.add_dependency(%q<json>, [">= 0"])
89
+ s.add_dependency(%q<addressable>, [">= 0"])
90
+ end
91
+ end
@@ -0,0 +1,126 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'extlib/assertions'
6
+ require 'addressable/uri'
7
+
8
+ dir = Pathname(__FILE__).dirname.expand_path + 'ken'
9
+
10
+ require dir + 'util'
11
+ require dir + 'resource'
12
+ require dir + 'type'
13
+ require dir + 'view'
14
+ require dir + 'property'
15
+ require dir + 'attribute'
16
+ require dir + 'collection'
17
+ require dir + 'session'
18
+ require dir + 'logger'
19
+
20
+ # init logger as soon as the library is required
21
+ Ken::Logger.new(STDOUT, :error)
22
+
23
+ # init default session
24
+ Ken::Session.new('http://www.freebase.com', 'ma', 'xxxxx')
25
+
26
+ module Ken
27
+ extend Extlib::Assertions
28
+
29
+ # store query as a constant here.
30
+ # if the hash gets updated using
31
+ # #merge! or #update, this will mean
32
+ # that it actually stores the last
33
+ # query used. there are 2 sides to this.
34
+ # on the one hand, it isn't really a
35
+ # constant anymore (ruby doesn't complain)?
36
+ # on the other hand, there is no need to
37
+ # create a new object everytime a query is
38
+ # executed. maybe this is fine, maybe not,
39
+ # this needs to be discussed.
40
+
41
+ FETCH_DATA_QUERY = {
42
+ # :id => id, # needs to be merge!d in instance method
43
+ :guid => nil,
44
+ :name => nil,
45
+ :"ken:type" => [{
46
+ :id => nil,
47
+ :name => nil,
48
+ :properties => [{
49
+ :id => nil,
50
+ :name => nil,
51
+ :expected_type => nil,
52
+ :unique => nil,
53
+ :reverse_property => nil,
54
+ :master_property => nil,
55
+ }]
56
+ }],
57
+ :"/type/reflect/any_master" => [
58
+ {
59
+ :id => nil,
60
+ :link => nil,
61
+ :name => nil,
62
+ :optional => true,
63
+ :limit => 999999
64
+ }
65
+ ],
66
+ :"/type/reflect/any_reverse" => [
67
+ {
68
+ :id => nil,
69
+ :link => nil,
70
+ :name => nil,
71
+ :optional => true,
72
+ :limit => 999999
73
+ }
74
+ ],
75
+ :"/type/reflect/any_value" => [
76
+ {
77
+ :link => nil,
78
+ :value => nil,
79
+ :optional => true,
80
+ :limit => 999999
81
+ # TODO: support multiple language
82
+ # :lang => "/lang/en",
83
+ # :type => "/type/text"
84
+ }
85
+ ]
86
+ }
87
+
88
+ # Executes an Mql Query against the Freebase API and returns the result as
89
+ # a <tt>Collection</tt> of <tt>Resources</tt>.
90
+ #
91
+ # performs a cursored query unless there's a limit specified
92
+ # == Examples
93
+ #
94
+ # Ken.all(:name => "Apple", :type => "/music/album")
95
+ #
96
+ # Ken.all(
97
+ # :directed_by => "George Lucas",
98
+ # :starring => [{
99
+ # :actor => "Harrison Ford"
100
+ # }],
101
+ # :type => "/film/film"
102
+ # )
103
+ # @api public
104
+ def self.all(options = {})
105
+ assert_kind_of 'options', options, Hash
106
+ query = { :name => nil }.merge!(options).merge!(:id => nil)
107
+ result = Ken.session.mqlread([ query ], :cursor => !options[:limit])
108
+ Ken::Collection.new(result.map { |r| Ken::Resource.new(r) })
109
+ end
110
+
111
+
112
+ # Executes an Mql Query against the Freebase API and returns the result wrapped
113
+ # in a <tt>Resource</tt> Object.
114
+ #
115
+ # == Examples
116
+ #
117
+ # Ken.get('/en/the_police') => #<Resource id="/en/the_police" name="The Police">
118
+ # @api public
119
+ def self.get(id)
120
+ assert_kind_of 'id', id, String
121
+ result = Ken.session.mqlread(FETCH_DATA_QUERY.merge!(:id => id))
122
+ raise ResourceNotFound unless result
123
+ Ken::Resource.new(result)
124
+ end
125
+
126
+ end # module Ken
@@ -0,0 +1,62 @@
1
+ module Ken
2
+ class Attribute
3
+
4
+ include Extlib::Assertions
5
+ attr_reader :property
6
+
7
+ # initializes a resource by json result
8
+ def initialize(data, property)
9
+ assert_kind_of 'data', data, Array
10
+ assert_kind_of 'property', property, Ken::Property
11
+ @data, @property = data, property
12
+ end
13
+
14
+ # factory method for creating an attribute instance
15
+ # @api semipublic
16
+ def self.create(data, property)
17
+ Ken::Attribute.new(data, property)
18
+ end
19
+
20
+ # @api public
21
+ def to_s
22
+ subject.to_s
23
+ end
24
+
25
+ # @api public
26
+ def inspect
27
+ result = "#<Attribute property=\"#{property.id || "nil"}\">"
28
+ end
29
+
30
+ # returns a collection of values
31
+ # in case of a unique property the array holds just one value
32
+ # @api public
33
+ def values
34
+ subject
35
+ end
36
+
37
+ # unique properties can have at least one value
38
+ # @api public
39
+ def unique?
40
+ @property.unique?
41
+ end
42
+
43
+ # object type properties always link to resources
44
+ # @api public
45
+ def object_type?
46
+ @property.object_type?
47
+ end
48
+
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
55
+
56
+ private
57
+ # initializes the subject if used for the first time
58
+ def subject
59
+ @subject ||= Ken::Collection.new(@data.map { |r| object_type? ? Ken::Resource.new(r) : r["value"] })
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,8 @@
1
+ module Ken
2
+ class Collection < Array
3
+ # add a linebreak after each entry
4
+ def to_s
5
+ self.inject("") { |m,i| "#{m}#{i.to_s}\n"}
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,233 @@
1
+ require "time"
2
+
3
+ # ==== Public Ken Logger API
4
+ #
5
+ # Logger taken from Merb/Datamapper :)
6
+ #
7
+ # To replace an existing logger with a new one:
8
+ # Ken.logger.set_log(log{String, IO},level{Symbol, String})
9
+ #
10
+ # Available logging levels are:
11
+ # :off, :fatal, :error, :warn, :info, :debug
12
+ #
13
+ # Logging via:
14
+ # Ken.logger.fatal(message<String>)
15
+ # Ken.logger.error(message<String>)
16
+ # Ken.logger.warn(message<String>)
17
+ # Ken.logger.info(message<String>)
18
+ # Ken.logger.debug(message<String>)
19
+ #
20
+ # Flush the buffer to
21
+ # Ken.logger.flush
22
+ #
23
+ # Remove the current log object
24
+ # Ken.logger.close
25
+ #
26
+ # ==== Private Ken Logger API
27
+ #
28
+ # To initialize the logger you create a new object, proxies to set_log.
29
+ # ken::Logger.new(log{String, IO}, level{Symbol, String})
30
+ #
31
+ # Logger will not create the file until something is actually logged
32
+ # This avoids file creation on Ken init when it creates the
33
+ # default logger.
34
+ module Ken
35
+
36
+ class << self #:nodoc:
37
+ attr_accessor :logger
38
+ end
39
+
40
+ class Logger
41
+ attr_accessor :aio
42
+ attr_accessor :delimiter
43
+ attr_reader :level
44
+ attr_reader :buffer
45
+ attr_reader :log
46
+
47
+ # @note
48
+ # Ruby (standard) logger levels:
49
+ # off: absolutely nothing
50
+ # fatal: an unhandleable error that results in a program crash
51
+ # error: a handleable error condition
52
+ # warn: a warning
53
+ # info: generic (useful) information about system operation
54
+ # debug: low-level information for developers
55
+ #
56
+ # Ken::Logger::LEVELS[:off, :fatal, :error, :warn, :info, :debug]
57
+
58
+ LEVELS =
59
+ {
60
+ :off => 99999,
61
+ :fatal => 7,
62
+ :error => 6,
63
+ :warn => 4,
64
+ :info => 3,
65
+ :debug => 0
66
+ }
67
+
68
+ def level=(new_level)
69
+ @level = LEVELS[new_level.to_sym]
70
+ reset_methods(:close)
71
+ end
72
+
73
+ private
74
+
75
+ # The idea here is that instead of performing an 'if' conditional check on
76
+ # each logging we do it once when the log object is setup
77
+ def set_write_method
78
+ @log.instance_eval do
79
+
80
+ # Determine if asynchronous IO can be used
81
+ def aio?
82
+ @aio = !RUBY_PLATFORM.match(/java|mswin/) &&
83
+ !(@log == STDOUT) &&
84
+ @log.respond_to?(:write_nonblock)
85
+ end
86
+
87
+ # Define the write method based on if aio an be used
88
+ undef write_method if defined? write_method
89
+ if aio?
90
+ alias :write_method :write_nonblock
91
+ else
92
+ alias :write_method :write
93
+ end
94
+ end
95
+ end
96
+
97
+ def initialize_log(log)
98
+ close if @log # be sure that we don't leave open files laying around.
99
+ @log = log || "log/dm.log"
100
+ end
101
+
102
+ def reset_methods(o_or_c)
103
+ if o_or_c == :open
104
+ alias internal_push push_opened
105
+ elsif o_or_c == :close
106
+ alias internal_push push_closed
107
+ end
108
+ end
109
+
110
+ def push_opened(string)
111
+ message = Time.now.httpdate
112
+ message << delimiter
113
+ message << string
114
+ message << "\n" unless message[-1] == ?\n
115
+ @buffer << message
116
+ flush # Force a flush for now until we figure out where we want to use the buffering.
117
+ end
118
+
119
+ def push_closed(string)
120
+ unless @log.respond_to?(:write)
121
+ log = Pathname(@log)
122
+ log.dirname.mkpath
123
+ @log = log.open('a')
124
+ @log.sync = true
125
+ end
126
+ set_write_method
127
+ reset_methods(:open)
128
+ push(string)
129
+ end
130
+
131
+ alias internal_push push_closed
132
+
133
+ def prep_msg(message, level)
134
+ level << delimiter << message
135
+ end
136
+
137
+ public
138
+
139
+ # To initialize the logger you create a new object, proxies to set_log.
140
+ # Ken::Logger.new(log{String, IO},level{Symbol, String})
141
+ #
142
+ # @param log<IO,String> either an IO object or a name of a logfile.
143
+ # @param log_level<String> the message string to be logged
144
+ # @param delimiter<String> delimiter to use between message sections
145
+ # @param log_creation<Boolean> log that the file is being created
146
+ def initialize(*args)
147
+ set_log(*args)
148
+ end
149
+
150
+ # To replace an existing logger with a new one:
151
+ # Ken.logger.set_log(log{String, IO},level{Symbol, String})
152
+ #
153
+ # @param log<IO,String> either an IO object or a name of a logfile.
154
+ # @param log_level<Symbol> a symbol representing the log level from
155
+ # {:off, :fatal, :error, :warn, :info, :debug}
156
+ # @param delimiter<String> delimiter to use between message sections
157
+ # @param log_creation<Boolean> log that the file is being created
158
+ def set_log(log, log_level = :off, delimiter = " ~ ", log_creation = false)
159
+ delimiter ||= " ~ "
160
+
161
+ if log_level && LEVELS[log_level.to_sym]
162
+ self.level = log_level.to_sym
163
+ else
164
+ self.level = :debug
165
+ end
166
+
167
+ @buffer = []
168
+ @delimiter = delimiter
169
+
170
+ initialize_log(log)
171
+
172
+ Ken.logger = self
173
+
174
+ self.info("Logfile created") if log_creation
175
+ end
176
+
177
+ # Flush the entire buffer to the log object.
178
+ # Ken.logger.flush
179
+ #
180
+ def flush
181
+ return unless @buffer.size > 0
182
+ @log.write_method(@buffer.slice!(0..-1).join)
183
+ end
184
+
185
+ # Close and remove the current log object.
186
+ # Ken.logger.close
187
+ #
188
+ def close
189
+ flush
190
+ @log.close if @log.respond_to?(:close)
191
+ @log = nil
192
+ end
193
+
194
+ # Appends a string and log level to logger's buffer.
195
+
196
+ # @note
197
+ # Note that the string is discarded if the string's log level less than the
198
+ # logger's log level.
199
+ # @note
200
+ # Note that if the logger is aio capable then the logger will use
201
+ # non-blocking asynchronous writes.
202
+ #
203
+ # @param level<Fixnum> the logging level as an integer
204
+ # @param string<String> the message string to be logged
205
+ def push(string)
206
+ internal_push(string)
207
+ end
208
+ alias << push
209
+
210
+ # Generate the following logging methods for Ken.logger as described
211
+ # in the API:
212
+ # :fatal, :error, :warn, :info, :debug
213
+ # :off only gets a off? method
214
+ LEVELS.each_pair do |name, number|
215
+ unless name.to_s == 'off'
216
+ class_eval <<-EOS, __FILE__, __LINE__
217
+ # DOC
218
+ def #{name}(message)
219
+ self.<<( prep_msg(message, "#{name}") ) if #{name}?
220
+ end
221
+ EOS
222
+ end
223
+
224
+ class_eval <<-EOS, __FILE__, __LINE__
225
+ # DOC
226
+ def #{name}?
227
+ #{number} >= level
228
+ end
229
+ EOS
230
+ end
231
+
232
+ end # class Logger
233
+ end # module Ken