ken 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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