factual-api 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/CHANGELOG.md +3 -0
  2. data/README.md +6 -2
  3. data/lib/factual_api.rb +112 -102
  4. metadata +1 -1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## v0.2
2
+ * refactoring
3
+
1
4
  ## v0.1
2
5
  * initial version
3
6
  ** a table agnostic read api, usage as: api.table(table_alias).rows
data/README.md CHANGED
@@ -51,16 +51,20 @@ This is the Factual supported Ruby driver for [Factual's public API](http://deve
51
51
 
52
52
  # Concordance information of a place
53
53
  FACTUAL_ID = "110ace9f-80a7-47d3-9170-e9317624ebd9"
54
- factual.crosswalk(FACTUAL_ID)
54
+ query = factual.crosswalk(FACTUAL_ID)
55
+ query.rows
55
56
 
56
57
  ## Resolve
57
58
 
58
59
  # Returns resolved entities as Factual::Row objects
59
- factual.resolve("name" => "McDonalds",
60
+ query = factual.resolve("name" => "McDonalds",
60
61
  "address" => "10451 Santa Monica Blvd",
61
62
  "region" => "CA",
62
63
  "postcode" => "90025")
63
64
 
65
+ query.first.resolved # true or false
66
+ query.rows # resolved rows
67
+
64
68
  ## Schema
65
69
 
66
70
  # Returns a Factual::Table object whose fields are an array of Factual::Field objects
data/lib/factual_api.rb CHANGED
@@ -5,11 +5,65 @@ require 'ostruct'
5
5
 
6
6
  module Factual
7
7
  class Api
8
- API_V3_HOST = "http://api.v3.factual.com"
8
+ API_V3_HOST = "http://api.v3.factual.com"
9
9
  DRIVER_VERSION_TAG = "factual-ruby-driver-1.0"
10
+ PARAM_ALIASES = { :search => :q }
10
11
 
12
+ # initializers
13
+ # ----------------
14
+ def initialize(key, secret)
15
+ @access_token = OAuth::AccessToken.new(
16
+ OAuth::Consumer.new(key, secret))
17
+ end
18
+
19
+ # actions
20
+ # ----------------
21
+ def crosswalk(factual_id, format = :object)
22
+ query = Query.new(self, :crosswalk, "places/crosswalk", { :factual_id => factual_id }, format)
23
+
24
+ return query
25
+ end
26
+
27
+ def resolve(values, format = :object)
28
+ query = Query.new(self, :resolve, "places/resolve", { :values => values }, format)
29
+
30
+ return query
31
+ end
32
+
33
+ def table(table_id_or_alias, format = :object)
34
+ query = Query.new(self, :read, "t/#{table_id_or_alias}", Hash.new, format)
35
+
36
+ return query
37
+ end
38
+
39
+ # requesting
40
+ # ----------------
41
+ def request(path, params)
42
+ url = "#{API_V3_HOST}/#{path}?#{query_string(params)}"
43
+ headers = {"X-Factual-Lib" => DRIVER_VERSION_TAG}
44
+
45
+ return @access_token.get(url, headers)
46
+ end
47
+
48
+ private
49
+
50
+ def query_string(params)
51
+ arr = []
52
+ params.each do |param, v|
53
+ param_alias = PARAM_ALIASES[param.to_sym] || param.to_sym
54
+
55
+ v = v.to_json if v.class == Hash
56
+ arr << "#{param_alias}=#{URI.escape(v.to_s)}"
57
+ end
58
+ return arr.join("&")
59
+ end
60
+
61
+ end
62
+
63
+ class Query
64
+
65
+ # helper functions
11
66
  DEFAULT_LIMIT = 20
12
- PARAM_ALIASES = { :search => :q }
13
67
 
14
68
  VALID_PARAMS = {
15
69
  :read => [ :filters, :search, :geo, :sort, :select, :limit, :offset ],
@@ -20,30 +74,24 @@ module Factual
20
74
  :any => [ :include_count ]
21
75
  }
22
76
 
23
- attr_accessor :access_token, :path, :params, :action, :format
77
+ attr_accessor :params
24
78
 
25
79
  # initializers
26
80
  # ----------------
27
- def initialize(key, secret, format = :object)
28
- @access_token = OAuth::AccessToken.new(
29
- OAuth::Consumer.new(key, secret))
30
-
81
+ def initialize(api, action, path, params = nil, format = :object)
82
+ @api = api
83
+ @action = action
84
+ @path = path
85
+ @params = params || Hash.new
31
86
  @format = format
32
- @params = Hash.new
33
87
  end
34
88
 
35
89
  # helper functions
36
90
  # ----------------
37
- def self.clone(api)
38
- new_api = self.new(nil, nil)
39
-
40
- new_api.access_token = api.access_token
41
- new_api.path = api.path
42
- new_api.action = api.action
43
- new_api.format = api.format
44
- new_api.params = api.params.clone
91
+ def clone
92
+ new_query = self.class.new(@api, @action, @path, @params.clone, @format)
45
93
 
46
- return new_api
94
+ return new_query
47
95
  end
48
96
 
49
97
  def set_param(key, value)
@@ -53,20 +101,35 @@ module Factual
53
101
  # attributes, after 'get'
54
102
  # ----------------
55
103
  def first
56
- row_data = response["data"].first
104
+ row_data = read_response["data"].first
57
105
 
58
- if (@format == :json) # or :hash ?
106
+ if (@format == :json) # or :object
59
107
  return row_data
60
108
  else
61
109
  return Row.new(row_data)
62
110
  end
63
111
  end
64
112
 
113
+ def rows
114
+ return read_response["data"] if (@format == :json)
115
+
116
+ return read_response["data"].collect do |row_data|
117
+ Row.new(row_data)
118
+ end
119
+ end
120
+
121
+ def total_count
122
+ read_response["total_row_count"]
123
+ end
124
+
125
+
65
126
  def schema
66
- @path += "/schema"
67
- @action = :schema
127
+ unless @schema_response
128
+ @path += "/schema"
129
+ @schema_response = response(:schema)
130
+ end
68
131
 
69
- view = response["view"]
132
+ view = @schema_response["view"]
70
133
  fields = view["fields"]
71
134
 
72
135
  schema = Table.new(view)
@@ -80,30 +143,20 @@ module Factual
80
143
  end
81
144
 
82
145
  def facets
83
- @path += "/facets"
84
- @action = :facets
85
- columns = response["data"]
86
-
87
- return Facet.new(columns)
88
- end
89
-
90
- def total_count
91
- response["total_row_count"]
92
- end
93
-
94
- def rows
95
- return response["data"] if (@format == :json)
96
-
97
- return response["data"].collect do |row_data|
98
- Row.new(row_data)
146
+ unless @facets_response
147
+ @path += "/facets"
148
+ @facets_response = response(:facets)
99
149
  end
150
+ columns = @facets_response["data"]
151
+
152
+ return Facet.new(columns)
100
153
  end
101
154
 
102
155
  # query builder, returns immutable ojbects
103
156
  # ----------------
104
157
  VALID_PARAMS.values.flatten.uniq.each do |param|
105
158
  define_method(param) do |*args|
106
- api = self.class.clone(self)
159
+ api = self.clone()
107
160
  val = (args.length == 1) ? args.first : args.join(',')
108
161
 
109
162
  api.set_param(param, val)
@@ -115,7 +168,7 @@ module Factual
115
168
  # sugers
116
169
  # ----------------
117
170
  def sort_desc(*args)
118
- api = self.class.clone(self)
171
+ api = self.clone
119
172
  columns = args.collect{ |col|"#{col}:desc" }
120
173
  api.set_param(:sort, columns.join(','))
121
174
 
@@ -130,92 +183,49 @@ module Factual
130
183
  page_num = 1 if page_num < 1
131
184
  offset = (page_num - 1) * limit
132
185
 
133
- api = self.class.clone(self)
186
+ api = self.clone
134
187
  api.set_param(:limit, limit)
135
188
  api.set_param(:offset, offset)
136
189
 
137
190
  return api
138
191
  end
139
192
 
140
- # actions
193
+ # requesting
141
194
  # ----------------
142
- def crosswalk(factual_id)
143
- api = self
195
+ private
144
196
 
145
- api.path = "places/crosswalk"
146
- api.action = :crosswalk
147
- api.params = { :factual_id => factual_id }
197
+ def read_response
198
+ unless @read_response
199
+ # always include count for reads
200
+ @params[:include_count] = true
201
+ @read_response = response(@action || :read)
202
+ end
148
203
 
149
- return api
204
+ return @read_response
150
205
  end
151
206
 
152
- def resolve(values)
153
- api = self
154
-
155
- api.action = :resolve
156
- api.path = "places/resolve"
157
- api.params = { :values => values }
158
-
159
- return api
160
- end
161
-
162
- def table(table_id_or_alias)
163
- api = self
164
- if @response
165
- api = self.class.clone(self)
207
+ def check_params!(action)
208
+ @params.each do |param, val|
209
+ unless (VALID_PARAMS[action] + VALID_PARAMS[:any]).include?(param)
210
+ raise StandardError.new("InvalidArgument #{param} for #{action}")
211
+ end
166
212
  end
167
-
168
- api.path = "t/#{table_id_or_alias}"
169
- api.action = :read
170
-
171
- return api
172
213
  end
173
214
 
174
- private
175
-
176
- # real requesting
177
- # ----------------
178
- def response
179
- @response ||= {}
180
- return @response[@action] if @response[@action]
181
-
182
- # always include count for reads
183
- @params[:include_count] = true unless @action == :schema
215
+ def response(action)
216
+ check_params!(action)
184
217
 
185
- res = request()
218
+ res = @api.request(@path, @params)
186
219
 
187
220
  code = res.code
188
221
  json = res.body
189
222
  payload = JSON.parse(json)
190
223
 
191
224
  if payload["status"] == "ok"
192
- @response[@action] = payload["response"]
225
+ return payload["response"]
193
226
  else
194
227
  raise StandardError.new(payload["message"])
195
228
  end
196
-
197
- return @response[@action]
198
- end
199
-
200
- def request
201
- url = "#{API_V3_HOST}/#{@path}?#{query_string}"
202
- headers = {"X-Factual-Lib" => DRIVER_VERSION_TAG}
203
-
204
- return @access_token.get(url, headers)
205
- end
206
-
207
- def query_string()
208
- arr = []
209
- @params.each do |param, v|
210
- unless (VALID_PARAMS[@action] + VALID_PARAMS[:any]).include?(param)
211
- raise StandardError.new("InvalidArgument #{param} for #{@action}")
212
- end
213
- param_alias = PARAM_ALIASES[param.to_sym] || param.to_sym
214
-
215
- v = v.to_json if v.class == Hash
216
- arr << "#{param_alias}=#{URI.escape(v.to_s)}"
217
- end
218
- return arr.join("&")
219
229
  end
220
230
 
221
231
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: factual-api
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: "0.1"
5
+ version: "0.2"
6
6
  platform: ruby
7
7
  authors:
8
8
  - Forrest Cao