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
.
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:
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.
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.
Linking the User
model with the Contact
model will give us the option to show only the relevant contacts for each user.
The dashboard is currently empty. We’re going to extend it, and add a list of the last contacts.
Contacts
engineIn 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
deface
and modular_engine
gemsAdd the modular_engine
and deface
gems to the parent Gemfile
, and run bundle install
.
blast_crm/Gemfile
.
.
.
gem 'modular_engine', '~> 0.9.5'
gem 'deface', '~> 1.4.0'
gem 'blast_core', path: './engines/core'
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.
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.
Lets first fix our contacts_gemspec
file:
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
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:
blast_crm/engines/contacts/Gemfile
.
.
.
gem 'blast_core', path: '../core'
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
:
contacts/config/routes.rb
Blast::Core::Engine.routes.draw do
end
Let’s add the new engine to the 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.
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:
test/
directory with the following code:
rm -r test/
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'
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.
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.
Once again,
git status
git add .
git commit -m "Added Contacts engine and added the Contact Model"
git push origin Chapter-9
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.
modular_engine
gem
Contacts
engine with the existing Core
engine
Contacts
routes to extend the Core
module’s routes
In the next chapter we will create the first controller for this module, the Contacts
controller.