In the previous chapter, we built our first feature
engine, Contacts
. We saw how we could extend the Core
module using the Deface gem and class_eval
. In this module, we’re going to keep extending the Core
module, but now we will also extend the Contacts
module. We don’t want Tasks
, though, to depend on Contacts
, so we’ll see how we can link them while still having the option to run them separately.
Tasks
moduleThe Tasks
module will allow users to add tasks, and assign them to contacts (if the Contacts
module is also loaded). Most of this module will be a bit boring, because we will be performing the same steps we did for the Contacts
module, so feel free to copy/paste the code until we get to Chapter cha:04_16_tasks_listing_tasks_in_contacts, where we will be extending the Contacts
module.
When we start extending the Contacts
module, we will see how we can check if another engine is loaded, which will keep the Tasks
engine independent. This will allow us to run the Tasks
module even if we decide to remove the Contacts
module.
The Tasks
module will include:
We’re going to add the basic views to create, edit and delete tasks. Users will be able to manage their tasks and assign them to other users and contacts.
Typing /tasks
in the URL bar every time we want to see our tasks wouldn’t be very user-friendly, so we will add a link in the main navigation, just like we did for Contacts.
A user will be able to have many contacts, and tasks and contacts will be able to have many tasks associated with them. For example, a contact could have the following tasks linked to them: ‘call John at 09:00 tomorrow’ or ‘send email to check if still interested’. We will show a list of tasks under each contact.
Tasks
engineLet’s not forget to create a new branch for this chapter:
git checkout -b Chapter-14
Generate the engine by running the command below:
rails g modular:engine engines/tasks --namespace=Blast
Update the blast_tasks.gemspec
file with the contents of Listing 1:
engines/tasks/blast_tasks.gemspec
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "blast/tasks/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "blast_tasks"
s.version = Blast::Tasks::VERSION
s.authors = ["Devblast"]
s.email = ["info@devblast.com"]
s.homepage = "https://devblast.com"
s.summary = "Task feature for BlastCRM."
s.description = "Task 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
test/
folderSince the modular_engine
gem created our test folders, we have to remove them manually:
rm -r engines/tasks/test/
tasks/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'
Add the Core gem to the Tasks gemfile, as shown below:
blast_crm/engines/tasks/Gemfile
.
.
.
gem 'blast_core', path: '../core'
Update the Tasks
engine route file to extend the parent application’s route file:
engines/tasks/config/routes.rb
Blast::Core::Engine.routes.draw do
end
Add the Tasks
engine to the parent application’s Gemfile, as shown below:
blast_crm/Gemfile
.
.
.
gem 'blast_core', path: './engines/core'
gem 'blast_contacts', path: './engines/contacts'
gem 'blast_tasks', path: './engines/tasks'
bundle install
Run the bundle install
command from the parent app folder, and restart your server.
As with Contacts
, the first step to building our Tasks
engine is to create the Task
model.
Step 1: Navigate to the Tasks engine folder and run bundle install
Step 2: Generate the Task model
A task belongs to a user, and to a potential contact. This command creates the desired model:
rails g model Task title:string content:text user:references \
contact:references --no-test-framework
Step 3: Correct the users
reference
Go to the Task
model migration, and change the following 2 lines:
t.references :user, foreign_key: true
t.references :contact, foreign_key: true
to
t.references :user, foreign_key: { to_table: :blast_users }
t.references :contact, foreign_key: { to_table: :blast_contacts_contacts }
Step 4: Make Contact relation optional
In Rails 5.2, belongs_to
have a built-in presence validation. In other words, if we were to try and create a task without a contact, it will give us an error that the contact is missing. Since that is not what we want, and we do, in fact, want to create a task without a contact, we need to make the belongs_to
optional, by updating the model to what you see in Listing 5:
app/models/blast/tasks/task.rb
module Blast::Tasks
class Task < ApplicationRecord
belongs_to :user
# Made :contact optional
belongs_to :contact, optional: true
end
end
Step 5: Migrate!
Run rails db:migrate
from the parent app folder.
That’s it! Now to add the Tasks Controller.
Once again,
git status
git add .
git commit -m "Added Tasks engine and added the Task Model"
git push origin Chapter-14
We have now created our second business module, the Tasks
module.
In the next chapter we will create our views and controllers for this module.