ruby-factual 0.0.7 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +8 -0
- data/lib/factual.rb +90 -35
- data/test/unit/adapter.rb +5 -0
- data/test/unit/table.rb +3 -2
- metadata +6 -7
data/README.md
CHANGED
@@ -27,3 +27,11 @@ A block of code is worth a thousand words.
|
|
27
27
|
> puts "inputted"
|
28
28
|
> end
|
29
29
|
> end
|
30
|
+
>
|
31
|
+
> # add a row
|
32
|
+
> ret = table.input(:state => "Nebraska", :two_letter_abbrev => "NE")
|
33
|
+
>
|
34
|
+
> # get a row object from resp
|
35
|
+
> subject_key = ret["subjectKey"]
|
36
|
+
> row = Factual::Row.new(table, subject_key)
|
37
|
+
> puts row["state"].value
|
data/lib/factual.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# A Ruby Lib for using Facutal API
|
2
2
|
#
|
3
|
-
# For more information, visit http://github.com/factual/ruby-
|
3
|
+
# For more information, visit http://github.com/factual/ruby-factual,
|
4
4
|
# and {Factual Developer Tools}[http://www.factual.com/devtools]
|
5
5
|
#
|
6
6
|
# Author:: Forrest Cao (mailto:forrest@factual.com)
|
7
7
|
# Copyright:: Copyright (c) 2010 {Factual Inc}[http://www.factual.com].
|
8
|
-
# License::
|
8
|
+
# License:: MIT
|
9
9
|
|
10
|
+
require 'rubygems'
|
10
11
|
require 'net/http'
|
11
12
|
require 'json'
|
12
13
|
require 'uri'
|
@@ -15,26 +16,25 @@ module Factual
|
|
15
16
|
# The start point of using Factual API
|
16
17
|
class Api
|
17
18
|
|
18
|
-
# To initialize a Factual::Api, you will have to get an
|
19
|
+
# To initialize a Factual::Api, you will have to get an API Key from {Factual Developer Tools}[http://www.factual.com/developers/api_key]
|
19
20
|
#
|
20
21
|
# Params: opts as a hash
|
21
22
|
# * <tt>opts[:api_key]</tt> required
|
22
23
|
# * <tt>opts[:debug]</tt> optional, default is false. If you set it as true, it will print the Factual Api Call URLs on the screen
|
23
|
-
# * <tt>opts[:version]</tt> optional, default value is 2, just do not change it
|
24
24
|
# * <tt>opts[:domain]</tt> optional, default value is www.factual.com (only configurable by Factual employees)
|
25
25
|
#
|
26
26
|
# Sample:
|
27
27
|
# api = Factual::Api.new(:api_key => MY_API_KEY, :debug => true)
|
28
28
|
def initialize(opts)
|
29
29
|
@api_key = opts[:api_key]
|
30
|
-
@version =
|
30
|
+
@version = 2
|
31
31
|
@domain = opts[:domain] || 'www.factual.com'
|
32
32
|
@debug = opts[:debug]
|
33
33
|
|
34
34
|
@adapter = Adapter.new(@api_key, @version, @domain, @debug)
|
35
35
|
end
|
36
36
|
|
37
|
-
# Get a Factual::Table object by
|
37
|
+
# Get a Factual::Table object by table_key
|
38
38
|
#
|
39
39
|
# Sample:
|
40
40
|
# api.get_table('g9R1u2')
|
@@ -63,9 +63,12 @@ module Factual
|
|
63
63
|
self.send("#{attr}=", @schema[k])
|
64
64
|
end
|
65
65
|
|
66
|
+
@fields_lookup = {}
|
66
67
|
@fields.each do |f|
|
67
68
|
fid = f['id']
|
68
|
-
|
69
|
+
field_ref = @schema["fieldRefs"][fid.to_s]
|
70
|
+
f['field_ref'] = field_ref
|
71
|
+
@fields_lookup[field_ref] = f
|
69
72
|
end
|
70
73
|
end
|
71
74
|
|
@@ -126,10 +129,11 @@ module Factual
|
|
126
129
|
# * <tt>table.filter(:state => 'CA').sort(:city => 1).find_one</tt>
|
127
130
|
def find_one
|
128
131
|
resp = @adapter.read_table(@table_key, @filters, @sorts, 1)
|
129
|
-
row_data = resp["data"]
|
132
|
+
row_data = resp["data", 0]
|
130
133
|
|
131
|
-
if row_data
|
132
|
-
|
134
|
+
if row_data.is_a?(Array)
|
135
|
+
subject_key = row_data.unshift
|
136
|
+
return Row.new(self, subject_key, row_data)
|
133
137
|
else
|
134
138
|
return nil
|
135
139
|
end
|
@@ -148,23 +152,41 @@ module Factual
|
|
148
152
|
rows = resp["data"]
|
149
153
|
|
150
154
|
rows.each do |row_data|
|
151
|
-
|
155
|
+
subject_key = row_data.shift
|
156
|
+
row = Row.new(self, subject_key, row_data)
|
152
157
|
yield(row) if block_given?
|
153
158
|
end
|
154
159
|
end
|
155
160
|
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
161
|
+
# Suggest values for a row, it can be used to create a row, or edit an existing row
|
162
|
+
#
|
163
|
+
# Parameters:
|
164
|
+
# * +values_hash+
|
165
|
+
# * values in hash, field_refs as keys
|
166
|
+
# * +opts+
|
167
|
+
# * <tt>opts[:source]</tt> the source of an input, can be a URL or something else
|
168
|
+
# * <tt>opts[:comments]</tt> the comments of an input
|
160
169
|
#
|
161
170
|
# Returns:
|
162
|
-
#
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
171
|
+
# { "subjectKey" => <subject_key>, "exists" => <true|false> }
|
172
|
+
# subjectKey: a Factual::Row object can be initialized by a subjectKey, e.g. Factual::Row.new(@table, subject_key)
|
173
|
+
# exists: if "exists" is true, it means an existing row is edited, otherwise, a new row added
|
174
|
+
#
|
175
|
+
# Sample:
|
176
|
+
# table.input :two_letter_abbrev => 'NE', :state => 'Nebraska'
|
177
|
+
# table.input({:two_letter_abbrev => 'NE', :state => 'Nebraska'}, {:source => 'http://website.com', :comments => 'cornhusker!'})
|
178
|
+
def input(values_hash, opts={})
|
179
|
+
values = values_hash.collect do |field_ref, value|
|
180
|
+
field = @fields_lookup[field_ref.to_s]
|
181
|
+
raise Factual::ArgumentError.new("Wrong field ref.") unless field
|
182
|
+
|
183
|
+
{ :fieldId => field['id'], :value => value}
|
184
|
+
end
|
185
|
+
|
186
|
+
hash = opts.merge({ :values => values })
|
187
|
+
|
188
|
+
ret = @adapter.input(@table_key, hash)
|
189
|
+
return ret
|
168
190
|
end
|
169
191
|
|
170
192
|
private
|
@@ -181,14 +203,18 @@ module Factual
|
|
181
203
|
class Row
|
182
204
|
attr_reader :subject_key, :subject
|
183
205
|
|
184
|
-
def initialize(table, row_data) # :nodoc:
|
185
|
-
@subject_key =
|
206
|
+
def initialize(table, subject_key, row_data=[]) # :nodoc:
|
207
|
+
@subject_key = subject_key
|
186
208
|
|
187
209
|
@table = table
|
188
210
|
@fields = @table.fields
|
189
211
|
@table_key = @table.key
|
190
212
|
@adapter = @table.adapter
|
191
213
|
|
214
|
+
if row_data.empty? && subject_key
|
215
|
+
row_data = @adapter.read_row(@table_key, subject_key)
|
216
|
+
end
|
217
|
+
|
192
218
|
@subject = []
|
193
219
|
@fields.each_with_index do |f, idx|
|
194
220
|
next unless f["isPrimary"]
|
@@ -210,11 +236,6 @@ module Factual
|
|
210
236
|
def [](field_ref)
|
211
237
|
@facts_hash[field_ref]
|
212
238
|
end
|
213
|
-
|
214
|
-
# TODO
|
215
|
-
def input(values)
|
216
|
-
|
217
|
-
end
|
218
239
|
end
|
219
240
|
|
220
241
|
# This class holds the subject_key, value, field_ref field (field metadata in hash). The input method is for suggesting a new value for the fact.
|
@@ -239,10 +260,10 @@ module Factual
|
|
239
260
|
# Parameters:
|
240
261
|
# * +value+
|
241
262
|
# * <tt>opts[:source]</tt> the source of an input, can be a URL or something else
|
242
|
-
# * <tt>opts[:
|
263
|
+
# * <tt>opts[:comments]</tt> the comments of an input
|
243
264
|
#
|
244
265
|
# Sample:
|
245
|
-
# fact.input('new value', :source => 'http://website.com', :
|
266
|
+
# fact.input('new value', :source => 'http://website.com', :comments => 'because it is new value.'
|
246
267
|
def input(value, opts={})
|
247
268
|
return false if value.nil?
|
248
269
|
|
@@ -268,6 +289,29 @@ module Factual
|
|
268
289
|
end
|
269
290
|
|
270
291
|
|
292
|
+
class Response
|
293
|
+
def initialize(obj)
|
294
|
+
@obj = obj
|
295
|
+
end
|
296
|
+
|
297
|
+
def [](*keys)
|
298
|
+
begin
|
299
|
+
ret = @obj
|
300
|
+
keys.each do |key|
|
301
|
+
ret = ret[key]
|
302
|
+
end
|
303
|
+
|
304
|
+
if ret.is_a?(Hash)
|
305
|
+
return Response.new(ret)
|
306
|
+
else
|
307
|
+
return ret
|
308
|
+
end
|
309
|
+
rescue Exception => e
|
310
|
+
Factual::ResponseError.new("Unexpected API response")
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
271
315
|
class Adapter # :nodoc:
|
272
316
|
CONNECT_TIMEOUT = 30
|
273
317
|
DEFAULT_LIMIT = 20
|
@@ -292,7 +336,8 @@ module Factual
|
|
292
336
|
raise ApiError.new(e.to_s + " when getting " + api_url)
|
293
337
|
end
|
294
338
|
|
295
|
-
|
339
|
+
obj = JSON.parse(json)
|
340
|
+
resp = Factual::Response.new(obj)
|
296
341
|
raise ApiError.new(resp["error"]) if resp["status"] == "error"
|
297
342
|
return resp
|
298
343
|
end
|
@@ -304,6 +349,15 @@ module Factual
|
|
304
349
|
return resp["schema"]
|
305
350
|
end
|
306
351
|
|
352
|
+
def read_row(table_key, subject_key)
|
353
|
+
url = "/tables/#{table_key}/read.jsaml?subject_key=#{subject_key}"
|
354
|
+
resp = api_call(url)
|
355
|
+
|
356
|
+
row_data = resp["response", "data", 0] || []
|
357
|
+
row_data.unshift # remove the subject_key
|
358
|
+
return row_data
|
359
|
+
end
|
360
|
+
|
307
361
|
def read_table(table_key, filters=nil, sorts=nil, page_size=nil, page=nil)
|
308
362
|
limit = page_size.to_i
|
309
363
|
limit = DEFAULT_LIMIT unless limit > 0
|
@@ -325,7 +379,7 @@ module Factual
|
|
325
379
|
end
|
326
380
|
|
327
381
|
def input(table_key, params)
|
328
|
-
query_string = params.to_a.collect{ |k,v| URI.escape(k.to_s) + '=' + URI.escape(v.
|
382
|
+
query_string = params.to_a.collect{ |k,v| URI.escape(k.to_s) + '=' + URI.escape(v.to_json) }.join('&')
|
329
383
|
|
330
384
|
url = "/tables/#{table_key}/input.js?" + query_string
|
331
385
|
resp = api_call(url)
|
@@ -334,7 +388,8 @@ module Factual
|
|
334
388
|
end
|
335
389
|
end
|
336
390
|
|
337
|
-
# Exception
|
338
|
-
class ApiError <
|
339
|
-
end
|
391
|
+
# Exception classes for Factual Errors
|
392
|
+
class ApiError < StandardError; end
|
393
|
+
class ArgumentError < StandardError; end
|
394
|
+
class ResponseError < StandardError; end
|
340
395
|
end
|
data/test/unit/adapter.rb
CHANGED
@@ -6,6 +6,11 @@ class AdapterTest < Factual::TestCase # :nodoc:
|
|
6
6
|
@adapter = Factual::Adapter.new(API_KEY, API_VERSION, API_DOMAIN, DEBUG_MODE)
|
7
7
|
end
|
8
8
|
|
9
|
+
def test_read_row
|
10
|
+
row_data = @adapter.read_row(TABLE_KEY, SUBJECT_KEY)
|
11
|
+
assert_not_nil row_data
|
12
|
+
end
|
13
|
+
|
9
14
|
def test_corret_request
|
10
15
|
url = "/tables/#{TABLE_KEY}/schema.json"
|
11
16
|
|
data/test/unit/table.rb
CHANGED
@@ -60,11 +60,12 @@ class TableTest < Factual::TestCase # :nodoc:
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def test_adding_row
|
63
|
-
row = @table.
|
63
|
+
row = @table.input(:two_letter_abbrev => 'NE', :state => 'Nebraska')
|
64
64
|
end
|
65
65
|
|
66
66
|
def test_row
|
67
|
-
row = @table
|
67
|
+
row = Factual::Row.new(@table, SUBJECT_KEY)
|
68
|
+
assert_equal row["state"].value, "California"
|
68
69
|
end
|
69
70
|
|
70
71
|
def test_fact
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-factual
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 9
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
|
10
|
-
version: 0.0.7
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Forrest Cao
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-
|
17
|
+
date: 2010-11-17 00:00:00 -08:00
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
@@ -34,7 +33,7 @@ dependencies:
|
|
34
33
|
version: 1.2.0
|
35
34
|
type: :runtime
|
36
35
|
version_requirements: *id001
|
37
|
-
description:
|
36
|
+
description: Ruby gem to access Factual API
|
38
37
|
email: forrest@factual.com
|
39
38
|
executables: []
|
40
39
|
|
@@ -48,7 +47,7 @@ files:
|
|
48
47
|
- test/unit/adapter.rb
|
49
48
|
- test/unit/table.rb
|
50
49
|
has_rdoc: true
|
51
|
-
homepage: http://github.com/
|
50
|
+
homepage: http://github.com/Factual/ruby-factual
|
52
51
|
licenses: []
|
53
52
|
|
54
53
|
post_install_message:
|