Class: Oaken::Stored::ActiveRecord

Inherits:
Object
  • Object
show all
Defined in:
lib/oaken/stored/active_record.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(loader, type) ⇒ ActiveRecord

Returns a new instance of ActiveRecord.



2
3
4
5
# File 'lib/oaken/stored/active_record.rb', line 2

def initialize(loader, type)
  @loader, @type = loader, type
  @attributes = loader.defaults_for(*type.column_names)
end

Instance Attribute Details

#typeObject (readonly)

Returns the value of attribute type.



6
7
8
# File 'lib/oaken/stored/active_record.rb', line 6

def type
  @type
end

Instance Method Details

#attributes_for(**attributes) ⇒ Object

Build attributes used for ‘create`/`upsert`, applying loader and per-type `defaults`.

loader.defaults name: -> { "Global" }, email_address: -> {  }
users.defaults name: -> { Faker::Name.name } # This `name` takes precedence on users.

users.attributes_for(email_address: "[email protected]") # => { name: "Some Faker Name", email_address: "[email protected]" }


38
39
40
# File 'lib/oaken/stored/active_record.rb', line 38

def attributes_for(**attributes)
  @attributes.merge(attributes).transform_values! { _1.respond_to?(:call) ? _1.call : _1 }
end

#create(label = nil, unique_by: nil, **attributes) ⇒ Object

Create a record in the database with the passed ‘attributes`.



11
12
13
14
15
16
17
18
19
20
# File 'lib/oaken/stored/active_record.rb', line 11

def create(label = nil, unique_by: nil, **attributes)
  attributes = attributes_for(**attributes)

  finders  = attributes.slice(*unique_by)
  record   = type.find_by(finders)&.tap { _1.update!(**attributes) } if finders.any?
  record ||= type.create!(**attributes)

  _label label, record.id if label
  record
end

#defaults(**attributes) ⇒ Object

Set defaults for all types:

loader.defaults name: -> { "Global" }, email_address: -> {  }

These defaults are used and evaluated in ‘create`/`upsert`/`attributes_for`, but you can override on a per-type basis:

users.defaults name: -> { Faker::Name.name } # This `name` takes precedence on `users`.
users.create # => Uses the users' default `name` and the loader `email_address`


50
# File 'lib/oaken/stored/active_record.rb', line 50

def defaults(**attributes) = @attributes = @attributes.merge(attributes)

#label(**labels) ⇒ Object

Expose a record instance that’s setup outside of using ‘create`/`upsert`. Like this:

users.label someone: User.create!(name: "Someone")
users.label someone: FactoryBot.create(:user, name: "Someone")

Now ‘users.someone` returns the record instance.

Ruby’s Hash argument forwarding also works:

someone, someone_else = users.create(name: "Someone"), users.create(name: "Someone Else")
users.label someone:, someone_else:

Note: ‘users.method(:someone).source_location` also points back to the file and line of the `label` call.



65
# File 'lib/oaken/stored/active_record.rb', line 65

def label(**labels) = labels.each { |label, record| _label label, record.id }

#upsert(label = nil, unique_by: nil, **attributes) ⇒ Object

Upsert a record in the database with the passed ‘attributes`.



23
24
25
26
27
28
29
30
# File 'lib/oaken/stored/active_record.rb', line 23

def upsert(label = nil, unique_by: nil, **attributes)
  attributes = attributes_for(**attributes)

  type.new(attributes).validate!
  record = type.new(id: type.upsert(attributes, unique_by: unique_by, returning: :id).rows.first.first)
  _label label, record.id if label
  record
end