Back
Chapters

Rails generator and bundle commands

Search icon
Search Book
⌘K

What is a bundler?

Bundler is a dependency management tool for Ruby which is available as a gem that can be installed through the RubyGems package manager which comes built-in using the following command:

1gem install bundler

Bundler reads the Gemfile for the list of gems and ensures that the gems you need are present in development, staging, and production. It also fetches the metadata from the source provided, resolves the dependencies of each gem, installs them and then requires them while booting.

Without bundler, we would have to handle the installation and manage the dependencies manually.

Let's take a brief look at how Bundler works with Gemfile and Gemfile.lock for dependency management.

Don't use sudo bundle install

In any system, using sudo to install 3rd party packages or apps is not a good idea from a security perspective. When we are installing 3rd party packages those packages have the ability to run some scripts. If we install a package with sudo then we are giving the admin permission to the external package and now this external package can run any malicious scripts on your machine.

It can also mess up the permissions for the system. While installing the package it can update the machine's settings or permissions just to install the given package. This can lead us to endless permission issues afterward.

The same is the case with npm installations. We should not use sudo while installing a package with npm. So if we give sudo privileges while installing a 3rd party package, then it's a security issue and it's a hacky solution. We should dig deeper and fix the underlying issue rather using sudo in the commands.

If you have already used the sudo bundle install and now facing permission issues, then as a possible solution, you can fully uninstall ruby and its dependencies and then install ruby using rbenv again as mentioned in this section. Now change the directory to the project with Gemfile and run bundle install. Now ideally it should be finding the Gemfile and install the gems automatically under the currently logged in user's scope.

Gemfile and Gemfile.lock

Gemfile is a Ruby file which contains a list of gem dependencies of a project. When you install the gems using the command bundle install, bundler looks for the Gemfile at either the directory mentioned by the environment variable BUNDLE_GEMFILE or at the root of the project directory.

Bundler then makes use of RubyGems to install the gems listed on the Gemfile along with their dependencies and it creates a file called Gemfile.lock with the list of the gems installed along with their respective versions.

When you run bundle install the next time, bundler will read the Gemfile.lock and use RubyGems to install the exact versions of the gems specified in Gemfile.lock.

Gemfile.lock gets updated every time you install, update or remove a gem from the list of dependencies of your project.

bundle exec command

In this section we will learn why it is preferred to prefix commands with bundle exec while running them.

Sometimes RubyGems generates executables after installing a gem. Examples of these gems include rails, rake, rspec, etc.

Now, consider a scenario where multiple versions of the same gem are installed. In such a case multiple versions of executables are present for the same gem.

Let us take the example of rake gem. Suppose your project's Gemfile contains v10.4.0 of the rake gem whereas v10.4.2 of the same is installed on your system.

If you were to run the rake command in this case, you would get an error, like so:

1rake db:migrate
2rake aborted!
3Gem::LoadError: You have already activated rake 10.4.2, but your Gemfile requires rake 10.4.0. Prepending `bundle exec` to your command may solve this.

When rake gets called in the above example, there is nothing to ensure that the right version of rake gets activated. In fact, RubyGems will simply activate the latest rake version even if your project depends on an older version.

If something like this happens, you shouldn't remove the incompatible system-wide rake gem to solve this issue. It will lead to other errors in case other projects or tools are dependent on that version.

Additionally, there is no guarantee that simply updating the rake version in your project will fix this issue. Other gems in your project may not be compatible with the updated version.

The solution to this is to use the bundle exec command. bundle exec allows us to run an executable script in the specific context of the project's bundle.

For example, the error encountered in the above example can be fixed using the following command:

1bundle exec rake db:migrate

Upon running the above command, bundle exec will run the executable script for rake version specified in project's Gemfile thus avoiding any conflicts with other versions of rake installed system-wide.

There is however an exception in case of the rails command. The reason being, rails command first loads up the bundler and bundler checks Gemfile.lock for the correct version of command to execute. So if you look into config/application.rb, then you will be able to see the line Bundler.require(*Rails.groups), which does the magic and avoids dependency of prefixing the rails command with bundle exec.

So it's safe to say running bundle exec rails and simply rails is similar. But this is only specific to the rails command.

Running bundle exec and Bundler.require at the same time is not a problem. Thus it's safe to use bundle exec with all commands, even when not needed as long as there's a Gemfile in that directory, it won't activate the gems twice. If there are any performance differences between the two invocations, then it's negligible.

We advocate to use bundle exec at all times. It's a good practice and can save your unnecessary overhead in the long run.

Note that, in this chapter we have used two distinct terms bundle and bundler. Don't let this confuse you. bundler is a gem which whereas bundle is a command.

Although in some places you may notice bundler being used as a command in place of bundle. There is no difference and both bundler and bundle have the same functionality when used in commands.

Updating gems manually

Suppose you want to update a gem that you are using in your application to another version, to do so you should use the following command:

1bundle update gem-name

For example, consider the following from Gemfile.lock in the Granite application:

1rack (2.2.3)
2  rack-proxy (0.7.0)
3    rack
4  rack-test (1.1.0)
5    rack (>= 1.0, < 3)

It seems like the rack gem is being used by the Granite application directly and also as a dependency by other gems. If you wish to update the rack gem then you must only do so for the rack version which is being used by the application. If you update the rack version for rack-test gem as well then it can cause compatibility issues within rack-test gem's functionality.

Hence the correct way to update the rack gem would be like so:

1bundle update rack

Rails generators

In Rails, generators are simply scripts that use templates to create boilerplate code and improve your workflow saving you a quite a bit of time.

For example, when you create a new Rails application you are in fact using a Rails generator.

In the next section we will take a look at how we can use generators to create models, controllers etc.

Rails generate command

Rails includes a lot of generators by default such as model generator, controller generator etc.

You can get a list of all default as well as custom generators available in a Rails project using the following command:

1bundle exec rails generate
2
3Usage: rails generate GENERATOR [args] [options]
4
5General options:
6  -h, [--help]     # Print generator's options and usage
7  -p, [--pretend]  # Run but do not make any changes
8  -f, [--force]    # Overwrite files that already exist
9  -s, [--skip]     # Skip files that already exist
10  -q, [--quiet]    # Suppress status output
11
12Please choose a generator below.
13
14Rails:
15  application_record
16  assets
17  benchmark
18  channel
19  controller
20  generator
21  helper
22  integration_test
23  jbuilder
24  job
25  mailbox
26  mailer
27  migration
28  model
29  resource
30  scaffold
31  scaffold_controller
32  system_test
33  task
34
35ActiveRecord:
36  active_record:application_record
37
38FactoryBot:
39  factory_bot:model
40
41Pundit:
42  pundit:install
43  pundit:policy
44
45React:
46  react:component
47  react:install
48
49Rspec:
50  rspec:policy
51
52Sidekiq:
53  sidekiq:worker
54
55TestUnit:
56  test_unit:channel
57  test_unit:generator
58  test_unit:install
59  test_unit:mailbox
60  test_unit:plugin
61  test_unit:policy

You can also use g as an alias for generator in the above command, like so:

1bundle exec rails g

To get more information about what a generator can do, you can add --help or -h to the generate command like so:

1bundle exec rails generate generator_name --help

Make sure to replace generator_name in the above command with an appropriate generator name.

In the next section we will see how we can use the generate command to generate boilerplate code for migrations.

Working with migration generators

Rails ships with a migration generator out of the box. Before using the migration generator let's take a look at how to use it.

Run the following command to get more information on how to use the migration generator:

1bundle exec rails generate migration --help

Running the above command will fetch the following output:

1Usage:
2  rails generate migration NAME [field[:type][:index] field[:type][:index]] [options]
3
4Options:
5      [--skip-namespace], [--no-skip-namespace]              # Skip namespace (affects only isolated engines)
6      [--skip-collision-check], [--no-skip-collision-check]  # Skip collision check
7  -o, --orm=NAME                                             # ORM to be invoked
8                                                             # Default: active_record
9
10ActiveRecord options:
11      [--timestamps], [--no-timestamps]      # Indicates when to generate timestamps
12                                             # Default: true
13      [--primary-key-type=PRIMARY_KEY_TYPE]  # The type for primary key
14  --db, [--database=DATABASE]                # The database for your migration. By default, the current environment's primary database is used.
15
16Runtime options:
17  -f, [--force]                    # Overwrite files that already exist
18  -p, [--pretend], [--no-pretend]  # Run but do not make any changes
19  -q, [--quiet], [--no-quiet]      # Suppress status output
20  -s, [--skip], [--no-skip]        # Skip files that already exist
21
22Description:
23    Generates a new database migration. Pass the migration name, either
24    CamelCased or under_scored, and an optional list of attribute pairs as arguments.
25
26    A migration class is generated in db/migrate prefixed by a timestamp of the current date and time.
27
28    You can name your migration in either of these formats to generate add/remove
29    column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable
30
31Example:
32    `bin/rails generate migration AddSslFlag`
33
34    If the current date is May 14, 2008 and the current time 09:09:12, this creates the AddSslFlag migration
35    db/migrate/20080514090912_add_ssl_flag.rb
36
37    `bin/rails generate migration AddTitleBodyToPost title:string body:text published:boolean`
38
39    This will create the AddTitleBodyToPost in db/migrate/20080514090912_add_title_body_to_post.rb with this in the Change migration:
40
41      add_column :posts, :title, :string
42      add_column :posts, :body, :text
43      add_column :posts, :published, :boolean
44
45Migration names containing JoinTable will generate join tables for use with
46has_and_belongs_to_many associations.
47
48Example:
49    `bin/rails g migration CreateMediaJoinTable artists musics:uniq`
50
51    will create the migration
52
53    create_join_table :artists, :musics do |t|
54      # t.index [:artist_id, :music_id]
55      t.index [:music_id, :artist_id], unique: true
56    end

According to the description of the migration generator, this command accepts a migration name and an optional list of arguments. To understand this better let's consider a hypothetical example of adding a priority column to the task table which will accept integer values.

Following command is only to present an example for generating a migration. Do not run this command as it is not required for the Granite application.

You can use the following command to generate the migration:

1bundle exec rails generate migration AddPriorityToTask

Running the above command will generate the following migration:

1class AddPriorityToTask < ActiveRecord::Migration[7.0]
2  def change
3  end
4end

The generated migration file contains the boilerplate code for a migration with along with the change method.

Now it is up to you to add the relevant code which will add a new column to the tasks table. We shall not be discussing that here. If you wish to understand how that works you can refer to Rails migration and Rails migrations in depth chapters.

If you recall, the description for migration generator also mentioned that the command accepts arguments other than the migration name. We can pass the attribute name and attribute type to the migration generator command in the form of add_column_name_to_table_name column_name:type to pre-populate the change method.

The following command will generate a migration with pre-populated change method:

1bundle exec rails d migration AddPriorityToTask priority:integer

The above command will generate the following migration file:

1class AddPriorityToTask < ActiveRecord::Migration[7.0]
2  def change
3    add_column :tasks, :priority, :integer
4  end
5end

That was nice! Rails inferred the column name and table name from the migration name itself and generated the code for that. Not just that, it even inferred the column type from the command.

You can use other generators in a similar manner. For example, you can use the controller generator to generate a controller and if you pass in the correct arguments, Rails will pre-populate the generated controllers with actions.

Play around with the available generators to learn how they work and if you ever get stuck, pass the --help flag to see how a particular generator works.

Generating scaffold

So far we have seen how to use generators to generate a single migration or a controller. Now, let us see how we can even generate multiple files using the scaffold generator.

A scaffold in Rails is a full set of model, database migration for that model, controller to manipulate it, views to view and manipulate the data, and a test suite for each of the above.

The following command will generate a scaffold for a single resource called Task:

1bundle exec rails generate scaffold Task title:string
2  invoke  active_record
3    create    db/migrate/20210917025409_create_tasks.rb
4    create    app/models/task.rb
5    invoke    test_unit
6    create      test/models/task_test.rb
7    invoke  resource_route
8     route    resources :tasks
9    invoke  scaffold_controller
10    create    app/controllers/tasks_controller.rb
11    invoke    erb
12    create      app/views/tasks
13    create      app/views/tasks/index.html.erb
14    create      app/views/tasks/edit.html.erb
15    create      app/views/tasks/show.html.erb
16    create      app/views/tasks/new.html.erb
17    create      app/views/tasks/_form.html.erb
18    invoke    resource_route
19    invoke    test_unit
20    create      test/controllers/tasks_controller_test.rb
21    create      test/system/tasks_test.rb
22    invoke    helper
23    create      app/helpers/tasks_helper.rb
24    invoke      test_unit
25    invoke    jbuilder
26    create      app/views/tasks/index.json.jbuilder
27    create      app/views/tasks/show.json.jbuilder
28    create      app/views/tasks/_tak.json.jbuilder
29    invoke  assets
30    invoke    scss
31    create      app/assets/stylesheets/tasks.scss
32    invoke  scss
33    create    app/assets/stylesheets/scaffolds.scss

A scaffold generator can be used to speed up the development process.

It is worth mentioning the -p flag here. If you add this to the command it will simply do a test run and show you what files will be generated without actually generating them, like so:

1bundle exec rails generate scaffold Task title:string -p

If everything looks good, run the command again without the -p flag.

Under the hood, scaffold generator invokes different generators separately to generate the files. Since each generator has a single responsibility, they are easy to reuse, avoiding code duplication.

For instance, the scaffold generator invokes the scaffold_controller generator, which invokes erb, test_unit and helper generators.

This allows us to add/replace/remove any of those invocations. Let's see how we can customize a scaffold generator as per our requirements.

Suppose we do not want to generate the app/assets/stylesheets/scaffolds.scss and test fixture files when scaffolding a new resource. We can do so by updating config/application.rb with the following lines of code:

1config.generators do |g|
2  g.test_framework  :test_unit, fixture: false
3  g.scaffold_stylesheet false
4end

Rails destroy command

Suppose you wish to undo the changes introduced by a generator. Manually going through all the changes and undoing them will be very time consuming and you might end up changing something you didn't intend do.

Rails comes with an easy solution for this. You can reverse the changes introduced by a generator using the destroy command, like so:

1bundle exec rails d scaffold Task title:string

You can also use the destroy command to delete certain files. You just need to pass the relative path of the file to the command.

You can replace destroy with the alias d in the above command.

Rails command line executable

In this chapter we have so far discussed the generate and destroy commands in Rails. There are a few more commands that are absolutely critical to your everyday usage of Rails.

Let's take a brief look at those commands.

  1. bundle exec rails new app_name

This command generates a new Rails application. It creates the entire Rails directory structure with all the code you need to run a simple application right out of the box.

  1. bundle exec rails server

This command launches a local development server named Puma which comes bundled with Rails. You'll use this any time you want to access your application through a web browser.

You can also replace server with the alias s to launch the server.

  1. bundle exec rails console

The console command lets you interact with your Rails application from the command line. It uses IRB under the hood.

You can also replace console with the alias c to invoke the console.

  1. bundle exec rails test

This command lets you run the tests you have added in your Rails project. You can either all the tests at once, like so:

1bundle exec rails test -v

Or you can run tests from a specific test file by passing the relative path of the test file to the above command, like so:

1bundle exec rails test -v test/models/user_test.rb

You can also replace test with the alias t to run the tests.

Passing the -v flag in above command is completely optional. Doing so generates an output with higher verbosity.

  1. bundle exec rails db:create

When you create your Rails application for the first time, it will not have a database yet. You will need to make sure the database is up and running before implementing the CRUD features.

This command lets you create a database if it doesn't already exist.

  1. bundle exec rails db:migrate

Every time you create a database migration that adds or deletes a row or a column, creates a new table, you have to run this command for the changes to reflect in your database.

  1. bundle exec rails routes

This command will list all of your defined routes, giving you a good overview of the URLs in your application.

Creating a Rails app from a specific Rails commit

Suppose the latest Rails release was 10 days ago and you want to use the Rails version from a commit different than the latest release. To do so you can update the rails gem line within Gemfile like so:

1gem "rails", git: "git://github.com/rails/rails.git", ref: commit_id

Replace the commit_id with the commit id you wish to use. For example if the commit id is a8d088f, then update the gemfile like so:

1gem "rails", git: "git://github.com/rails/rails.git", ref: "a8d088f"

You also have an option to pass the branch along with the commit id. If you do not pass the branch name then the default branch would be main. If you don't pass a commit id then the Rails version from latest commit in the main branch will be used.

This is an in-depth chapter and hence you do not need to commit any of these changes.