public
Description: A ruby client for freebase
Clone URL: git://github.com/dustin/ruby-freebase.git
Search Repo:
Migrate from svn to hg, add gem support
Chris Eppstein (author)
Sun Feb 03 09:24:11 -0800 2008
commit  b1636717abbd97d048fcebc0488b34653c94bd14
tree    0035d859b8a5529d0baebfbad205d8431eff6204
0
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
0
@@ -0,0 +1,25 @@
0
+= Freebase
0
+This Ruby-on-Rails plugin provides access to the Freebase API (http://www.freebase.com). Freebase is a collaborative, semantic database similar to Wikipedia only for structured data. Freebase.com provides a JSON-over-HTTP API that this library uses.
0
+
0
+Currently only reads are implemented. Contributions, API Suggestions, bug reports are welcome!
0
+
0
+This code is ALPHA. The API will change, features will be added. It probably has a bug or two in it. Use it at your own peril.
0
+
0
+Author:: Christopher Eppstein (mailto:chris@eppsteins.net)
0
+Copyright:: Copyright (c) 2007 Christopher Eppstein
0
+License:: Released under the MIT license
0
+
0
+== Installation
0
+Install the plugin:
0
+script/plugin install svn://rubyforge.org/var/svn/freebaseapi/trunk/freebase
0
+
0
+Then copy freebase.yml to your rails config directory
0
+
0
+== Usage Examples
0
+See the following examples:
0
+* albums.rb[link:../examples/albums.rb]
0
+
0
+== Contributors
0
+* Pat Allan (mailto:pat@freelancing-gods.com) provided code snippets that
0
+ exemplified automatic freebase class creation when the class is first referenced.
0
+
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
0
@@ -0,0 +1,48 @@
0
+require 'rake'
0
+require 'rubygems'
0
+Gem::manage_gems
0
+require 'rake/gempackagetask'
0
+require 'rake/testtask'
0
+require 'rake/rdoctask'
0
+
0
+spec = Gem::Specification.new do |s|
0
+ s.name = "freebase"
0
+ s.version = "0.0.1"
0
+ s.author = "Chris Eppstein"
0
+ s.email = "chris@eppsteins.net"
0
+ s.homepage = "http://rubyforge.org/projects/freebaseapi/"
0
+ s.rubyforge_project = "freebaseapi"
0
+ s.platform = Gem::Platform::RUBY
0
+ s.summary = "Ruby wrapper for the Freebase.com API that makes interacting with freebase.com in your Ruby on Rails application as easy as using Active Record. Freebase is a free, collaborative semantic database."
0
+ s.files = FileList["{bin,lib}/**/*"].to_a
0
+ s.require_path = "lib"
0
+ s.autorequire = "freebase"
0
+ s.test_files = FileList["{test}/**/*test.rb"].to_a
0
+ s.has_rdoc = true
0
+ s.extra_rdoc_files = ["README"]
0
+ s.add_dependency("activesupport", ">= 2.0.2")
0
+ s.add_dependency("json", ">= 1.1.2")
0
+end
0
+
0
+Rake::GemPackageTask.new(spec) do |pkg|
0
+ pkg.need_tar = true
0
+end
0
+
0
+desc 'Default: run unit tests.'
0
+task :default => :test
0
+
0
+desc 'Test the freebase plugin.'
0
+Rake::TestTask.new(:test) do |t|
0
+ t.libs << 'lib'
0
+ t.pattern = 'test/**/*_test.rb'
0
+ t.verbose = true
0
+end
0
+
0
+desc 'Generate documentation for the freebase plugin.'
0
+Rake::RDocTask.new(:rdoc) do |rdoc|
0
+ rdoc.rdoc_dir = 'rdoc'
0
+ rdoc.title = 'Freebase'
0
+ rdoc.options << '--line-numbers' << '--inline-source'
0
+ rdoc.rdoc_files.include('README')
0
+ rdoc.rdoc_files.include('lib/**/*.rb')
0
+end
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
0
@@ -0,0 +1,41 @@
0
+# (c) Copyright 2007 Chris Eppstein. All Rights Reserved.
0
+# Usage:
0
+# ruby albums.rb "The Police"
0
+
0
+
0
+require "freebase"
0
+
0
+
0
+# add these methods to the Track class
0
+module Freebase::Mixins::Music
0
+ module Track
0
+ def formatted_length
0
+ "#{self.length.to_i / 60}:#{sprintf("%02i", self.length.to_i % 60)}"
0
+ end
0
+ end
0
+end
0
+
0
+
0
+artist = Freebase::Types::Music::Artist.find(:first,
0
+ :conditions => {
0
+ :name => {:value => ARGV[0], :lang => {:name => "English"}},
0
+ :album => [{
0
+ :fb_object => true,
0
+ :name => {:value => nil, :lang => {:name => "English"}},
0
+ :release_date => nil,
0
+ :track => [{
0
+ :fb_object => true,
0
+ :length => nil,
0
+ :name => {:value => nil, :lang => {:name => "English"}}
0
+ }]
0
+ }]
0
+ }
0
+)
0
+
0
+artist.albums.each_with_index do |album, i|
0
+ next unless album # XXX I don't know why I'm getting random nils here.
0
+ puts "#{i+1}) #{album.name} (#{album.release_date || '?'})"
0
+ album.tracks.compact.each_with_index do |track, j|
0
+ puts "\tT#{j+1}. #{track.name || '???'} (#{track.formatted_length})"
0
+ end
0
+end
0
\ No newline at end of file
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0
@@ -0,0 +1,17 @@
0
+# Need to modify the inbuilt Module class, to catch const_missing calls.
0
+class Module
0
+
0
+ # New version of const_missing which catches requests made to the
0
+ # Metaweb module/namespace - and generates modules to represent
0
+ # data domains if necessary.
0
+ def const_missing_with_freebase_support(class_id)
0
+ if self.name[/^Freebase::Types/]
0
+ new_freebase_type(class_id)
0
+ else
0
+ const_missing_without_freebase_support(class_id)
0
+ end
0
+ end
0
+
0
+ alias_method_chain :const_missing, :freebase_support
0
+
0
+end
0
\ No newline at end of file
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
0
@@ -0,0 +1,160 @@
0
+# (c) Copyright 2007 Chris Eppstein. All Rights Reserved.
0
+
0
+require 'rubygems'
0
+require 'activesupport'
0
+require 'net/http'
0
+require 'core_extensions'
0
+
0
+module Freebase
0
+end
0
+
0
+require 'freebase/api'
0
+
0
+module Freebase
0
+ # This module is a namespace scope for the Freebase domains
0
+ module Types
0
+ # Automagically creates modules for domains and classes for types
0
+ # for matching namespaces to Freebase's domain/type naming structure.
0
+ # Shouldn't need to be called manually because this is called by the module
0
+ # whenever the constant is missing.
0
+ def new_freebase_type(name)
0
+ Freebase::Api::Logger.trace {"new freebase module = #{name}"}
0
+ self.const_set(name, Module.new do
0
+ def new_freebase_type(name)
0
+ Freebase::Api::Logger.trace {"new freebase class = #{name}"}
0
+ klass = self.const_set(name, Class.new(Freebase::Base))
0
+ klass.class_eval do
0
+ cattr_accessor :properties, :schema_loaded
0
+ end
0
+ returning(klass) do |tc|
0
+ tc.load_schema! unless tc.schema_loaded?
0
+ Freebase::Api::Logger.trace { "Attempting Mixin include: #{tc.name.sub(/Types/,"Mixins")}" }
0
+ begin
0
+ tc.send(:include, tc.name.sub(/Types/,"Mixins").constantize)
0
+ rescue NameError => e
0
+ Freebase::Api::Logger.trace "failed: #{e}"
0
+ end
0
+ end
0
+ end
0
+
0
+ module_function :new_freebase_type
0
+ end)
0
+ end
0
+
0
+ module_function :new_freebase_type
0
+ end
0
+
0
+ # Add a module within this module that corresponds to a freebase class within Freebase::Types
0
+ # and the methods will be mixed in automatically. E.g.:
0
+ # module Freebase::Mixins::Music
0
+ # module Track
0
+ # def formatted_length
0
+ # "#{self.length.to_i / 60}:#{sprintf("%02i", self.length.to_i % 60)}"
0
+ # end
0
+ # end
0
+ # end
0
+ #
0
+ # Will be mixed in to the Freebase::Types:Music:Track class
0
+ module Mixins
0
+ end
0
+
0
+ # This is the base class for all dynamically defined Freebase Types.
0
+ class Base < Api::FreebaseResult
0
+ extend Api
0
+ alias_method :attributes, :result
0
+ def self.schema_loaded?
0
+ self.schema_loaded || false
0
+ end
0
+ def self.freebase_type
0
+ @freebase_type ||= self.name["Freebase::Types".length..self.name.length].underscore
0
+ end
0
+ def self.load_schema!
0
+ self.properties = {}
0
+ propobjs = mqlread(:type => '/type/type', :id => self.freebase_type, :properties => [{:name => nil, :id => nil, :type => nil, :expected_type => nil}]).properties
0
+ propobjs.each {|propobj|
0
+ self.properties[propobj.id.split(/\//).last.to_sym] = propobj
0
+ }
0
+ self.schema_loaded = true
0
+ end
0
+ def self.find(*args)
0
+ options = args.extract_options!
0
+ case args.first
0
+ when :first
0
+ raise ArgumentError.new("Too many arguments for find(:first)") if args.size > 1
0
+ find_first(options)
0
+ when :all
0
+ raise ArgumentError.new("Too many arguments for find(:all)") if args.size > 1
0
+ find_all(options)
0
+ end
0
+ end
0
+ def self.add_required_query_attributes(conditions)
0
+ case conditions
0
+ when Array
0
+ conditions.map! {|c| add_required_query_attributes(c)}
0
+ when Hash
0
+ if conditions.delete(:fb_object)
0
+ conditions.reverse_merge!(:type => [], :id => nil) unless conditions.has_key?(:*)
0
+ else
0
+ conditions.reverse_merge!(:type => nil) unless conditions.has_key?(:*)
0
+ end
0
+ conditions.each {|k,v| add_required_query_attributes(v) unless k == :*}
0
+ else
0
+ conditions
0
+ end
0
+ end
0
+ # Don't to call this directly. find(:first, options) will be dispatched here.
0
+ # This method is provided for extensibility
0
+ def self.find_first(options = {})
0
+ conditions = options.fetch(:conditions, {}).reverse_merge(:type => self.freebase_type, :name=>nil, :* => [{}], :limit => 1)
0
+ add_required_query_attributes(conditions)
0
+ self.new(mqlread(conditions, :raw => true))
0
+ end
0
+
0
+ # Don't to call this directly. find(:all, options) will be dispatched here.
0
+ # This method is provided for extensibility
0
+ def self.find_all(options = {})
0
+ query = options.fetch(:conditions, {}).merge(:type => self.freebase_type, :name=>nil, :* => [{}])
0
+ query[:limit] = options[:limit] if options[:limit]
0
+ add_required_query_attributes(query)
0
+ mqlread([query], :raw => true).map{|i| self.new(i)}
0
+ end
0
+
0
+ # ActiveRecord:Base-like to_s for the class
0
+ def self.to_s
0
+ if respond_to?(:properties) && !self.properties.blank?
0
+ %Q{#<#{name} #{self.properties.map{|k,v| "#{k}:#{v.expected_type}"}.join(", ")}>}
0
+ else
0
+ "#<#{name}>"
0
+ end
0
+ end
0
+
0
+ # (re)load all properties of this object
0
+ def reload
0
+ query = {:id => self.id, :type=>self.class.freebase_type, :name=> nil, :limit => 1}
0
+ self.class.properties.each do |k,v|
0
+ query[k] = [{}] unless query.has_key?(k)
0
+ end
0
+ @result = self.class.mqlread(query, :raw => true).symbolize_keys!
0
+ Freebase::Api::Logger.trace { @result.inspect }
0
+ return self
0
+ end
0
+
0
+ # access the properties of this object, lazy loading associations as required.
0
+ def method_missing(name,*args)
0
+ if self.class.properties.has_key?(name)
0
+ reload unless attributes.has_key?(name)
0
+ resultify attributes[name]
0
+ elsif self.class.properties.has_key?((singularized_name = name.to_s.singularize.to_sym))
0
+ reload unless attributes.has_key?(singularized_name)
0
+ resultify attributes[singularized_name]
0
+ else
0
+ super
0
+ end
0
+ end
0
+
0
+ # If the object has a name, return it, otherwise the id.
0
+ def to_s
0
+ respond_to?(:name) ? name : id
0
+ end
0
+ end
0
+end
0
\ No newline at end of file
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
0
@@ -0,0 +1,222 @@
0
+module Freebase::Api
0
+ # A class for returing errors from the freebase api.
0
+ # For more infomation see the freebase documentation:
0
+ # http://www.freebase.com/view/help/guid/9202a8c04000641f800000000544e139#mqlreaderrors
0
+ class MqlReadError < ArgumentError
0
+ attr_accessor :code, :freebase_message
0
+ def initialize(code,message)
0
+ self.code = code
0
+ self.freebase_message = message
0
+ end
0
+ def message
0
+ "#{code}: #{freebase_message}"
0
+ end
0
+ end
0
+
0
+ # Encapsulates a Freebase result, enables method-based access to the returned values.
0
+ # E.g.
0
+ # result = mqlread(:type => "/music/artist", :name => "The Police", :id => nil)
0
+ # result.id => "/topic/en/the_police"
0
+ class FreebaseResult
0
+
0
+ attr_accessor :result
0
+
0
+ def initialize(result)
0
+ @result = result.symbolize_keys!
0
+ end
0
+
0
+ def id
0
+ @result[:id]
0
+ end
0
+
0
+ # result.type is reserved in ruby. Call result.fb_type to access :type instead.
0
+ def fb_type
0
+ @result[:type]
0
+ end
0
+
0
+ # returns the first element of an array if it is one
0
+ # this for handling generic mql queries like [{}] that return only a single value
0
+ def depluralize(v)
0
+ Array(v).first
0
+ end
0
+
0
+ # converts a returned value from freebase into the corresponding ruby object
0
+ # This is done first by the core data type and then by the type attribute for an object
0
+ # The casing is done using a method dispatch pattern which
0
+ # should make it easy to mix-in new behaviors and type support
0
+ def resultify(v)
0
+ resultify_method = "resultify_#{v.class.to_s.downcase}".to_sym
0
+ v = send(resultify_method, v) if respond_to? resultify_method
0
+ return v
0
+ end
0
+
0
+ # resultifies each value in the array
0
+ def resultify_array(v)
0
+ v.map{|vv| resultify(vv)}
0
+ end
0
+
0
+ # resultifies an object hash
0
+ def resultify_hash(v)
0
+ vtype = indifferent_access(v,:type)
0
+ if value_type? vtype
0
+ resultify_value(vtype,v)
0
+ elsif vtype.blank?
0
+ Logger.debug "What's This: #{v.inspect}"
0
+ FreebaseResult.new(v)
0
+ elsif vtype.is_a? Array
0
+ "Freebase::Types#{vtype.first.classify}".constantize.new(v) #TODO: Union these types
0
+ else
0
+ "Freebase::Types#{vtype.classify}".constantize.new(v)
0
+ end
0
+ end
0
+
0
+ #decides if a type is just an expanded simple value object
0
+ def value_type?(t)
0
+ ['/type/text', '/type/datetime'].include?(t)
0
+ end
0
+
0
+ # dispatches to a value method for the type
0
+ # or returns the simple value if it doesn't exist
0
+ # for example /type/text would dispatch to resultify_value_type_text
0
+ def resultify_value(vtype,v)
0
+ resultify_method = "resultify_value#{vtype.gsub(/\//,'_')}".to_sym
0
+ if respond_to? resultify_method
0
+ send(resultify_method, v)
0
+ else
0
+ indifferent_access(v,:value)
0
+ end
0
+ end
0
+
0
+ #provides method based access to the result properties
0
+ def method_missing(name, *args)
0
+ raise NoMethodError.new(name.to_s) unless args.length == 0
0
+ if @result.has_key?(name)
0
+ resultify @result[name]
0
+ elsif @result.has_key?((singularized_name = name.to_s.singularize.to_sym)) and @result[singularized_name].is_a?(Array)
0
+ resultify @result[singularized_name]
0
+ else
0
+ raise NoMethodError.new(name.to_s)
0
+ end
0
+ end
0
+
0
+ protected
0
+ def indifferent_access(h,k)
0
+ h[k] || h[k.to_s] if (h.has_key?(k) || h.has_key?(k.to_s))
0
+ end
0
+
0
+ end
0
+
0
+ # the configuration class controls access to the freebase.yml configuration file.
0
+ # it will load the rails-environment specific configuration
0
+ class Configuration
0
+
0
+ include Singleton
0
+
0
+ attr_accessor :filename
0
+
0
+ DEFAULTS = {:host => 'sandbox.freebase.com', :debug => true, :trace => false}
0
+
0
+ def initialize
0
+ @configuration = {}.reverse_merge!(DEFAULTS)
0
+ configure_rails if defined?(RAILS_ROOT)
0
+ end
0
+
0
+ def configure_rails
0
+ @filename = "#{RAILS_ROOT}/config/freebase.yml"
0
+ unless File.exists? @filename
0
+ puts "WARNING: #{RAILS_ROOT}/config/freebase.yml configuration file is not found. Using sandbox.freebase.com."
0
+ else
0
+ set_all YAML.load_file(@filename)[RAILS_ENV].symbolize_keys!
0
+ end
0
+ end
0
+
0
+ def set_all(opts = {})
0
+ opts.each {|k,v| self[k] = v}
0
+ end
0
+
0
+ def []=(k,v)
0
+ @configuration[k] = v
0
+ end
0
+
0
+ def [](k)
0
+ @configuration[k]
0
+ end
0
+ end
0
+
0
+ # logging service. Is it a bad idea?
0
+ class Logger
0
+ #TODO: log4r or rails logging?
0
+ [:trace, :debug, :warn, :error].each do |level|
0
+ eval %Q{
0
+ def self.#{level}(message = nil)
0
+ if Configuration.instance[:#{level}]
0
+ puts message || yield
0
+ end
0
+ end
0
+ }
0
+ end
0
+ end
0
+
0
+ SERVICES = { :mqlread => '/api/service/mqlread',
0
+ :mqlwrite => '/api/service/mqlwrite',
0
+ :login => '/api/account/login',
0
+ :upload => '/api/service/upload'
0
+ }
0
+
0
+ # get the service url for the specified service.
0
+ def service_url(svc)
0
+ "http://#{Configuration.instance[:host]}#{SERVICES[svc]}"
0
+ end
0
+
0
+ SERVICES.each_key do |k|
0
+ define_method("#{k}_service_url") do
0
+ service_url(k)
0
+ end
0
+ end
0
+
0
+ # raise an error if the inner response envelope is encoded as an error
0
+ def handle_read_error(inner)
0
+ unless inner['code'].starts_with?('/api/status/ok')
0
+ Logger.error "<<< Received Error: #{inner.inspect}"
0
+ error = inner['messages'][0]
0
+ raise MqlReadError.new(error['code'], error['message'])
0
+ end
0
+ end
0
+
0
+
0
+
0
+ # perform a mqlread and return the results
0
+ # Specify :raw => true if you don't want the results converted into a FreebaseResult object.
0
+ def mqlread(query, options = {})
0
+ Logger.trace {">>> Sending Query: #{query.inspect}"}
0
+ envelope = { :qname => {:query => query }}
0
+ response = http_request mqlread_service_url, :queries => envelope.to_json
0
+ result = ActiveSupport::JSON.decode(response)
0
+ inner = result['qname']
0
+ handle_read_error(inner)
0
+ Logger.trace {"<<< Received Response: #{inner['result'].inspect}"}
0
+ if options[:raw]
0
+ inner['result']
0
+ else
0
+ inner['result'] ? FreebaseResult.new(inner['result']) : nil
0
+ end
0
+ end
0
+
0
+ protected
0
+ def params_to_string(parameters)
0
+ parameters.keys.map {|k| "#{URI.encode(k.to_s)}=#{URI.encode(parameters[k])}" }.join('&')
0
+ end
0
+ def http_request(url, parameters = {})
0
+ params = params_to_string(parameters)
0
+ url << '?'+params unless params.blank?
0
+ returning(Net::HTTP.get_response(URI.parse(url)).body) do |response|
0
+ Logger.trace do
0
+ fname = "#{MD5.md5(params)}.mql"
0
+ open(fname,"w") do |f|
0
+ f << response
0
+ end
0
+ "Wrote response to #{fname}"
0
+ end
0
+ end
0
+ end
0
+end
0
\ No newline at end of file
...
 
...
1
0
@@ -0,0 +1 @@
0
+# Logfile created on Sun Feb 03 09:14:23 -0800 2008 by /
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
0
@@ -0,0 +1,55 @@
0
+{
0
+ "status": "200 OK",
0
+ "code": "/api/status/ok",
0
+ "qname": {
0
+ "code": "/api/status/ok",
0
+ "result": {
0
+ "type": "/type/type",
0
+ "id": "/type/property",
0
+ "properties": [
0
+ {
0
+ "expected_type": "/type/type",
0
+ "type": "/type/property",
0
+ "id": "/type/property/expected_type",
0
+ "name": "expected_type"
0
+ },
0
+ {
0
+ "expected_type": "/type/property",
0
+ "type": "/type/property",
0
+ "id": "/type/property/master_property",
0
+ "name": "master_property"
0
+ },
0
+ {
0
+ "expected_type": "/type/boolean",
0
+ "type": "/type/property",
0
+ "id": "/type/property/unique",
0
+ "name": "unique"
0
+ },
0
+ {
0
+ "expected_type": "/type/property",
0
+ "type": "/type/property",
0
+ "id": "/type/property/reverse_property",
0
+ "name": "reverse_property"
0
+ },
0
+ {
0
+ "expected_type": "/type/type",
0
+ "type": "/type/property",
0
+ "id": "/type/property/schema",
0
+ "name": "schema"
0
+ },
0
+ {
0
+ "expected_type": "/type/unit",
0
+ "type": "/type/property",
0
+ "id": "/type/property/unit",
0
+ "name": "Unit"
0
+ },
0
+ {
0
+ "expected_type": "/type/namespace",
0
+ "type": "/type/property",
0
+ "id": "/type/property/enumeration",
0
+ "name": "enumeration"
0
+ }
0
+ ]
0
+ }
0
+ }
0
+}
0
\ No newline at end of file
...