Module: Airtable::ORM::Persistence

Extended by:
ActiveSupport::Concern
Included in:
Base
Defined in:
lib/airtable/orm/persistence.rb

Constant Summary collapse

BATCH_SIZE =
10
MAX_FIND_MANY_IDS =
500

Instance Method Summary collapse

Instance Method Details

#apply_response_fields(data) ⇒ Object

Apply response fields from a create, update, or batch operation. Uses changes_applied (not clear_changes_information) so after_save callbacks can inspect previous_changes / saved_change_to_*? methods.



304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/airtable/orm/persistence.rb', line 304

def apply_response_fields(data)
  @previously_new_record = new_record?

  data = data.with_indifferent_access
  symbol_attrs = self.class.fields_to_symbol_attributes(data[:fields])
  symbol_attrs.each { |key, value| write_raw_attribute(key, value) }

  assign_persistence_state(
    id: data[:id],
    created_at: data[:createdTime] ? Time.iso8601(data[:createdTime].to_s) : nil,
    persisted: true
  )
  changes_applied
end

#assign_persistence_state(id:, created_at:, persisted:) ⇒ Object

Assign persistence metadata (id, created_at, persisted flag)



295
296
297
298
299
# File 'lib/airtable/orm/persistence.rb', line 295

def assign_persistence_state(id:, created_at:, persisted:)
  @id = id
  @created_at = created_at
  @persisted = persisted
end

#clientObject



319
320
321
# File 'lib/airtable/orm/persistence.rb', line 319

def client
  self.class.client
end

#create_recordObject

Create a new record via API



335
336
337
338
339
340
341
342
# File 'lib/airtable/orm/persistence.rb', line 335

def create_record
  run_callbacks :create do
    body = { fields: fields_for_create }
    response = client.connection.post(self.class.api_path, body)
    handle_persistence_response(response)
    true
  end
end

#destroyObject

Destroy the record



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/airtable/orm/persistence.rb', line 259

def destroy
  raise Airtable::ORM::RecordNotDestroyed, "Cannot destroy a new record" if new_record?

  run_callbacks :destroy do
    response = client.connection.delete(self.class.api_path(id))
    parsed_response = response.body

    if response.success?
      @persisted = false
      @destroyed = true
      freeze
      self
    else
      Airtable::ORM::Http::Client.raise_api_error(response.status, parsed_response)
    end
  end
end

#destroyed?Boolean

Check if this record has been destroyed

Returns:

  • (Boolean)


210
211
212
# File 'lib/airtable/orm/persistence.rb', line 210

def destroyed?
  @destroyed == true
end

#handle_persistence_response(response) ⇒ Object

Handle API response from create/update operations



324
325
326
327
328
329
330
331
332
# File 'lib/airtable/orm/persistence.rb', line 324

def handle_persistence_response(response)
  parsed = response.body

  if response.success?
    apply_response_fields(parsed)
  else
    Airtable::ORM::Http::Client.raise_api_error(response.status, parsed)
  end
end

#initialize(**attributes) ⇒ Object

Initialize a record with symbol attributes Usage: new(email: "test@example.com", first_name: "John")



184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/airtable/orm/persistence.rb', line 184

def initialize(**attributes)
  # Initialize as a new record (not persisted, not destroyed)
  @id = nil
  @created_at = nil
  @persisted = false
  @destroyed = false
  @previously_new_record = false

  # Initialize ActiveModel::Attributes
  super()

  # Set attributes using ActiveModel's assign_attributes
  assign_attributes(attributes) if attributes.present?
end

#new_record?Boolean

Check if this is a new record (not yet saved)

Returns:

  • (Boolean)


200
201
202
# File 'lib/airtable/orm/persistence.rb', line 200

def new_record?
  !@persisted && !@destroyed
end

#persisted?Boolean

Check if this record has been saved

Returns:

  • (Boolean)


205
206
207
# File 'lib/airtable/orm/persistence.rb', line 205

def persisted?
  @persisted == true
end

#previously_new_record?Boolean

Check if this record was new before the last save

Returns:

  • (Boolean)


215
216
217
# File 'lib/airtable/orm/persistence.rb', line 215

def previously_new_record?
  @previously_new_record == true
end

#reloadObject

Reload the record from the API



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/airtable/orm/persistence.rb', line 278

def reload
  raise Airtable::ORM::RecordNotPersisted, "Cannot reload a new record" if new_record?

  fresh_record = self.class.find(id)

  # Copy attributes from fresh record using raw attribute access
  self.class.attribute_types.each_key do |name|
    write_raw_attribute(name.to_sym, fresh_record.read_raw_attribute(name.to_sym))
  end

  @created_at = fresh_record.created_at
  clear_changes_information

  self
end

#save(validate: true) ⇒ Object

Save the record (create or update)



220
221
222
223
224
225
226
227
228
229
230
# File 'lib/airtable/orm/persistence.rb', line 220

def save(validate: true)
  return false if validate && invalid?

  result = run_callbacks(:save) do
    new_record? ? create_record : update_record
  end

  result != false
rescue Airtable::ORM::ApiError
  false
end

#save!(validate: true) ⇒ Object

Save the record, raising an error if it fails. Unlike save, lets ApiError propagate with full error details.



234
235
236
237
238
239
240
241
242
243
244
# File 'lib/airtable/orm/persistence.rb', line 234

def save!(validate: true)
  raise Airtable::ORM::RecordInvalid, self if validate && invalid?

  result = run_callbacks(:save) do
    new_record? ? create_record : update_record
  end

  raise Airtable::ORM::RecordNotSaved, "Failed to save the record" if result == false

  true
end

#update(attributes) ⇒ Object

Update attributes and save



247
248
249
250
# File 'lib/airtable/orm/persistence.rb', line 247

def update(attributes)
  assign_attributes(attributes)
  save
end

#update!(attributes) ⇒ Object

Update attributes and save, raising an error if it fails



253
254
255
256
# File 'lib/airtable/orm/persistence.rb', line 253

def update!(attributes)
  assign_attributes(attributes)
  save!
end

#update_recordObject

Update only changed fields via PATCH. Fields changed TO nil are sent (to clear them in Airtable); unchanged nil fields are not sent.



346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/airtable/orm/persistence.rb', line 346

def update_record
  unless changed?
    clear_changes_information
    return true
  end

  run_callbacks :update do
    body = { fields: fields_for_update }
    response = client.connection.patch(self.class.api_path(id), body)
    handle_persistence_response(response)
    true
  end
end