Modifying Existing Migrations in Ruby

Periodically, you will make mistakes when writing migrations. If you’ve already run a migration, you can’t just edit the migration and run it again: Rails will assume it has already run a migration and will do nothing when you run rails db:migrate. You must rollback the migration (using bin/rails db:rollback, for example), edit the migration, and then run rails db:migrate to run the corrected version.

In general, editing existing migrations is not a good idea. You will create extra work for yourself and your colleagues, and cause a lot of headaches if the existing version of the migration has already been run in production. Instead, you should write a new migration that implements the required changes. Editing a newly generated migration that hasn’t been committed to version control yet (or at least not past your production machine) is relatively harmless.

The revert method can be very useful when writing a new migration to revert a previous migration in whole or in part (see Reverting to previous migrations).

(Schema Dumping and You) Export Schema

What are schema files for?

Migrations, as powerful as they are, are not an authoritative source for your database schema. This role goes to either the db/schema.rb file or the SQL file that Active Record generates when it examines the database. They are not designed for editing, they just reflect the current state of the database.

It is not necessary (which can lead to an error) to deploy a new instance of the application by applying the entire migration history. It is much easier and faster to load a description of the current schema into the database.

For example, how a test database is created: the current working database is unloaded (either to db/schema.rb or db/structure.sql) and then loaded into the test database.

Schema files are also useful if you want to take a quick look at what attributes an Active Record object has. This information is not contained in the model code and is often spread across multiple migrations, but brought together in a schema file. There is an annotate_models gem that automatically adds and updates comments at the beginning of each of the models that make up the schema if you want this functionality.

Types of schema uploads

There are two ways to download a schema. They are set in config/environment.rb in the config.active_record.schema_format property, which can be either :sql or :ruby.

If :ruby is selected, then the schema is stored in db/schema.rb. Looking at this file, you can see that it is very similar to one big migration:

ActiveRecord::Schema.define(version: 20080906171750) do

  create_table “authors”, force: true do |t|

    t.string “name”

    t.datetime “created_at”

    t.datetime “updated_at”

  end

  create_table “products”, force: true do |t|

    t.string “name”

    t.text “description”

    t.datetime “created_at”

    t.datetime “updated_at”

    t.string “part_number”

  end

end

In many cases this is sufficient. This file is created with a database check and describes its structure using create_table , add_index and so on. Since it is independent of the database type, it can be loaded into any database supported by Active Record. This is very useful if you are distributing an application that can run on different databases.

NOTE: db/schema.rb cannot describe database-specific elements such as triggers, sequences, stored procedures, CHECK constraints, and so on. Note that while you can execute arbitrary SQL statements in migrations, these statements cannot be played back by the schema unloader. If you use these features, you need to set the schema format to :sql.

Instead of using the Active Records schema unloader, the database structure will be dumped using the database specific tool (using the rails db:structure:dump task) in db/structure.sql. For example, for PostgreSQL, the pg_dump utility is used. For MySQL and MariaDB, this file will contain the result of SHOW CREATE TABLE for different tables.

Loading such schemas is simply running the SQL statements they contain. By definition, an exact copy of the database structure will be created. Using the schema :sql format, however, prevents the schema from being loaded into the DBMS other than the one used when it was created.

Schema uploads and source control

Because schema dumps are the authoritative source for your database schema, it is highly recommended that you include them in source code control.

db/schema.rb contains the number of the current version of the database. This ensures that conflicts arise in the event of a merge of two branches, each of which affected the schema. When this happens, fix the conflicts manually, leaving the highest version number.

(Active Record and Referential Integrity) Active Record and Referential Integrity

Active Record and Referential Integrity

The Active Record way requires the logic to be in the models, not in the database. By and large, features such as triggers or constraints that push some logic back into the database are not actively used.

Validations like validates :foreign_key, uniqueness: true are one way your models can maintain referential integrity. The :dependent option on relationships allows models to automatically destroy child objects when the parent is destroyed. Like anything that works at the application level, this cannot guarantee referential integrity, so someone could add foreign keys as referential integrity delimiters in the database as well.

Although Active Record does not provide any tools to work directly with these functions, the execute method can be used to run arbitrary SQL.

Migrations and seeds

The main purpose of a Rails migration is to run commands that change the schema in a sequential manner. Migrations can also be used to add or change data. This is useful for an existing database that cannot be dropped and recreated, such as a production database.

class AddInitialProducts < ActiveRecord::Migration[5.0]

  def up

    5 times do |i|

      Product.create(name: “Product ##{i}”, description: “A product.”)

    end

  end

  def down

    product.delete_all

  end

end

To add initial data to the database after creation, Rails has a built-in ‘seeds’ feature that makes the process quick and easy. This is especially useful for frequent database reloads in development and test environments. This feature is easy to get started: just populate db/seeds.rb with some Ruby code and run rails db:seed:

5 times do |i|

  Product.create(name: “Product ##{i}”, description: “A product.”)

end

Basically, it’s a cleaner way to set up a database for an empty application.

Using reversible in Ruby

A complex migration may involve processes that Active Record does not know how to reverse. You can use reversible to specify what to do when a migration is run and when it needs to be rolled back. For example:

class ExampleMigration < ActiveRecord::Migration

  def change

    create_table :distributors do |t|

      t.string :zipcode

    end

    do |dir|

      dir up do

        # add a CHECK constraint

        execute<<-SQL

          ALTER TABLE distributors

            ADD CONSTRAINT zipchk

              CHECK (char_length(zipcode) = 5) NO INHERIT;

        SQL

      end

      dir.down do

        execute<<-SQL

          ALTER TABLE distributors

            DROP CONSTRAINT zipchk

        SQL

      end

    end

    add_column :users, :home_page_url, :string

    rename_column :users, :email, :email_address

  end

end

Using reversible ensures that the instructions are executed in the correct order. If the previous migration example is rolled back, the down block will run after the home_page_url column is dropped and before the distributors table is dropped.

Sometimes a migration will do things that are simply irreversible; for example, it may destroy some data. In such cases, you can call ActiveRecord::IrreversibleMigration in your down block. If anyone tries to undo your migration, an error will be displayed stating that it can’t be done.

Using up/down methods

You can also use the old style of migrations using the up and down methods instead of change. The up method should describe the changes you want to make to your schema, and the down method of your migration should reverse the changes made by the up method. In other words, the database schema must remain the same after executing up and then down. For example, if you created a table in the up method, it should be deleted in the down method. It is reasonable to undo changes in the exact opposite order to the one in which they were made in the up method. Then the example from the reversible section would be equivalent to:

class ExampleMigration < ActiveRecord::Migration[5.0]

  def up

    create_table :distributors do |t|

      t.string :zipcode

    end

    #add a CHECK constraint

    execute<<-SQL

      ALTER TABLE distributors

        ADD CONSTRAINT zipchk

        CHECK (char_length(zipcode) = 5);

    SQL

    add_column :users, :home_page_url, :string

    rename_column :users, :email, :email_address

  end

  def down

    rename_column :users, :email_address, :email

    remove_column :users, :home_page_url

    execute<<-SQL

      ALTER TABLE distributors

        DROP CONSTRAINT zipchk

    SQL

    drop_table :distributors

  end

end

If your migration is not reversible you should call ActiveRecord::IrreversibleMigration from your down method. If anyone tries to undo your migration, an error will be displayed stating that it can’t be done.

(reverting-previous-migrations) Reverting to previous migrations

You can use the Active Record feature to roll back migrations using the revert method:

require_relative ‘20121212123456_example_migration’

class FixupExampleMigration < ActiveRecord::Migration[5.0]

  def change

    revert ExampleMigration

    create_table(:apples) do |t|

      t.string :variety

    end

  end

end

The revert method can also accept a block. This can be useful for rolling back a selected part of previous migrations. As an example, let’s imagine that ExampleMigration is committed, and later we decide that it would be better to use Active Record validations, instead of the CHECK constraint, to check the zipcode.

A similar migration could also be written without using revert, but that would involve a few more steps: reordering create table and reversible, replacing create_table with drop_table, and eventually changing up to down and vice versa. Revert has already taken care of all this.

NOTE: If you want to add CHECK constraints like in the examples above, you must use structure.sql as the export method. See Export Schema.

Running migrations

Rails provides a number of bin/rails tasks to run specific sets of migrations.

The very first bin/rails task migration we’ll be using is rails db:migrate. In its main form, it just runs the change or up method on all migrations that haven’t been run yet. If there are no such migrations, it exits. It will run these migrations in order based on the date of the migration.

Note that running the db:migrate task also invokes the db:schema:dump task, which updates your db/schema.rb file to match your database structure.

If you specify a target version, Active Record will run the required migrations (up, down, or change methods) until it reaches the required version. Version is a numeric prefix for the migration file. For example, to migrate to version 20080906120000, run:

$ bin/rails db:migrate VERSION=20080906120000

If version 20080906120000 is greater than the current version (i.e. forward migration) this will run the change (or up) method on all migrations up to and including 20080906120000, but will not run any later migrations. If the migration is backward, this will run the down method on all migrations up to, but not including, 20080906120000.

Rollback

A common task is to rollback the last migration. For example, you made a mistake and want to fix it. You can track down a version of a previous migration and migrate to it, but you can do it easier by running:

$ bin/rails db:rollback

This will revert the situation to the last migration, either by reversing the change method or by running the down method. If you need to undo multiple migrations, you can specify the STEP option:

$ bin/rails db:rollback STEP=3

there will be a rollback to the last 3 migrations.

The db:migrate:redo task is a shortcut to perform a rollback and then run the migration again. Just as with the db:rollback task, you can specify the STEP option if you need to work with more than one version, for example:

$ bin/rails db:migrate:redo STEP=3

None of these bin/rails tasks can do anything that can’t be done with db:migrate. They’re just more convenient because you don’t have to explicitly specify which migration version to migrate to.

Database installation

The rails db:setup task will create the database, load the schema, and initialize it with the seed data.

Reset database

The rails db:reset task will remove the database and reinstall it. This is functionally equivalent to rails db:drop db:setup.

NOTE. This is not the same as running all migrations. It only uses the current contents of the db/schema.rb or db/structure.sql file. If the migration cannot be rolled back, rails db:reset may not help you. For more information about exporting a schema, see Exporting a Schema.

Running specific migrations

If you need to run a specific migration (up or down), the db:migrate:up and db:migrate:down tasks will do that. Just define the appropriate option and the appropriate migration will have its change, up or down method called, like so:

$ bin/rails db:migrate:up VERSION=20080906120000

will run the up method on migration 20080906120000. This task will first check if the migration has already been run, and will do nothing if Active Record thinks it has already run.

Running migrations in different environments

By default, running bin/rails db:migrate will run in the development environment. To run migrations in a different environment, it can be specified using the RAILS_ENV environment variable when running the command. For example, to run migrations in the test environment, you would run:

$ bin/rails db:migrate RAILS_ENV=test

Changing the output of running migrations

By default, migrations only tell us what they do and how long it took. A migration that creates a table and adds an index will output something like this:

== CreateProducts: migrating =========================================== ====

— create_table(:products)

   -> 0.0028s

== CreateProducts: migrated (0.0028s) =======================================

Some methods in migrations allow you to control all this:

Method Purpose

suppress_messages Takes a block as an argument and suppresses any output generated by this block.

say Takes a message as an argument and prints it as is. A second boolean argument can be passed to indicate whether indentation is needed or not.

say_with_time Prints the text along with the duration of the block. If the block returns a number, it is assumed to be the number of affected rows.

Model Generators

The model and scaffold generators will generate migrations suitable for creating a new model. The migration will contain instructions for creating the appropriate table. If you tell Rails which columns you want, expressions to add those columns will also be generated. For example, running:

$ bin/rails generate model Product name:string description:text

will create a migration that looks like this

class CreateProducts < ActiveRecord::Migration[5.0]

  def change

    create_table :products do |t|

      t.string :name

      t.text :description

      t.timestamps

    end

  end

end

You can define as many column_name/type pairs as you like.

Passing modifiers

Some commonly used type modifiers can be passed directly on the command line. They are specified in curly braces and follow the field type:

For example, running:

$ bin/rails generate migration AddDetailsToProducts ‘price:decimal{5,2}’ supplier:references{polymorphic}

will create a migration that looks like this:

class AddDetailsToProducts < ActiveRecord::Migration[5.0]

  def change

    add_column :products, :price, :decimal, precision: 5, scale: 2

    add_reference :products, :supplier, polymorphic: true

  end

end

TIP: For details, look at the generated messages from the generator.

(writing-a-migration) Writing a migration

Once you’ve created your migration using one of the generators, it’s time to get to work!

Create a table

The create_table method is one of the most fundamental, but in most cases, it will be generated for you by the model or scaffold generator. The usual use is

create_table :products do |t|

  t.string :name

end

This will create a products table with a name column (and, as discussed above, an implied id column).

By default, create_table will create a primary key named id. You can change the name of the primary key with the :primary_key option (don’t forget to update the corresponding model as well), or if you don’t want a primary key at all, you can specify the id: false option. If you need to pass specific options to the database, you can put the SQL snippet in the :options option. For example:

create_table :products, options: “ENGINE=BLACKHOLE” do |t|

  t.string :name, null: false

end

will add ENGINE=BLACKHOLE to the SQL statement used to create the table (when using MySQL or MariaDB, ENGINE=InnoDB is passed by default).

It is also possible to pass the :comment option with any description for the table, which will be stored in the database itself and can be viewed using database administration tools such as MySQL Workbench or PgAdmin III. It is highly recommended to leave comments in migrations for applications with large databases, as it helps to understand the data model and generate documentation. Currently only MySQL and PostgreSQL adapters support comments.

Create a join table

The create_join_table migration method creates a HABTM (has and belongs to many) join table. The usual usage would be like this:

create_join_table :products, :categories

which will create a categories_products table with two columns named category_id and product_id. These columns have the :null option set to false by default. This can be overridden with the :column_options option:

create_join_table :products, :categories, column_options: { null: true }

By default, the name of the join table is obtained as the concatenation of the first two arguments passed to create_join_table, in alphabetical order. To customize the table name, pass the :table_name option:

create_join_table :products, :categories, table_name: :categorization

creates a categorization table.

By default, create_join_table will create two columns with no options, but you can specify these options using the :column_options option. For example,

create_join_table :products, :categories, column_options: {null: true}

will create a product_id and a category_id with the :null option set to true.

create_join_table also accepts a block that can be used to add indexes (which are not created by default) or additional columns:

create_join_table :products, :categories do |t|

  t.index :product_id

  t.index :category_id

end

Changing tables

A close relative of create_table is change_table, used to change existing tables. It is used like create_table, but the object passed to the block has more methods. For example:

change_table :products do |t|

  t.remove :description, :name

  t.string :part_number

  t.index :part_number

  t.rename :upccode, :upc_code

end

removes the description and name columns, creates a part_number string column, and adds an index on it. Finally, it renames the upccode column.

Changing columns

Like remove_column and add_column, Rails provides a migration change_column method.

change_column :products, :part_number, :text

It changes the type of the part_number column in the products table to :text. Note that the change_column command is irreversible.

In addition to change_column, the change_column_null and change_column_default methods are used to change the non-null constraint or default value of a column.

change_column_null :products, :name, false

change_column_default :products, :approved, from: true, to: false

This will set the :name field in products to be a NOT NULL column and change the default value for the :approved field from true to false.

Note: It is also possible to write the previous change_column_default migration as change_column_default :products, :approved, false, but unlike the previous example, this would make your migration irreversible.

(Column Modifiers) Column Modifiers

Column modifiers can be applied when creating or modifying a column:

limit Sets the maximum size of string/text/binary/integer fields.

precision Specifies the precision for decimal fields, which specifies the total number of digits in the number.

scale Specifies the scale for decimal fields, which specifies the number of digits after the decimal point.

polymorphic Adds a type column for belongs_to relationships.

null Allows or disallows NULL values ​​in the column.

default Allows you to set a default value for a column. Note that if you use a dynamic value (such as a date), the default value will only be calculated once (ie, on the date the migration is applied).

index Adds an index for a column.

comment Adds a comment for a column.

Some adapters may support additional options; see the API documentation for specific adapters for details.

NOTE: You cannot specify null and default on the command line.

(foreign-keys) Foreign keys

Although not required, you may want to add foreign key constraints to enforce referential integrity.

add_foreign_key :articles, :authors

This will add a new foreign key to the author_id column of the articles table. The key refers to the id column of the authors table. If column names cannot be derived from table names, the :column and :primary_key options can be used.

Rails will generate a name for each foreign key starting with fk_rails_ plus 10 characters that are deterministically generated based on from_table and column. There is also a :name option if you want to specify a different name.

NOTE: Active Record only supports foreign keys for individual columns. To use composite foreign keys, execute and structure.sql are required. See Export Schema

Removing a foreign key is also easy:

# let Active Record figure out the column name

remove_foreign_key :accounts, :branches

# remove the foreign key for a specific column

remove_foreign_key :accounts, column: :owner_id

# remove the foreign key by name

remove_foreign_key :accounts, name: :special_fk_name

When Helpers Are Not Enough

If the helpers provided by Active Record are not enough, you can use the execute method to run arbitrary SQL:

Product.connection.execute(“UPDATE products SET price = ‘free’ WHERE 1=1”)

See the API documentation for more details and examples of individual methods. In particular, the documentation for ActiveRecord::ConnectionAdapters::SchemaStatements (which provides methods available in the up, down, and change methods), ActiveRecord::ConnectionAdapters::TableDefinition (which provides methods available on the object passed in the create_table method), and ActiveRecord: :ConnectionAdapters::Table (which provides the methods available on the object passed in the change_table method).

Using the change method

The change method is the main method for writing migrations. It works in most cases where Active Record knows how to reverse the migration automatically. Currently, the change method only supports these migration definitions:

add_column

add_foreign_key

add_index

add_reference

add_timestamps

change_column_default (requires :from and :to options)

change_column_null

create_join_table

create_table

disable_extension

drop_join_table

drop_table (you need to specify a block)

enable_extension

remove_column (requires type)

remove_foreign_key (you need to specify the second table)

remove_index

remove_reference

remove_timestamps

rename_column

rename_index

rename_table

change_table is also reversible until the block calls change, change_default, or remove.

remove_column is reversible by providing the column type as the third argument. Also provide options for the original column, otherwise Rails won’t be able to exactly recreate that column on rollback:

remove_column :posts, :slug, :string, null: false, default: ”, index: true

If you need to use other methods, you should use reversible or write the up and down methods instead of the change method.

Validations in Ruby

Active Record allows you to check the state of a model before it is written to the database. There are several methods that can be used to check your models and validate that an attribute’s value is non-empty, unique (not existing in the database), conforms to a certain format, and many more.

Validation is a very important issue to consider when saving to a database, so the save and update methods take this into account when they run: they return false when validation fails, and they don’t actually perform any database operations. Each of these methods has an exclamation mark pair (save! and update!), which are stricter in that they throw an ActiveRecord::RecordInvalid exception if the validation fails. Short example:

class User<ApplicationRecord

  validates :name, presence: true

end

user = User.new

user.save # => false

user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank

You can read more about validations in the Active Record Validations guide.

callbacks

Active Record callbacks allow you to attach code to specific events in the life cycle of your models. This allows you to add behavior to the model by transparently running code when these events occur, such as when you create a new record, update it, delete it, and so on. You can read more about callbacks in the Active Record callbacks guide.

Migrations

Rails introduces a DSL for database schema management called migrations. Migrations are stored in files that run against any database supported by Active Record using rake. Here is the migration that creates the table:

class CreatePublications < ActiveRecord::Migration[5.0]

  def change

    create_table :publications do |t|

      t.string :title

      t.text :description

      t.references :publication_type

      t.integer :publisher_id

      t.string :publisher_type

      t.boolean :single_issue

      t.timestamps

    end

    add_index :publications, :publication_type_id

  end

end

Rails keeps track of which files are committed to the database and provides an option to rollback. To actually create the table, you need to run rails db:migrate, and to roll it back, rails db:rollback.

Note that the code above is database independent: it will run in MySQL, PostgreSQL, Oracle, and others. You can read more about migrations in the Active Record migrations guide.

Active Record Migrations

Migrations are a feature of Active Record that allow you to change your database schema from time to time. Instead of writing schema changes in pure SQL, migrations allow you to use a simple Ruby DSL to describe changes to your tables.

After reading this guide, you will learn about:

The generators used to create them

Active Record methods to interact with your database

bin/rails tasks affecting migrations and your schema

How migrations are related to schema.rb

Overview of migrations

Migrations are a convenient way to change your database schema all the time in a consistent and simple way. They use Ruby DSL. So you don’t have to write SQL by hand, allowing your schema to be database independent.

Each migration can be thought of as a new ‘version’ of the database. The schema initially contains nothing, and each migration changes it by adding or removing tables, columns, or records. Active Record knows how to update your schema over time, moving it from a certain point in the past to the latest version. Active Record also updates your db/schema.rb file to match the current structure of your database.

Here is an example migration:

class CreateProducts < ActiveRecord::Migration[5.0]

  def change

    create_table :products do |t|

      t.string :name

      t.text :description

      t.timestamps

    end

  end

end

This migration adds a products table with a name string column and a description text column. A primary key named id will also be implicitly added by default, as this is the default primary key for all Active Record models. The timestamps macro adds two columns, created_at and updated_at. These special columns are automatically managed by Active Record if they exist.

Note that we have defined the change we want to happen as we move forward in time. Before running this migration, there is no table. After – the table will exist. Active Record also knows how to reverse this migration: if we rollback this migration, it will drop the table.

In databases that support transactions with schema-changing expressions, migrations are wrapped in a transaction. If the database does not support this and the migration fails, the parts that succeed will not be rolled back. You need to manually rollback.

NOTE: Some queries cannot be run in a transaction. If your adapter supports DDL transactions, disable_ddl_transaction can be used! to disable them for a separate migration.

If you want a migration for something that Active Record doesn’t know how to reverse, you can use reversible:

class ChangeProductsPrice < ActiveRecord::Migration[5.0]

  def change

    do |dir|

      change_table :products do |t|

        dir.up { t.change :price, :string }

        dir.down { t.change :price, :integer }

      end

    end

  end

end

On the other hand, you can use up and down instead of change:

class ChangeProductsPrice < ActiveRecord::Migration[5.0]

  def up

    change_table :products do |t|

      t.change :price, :string

    end

  end

  def down

    change_table :products do |t|

      t.change :price, :integer

    end

  end

end

Create a migration

Create an offline migration

Migrations are stored as files in the db/migrate directory, one file per class. The filename is YYYYMMDDHHMMSS_create_products.rb, which means that the UTC timestamp identifies the migration, followed by an underscore, followed by the migration name, where the words are separated by underscores. The name of the migration class contains the literal part of the file name, but in the CamelCase format (i.e. words are written together, each word begins with a capital letter). For example, 20080906120000_create_products.rb should define the CreateProducts class, and 20080906120001_add_details_to_products.rb should define AddDetailsToProducts. Rails uses this flag to determine which migrations should be run and in what order, so if you’re copying migrations from another application or generating the file yourself, be more vigilant.

Of course, calculating timestamps isn’t fun, so Active Record provides a generator to handle this:

$ bin/rails generate migration AddPartNumberToProducts

This will create an empty but properly named migration:

class AddPartNumberToProducts < ActiveRecord::Migration[5.0]

  def change

  end

end

If the migration name is of the form “AddXXXToYYY” or “RemoveXXXFromYYY” and is followed by a list of column names and their types, then the migration will generate the appropriate add_column and remove_column expressions.

$ bin/rails generate migration AddPartNumberToProducts part_number:string

will generate

class AddPartNumberToProducts < ActiveRecord::Migration[5.0]

  def change

    add_column :products, :part_number, :string

  end

end

Naming conventions in RoR development

By default, Active Record uses some naming convention to know how the relationship between models and database tables should be created. Rails pluralizes class names to find the appropriate database table. For example, for the Book class, create a database table named books. Rails’ pluralization mechanisms are very powerful, being able to pluralize (and singular) both correct and incorrect words. When using class names made up of two or more words, the model class name must follow Ruby conventions using the CamelCase form, while the table name must contain words separated by underscores. Examples:

Database table – Plural form with words separated by underscores (ie, book_clubs).

The model class is a single number with the first capital letter in each word (i.e., BookClub).

Model/Class Table/Scheme

articles

LineItem line_items

Deer deers

mouse mice

Person people

Schema Conventions

Active Record uses naming conventions for columns in database tables, depending on the purpose of those columns.

Foreign Keys – These fields should be named after singularized_table_name_id (ie, item_id, order_id). These are the fields that Active Record looks for when creating relationships between your models.

Primary Keys – By default, Active Record uses a numeric column named id as the table’s primary key. This column will be automatically created when using Active Record migrations to create tables.

There are also some optional column names that add extra features for Active Record instances:

created_at – The current date and time will be automatically set when the entry is first created.

updated_at – The current date and time will be automatically set whenever the entry is updated.

lock_version – Adds an optimistic lock to the model.

type – Indicates that the model uses Single Table Inheritance.

(association_name)_type – Stores the type for polymorphic associations.

(table_name)_count – Used to cache the number of objects owned by a link. For example, the comments_count column in the Article class, which can have multiple associated Comment instances, will cache the number of existing comments for each article.

NOTE: Although these column names are optional, they are actually reserved by Active Record. Avoid reserved keywords unless you want additional functionality. For example, type is a reserved word for defining a table using Single Table Inheritance (STI). If you are not using STI, try using a similar word such as “context”, which can also neatly describe the data you are modeling.

Creating Active Record Models

Creating Active Record models is very easy. All you need to do is subclass ApplicationRecord and you’re done:

class Product < ApplicationRecord

end

This will create a Product model by linking it to the products table in the database. By doing so, you will also be able to associate the columns of each row of this table with the attributes of your model instances. Let’s say the products table was created using the following SQL statement:

CREATE TABLE products (

   id int(11) NOT NULL auto_increment,

   name varchar(255),

   PRIMARY KEY (id)

);

Following the above scheme, it will be possible to write code like this:

p = product.new

p.name = “Some Book”

puts p.name # “Some Book”

Redefining naming conventions

But what if you’re following a different naming convention, or if you’re using a new Rails application with an old database? Not a problem, you can just override the default conventions.

ApplicationRecord inherits from ActiveRecord::Base which defines a number of useful methods. You can use the ActiveRecord::Base.table_name= method to specify the name of the table to be used:

class Product < ApplicationRecord

  self.table_name = “my_products”

end

If you do this, you must manually define the name of the class containing the fixtures (my_products.yml) using the set_fixture_class method in the test definition:

class ProductTest < ActiveSupport::TestCase

  set_fixture_class my_products: Product

  fixtures :my_products

  …

end

It is also possible to override the column that should be used as the table’s primary key using the ActiveRecord::Base.primary_key= method:

class Product < ApplicationRecord

  self.primary_key = “product_id”

end

CRUD: Reading and writing data

CRUD is an abbreviation for four verbs used to describe data operations: Create (create), Read (read), Update (update) and Delete (delete). Active Record automatically creates methods that allow an application to read and act on the data stored in its tables.

Creation

Active Record objects can be created from a hash, a block, or from manually specified attributes after creation. The new method will return a new object, while create will return an object and save it to the database.

For example, given a User model with name and occupation attributes, calling the create method will create and save a new record to the database:

user = User.create(name: “David”, occupation: “Code Artist”)

Using the new method, an object can be initialized without saving:

user = User.new

username = “David”

user.occupation = “Code Artist”

Calling user.save will commit the record to the database.

Finally, if a block is provided, both create and new will pass a new object into that block for initialization:

user = User.new do |u|

  u.name = “David”

  u.occupation = “Code Artist”

end

Reading

Active Record provides a rich API for accessing data in a database. Below are some examples of the various data access methods provided by Active Record.

# will return a collection with all users

users = User.all

# will return the first user

user = User.first

# will return the first user named David

david = User.find_by(name: ‘David’)

# will find all users named David who are Code Artists and sort them by created_at in reverse chronological order

users = User.where(name: ‘David’, occupation: ‘Code Artist’).order(created_at: :desc)

For more information about queries in Active Record models, see the Active Record Query Interface guide.

Update

Once an Active Record object has been received, its attributes can be changed and it can be saved to the database.

user = User.find_by(name: ‘David’)

user.name = ‘Dave’

user.save

A shortcut for this is to use a hash with the attributes associated with the desired values, thus:

user = User.find_by(name: ‘David’)

user.update(name: ‘Dave’)

This is most useful when you need to update multiple attributes at once. If, on the other hand, you need to update multiple records at once, the update_all class method is useful:

User.update_all “max_login_attempts = 3, must_change_password = ‘true'”

Removal

Moreover, once retrieved, the Active Record object can be destroyed, removing it from the database.

user = User.find_by(name: ‘David’)

user.destroy

If you need to delete multiple records at once, you can use the destroy_all method:

# find and remove all users named David

User.where(name: ‘David’).destroy_all

# delete all user

User.destroy_all

Safety during RoR development

(Basic Authentication) Basic authentication

If you publish your blog online, anyone can add, edit, and delete articles or delete comments.

Rails provides a very simple HTTP authentication system that works well in this situation.

In the ArticlesController we need a way to block access to various actions if the user is not authenticated. Here we can use the Rails http_basic_authenticate_with method to allow access to the required actions if the method allows it.

To use the authentication system, we’ll define it at the top of our ArticlesController in app/controllers/articles_controller.rb. In our case, we want the user to be authenticated for every action except for index and show, so we’ll write it like this:

class ArticlesController < ApplicationController

  http_basic_authenticate_with name: “dhh”, password: “secret”, except: [:index, :show]

  def index

    @articles = Article.all

  end

  # skipped for brevity

We also want to only allow authenticated users to delete comments, so in the CommentsController (app/controllers/comments_controller.rb) we’ll write:

class CommentsController < ApplicationController

  http_basic_authenticate_with name: “dhh”, password: “secret”, only: :destroy

  def create

    @article = Article.find(params[:article_id])

    # …

  end

  # skipped for brevity

Now, if you try to create a new article, you will be faced with a simple HTTP authentication call:

Simple HTTP Authentication Challenge

Other authentication methods are also available for Rails applications. Two popular Rails addons are Devise and Authlogic, among others.

Other Security Thoughts

Security, especially in web applications, is a vast and detailed area. The security of your Rails application is covered in more detail in the Ruby on Rails Security Guide.

What’s next?

Once you’ve created your first Rails application, you’re free to modify and experiment as you see fit.

Remember that you cannot do anything without outside help. Just as you did with this “Rails for Beginners” guide, you can continue to use these resources just as freely:

The simplest way to work with Rails is to store all external data in UTF-8. If not, Ruby and Rails libraries will often be able to convert your native data to UTF-8, but this doesn’t always work reliably, so it’s best to be sure that all external data is UTF-8.

If you make a mistake in this area, the most common symptom is a black diamond with a question mark inside it that appears in the browser. Another common symptom is characters such as “ü” appearing instead of “ü”. Rails takes a number of internal steps to mitigate common cases of problems that can be automatically detected and fixed. However, if there is external data that is not stored in UTF-8, this can lead to these kinds of problems that Rails can’t automatically detect and fix.

The two most common data sources that are not in UTF-8 are:

Your text editor: Most text editors (such as TextMate) save files as UTF-8 by default. If your text editor does not do this, it may cause special characters typed in your templates (such as é) to appear as a question mark diamond in the browser. This also applies to your i18N translation files. Most editors that do not default to UTF-8 (such as some versions of Dreamweaver) offer a way to change the defaults to UTF-8. Do so.

Your database: Rails will by default convert data from your database to UTF-8 on the boundary. However, if your database does not use UTF-8 internally, it may not be able to store all the characters your user types. For example, if your database internally uses Latin-1 and your user enters Russian, Hebrew, or Japanese characters, the data will be lost as soon as it enters the database. If possible, use UTF-8 as internal storage in your database.

Active Record Basics

This guide is an introduction to Active Record.

After reading this guide, you will know:

What are ORM (Object Relational Mapping) and Active Record and how are they used in Rails.

How Active Record fits into the Model-View-Controller paradigm.

How to use Active Record models to manage information stored in a relational database.

About the Active Record schema naming convention.

About the concepts of database migrations, validations and callbacks.

What is Active Record?

Active Record is the M in MVC – the model – which is the layer in the system responsible for representing business logic and data. Active Record makes it easy to create and use business objects whose data needs to be permanently stored in a database. By itself, this implementation of the Active Record pattern is a description of the ORM (Object Relational Mapping) system.

Active Record Pattern

Active Record was described by Martin Fowler in his book Patterns of Enterprise Application Architecture. In Active Record, objects contain both stored data and behavior that operates on that data. Active Record is of the opinion that providing data access logic as part of an object will show the users of that object how to read and write to the database.

Object Relational Mapping (ORM)

Object Relational Mapping, commonly referred to as an abbreviation of ORM, is a technique that connects complex application objects to tables in a relational database management system. With the use of an ORM, the properties and relationships of these application objects can be easily stored and retrieved from the database without writing SQL statements directly, and as a result, with less summary code to access the database.

Active Record is an ORM framework

Active Record provides us with several mechanisms, the most important of which are the ability to:

Representations of models and their data.

Representations of relationships between these models.

Representing inheritance hierarchies using linked models.

Validating models before they are saved to the database.

Performing database operations in an object-oriented style.

Configuration conventions in Active Record

When writing an application using other programming languages ​​or frameworks, it is often necessary to write a lot of configuration code. In particular, this is true for ORM frameworks. However, if you follow the conventions adopted by Rails, you will have to write very little configuration (and sometimes not at all) when creating Active Record models. The idea is that in most cases you configure your applications in the same way, and this way should be the default way. So explicit configuration will only be required when you don’t follow the conventions for some reason.

Ruby on Rails model which process comments on articles

Generating a Model

We intend to use the same generator that we used earlier when creating the Article model. This time we will create a Comment model containing a link to the article. Run the following command in a terminal:

$ bin/rails generate model Comment commenter:string body:text article:references

This command generates four files:

File Destination

db/migrate/20140120201010_create_comments.rb Migrate to create the comments table in your database (your filename will include a different timestamp)

app/models/comment.rb Model Comment

test/models/comment_test.rb Framework for testing the comment model

test/fixtures/comments.yml Sample comments for use in testing

First, let’s take a look at app/models/comment.rb:

class Comment < ApplicationRecord

  belongs_to :article

end

This is very similar to the Article model we saw earlier. The difference is in the belongs_to :article line, which establishes an Active Record link. You will learn about connections in the next section of the guide.

The (:references) keyword used in the bash command is a special data type for models. It creates a new column in your database with the name of the rendered model with an _id appended, which can contain numeric values. To better understand, analyze the db/schema.rb file after the migration is done.

In addition to the model, Rails also made a migration to create the corresponding database table:

class CreateComments < ActiveRecord::Migration[5.0]

  def change

    create_table :comments do |t|

      t.string :comment

      t.text :body

      t.references :article, foreign_key: true

      t.timestamps

    end

  end

end

The t.references line creates a numeric column named article_id, an index on it, and a foreign key constraint pointing to the id column of the articles table. Next, we run the migration:

$bin/railsdb:migrate

Rails is smart enough to only run those migrations that haven’t already been run against the current database, in our case you’ll see:

== CreateComments: migrating =========================================== ====

— create_table(:comments)

   -> 0.0115s

== CreateComments: migrated (0.0119s) ======================================

Linking Models

Active Record links allow you to easily declare relationships between two models. In the case of comments and articles, you can describe the relationship as follows:

Each comment belongs to one article.

One article can have many comments.

In fact, it’s very close to the syntax that Rails uses to declare this relationship. You have already seen the line of code in the Comment model (app/models/comment.rb) that makes each comment belong to an article:

class Comment < ApplicationRecord

  belongs_to :article

end

You need to edit app/models/article.rb to add the other side of the link:

class Article < ApplicationRecord

  has_many :comments

  validates :title, presence: true,

                    length: { minimum: 5 }

  […]

end

These two announcements automatically make a large number of features available. For example, if you have an @article instance variable containing an article, you can get all the comments that belong to that article in an array by calling @article.comments.

TIP: For more information about Active Record Links, see the Active Record Links manual.

Adding a route for comments

As with the welcome controller, we need to add a route so that Rails knows which address we want to go to in order to see the comments. Open the config/routes.rb file again and edit it as follows:

resources :articles do

  resources :comments

end

This will create comments as a nested resource in articles. This is the other side of capturing the hierarchical relationship that exists between articles and comments.

TIP: For more information about routing, see the Routing in Rails guide.

Generate a controller

With the model in place, let’s turn our attention to creating the appropriate controller. Again we will use the same generator that we used before:

$ bin/rails generate controller

Five files and an empty directory will be created:

File/Directory Purpose

app/controllers/comments_controller.rb Comments Controller

app/views/comments/ Controller views are stored here

test/controllers/comments_controller_test.rb Controller test

app/helpers/comments_helper.rb Helper for views

app/assets/javascripts/comments.coffee CoffeeScript for controller

app/assets/stylesheets/comments.scss Cascading style sheet for the controller

As with any other blog, our readers will create their comments immediately after reading the article, and after adding a comment, they will be directed back to the article display page and see that their comment has already been reflected. In this regard, our CommentsController serves as a means of creating comments and removing spam, if any.

This is a bit more complicated than what you saw in the article controller. This is a side effect of the attachment you set up. Each request for a comment keeps track of the article to which the comment is attached, so we first resolve the article retrieval issue by calling find on the Article model.

In addition, the code takes advantage of some of the methods available for relationships. We use the create method on @article.comments to create and save a comment. This automatically links the comment so that it belongs to a specific article.

Once we’ve created a new comment, we take the user back to the original article using the article_path(@article) helper. As we’ve seen, it calls the show action on the ArticlesController, which in turn renders the show.html.erb template. This is where we want to display the comments

You can now add articles and comments to your blog and display them in the right places.

Article with comments

Refactoring

Now that we have our articles and comments working, let’s take a look at the app/views/articles/show.html.erb template. It became long and uncomfortable. Let’s use partials to unload it.

Rendering Collections of Partials

First, let’s make a comment partial that shows all the comments for the article. Create a file app/views/comments/_comment.html.erb and put the following in it:

<p>

  <strong>Commenter:</strong>

  <%= comment.commenter %>

</p>

<p>

  <strong>Comment:</strong>

  <%= comment.body %>

</p>

You can then change app/views/articles/show.html.erb

This will now render the app/views/comments/_comment.html.erb partial once for each comment in the @article.comments collection. Since the render method iterates over the @article.comments collection, it assigns each comment to a local variable named like the partial, in our case comment, which is available to us in the partial for rendering.

Partial Shape Rendering

Let’s also move the new comment section to our partial. Again, create a file app/views/comments/_form.html.erb,

The second render just defines the template for the partial we want to render, comments/form. Rails is smart enough to substitute an underscore in this line and understand that you wanted to render the _form.html.erb file in the app/views/comments directory.

The @article object is available in any partials rendered in the view because we have defined it as an instance variable.

Deleting comments

Another important feature of the blog is the ability to remove spam. To do this, we need to insert some link in the view and a destroy action in the CommentsController.

Therefore, we first add a delete link to the app/views/comments/_comment.html.erb partial:

Clicking this new “Destroy Comment” link will trigger a DELETE /articles/:article_id/comments/:id in our CommentsController which will then be used to find the comment we want to delete, so let’s add a destroy action to our controller (app/controllers/ comments_controller.rb):

The destroy action will find the article we’re viewing, find the comment in the @article.comments collection, and then remove it from the database and bring us back to the article view.

Deleting related objects

If you delete an article, the comments associated with it must also be deleted, otherwise they will just take up space in the database. Rails allows you to use the dependent option on the link to achieve this. Edit the Article model, app/models/article.rb, as follows:

class Article < ApplicationRecord

  has_many :comments, dependent: :destroy

  validates :title, presence: true,

                    length: { minimum: 5 }

  […]

end

Article updates on website built with Ruby

We have revealed the “CR” part of CRUD. Now let’s focus on the “U” part, updating articles.

The first step is to add an edit action to the ArticlesController, usually between the new and create actions, as shown.

def new

  @article = article.new

end

def edit

  @article = Article.find(params[:id])

end

def create

  @article = Article.new(article_params)

  if @article.save

    redirect_to @article

  else

    render ‘new’

  end

end

The view will contain a form similar to the one we used when creating new articles. Create a file called app/views/articles/edit.html.erb and add the following to it:

<h1>Editing article</h1>

<%= form_with(model: @article, local: true) do |form| %>

  <% if @article.errors.any? %>

    <div id=”error_explanation”>

      <h2>

        <%= pluralize(@article.errors.count, “error”) %> prohibited

        this article from being saved:

      </h2>

      <ul>

        <% @article.errors.full_messages.each do |msg| %>

          <li><%= msg %></li>

        <% end %>

      </ul>

    </div>

  <% end %>

Right now, we’re pointing the form to an update action, which isn’t defined yet, but we’ll do that soon.

Passing an article object to the method will automatically generate a url to submit the edited article form. This option tells Rails that we want this form to be submitted using PATCH, an HTTP method that is expected to be used to update resources according to the REST protocol.

The form_with arguments can be model objects, such as model: @article , which will cause the helper to populate the form with the object’s fields. Passing a symbol (scope: :article) to the namespace just creates the fields, but doesn’t populate them with anything. Read more in the form_with documentation.

Next, we need to create an update action in app/controllers/articles_controller.rb. Add it between the create action and the private method:

The new method, update, is used when you want to update an entry that already exists, and it takes a hash containing the attributes you want to update. As before, if there is an error updating the article, we want to show the form to the user again.

We have reused the article_params method we defined earlier for the create action.

TIP: It is not necessary to pass all attributes in update. For example, if @article.update(title: ‘A new title’) was called, Rails would only update the title attribute, leaving all other attributes untouched.

Finally, we want to show a link to the edit action in the list of all articles, so let’s add it to app/views/articles/index.html.erb next to the “Show” link:

<table>

  <tr>

    <th>Title</th>

    <th>Text</th>

    <thcolspan=”2″></th>

  </tr>

  <% @articles.each do |article| %>

    <tr>

      <td><%= article.title %></td>

      <td><%= article.text %></td>

      <td><%= link_to ‘Show’, article_path(article) %></td>

      <td><%= link_to ‘Edit’, edit_article_path(article) %></td>

    </tr>

  <% end %>

</table>

And also add app/views/articles/show.html.erb to the template so that the “Edit” link is also on the article page. Add the following at the end of the template:

Using Partials to Clean Up Repetition in Views

Our edit page is very similar to the new page, in fact they use the same code to display the form. Let’s remove this duplication by using a view partial. By convention, partial files begin with an underscore.

TIP: For more on partials, see Layouts and Rendering in Rails.

Create a new file app/views/articles/_form.html.erb with the following content:

<%= form_with model: @article, local: true do |form| %>

  <% if @article.errors.any? %>

    <div id=”error_explanation”>

      <h2>

        <%= pluralize(@article.errors.count, “error”) %> prohibited

        this article from being saved:

      </h2>

      <ul>

        <% @article.errors.full_messages.each do |msg| %>

          <li><%= msg %></li>

        <% end %>

      </ul>

    </div>

  <% end %>

Everything except the form_with declaration is the same. The reason you can use this shorter and simpler form_with declaration than other forms is because @article is a resource that matches the full set of resource routes, and Rails is able to determine which URI and method to use. See Resource-oriented style for details on this use of form_with.

Let’s now update the app/views/articles/new.html.erb view to use this new partial, rewriting it completely:

Deleting articles

We are now ready to cover the “D” part of CRUD, deleting from a database. Following the REST convention, the route to remove articles in the bin/rails routes output is as follows:

DELETE /articles/:id(.:format) articles#destroy

The delete routing method must be used for routes that destroy resources. If left as a normal get route, it would be possible to generate the following malicious URLs:

<a href=’http://example.com/articles/1/destroy’>look at this cat!</a>

We’re using the delete method to destroy resources, and this route is associated with a destroy action in app/controllers/articles_controller.rb that doesn’t exist yet. The destroy method is usually the last CRUD action in the controller, and like all public CRUD actions, it must be placed before any private or protected methods.

You can call destroy on Active Record objects when you want to remove them from the database. Note that we don’t need to add a view for this action since we’re redirecting to the index action.

Finally, let’s add a ‘Destroy’ link to the index action template (app/views/articles/index.html.erb), bringing all the links together.

Here we use link_to in a different way. We pass the named route as the second argument, and the options as the other argument. The options method: :delete and data: { confirm: ‘Are you sure?’ } are used as html5 attributes, so when a link is clicked, Rails will first show the user a confirmation dialog and then send the link using the delete method. This is done using the rails-ujs JavaScript file, which is automatically included in the application layout (app/views/layouts/application.html.erb) when the application is generated. Without this file, the confirmation dialog will not be shown.

Confirmation dialog

TIP: Learn more about unobtrusive JavaScript in the Working with JavaScript in Rails guide.

Congratulations, now you can create, view all and individually, update and delete articles.

TIP: In general, Rails recommends using resource objects instead of manually declaring routes. See Routing in Rails for more information on routing.

Displaying articles on Ruby on Rails website

We still need a way to display a list of all our articles, let’s make one. The route displayed with bin/rails routes is the following:

articles GET /articles(.:format) articles#index

Add an index action corresponding to this route inside the ArticlesController in the app/controllers/articles_controller.rb file. When we write an index action, it’s common practice to place it as the first method in a controller. Let’s do it:

class ArticlesController < ApplicationController

  def index

    @articles = Article.all

  end

  def show

    @article = Article.find(params[:id])

  end

  def new

  end

  # omitted for brevity

And finally, the view for this action, located at app/views/articles/index.html.erb:

<h1>Listing articles</h1>

<table>

  <tr>

    <th>Title</th>

    <th>Text</th>

    <th></th>

  </tr>

  <% @articles.each do |article| %>

    <tr>

      <td><%= article.title %></td>

      <td><%= article.text %></td>

      <td><%= link_to ‘Show’, article_path(article) %></td>

    </tr>

  <% end %>

</table>

Now if you go to http://localhost:3000/articles you can see a list of all the articles you have already created.

Adding a link

You can now create and view individual and all articles. Let’s add some links to navigate between pages.

Open app/views/welcome/index.html.erb and change it like this:

<h1>Hello Rails!</h1>

<%= link_to ‘My Blog’, controller: ‘articles’ %>

The link_to method is one of Rails’ built-in helpers. It creates a hyperlink based on the text to display and indicate where to go – in our case the path for the articles controller.

Let’s add links to other views as well, starting by adding a “New Article” link to app/views/articles/index.html.erb, placing it above the <table> tag:

<%= link_to ‘New article’, new_article_path %>

This link will take you to the form to create a new article.

Now add another link in app/views/articles/new.html.erb, below the form, to get back to the index action:

<%= form_with scope: :article, url: articles_path, local: true do |form| %>

  …

<% end %>

<%= link_to ‘Back’, articles_path %>

Finally, add a link to the app/views/articles/show.html.erb template to also return to the index action so that viewers of the individual article can go back and view the full list again:

TIP: If you want to refer to an action on the same controller, you don’t need to specify the :controller option because Rails uses the current controller by default.

TIP: In development mode (which you use by default), Rails reloads your application with every browser request, so you don’t have to stop and restart your web server when you make changes.

Let’s add some validations

The model file app/models/article.rb is as simple as it looks:

class Article < ApplicationRecord

end

Not much is written in this file, but note that the Article class is derived from ApplicationRecord. ApplicationRecord inherits from ActiveRecord::Base, which provides a wealth of functionality for your Rails models, including basic database operations CRUD (Create, Read, Update, Destroy), data validation, sophisticated search support, and the ability to establish relationships between different models.

Rails includes methods to help validate the data you pass into the model. Open the app/models/article.rb file and edit:

class Article < ApplicationRecord

  validates :title, presence: true,

                    length: { minimum: 5 }

end

These changes will ensure that all articles have a title that is at least five characters long. Rails can check for various conditions in the model, including the existence or uniqueness of fields, their format, and the existence of related objects. More validations are covered in Active Record Validation.

Now that there are validations, calling @article.save on an invalid article will return false. If you open app/controllers/articles_controller.rb again, you’ll see that we don’t check the result of calling @article.save in the create action. If @article.save fails in this situation, we need to show the form to the user again. To do this, replace the new and create actions in app/controllers/articles_controller.rb with these:

def new

  @article = article.new

end

def create

  @article = Article.new(article_params)

  if @article.save

    redirect_to @article

  else

    render ‘new’

  end

end

private

  def article_params

    params.require(:article).permit(:title, :text)

  end

Now the new action creates a new instance variable called @article, and you’ll see why in a couple of paragraphs.

Note that in the create action we used render instead of redirect_to when save returns false. The render method is used to have the @article object passed back to the new template when it is rendered. This rendering is done within the same request as the form submission, while redirect_to tells the browser to make another request.

If you reload http://localhost:3000/articles/new and try to save an article without a title, Rails will take you back to the form, but that’s not very helpful. You need to tell the user that something went wrong. To do this, modify app/views/articles/new.html.erb to check for error messages:

<%= form_with scope: :article, url: articles_path, local: true do |form| %>

  <% if @article.errors.any? %>

    <div id=”error_explanation”>

      <h2>

        <%= pluralize(@article.errors.count, “error”) %> prohibited

        this article from being saved:

      </h2>

      <ul>

        <% @article.errors.full_messages.each do |msg| %>

          <li><%= msg %></li>

        <% end %>

      </ul>

    </div>

  <% end %>

  <p>

    <%= form.label :title %><br>

    <%= form.text_field :title %>

  </p>

  <p>

    <%= form.label :text %><br>

    <%= form.text_area :text %>

  </p>

  <p>

    <%= form.submit %>

  </p>

<% end %>

<%= link_to ‘Back’, articles_path %>

A few things about what’s going on. We check if there are any errors with @article.errors.any?, in which case we show a list of all errors with @article.errors.full_messages.

pluralize is a rails helper that takes a number and a string as arguments. If the number is greater than one, the string will be automatically pluralized.

The reason we added @article = Article.new to the ArticlesController is because otherwise @article would be nil in the view, and calling @article.errors.any? will cause an error.

TIP: Rails automatically wraps errored fields in a div with class field_with_errors. You can define a css rule to make them stand out.

We will now have a nice error message when saving an article without a title if you try this in the form of a new article