Chapter 4

The Core Module: Set Up

In this chapter we’re going to create our first module ever. But first, we need to install some dependencies and create a parent application to hold our engines.

4.1. Version requirements

Let’s get started by installing Ruby and Ruby on Rails. We’ll be using the following versions throughout this book:

  • Ruby: 2.6.3
  • Rails: 5.2.3

We recommend using RVM due to its simplicity. You can get the most up-to-date commands to install it from the RVM website. The commands are also provided below, as a reference:

gpg2 --recv-keys \
  409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
curl -sSL | bash -s stable

RVM will be installed with the most recent version of Ruby. This version “should” be fine to use, even though it is more recent, however, if you encounter weird issues while going through the book, you may want to install the tried-and-tested version suggested above.

Box 4.1. Why RVM?

RVM is a really useful tool for Ruby and Ruby on Rails developers. It allows you to install multiple rubies (i.e. multiple versions of Ruby), as well as make use of gemsets. A gemset allows us to install gems separately from the rest of the system, which means we can have different sets of gems for different applications. When using a gemset for a Rails project, all gems are installed in that gemset, keeping them separate and self-contained.

First, we need to install the version of Ruby we will use. We can do this with RVM by running the following command:

rvm install 2.6.3

If you have more than one version or Ruby installed, we need to make sure that we are using the version we just installed. This can be achieved with the following command:

rvm use ruby-2.6.3

Now, when we create our gemset, it will be created under the current version of Ruby. Let’s go ahead and create our gemset:

rvm gemset create modular-rails

You can confirm that the gemset was created under the the correct version of Ruby by looking at the output from the previous command that we ran (note the 2.6.3 in the output):

ruby-2.6.3 - #gemset created /home/[username]/.rvm/gems/ruby-2.6.3@modular-rails

Let’s select this gemset to use:

rvm use ruby-2.6.3@modular-rails

Finally, let’s install Rails in this gemset:

gem install rails -v 5.2.3

That’s it! We are now ready to start creating our application.

4.2. What is the core module?

Before we dive in the code, let’s define what our Core module is going to do. In a nutshell, this module represents the foundations of our application, containing the core features on which other engines will be built.

The Core module will include:

  • The Dashboard

    The dashboard is the first thing a user sees after logging in. In the Core module, it will only be a blank screen, but in the next chapters we will extend it by hooking into it from our other modules. This way, we will be able to fill the dashboard, bit by bit, with each new module.

  • The Admin Panel

    The admin panel is an important part of any CRM (Customer Relationship Management) tool. We will build a basic admin panel in the Core module that will allow us to manage Users. We will make it extendable so that other modules can integrate into it.

  • Authentication

    We will be using the Devise gem to handle authentication. With Devise being an engine, we will encounter a few problems while using it inside other engines. Don’t worry though, we will show you how we can make everything work together.

  • Authorization

    Authorization will be achieved by using Pundit. In future chapters, we will find out how we can extend Pundit’s abilities in other modules.

  • Specs

    The Core module will come with a set of tests to ensure that it is working correctly. The code and tests will be kept simple so you can focus your attention on the modular side of things. We will be using RSpec and factory_bot.

  • Modularity

    The modularity principles will be explained while building the application, and summed up at the end of each chapter.

Now, it’s time to start building.

4.3. Setting up the parent Rails application

Before we actually work on the Core engine, we need a parent application. Let’s generate one so we can create our first engine.

Fire up a terminal, and navigate to your workspace before running the following command:

rails new blast_crm --skip-test

This command will generate a regular Ruby on Rails application named blast_crm. We’re skipping the test unit setup because we’ll be using RSpec, and we’ll be keeping tests inside our engines.

Now you need to navigate into your new application’s directory and make sure that you are still using the modular-rails gemset we created. When you change directory, if you didn’t set modular-rails as your default gemset, rvm will switch back to the default gemset. In order to make sure that our application is using the gemset we just created, we need to do the following:

  1. Navigate to our new application folder:
    cd blast_crm
  2. Confirm contents of .ruby-version

    If you look at the contents of this directory, you will notice that there is a hidden file named .ruby-version. If we open it, we will see that it contains the version of ruby that we want to use, as shown in Listing 1:

    Listing 0.1: Contents of .ruby-version blast_crm/.ruby-version
  3. Create .ruby-gemset

    We also need a file to contain the gemset that we created, for the gems of this application. First, create a file called .ruby-gemset with the following command:

    touch .ruby-gemset

    Then we add the gemset name as the only line in the .ruby-gemset file, as shown in Listing 2:

    Listing 0.2: Contents of .ruby-gemset blast_crm/.ruby-gemset

If we’ve done everything correctly, when we navigate back into this directory, rvm should change our gemset to the gemset we created, namely ruby-2.6.3@modular-rails. Let’s find out… Let’s navigate out of the directory and back into it using the following commands:

cd .. && cd blast_crm

To confirm that we’ve done everything correctly, run the following command:

rvm gemset list

we should see an output that looks like the following:

gemsets for ruby-2.6.3 (found in /home/[username]/.rvm/gems/ruby-2.6.3)
=> modular-rails

If you see a => in front our your modular-rails then you know that you are using the correct gemset.

Now we’re all set and ready to continue creating our engines.

4.4. Generating a mountable engine

In our application’s path /blast_crm we will generate a mountable engine.

First we need to confirm that all our gems are installed, by running:

bundle install

With the following command we will generate our first engine, Core. Notice that, again, we are skipping the test unit because we will be using RSpec:

rails plugin new core --mountable --skip-test
Box 4.2. Rinse & Repeat

In this section you will be creating the Core module/engine. Just remember that you will do this for every engine that you will create in your application.

4.4.1. The engines folder

In order to keep our code organised, we will create an engines/ folder, and move the Core engine inside. All our engines will go inside this directory. We will then move into the core engine’s directory:

mkdir engines && mv core engines/ && cd engines/core

4.4.2. Add one more namespace level

On creation, everything inside an engine will only be namespaced based on its name. Since we are building a modular application and a set of engines that are supposed to work together, we recommend adding one global namespace to keep them grouped. For this application, we will use the namespace Blast.

Box 4.3. Namespacing your engines

What you use as a namespace doesn’t really matter; remember the purpose of the namespace. You could use your company name as a namespace to ensure that there will be no conflict with any gems/modules that were not developed by you. You could also use the name of your project if you’re not sure if you will use this gem in other projects. Your aim is to reduce conflicts and increase re-usability.

For now, let’s focus on the lib folder located at blast_crm/engines/core/lib. In this folder we’ll find the heart of our engine, but first we have to do a bit of reorganising.

Rearrange the lib folder until you have the structure shown in Listing 3. To achieve this result, run the following command from blast_crm/engines/core/ to create and update the relevant files and match the new structure:

cd lib && mkdir blast && mv core core.rb blast/ && touch blast_core.rb
Listing 0.3: The reorganised blast_crm/engines/core/lib folder
Box 4.4. Namespacing and Rails

Don’t forget that Rails uses your applications folder structure to determine class names and namespaces. This is why we needed to move everything inside the blast folder, in order to ensure that Rails knows that our files will have the extra namespace level.

4.4.3. blast_core.rb and core.rb

Before we write any actual code, we need to update the require paths in blast_core.rb and core.rb, to allow the parent application to discover the content of our engine, as shown in Listing 4 and Listing 5:

Listing 0.4: Updated blast_core.rb blast_crm/engines/core/lib/blast_core.rb
require 'blast/core'
Listing 0.5: Updated core.rb blast_crm/engines/core/lib/blast/core.rb
require_relative 'core/engine'

module Blast
  module Core

4.4.4. Define a version

In the future, the Core engine might be released as a gem, so we should give it a proper version straight away. We also need to add the top-level namespace (Blast), as shown in Listing 6:

Listing 0.6: Updated version.rb blast_crm/engines/core/lib/blast/core/version.rb
module Blast
  module Core
    VERSION = '0.1.0'

4.4.5. The Engine file

Each Ruby on Rails engine comes with a file named engine.rb. This file represents the heart of the engine. Since we’ve changed the structure of the engine, we need to add our new global namespace to this file, as well as remove the Core namespace from the isolate_namespace method (see Listing 7).

We won’t be namespacing the models and controllers with Core in the Core module. You will understand why in the next chapter, but for now, you should know that it makes extending the Core much easier.

Listing 0.7: Updated engine.rb blast_crm/engines/core/lib/blast/core/engine.rb
module Blast
  module Core
    class Engine < ::Rails::Engine
      isolate_namespace Blast
Box 4.5. What is isolate_namespace?

The isolate_namespace method is here to mark a clear separation between the engine’s controllers, models and routes, and the parent application’s entities. Without it, we might have unwanted conflicts and override issues.

4.4.6. Gemspec

Next, we need to fix the gemspec file for our Core engine, located at blast_crm/engines/core/core.gemspec.

First, we need to rename the file to blast_core.gemspec to match the namespaces we are now using. Run this command from the blast_crm/engines/core directory:

mv core.gemspec blast_core.gemspec

Then, we need to adjust some of the require paths and put some actual information in the specification.

Here’s the file with the changes:

Listing 0.8: Updated blast_core.gemspec blast_crm/engines/core/blast_core.gemspec
$LOAD_PATH.push File.expand_path('lib', __dir__)

# Maintain your gem's version:
require 'blast/core/version'

# Describe your gem and declare its dependencies: do |spec|        = 'blast_core'
  spec.version     = Blast::Core::VERSION
  spec.authors     = ['Devblast']       = ['']
  spec.homepage    = ''
  spec.summary     = 'Core features of blast_crm.'
  spec.description = 'Core features of blast_crm.'
  spec.license     = 'MIT'

  # Prevent pushing this gem to
  # To allow pushes either set the 'allowed_push_host'
  # to allow pushing to a single host or delete this section
  # to allow pushing to any host.
  if spec.respond_to?(:metadata)
    spec.metadata['allowed_push_host'] = "TODO: Set to ''"
    raise 'RubyGems 2.0 or newer is required to protect against ' \
      'public gem pushes.'

  spec.files = Dir['{app,config,db,lib}/**/*', 'MIT-LICENSE',
                   'Rakefile', '']

  spec.add_dependency 'rails', '~> 5.2.3'

  spec.add_development_dependency 'sqlite3', '~> 1.4.1'

That’s it for the gemspec file.

4.4.7. bin/rails

To make everything work smoothly, we need to change one small thing inside the engines/core/bin/rails file: We need to add blast to the ENGINE_PATH, as shown in Listing 9.

Listing 0.9: Updated rails file blast_crm/engines/core/bin/rails
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails gems
# installed from the root of your application.

ENGINE_ROOT = File.expand_path('..', __dir__)

# We changed the following line
ENGINE_PATH = File.expand_path('../lib/blast/core/engine', __dir__)

# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])

require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
require "sprockets/railtie"
# require "rails/test_unit/railtie"
require 'rails/engine/commands'

4.4.8. The Core routes

Now we need to deal with the routes.rb file. If you open the routes file for the Core engine located at blast_crm/engines/core/config/routes.rb, you will see that the Blast namespace is missing. Let’s add it as shown in Listing 10

Listing 0.10: Updated routes.rb file blast_crm/engines/core/config/routes.rb
Blast::Core::Engine.routes.draw do

4.4.9. Adding the module to the parent Gemfile

Let’s tie everything together. Go back to the parent application and open the blast_crm/Gemfile file. All we have to do now is add our module. If your Gemfile already contains the line gem 'core', path: 'core', update it to the value below. If not, simply add it at the end of the file:

Listing 0.11: The Gemfile blast_crm/Gemfile

gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]

# We added/updated the following line
gem 'blast_core', path: './engines/core'

4.10 Get the dependencies

Now, do a quick bundle install from the parent application and everything should work! If not, review the setup and see if you have changed everything correctly.

bundle install

If you’ve set up everything properly, the dependencies should be resolved without issues.

4.11 Mount the engine

The Core module is now integrated inside the parent application, but it’s not actually accessible yet. For that, we need to mount it inside the parent application’s routes file:

Listing 0.12: Mounted the Core engine blast_crm/config/routes.rb
Rails.application.routes.draw do
  mount Blast::Core::Engine => '/', as: 'blast'

4.12 Am I alive? Can you see me?

Et voila, we are done with the setup. You should be able to start your Rails server now, but you will end up on the default Rails page if you try to access it. How about adding some content?

4.5. Creating the first controller

To keep things simple, we won’t be writing tests in a TDD (Test-Driven Development) style. Instead, in a future chapter, we’ll go over the testing tools’ setup and write a few tests to see how we can test our modular application.

The first controller we will create is the dashboard_controller. The Dashboard is where users will land once they have logged in. On this page, they should be able to see an overview of the recent data they have access to.

For now, we will create an empty page, because we simply don’t have anything to show yet. In the next chapters, we will be adding content to that page with each new module we create.

4.5.1. Reorganize the controllers folder

The first step, before continuing, is to rearrange the controllers folder inside the Core engine. Since we will be using Blast to namespace our Core controllers instead of Core, we need to rename the core/ folder. Here’s a command you can run from the engine folder (engines/core/) to get the structure we want:

mv app/controllers/core app/controllers/blast

Your controllers folder should look like the Listing 13:

Listing 0.13: Updated Controllers path

We also need to update the engine’s ApplicationController to what is show in Listing 14:

Listing 0.14: Updated Core engine’s ApplicationController core/app/controllers/blast/application_controller.rb
module Blast
  class ApplicationController < ActionController::Base
    protect_from_forgery with: :exception

4.5.2. Create the Dashboard controller

Now, we can create the Dashboard controller, which will only contain an index action. The command to create it is below, which you must run from inside the engine:

touch app/controllers/blast/dashboard_controller.rb

Listing 15 shows what content we must provide:

Listing 0.15: Contents of dashboard_controller.rb core/app/controllers/blast/dashboard_controller.rb
module Blast
  class DashboardController < ApplicationController
    def index
Box 4.6. Using the generator

We could’ve used the Rails generator to create the controller, however that would create extra files that we are not ready to deal with yet, or we might not want. Since all we want is the controller (with no assets, helpers or views - for the moment) all we need for our controller to work is the file we just created.

4.5.3. Add the corresponding route

And for the route of this controller:

Listing 0.16: Contents of dashboard_controller.rb core/config/routes.rb
Blast::Core::Engine.routes.draw do
  root to: 'dashboard#index'

Now, let’s try to access the application. Run rails s and head over to localhost:3000. Or don’t, because it’s going to crash anyway. We have to fix the views before we can see anything.

4.5.4. Fix the layout and add the index view for the dashboard

Once again, due to the namespace we added, we need to create an extra folder for the views. Change your views folder until you have the structure below (or simply run the command from within engines/core/):

mv app/views/layouts/core app/views/layouts/blast && \
mkdir -p app/views/blast/dashboard && \
touch app/views/blast/dashboard/index.html.erb
Listing 0.17: Updated views folder structure

Now, if you access localhost:3000, the application works and returns a beautiful white screen.

Box 4.7. Using the generator (Revisited)

Ok… we lied a little. But we promise, it was for your own good. You could have used the generator. If you had run

rails g controller dashboard --no-assets --no-helper

rails would have created the dashboard controller in the correct directory, and would have even correctly created the /views/blast/dashboard directory. The reason we took the long route is because we wanted to make sure you understood what goes where, so that you know where to look for stuff when things go wrong.

4.6. Styling our application

4.6.1. Add the gems

We’re going to use the Bootstrap Ruby Gem to style BlastCRM. To do this, we need to add a few gems to the gemspec of the Core engine, as show in Listing 18.

Listing 0.18: Added Bootstrap dependencies core/bast_core.gemspec
  spec.add_dependency 'rails', '~> 5.2.3'

  spec.add_dependency 'bootstrap', '~> 4.3.1'
  spec.add_dependency 'jquery-rails', '~> 4.3.3'
  spec.add_dependency 'sass-rails', '~> 5.0'

  spec.add_development_dependency 'sqlite3', '~> 1.4.1'

When used inside an engine, gems are not automatically loaded. To load them, we need to require them inside blast_crm/engines/core/lib/blast/core.rb, as you can see below:

Listing 0.19: Requiring Bootstrap dependencies blast_crm/engines/core/lib/blast/core.rb
require_relative 'core/engine'
require 'sass-rails'
require 'bootstrap'
require 'jquery-rails'

module Blast
  module Core

Keep this in mind when you start working on your own engines.

Let’s run bundle install from the parent application to get everything installed. Don’t forget to restart your server.

4.6.2. Fix the assets folder

Once again, we have to fix and rearrange the assets folder to add one more level. Change your assets folder inside the Core engine to look like it does in Listing 20, or use the following commands (run them from within the Core engine folder):

mv app/assets/images/core app/assets/images/blast &&
mv app/assets/javascripts/core app/assets/javascripts/blast &&
mv app/assets/stylesheets/core app/assets/stylesheets/blast
Listing 0.20: New Core engine’s assets folder structure

4.6.3. Load the Bootstrap CSS

Since we’re going to use Bootstrap, we need to change the application.css file. Simply rename it to application.css.scss and change its content to that of Listing 20:

mv app/assets/stylesheets/blast/application.css \
Listing 0.21: New contents of engine’s application.css core/app/assets/stylesheets/blast/application.css.scss
@import "bootstrap";

4.6.4. Load the Bootstrap JavaScript files

Let’s add the Bootstrap javascript files in the application.js file:

Listing 0.22: New contents of engine’s application.js core/app/assets/stylesheets/blast/application.js
//= require rails-ujs
//= require activestorage
//= require jquery3
//= require popper
//= require bootstrap
//= require_tree .

Time to write some HTML.

4.6.5. Update the layout file

Because we’ve changed the assets folder structure, we need to update the layout file by changing the stylesheet and javascript inclusion links. We need to prefix them with blast/ instead of core/:

Listing 0.23: Updated application.html.erb layouts file core/app/views/layouts/blast/application.html.erb
<!DOCTYPE html>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    "blast/application", media: "all" %>
    <%= javascript_include_tag "blast/application" %>

    <%= yield %>


4.6.6. Add content to the layout file

Since we added Bootstrap, let’s leverage it. We’re going to update the file we’ve just modified with some content. Paste the code from Listing 24 in the <body> tag of the application.html.erb file, so that the file can look like Listing 25:

Listing 0.24: Code to paste in <body> tag
<nav class="navbar navbar-expand-lg navbar-light bg-light
            navbar-inverse navbar-fixed-top mb-4">
  <%= link_to 'BlastCRM', blast.root_path, class: 'navbar-brand' %>

<div class='container' role='main'>
  <%= yield %>
Listing 0.25: Using bootstrap in application.html.erb layouts file core/app/views/layouts/blast/application.html.erb
<!DOCTYPE html>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    "blast/application", media: "all" %>
    <%= javascript_include_tag "blast/application" %>
    <nav class="navbar navbar-expand-lg navbar-light bg-light
                navbar-inverse navbar-fixed-top mb-4">
      <%= link_to 'BlastCRM', blast.root_path, class: 'navbar-brand' %>

    <div class='container' role='main'>
      <%= yield %>

This is some pretty regular Bootstrap usage. We added a navbar that will contain the links of our menu, and we surrounded the yield call with the container class provided by Bootstrap. The jumbotron is just here to add a bit of style and break with the blank design.

Something important to note, however, is the use of blast.root_path. Why don’t we just put root_path? Well, it’s due to the Devise gem. Without blast in front of every route, the app will crash when we try to access the views from Devise in the future. There is some kind of path conflict when using Devise inside a namespaced engine. Regardless, it is good practice to prefix all our paths with our namespace (blast) in order to avoid any potential problem in the future.

If you check your browser, you should see what is shown in Figure 1.
Figure 1: So white… So empty....

4.6.7. Add a title to the dashboard

The last step before creating our first model, is to give a title to the blank dashboard. Just add the following code to the dashboard index view.

Listing 0.26: Title for the blank Dashboard core/app/views/blast/dashboard/index.html.erb
Figure 2: Now, it looks pretty good... right?

4.7. Versioning our project with Git

Now that we have a basic application, we are going to use Git to version it.

Git is a great tool that I’m hoping you are already using. If not, let me give you a quick introduction:

“Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.” Git website

Basically, Git allows you to version your code and collaborate on it with other people. The best part is that it makes it very simple to do these two things. You will see how great Git is as we use it throughout this book for BlastCRM.

Box 4.8. GUI for Git

There are many tools with a nice graphical interface to manage Git repositories. If you prefer a more minimalistic option, GitX is a good one. A popular choice is SourceTree, so give it a try if you’re looking for a tool to handle the entire Git flow in a graphical way.

If you already have your favorite, feel free to use it, however, the instructions in this book will be oriented towards running Git in a terminal so that you can learn its various commands. Git on the command line allows for more control.

4.7.1. Installing Git

On Mac OS X

You can use Homebrew to install Git on Mac OS x:

brew install git

If you don’t want to use Homebrew, checkout this link to install Git.

On Linux

Just use your packet manager to install Git. For Debian-based distribution, use the following command:

sudo apt-get install git

4.7.2. The Initial Commit

The first thing we need to do is to tell Git that we want to version the current project. At the root of the project, run the following command:

git init

Then we are going to stage all the files as ready to be committed:

git add .

Finally, we can commit the staged changes:

git commit -m "Initial Commit"

You can create an account on GitHub if you want to push your changes to a remote repository. If you prefer to store your repositories on your own servers, there are plenty of alternatives, like Gitolite. We’ll be using GitHub as an example for the rest of this book.

Once you’ve signed up and created a repository, grab the URL and add it as the origin for your repository with:

git remote add origin YOUR-GITHUB-REPO-URL

Don’t forget to replace YOUR-GITHUB-REPO-URL with an actual URL.

Finally, we can push all our local changes to the remote repository on GitHub, using the git push command.

git push origin master
Box 4.9. Branches as Chapters

We have just committed all our code to the master branch. In order for you to follow the code easily within the source code, we will be creating a new branch for every chapter. This means that if you want to follow what we do in Chapter 7, all you need to do is checkout Chapter 6, branch off and you will be able to code along with us. If you want to compare your results with ours, simply checkout Chapter 7 and ensure that they look the same.

4.8. Wrap Up

WOW! That was one heck of a chapter. In this chapter we set up our application in a modular way, and checked it into version control. We have also defined the structure we need in order to proceed with our next step, authentication.

4.8.1. What did we learn?

  • Engines are generated with rails plugin new core --mountable
  • We need an empty Ruby on Rails application to hold our engines as gems
  • When using gems inside an engine, they need to be required when the engine is loaded

4.8.2. Next Step

In the next chapter, we’ll work on restricting the access to BlastCRM through authentication.