ruby-factual 0.0.7 → 0.1
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/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:
|