Database and data migrations¶
Each ActiveRecord model must have a database table. Database tables are created and modified by migrations.
A migration is a Ruby class that inherits from the current Rails migration version. Schema migrations live in the db/migrate directory of your plugin (for example plugins/easyproject/easy_plugins/modification_acme/db/migrate). Use ActiveRecord::Migration[8.1]. After a Rails upgrade, use the version that rails g migration generates.
Name the class Action + ModelClassName, for example CreateMyModel or AddMyColumnToMyModel. Use a single change method; the up / down format appears only in legacy code.
Filename timestamp format¶
Migration filenames follow this convention:
The timestamp is the current UTC time, generated with Time.now.utc.strftime("%Y%m%d%H%M%S") (14 digits). Do not use placeholder timestamps such as 20210101000000.
Example breakdown for 20240502101659:
| Part | Value | Meaning |
|---|---|---|
2024 |
YYYY | Year |
05 |
MM | Month |
02 |
DD | Day |
10 |
HH | Hour (24h, UTC) |
16 |
MM | Minute |
59 |
SS | Second |
Example filename:
Creating a new migration¶
Use the Rails generator:
The generator writes db/migrate/20240502101659_create_my_model.rb in the application root. Move the file to the db/migrate directory of your plugin. The generated content:
class CreateMyModel < ActiveRecord::Migration[8.1]
def change
create_table :my_models do |t|
t.belongs_to :author, null: false
t.belongs_to :collection, null: true
t.string :name, null: false
t.text :description, null: false
t.integer :status, null: false, index: true
t.timestamps
end
end
end
Indexing¶
Columns used for search should be indexed. Define indexes inline:
or explicitly with add_index:
Currency fields¶
Register money columns in the model with acts_as_easy_currency. Do not create currency shadow columns such as price_EUR in a migration; the Easy8 install creates them (easyproject:currency_update_tables).
Using an existing table¶
To reuse an existing table, set self.table_name in the model.
Changing an existing table¶
Never alter a migration after it has been executed. Always create a new migration for any change. Example:
Generated migration:
class RemoveDescriptionFromMyModels < ActiveRecord::Migration[8.1]
def change
remove_column :my_models, :description, :text
end
end
Before release¶
Before the first release of a new model, merge all migrations created during development into a single migration that creates the table with all columns.
While the model changes frequently, use force: true on create_table to drop and recreate the table on each run:
To re-run such a migration in an already migrated environment, raise its timestamp by one second, for example 20240502101659_create_my_model.rb to 20240502101700_create_my_model.rb.
Data migration¶
Data migrations change existing data, for example to prepare data before the first release or after a significant change. They live in the db/data directory of your plugin and use the same filename convention as schema migrations.
A data migration inherits from EasyExtensions::EasyDataMigration and implements up and down:
class DataChangeInDb < EasyExtensions::EasyDataMigration
def up
# data change implementation
end
def down
# rollback implementation
end
end
Bulk changes of enabled modules¶
Project.project_ids_without_module is cached across requests (EasyProjectIdsCache) and
kept fresh by the EnabledModule and Project callbacks. A data migration or rake task that
changes enabled_modules rows without callbacks (update_all, delete_all, insert_all, raw SQL)
must end with Project.invalidate_project_ids_caches, otherwise permission conditions can use the
stale list until the next process boot (every boot rotates the cache as a safety net).
Running migrations¶
db:migrate alone does not run plugin migrations. Run the Easy8 install task, which migrates the core, engines and plugins and runs the data migrations: