Chapter 9

The Contacts Module: Set Up

In the previous chapters we built our first module, the Core module. We are now about to tackle building on top of that engine, and more importantly, how to extend it. We will learn all this by building a new module: Contacts.

9.1. What is the Contacts module?

In most CRMs, you will find a way to manage contacts. This is where users can fill in information about their clients, leads, and so on. BlastCRM will follow the same principle, and we will give users a way to manage their contacts. We will also extend the dashboard to add a summary of a user’s last contacts.

In this new module, we will implement the following:

  • Contact CRUD (models/controllers/views)

    We’re going to add the basic views to create, edit and delete contacts. Users will be able to manage their contacts with these views. Initially, we will simply list all the contacts.

  • A “Contacts” link in the main navigation bar

    We want to be able to easily access the list of contacts from the menu, and that means hooking up in the navigation we built in the Core module.

  • Users and Contacts relationship

    Linking the User model with the Contact model will give us the option to show only the relevant contacts for each user.

  • Contact overview on the dashboard

    The dashboard is currently empty. We’re going to extend it, and add a list of the last contacts.

9.2. Generating the Contacts engine

In the previous chapter we had to do a lot of folder-rearranging, which was quite boring. You’ll be happy to know that there is a better way to generate a modular engine the way it should look, but for the first chapter we wanted you to have a deeper understanding of how and why we organized the engine like we did, so we went through all the annoying steps. Now, we’re about to generate a new engine, a feature module, which will get plugged in on a Core type engine.

To do this, Thibault wrote a small and simple gem that we can use, called modular_engine. Let’s see how it works, but first, let’s checkout a branch for this chapter:

git checkout -b Chapter-9

9.2.1. Adding the deface and modular_engine gems

Add the modular_engine and deface gems to the parent Gemfile, and run bundle install.

Listing 0.1: Add modular_engine and deface to parent Gemfile blast_crm/Gemfile
.
.
.
gem 'modular_engine', '~> 0.9.5'
gem 'deface', '~> 1.4.0'

gem 'blast_core', path: './engines/core'
Box 9.1. What is deface doing there?

No, this is not a typo. Deface is a gem that we will be using in Chapter cha:03_11_contacts_extending_views, when we will be discussing extending the Core views. We have included it from now, as it is used by the modular_engine generator.

9.2.2. Generating the engine

Run the following command from the parent application folder:

rails g modular:engine engines/contacts --namespace=Blast

And we get an almost ready to use engine! Great, isn’t it?

Our next step will be to edit a few values in the gemspec (homepage, summary, description) and add the Core engine as a dependency.

9.2.3. Fixing the newly generated engine gemspec

Lets first fix our contacts_gemspec file:

Listing 0.2: Updated Contacts gemspec blast_crm/engines/contacts/blast_contacts.gemspec
$:.push File.expand_path('lib', __dir__)

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

# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
  s.name        = 'blast_contacts'
  s.version     = Blast::Contacts::VERSION
  s.authors     = ['Devblast']
  s.email       = ['info@devlast.com']
  s.homepage    = 'https://devblast.com'
  s.summary     = "Contact feature for BlastCRM."
  s.description = "Contact feature for BlastCRM."
  s.license     = 'MIT'

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

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

  s.add_dependency 'deface', '~> 1.4.0'
end

9.2.4. Adding the Core gem to the Contacts engine Gemfile

Now lets add the Core gem as a dependency. We are working with local gems and the method s.add_dependency won’t let us specify a local path. To load the gems correctly, we need to add blast_core in the Contacts Gemfile with the local path, as you can see in Listing 3 below:

Listing 0.3: Added Core gem to Contacts Gemfile blast_crm/engines/contacts/Gemfile
.
.
.
gem 'blast_core', path: '../core'

9.2.5. Updating the routes file

Since we are building this engine on top of the Core and we want to make it easy to work with a set of engines, all our feature engines will extend the Core routes instead of implementing their own. How do we do that? Well, it’s very simple. Just open the Contacts routes file and change Contacts to Core:

Listing 0.4: Updating Contacts routes.rb file contacts/config/routes.rb
Blast::Core::Engine.routes.draw do
end

9.2.6. Adding the new engine to the Parent Gemfile

Let’s add the new engine to the parent Gemfile:

Listing 0.5: Adding Contacts engine to parent Gemfile blast_crm/Gemfile
.
.
.
gem 'blast_core', path: './engines/core'
gem 'blast_contacts', path: './engines/contacts'

Now you can run a quick bundle install from the parent app, restart your server and everything should work. See? That wasn’t that hard thanks to the modular_engine gem.

Box 9.2. The resurrection of the Test folder

You might have noticed that our contacts engine has a test/ directory. This is because the version of the modular_engine gem that we’re using uses the command –skip-test-unit when creating the engine, instead of the updated –skip-test version. This is not difficult to rectify. All you need to do is:

  • Remove the test/ directory with the following code:
    rm -r test/
    
  • Update contacts/bin/rails file to the following:
    .
    .
    .
    # We commented this out so that we can choose what we want to include
    # require 'rails/all'
    # We added all the frameworks, excluding the test_unit one
    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'
    
  • Remove the below line from the blast_contacts.gemspec file, that includes the deleted test/ directory:
    s.test_files = Dir['test/**/*']
    

Don’t forget to run bundle install and restart your server.

Now you won’t have to worry about Rails generating test files when you generate Models, Controllers, etc., and we can add our own RSpec files.

Let’s start adding some content to the Contacts engine.

9.3. Contact Model

The first step to building the Contacts engine is to create the Contact model. We’ll then work on the controller.

Step 1: Navigate to the Contacts engine folder and run bundle install

Step 2: Generate the Contact model

rails g model Contact first_name:string last_name:string company:string \
email:string phone:string user:references --no-test-framework

Step 3: Correct the users reference

You will notice that the migration that was created contains the line:

t.references :user, foreign_key: true

Although this is technically correct, if you try and add a Contact you will be faced with a

SQLite3::SQLException: no such table: main.users

error. This is because our User table is not called users but blast_users. To fix this, we simply need to update the reference line to reflect that table name, as shown below:

t.references :user, foreign_key: { to_table: :blast_users }

Step 4: Migrate!

Run rails db:migrate from the parent app folder.

That’s it! Now to add the Contacts Controller.

9.4. Pushing Our Changes

Once again,

  1. Check the changes:
    git status
    
  2. Stage them:
    git add .
    
  3. Commit them:
    git commit -m "Added Contacts engine and added the Contact Model"
    
  4. Push to your GitHub repo if you’ve configured it:
    git push origin Chapter-9
    

9.5. Wrap Up

In this chapter we created our first business module, the Contacts module. We did this in a much quicker way by using Thibault’s modular_engine gem, and we created our first model, the Contact model. Finally, we integrated it with the Core module.

9.5.1. What did we learn?

  • How to use Thibault’s modular_engine gem
  • Integrated our new Contacts engine with the existing Core engine
  • Updated the Contacts routes to extend the Core module’s routes

9.5.2. Next Step

In the next chapter we will create the first controller for this module, the Contacts controller.