factual-api 0.1 → 0.2

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