norairrecord 0.2.1 → 0.3.0
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.
- checksums.yaml +4 -4
- data/README.md +4 -1
- data/lib/norairrecord/table.rb +121 -26
- data/lib/norairrecord/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f5fc9903052fc25fe8bdd071a9989ab92561387d0dabc70ad82045a8335eab1
|
4
|
+
data.tar.gz: d445e59398dbb6c10e08ce645ad12f2dd8dd763ca16334f4ca93477ebe8c9d37
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c78431764c880fbf87535717d223b9ce805c7b6a38a45a64dcf649b073c6dae6d4d322a7130a59d5f0e3cef51389887858568f260a83f34c8cca6e66ef1f3d8
|
7
|
+
data.tar.gz: c97892cf8cb38a5ac9103e6e5358c4993e3fcca8f0e5e42a8d68ac9e22daad3d83320dcf9cc8592e46bd26965ff394fac9fbfdce385d341682aa6f6ab73467eb
|
data/README.md
CHANGED
@@ -52,4 +52,7 @@ stuff not in the OG:
|
|
52
52
|
* `Norairrecord::RecordNotFoundError`
|
53
53
|
* never again wonder if an error is because you goofed up an ID or you're getting ratelimited
|
54
54
|
* `where` argument on `has_many` lookups
|
55
|
-
* `Table#first`, `Table#first_where`
|
55
|
+
* `Table#first`, `Table#first_where`
|
56
|
+
* you're not gonna believe it:
|
57
|
+
* `Table.batch_`{update,upsert,create,save}
|
58
|
+
* makes ratelimits much less painful
|
data/lib/norairrecord/table.rb
CHANGED
@@ -2,6 +2,8 @@ require 'rubygems' # For Gem::Version
|
|
2
2
|
|
3
3
|
module Norairrecord
|
4
4
|
class Table
|
5
|
+
BATCH_SIZE = 10
|
6
|
+
|
5
7
|
class << self
|
6
8
|
attr_writer :api_key, :base_key, :table_name
|
7
9
|
|
@@ -64,7 +66,7 @@ module Norairrecord
|
|
64
66
|
def find_many(ids, where: nil, sort: nil)
|
65
67
|
return [] if ids.empty?
|
66
68
|
|
67
|
-
or_args = ids.map { |id| "RECORD_ID() = '#{id}'"}.join(',')
|
69
|
+
or_args = ids.map { |id| "RECORD_ID() = '#{id}'" }.join(',')
|
68
70
|
formula = "OR(#{or_args})"
|
69
71
|
formula = "AND(#{formula},#{where})" if where
|
70
72
|
records(filter: formula, sort:).sort_by { |record| or_args.index(record.id) }
|
@@ -87,7 +89,6 @@ module Norairrecord
|
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
90
|
-
|
91
92
|
def create(fields, options = {})
|
92
93
|
new(fields).tap { |record| record.save(options) }
|
93
94
|
end
|
@@ -97,7 +98,7 @@ module Norairrecord
|
|
97
98
|
clazz = self
|
98
99
|
st = @subtype_mapping[fields[@subtype_column]]
|
99
100
|
raise Norairrecord::UnknownTypeError, "#{fields[@subtype_column]}?????" if @subtype_strict && st.nil?
|
100
|
-
clazz =
|
101
|
+
clazz = Kernel.const_get(st) if st
|
101
102
|
clazz.new(fields, id:, created_at:)
|
102
103
|
else
|
103
104
|
self.new(fields, id: id, created_at: created_at)
|
@@ -125,22 +126,19 @@ module Norairrecord
|
|
125
126
|
parsed_response = client.parse(response.body)
|
126
127
|
|
127
128
|
if response.success?
|
128
|
-
records = parsed_response["records"]
|
129
|
-
records.map! { |record|
|
130
|
-
self.new_with_subtype(record["fields"], id: record["id"], created_at: record["createdTime"])
|
131
|
-
}
|
129
|
+
records = map_new parsed_response["records"]
|
132
130
|
|
133
131
|
if paginate && parsed_response["offset"]
|
134
132
|
records.concat(records(
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
133
|
+
filter: filter,
|
134
|
+
sort: sort,
|
135
|
+
view: view,
|
136
|
+
paginate: paginate,
|
137
|
+
fields: fields,
|
138
|
+
offset: parsed_response["offset"],
|
139
|
+
max_records: max_records,
|
140
|
+
page_size: page_size,
|
141
|
+
))
|
144
142
|
end
|
145
143
|
|
146
144
|
records
|
@@ -161,10 +159,105 @@ module Norairrecord
|
|
161
159
|
records(**options.merge(filter:))
|
162
160
|
end
|
163
161
|
|
164
|
-
|
165
|
-
|
162
|
+
def map_new(arr)
|
163
|
+
arr.map do |record|
|
164
|
+
self.new_with_subtype(record["fields"], id: record["id"], created_at: record["createdTime"])
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def batch_update(recs, options = {})
|
169
|
+
res = []
|
170
|
+
recs.each_slice(BATCH_SIZE) do |chunk|
|
171
|
+
body = {
|
172
|
+
records: chunk.map do |record|
|
173
|
+
{
|
174
|
+
fields: record.update_hash,
|
175
|
+
id: record.id,
|
176
|
+
}
|
177
|
+
end,
|
178
|
+
**options
|
179
|
+
}.to_json
|
180
|
+
|
181
|
+
response = client.connection.patch("v0/#{base_key}/#{client.escape(table_name)}", body, { 'Content-Type' => 'application/json' })
|
182
|
+
parsed_response = client.parse(response.body)
|
183
|
+
if response.success?
|
184
|
+
res.concat(parsed_response["records"])
|
185
|
+
else
|
186
|
+
client.handle_error(response.status, parsed_response)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
map_new res
|
190
|
+
end
|
191
|
+
|
192
|
+
def batch_upsert(recs, merge_fields, options = {}, include_ids: nil, hydrate: false)
|
193
|
+
merge_fields = Array(merge_fields) # allows passing in a single field
|
166
194
|
|
195
|
+
created, updated, records = [], [], []
|
167
196
|
|
197
|
+
recs.each_slice(BATCH_SIZE) do |chunk|
|
198
|
+
body = {
|
199
|
+
records: chunk.map { |rec| { fields: rec.fields, id: (include_ids ? rec.id : nil) }.compact },
|
200
|
+
**options,
|
201
|
+
performUpsert: { fieldsToMergeOn: merge_fields }
|
202
|
+
}.to_json
|
203
|
+
|
204
|
+
response = client.connection.patch("v0/#{base_key}/#{client.escape(table_name)}", body, { 'Content-Type' => 'application/json' })
|
205
|
+
parsed_response = response.success? ? client.parse(response.body) : client.handle_error(response.status, client.parse(response.body))
|
206
|
+
|
207
|
+
if response.success?
|
208
|
+
created.concat(parsed_response.fetch('createdRecords', []))
|
209
|
+
updated.concat(parsed_response.fetch('updatedRecords', []))
|
210
|
+
records.concat(parsed_response.fetch('records', []))
|
211
|
+
else
|
212
|
+
client.handle_error(response.status, parsed_response)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
if hydrate && records.any?
|
217
|
+
record_hash = records.map { |record| [record["id"], self.new_with_subtype(record["fields"], id: record["id"], created_at: record["createdTime"])] }.to_h
|
218
|
+
|
219
|
+
created.map! { |id| record_hash[id] }.compact!
|
220
|
+
updated.map! { |id| record_hash[id] }.compact!
|
221
|
+
records = record_hash.values
|
222
|
+
end
|
223
|
+
|
224
|
+
{ created:, updated:, records: }
|
225
|
+
end
|
226
|
+
|
227
|
+
def batch_create(recs, options = {})
|
228
|
+
records = []
|
229
|
+
recs.each_slice(BATCH_SIZE) do |chunk|
|
230
|
+
body = {
|
231
|
+
records: chunk.map { |record| { fields: record.serializable_fields } },
|
232
|
+
**options
|
233
|
+
}.to_json
|
234
|
+
|
235
|
+
response = client.connection.post("v0/#{base_key}/#{client.escape(table_name)}", body, { 'Content-Type' => 'application/json' })
|
236
|
+
parsed_response = client.parse(response.body)
|
237
|
+
|
238
|
+
if response.success?
|
239
|
+
records.concat(parsed_response["records"])
|
240
|
+
else
|
241
|
+
client.handle_error(response.status, parsed_response)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
map_new records
|
245
|
+
end
|
246
|
+
|
247
|
+
def upsert(fields, merge_fields, options = {})
|
248
|
+
record = batch_upsert([self.new(fields)], merge_fields, options)&.dig(:records, 0)
|
249
|
+
record ? new(record) : nil
|
250
|
+
end
|
251
|
+
|
252
|
+
def batch_save(records)
|
253
|
+
res = []
|
254
|
+
to_be_created, to_be_updated = records.partition &:new_record?
|
255
|
+
res.concat(batch_create(to_be_created))
|
256
|
+
res.concat(batch_update(to_be_updated))
|
257
|
+
end
|
258
|
+
|
259
|
+
alias all records
|
260
|
+
end
|
168
261
|
|
169
262
|
attr_reader :fields, :id, :created_at, :updated_keys
|
170
263
|
|
@@ -201,10 +294,10 @@ module Norairrecord
|
|
201
294
|
fields[key] = value
|
202
295
|
end
|
203
296
|
|
204
|
-
def patch(
|
205
|
-
|
206
|
-
return @fields if
|
207
|
-
@fields.merge!(self.class.update(self.id,
|
297
|
+
def patch(updates = {}, options = {})
|
298
|
+
updates.reject! { |key, value| @fields[key] == value }
|
299
|
+
return @fields if updates.empty? # don't hit AT if we don't have real changes
|
300
|
+
@fields.merge!(self.class.update(self.id, updates, options).reject { |key, _| updated_keys.include?(key) })
|
208
301
|
end
|
209
302
|
|
210
303
|
def create(options = {})
|
@@ -230,11 +323,13 @@ module Norairrecord
|
|
230
323
|
def save(options = {})
|
231
324
|
return create(options) if new_record?
|
232
325
|
return true if @updated_keys.empty?
|
326
|
+
self.fields = self.class.update(self.id, self.update_hash, options)
|
327
|
+
end
|
233
328
|
|
234
|
-
|
329
|
+
def update_hash
|
330
|
+
Hash[@updated_keys.map { |key|
|
235
331
|
[key, fields[key]]
|
236
332
|
}]
|
237
|
-
self.fields = self.class.update(self.id, update_hash, options)
|
238
333
|
end
|
239
334
|
|
240
335
|
def destroy
|
@@ -255,7 +350,7 @@ module Norairrecord
|
|
255
350
|
end
|
256
351
|
|
257
352
|
def comment(text)
|
258
|
-
response = client.connection.post("v0/#{self.class.base_key}/#{client.escape(self.class.table_name)}/#{self.id}/comments", {text:}.to_json, { 'Content-Type' => 'application/json' })
|
353
|
+
response = client.connection.post("v0/#{self.class.base_key}/#{client.escape(self.class.table_name)}/#{self.id}/comments", { text: }.to_json, { 'Content-Type' => 'application/json' })
|
259
354
|
parsed_response = client.parse(response.body)
|
260
355
|
|
261
356
|
if response.success?
|
@@ -273,6 +368,7 @@ module Norairrecord
|
|
273
368
|
self.class == other.class &&
|
274
369
|
serializable_fields == other.serializable_fields
|
275
370
|
end
|
371
|
+
|
276
372
|
alias eql? ==
|
277
373
|
|
278
374
|
def hash
|
@@ -307,7 +403,6 @@ module Norairrecord
|
|
307
403
|
result
|
308
404
|
end
|
309
405
|
|
310
|
-
|
311
406
|
protected
|
312
407
|
|
313
408
|
def fields=(fields)
|
data/lib/norairrecord/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: norairrecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nora
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|