-
Notifications
You must be signed in to change notification settings - Fork 0
Writing a Custom Adapter
Planter uses adapters to keep seeding logic separate from the persistence layer. Active Record is the default adapter, but custom adapters can seed through another ORM, raw SQL, an API client, or any other storage system.
A custom adapter is a plain Ruby object. It only needs to implement the adapter methods Planter calls.
You can generate a starting point with:
rails generate planter:adapter sequelThis creates an adapter file and updates config/initializers/planter.rb to use it.
Custom adapters must implement:
class MyAdapter
def create_record(context:, lookup_attributes:, create_attributes:)
end
def parent_ids(context:)
end
def foreign_key(context:)
end
def table_columns(context:)
end
def table_names
end
endEach adapter method receives a Planter::SeedContext object. This is a small parameter object with normalized seeder configuration.
Available readers:
context.table_name
context.seed_method
context.csv_name
context.parent
context.number_of_records
context.unique_columns
context.erb_trim_modeFor example, a UsersSeeder usually has:
context.table_name # => "users"If a seeder name does not match the table, users can configure it:
class PeopleSeeder < Planter::Seeder
seeding_method :csv, table: :users
enddef create_record(context:, lookup_attributes:, create_attributes:)
endCreate a record in context.table_name unless one already exists.
lookup_attributes are used to find an existing record. create_attributes are used only when creating a new record.
def parent_ids(context:)
endReturn parent record IDs for parent-based seeding. This is only used when the seeder has a parent: option.
The meaning of context.parent is adapter-defined. In the Active Record adapter, it is a belongs_to association name.
def foreign_key(context:)
endReturn the field Planter should use to attach a parent ID to each seeded record.
For example, if context.parent == :user, this might return :user_id.
def table_columns(context:)
endReturn native column or field names for context.table_name.
Planter uses this to avoid looking up records by non-column CSV headers. Non-column lookup attributes are moved into create attributes before create_record is called.
def table_names
endReturn table or collection names that can have seeders generated by:
rails generate planter:seeder ALLclass SqlAdapter
def initialize(connection)
@connection = connection
end
def create_record(context:, lookup_attributes:, create_attributes:)
existing = find_record(context.table_name, lookup_attributes)
return existing if existing
insert_record(context.table_name, lookup_attributes.merge(create_attributes))
end
def parent_ids(context:)
@connection.select_values("SELECT id FROM #{context.parent}")
end
def foreign_key(context:)
:"#{context.parent.to_s.singularize}_id"
end
def table_columns(context:)
@connection.columns(context.table_name).map(&:name)
end
def table_names
@connection.tables
end
private
attr_reader :connection
endPlanter.configure do |config|
config.adapter = SqlAdapter.new(MyConnection)
config.seeders = %i[
users
]
endAdapters should not need to know about Planter::Seeder subclasses directly. They should rely on context.table_name and the attributes passed into each method.
The Active Record adapter maps context.table_name to Rails models internally, but custom adapters do not need to use models at all.