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 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: