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 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-lib (TODO),
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:: GPL
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 api_key from {Factual Developer Tools}[http://www.factual.com/developers/api_key]
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 = opts[:version] || 2
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 inputting the table_key
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
- f['field_ref'] = @schema["fieldRefs"][fid.to_s]
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"].first
132
+ row_data = resp["data", 0]
130
133
 
131
- if row_data
132
- return Row.new(self, row_data)
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
- row = Row.new(self, row_data)
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
- # Samples:
157
- # api.get_table('g9R1u2').add_row('NE') # add row with single subject
158
- # api.get_table('g9R1u2').add_row('NE', :state => 'Nebraska') # add single subject with fact values
159
- # api.get_table('EZ21ij').add_row(:last_name => 'Newguy') # if the table use UUID as their subject, the subject fields are not required
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
- # a Factual::Row object
163
- def add_row(*params)
164
- fact_values = params.last.is_a?(Hash) ? params.pop : {}
165
- subject_values = params
166
- puts subject_values.inspect
167
- puts fact_values.inspect
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 = row_data[0]
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[:comment]</tt> the comment of an input
263
+ # * <tt>opts[:comments]</tt> the comments of an input
243
264
  #
244
265
  # Sample:
245
- # fact.input('new value', :source => 'http://website.com', :comment => 'because it is new value.'
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
- resp = JSON.parse(json)
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.to_s) }.join('&')
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 class for Factual Api Errors
338
- class ApiError < Exception
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.add_row('NE', :state => 'Nebraska')
63
+ row = @table.input(:two_letter_abbrev => 'NE', :state => 'Nebraska')
64
64
  end
65
65
 
66
66
  def test_row
67
- row = @table.find_one
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: 17
4
+ hash: 9
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 0
9
- - 7
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-10-20 00:00:00 +08:00
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/forrestc/ruby-factual
50
+ homepage: http://github.com/Factual/ruby-factual
52
51
  licenses: []
53
52
 
54
53
  post_install_message: