The Power of Rails Migration Generator
Published: 2023-01-21 21:22:10

As a beginner to Rails, I was impressed by the power of Rails migration generator.  Rails generators enable developers to utilize the Command Line Use interface to create an enormous amount of code in a streamlined manner.  

If you run "rails g" at the command prompt, you get the following output:

```console

General options:
  -h, [--help]     # Print generator's options and usage
  -p, [--pretend]  # Run but do not make any changes
  -f, [--force]    # Overwrite files that already exist
  -s, [--skip]     # Skip files that already exist
  -q, [--quiet]    # Suppress status output

Please choose a generator below.

Rails:
  application_record
  benchmark
  channel
  controller
  generator
  integration_test
  job
  mailbox
  mailer
  migration
  model
  resource
  scaffold
  scaffold_controller
  serializer
  system_test
  task

```

We will be discussing the migration generator.  The migration generator allows the developer to define a new table or make a change to an existing table.  For example, let's create a teams table with columns name and city.  We can run the following generator at the CLI prompt.

rails g migration CreateTeams name:string city:string

The output from running this generator is the following.

```console
(base) craigjford@Craigs-iMac test-app % rails g migration CreateTeams name:string city:string
      invoke  active_record
      create    db/migrate/20230121200306_create_teams.rb
```

Rails will generate the following migration.

```console
class CreateTeams < ActiveRecord::Migration[7.0]
  def change
    create_table :teams do |t|
      t.string :name
      t.string :city

      t.timestamps
    end
  end
end
``` 

We will now run "Rails db:migrate" from the CLI and recive the following input. 


```console
== 20230121200306 CreateTeams: migrating ======================================
-- create_table(:teams)
   -> 0.0014s
== 20230121200306 CreateTeams: migrated (0.0014s) =============================

```

Since this is our first table, a new schema file will be created after we run the "Rails db:migrate" command.

```console
ActiveRecord::Schema[7.0].define(version: 2023_01_21_200306) do
  create_table "teams", force: :cascade do |t|
    t.string "name"
    t.string "city"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end
```

Suppose we forgot to include a type field to the table.  We
can simply create an additional migration as follows.

rails g migration AddTypeToTeams

This will create another migration file as seen below.

```console
class AddTypeToTeams < ActiveRecord::Migration[7.0]
  def change
    add_column :teams, :type, :string  #we need to add this
  end
end
``` 

If we run db:migrate once more, the schema will look as follows.

```console
ActiveRecord::Schema[7.0].define(version: 2023_01_21_203133) do
  create_table "teams", force: :cascade do |t|
    t.string "name"
    t.string "city"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "type"
  end

end
```

Suppose we now want to add a Players table and that we want the relationships that a teams has many players and a players belongs to a team.  We can utilize the migration generator in the following way.

```console
rails g migration CreatePlayers team:belongs_to player_team:string
```

Rails will provide the following output.

```console
(base) craigjford@Craigs-iMac test-app % rails g migration CreatePlayers team:belongs_to player_team:string
      invoke  active_record
      create    db/migrate/20230121205723_create_players.rb
```

Rails migration generator creates the following output.

```console
(base) craigjford@Craigs-iMac test-app % rails g migration CreatePlayers team:belongs_to player_team:string
      invoke  active_record
      create    db/migrate/20230121205723_create_players.rb

```

The migration generator creates the following migration file.

```console
class CreatePlayers < ActiveRecord::Migration[7.0]
  def change
    create_table :players do |t|
      t.belongs_to :team, null: false, foreign_key: true
      t.string :player_team

      t.timestamps
    end
  end
end
```

Running "rails db:migrate" now updates the schema to look like the following.

```console
ActiveRecord::Schema[7.0].define(version: 2023_01_21_205723) do
  create_table "players", force: :cascade do |t|
    t.integer "team_id", null: false
    t.string "player_team"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["team_id"], name: "index_players_on_team_id"
  end

  create_table "teams", force: :cascade do |t|
    t.string "name"
    t.string "city"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "type"
  end

  add_foreign_key "players", "teams"
end

```

As you can see, in a short period of time we have created three migration files.  We have run the db:migrates and have created a working schema file that has a one-to-many relationship.


This post really just skims the surface of what you can do with the rails migration generator.  If we run the following command "rails g migration --help", we can see all the other options available with rails g migration.

```console
Usage:
  rails generate migration NAME [field[:type][:index] field[:type][:index]] [options]

Options:
      [--skip-namespace], [--no-skip-namespace]              # Skip namespace (affects only isolated engines)
      [--skip-collision-check], [--no-skip-collision-check]  # Skip collision check
  -o, --orm=NAME                                             # ORM to be invoked
                                                             # Default: active_record

ActiveRecord options:
      [--timestamps], [--no-timestamps]      # Indicates when to generate timestamps
                                             # Default: true
      [--primary-key-type=PRIMARY_KEY_TYPE]  # The type for primary key
  --db, [--database=DATABASE]                # The database for your migration. By default, the current environment's primary database is used.

Runtime options:
  -f, [--force]                    # Overwrite files that already exist
  -p, [--pretend], [--no-pretend]  # Run but do not make any changes
  -q, [--quiet], [--no-quiet]      # Suppress status output
  -s, [--skip], [--no-skip]        # Skip files that already exist

Description:
    Generates a new database migration. Pass the migration name, either
    CamelCased or under_scored, and an optional list of attribute pairs as arguments.

    A migration class is generated in db/migrate prefixed by a timestamp of the current date and time.

    You can name your migration in either of these formats to generate add/remove
    column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable

Example:
    `bin/rails generate migration AddSslFlag`

    If the current date is May 14, 2008 and the current time 09:09:12, this creates the AddSslFlag migration
    db/migrate/20080514090912_add_ssl_flag.rb

    `bin/rails generate migration AddTitleBodyToPost title:string body:text published:boolean`

    This will create the AddTitleBodyToPost in db/migrate/20080514090912_add_title_body_to_post.rb with this in the Change migration:

      add_column :posts, :title, :string
      add_column :posts, :body, :text
      add_column :posts, :published, :boolean

Migration names containing JoinTable will generate join tables for use with
has_and_belongs_to_many associations.

Example:
    `bin/rails g migration CreateMediaJoinTable artists musics:uniq`

    will create the migration

    create_join_table :artists, :musics do |t|
      # t.index [:artist_id, :music_id]
      t.index [:music_id, :artist_id], unique: true
    end

``` 

Happy hacking!