airtable-orm 0.2.0 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/lib/airtable/orm/persistence.rb +20 -15
- data/lib/airtable/orm/schema.rb +4 -7
- data/lib/airtable/orm/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc3056cbf98026ea8a89b01d99c632ad01da304d85841544552b44565f897793
|
|
4
|
+
data.tar.gz: cf839f12c5812c4834604dc46c716fdb671c47110d94829fa863d82f5d56e489
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dfcd829b2a0df28d151770f30c0f51e98680bc6ab0c6f8fd5f0ff9fd2fd6ffefdef4f3c30fbe7dbcb3f3afa08689d6dffb6fc234d28a4403c3ec56f004f0abfa
|
|
7
|
+
data.tar.gz: 6c31d239c24e6c86e6303cc5eb7fdd34a5916c914c27999625c6b0c945178f9d7def6386855ba77cc13493054d14dc8abf1f087ca27474b8ff6a21f1e34f2e57
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.1] - 2026-07-15
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- The schema cache stores the full base schema instead of only the tables configured at fetch
|
|
8
|
+
time — a table added to `config.tables` after the cache warmed up (24 h expiry, shared store
|
|
9
|
+
in Rails hosts) now resolves immediately instead of raising `ConfigurationError` until expiry.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Record payloads are parsed by string keys directly instead of deep-converting every record
|
|
14
|
+
with `with_indifferent_access` — one avoided deep copy per record on `where`/`all`/`find`,
|
|
15
|
+
two per record on batch updates. The (undocumented) `instantiate_from_api_response` and
|
|
16
|
+
`apply_response_fields` now expect string-keyed parsed payloads; `createdTime` parsing lives
|
|
17
|
+
in the single `Persistence.parse_created_time` helper.
|
|
18
|
+
|
|
3
19
|
## [0.2.0] - 2026-07-14
|
|
4
20
|
|
|
5
21
|
Hardening release from a deep code review of the whole gem. Minor (not patch) because a few
|
|
@@ -111,21 +111,28 @@ module Airtable
|
|
|
111
111
|
record
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
-
# Instantiate a record from API
|
|
114
|
+
# Instantiate a record from a parsed API payload. Expects string keys (the shape the
|
|
115
|
+
# JSON middleware produces) — this runs once per record on every where/all/find, so no
|
|
116
|
+
# per-record deep conversion here.
|
|
115
117
|
def instantiate_from_api_response(response)
|
|
116
|
-
|
|
117
|
-
symbol_attrs = fields_to_symbol_attributes(data[:fields])
|
|
118
|
-
|
|
119
|
-
record = new(**symbol_attrs)
|
|
118
|
+
record = new(**fields_to_symbol_attributes(response["fields"]))
|
|
120
119
|
record.send(:assign_persistence_state,
|
|
121
|
-
id:
|
|
122
|
-
created_at:
|
|
120
|
+
id: response["id"],
|
|
121
|
+
created_at: parse_created_time(response),
|
|
123
122
|
persisted: true)
|
|
124
123
|
record.clear_changes_information
|
|
125
124
|
|
|
126
125
|
record
|
|
127
126
|
end
|
|
128
127
|
|
|
128
|
+
# @api private — createdTime is ISO-8601 UTC, parsed with Time.iso8601 (Time.zone
|
|
129
|
+
# doesn't exist outside Rails); nil-guarded. The single parse site for both
|
|
130
|
+
# instantiate_from_api_response and #apply_response_fields.
|
|
131
|
+
def parse_created_time(data)
|
|
132
|
+
created_time = data["createdTime"]
|
|
133
|
+
created_time ? Time.iso8601(created_time.to_s) : nil
|
|
134
|
+
end
|
|
135
|
+
|
|
129
136
|
# Convert API field IDs to symbol attributes.
|
|
130
137
|
# field_mapping already contains only declared attributes.
|
|
131
138
|
def fields_to_symbol_attributes(fields)
|
|
@@ -148,9 +155,8 @@ module Airtable
|
|
|
148
155
|
parsed = response.body
|
|
149
156
|
|
|
150
157
|
if response.success?
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
response_by_id = response_records.index_by { |r| r[:id] }
|
|
158
|
+
response_records = (parsed.is_a?(Hash) && parsed["records"]) || []
|
|
159
|
+
response_by_id = response_records.index_by { |r| r["id"] }
|
|
154
160
|
|
|
155
161
|
batch.each do |record|
|
|
156
162
|
record_data = response_by_id[record.id]
|
|
@@ -163,7 +169,7 @@ module Airtable
|
|
|
163
169
|
end
|
|
164
170
|
else
|
|
165
171
|
batch.each { |record| result.failed << record }
|
|
166
|
-
error_message = parsed.is_a?(Hash) ? parsed.
|
|
172
|
+
error_message = parsed.is_a?(Hash) ? parsed.dig("error", "message") : parsed
|
|
167
173
|
ORM.config.logger.error(
|
|
168
174
|
"Airtable batch update failed: HTTP #{response.status}: #{error_message.to_s.truncate(200)}"
|
|
169
175
|
)
|
|
@@ -312,13 +318,12 @@ module Airtable
|
|
|
312
318
|
def apply_response_fields(data)
|
|
313
319
|
@previously_new_record = new_record?
|
|
314
320
|
|
|
315
|
-
|
|
316
|
-
symbol_attrs = self.class.fields_to_symbol_attributes(data[:fields])
|
|
321
|
+
symbol_attrs = self.class.fields_to_symbol_attributes(data["fields"])
|
|
317
322
|
symbol_attrs.each { |key, value| write_raw_attribute(key, value) }
|
|
318
323
|
|
|
319
324
|
assign_persistence_state(
|
|
320
|
-
id: data[
|
|
321
|
-
created_at:
|
|
325
|
+
id: data["id"],
|
|
326
|
+
created_at: self.class.parse_created_time(data),
|
|
322
327
|
persisted: true
|
|
323
328
|
)
|
|
324
329
|
changes_applied
|
data/lib/airtable/orm/schema.rb
CHANGED
|
@@ -19,10 +19,6 @@ module Airtable
|
|
|
19
19
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
|
-
def required_table_ids
|
|
23
|
-
ORM.config.table_ids
|
|
24
|
-
end
|
|
25
|
-
|
|
26
22
|
def fetch_from_api(base_id)
|
|
27
23
|
response = client.connection.get("/v0/meta/bases/#{base_id}/tables")
|
|
28
24
|
parsed_response = response.body
|
|
@@ -34,12 +30,13 @@ module Airtable
|
|
|
34
30
|
end
|
|
35
31
|
end
|
|
36
32
|
|
|
33
|
+
# Index the FULL base schema — no filtering by the tables configured at fetch time.
|
|
34
|
+
# The cached payload outlives the config (24h, shared store in Rails hosts), so a
|
|
35
|
+
# fetch-time filter would hide tables added to config after the cache warmed up.
|
|
36
|
+
# Reads are keyed lookups (fetch(base_id)[table_id]), so no read-time filter is needed.
|
|
37
37
|
def indexed_schema(parsed_response)
|
|
38
38
|
tables = parsed_response.deep_symbolize_keys[:tables]
|
|
39
39
|
|
|
40
|
-
# Filter only required tables
|
|
41
|
-
tables.select! { |table| required_table_ids.include?(table[:id]) }
|
|
42
|
-
|
|
43
40
|
# Transform fields into hash indexed by field id
|
|
44
41
|
tables.each do |table|
|
|
45
42
|
table[:fields] = table[:fields].index_by { |field| field[:id] }
|
data/lib/airtable/orm/version.rb
CHANGED