Chapter 14

The Tasks Module: Set Up

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.

14.1. What is the Tasks module

The 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:

  • Task CRUD (models/controllers/views)

    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.

  • A “Tasks” link in the main navigation

    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.

  • Linking between User, Contact and Task

    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.

14.2. Generating the Tasks engine

14.2.1. Create a new branch

Let’s not forget to create a new branch for this chapter:

git checkout -b Chapter-14

14.2.2. Generate the engine

Generate the engine by running the command below:

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

14.2.3. Fix the engine gemspec

Update the blast_tasks.gemspec file with the contents of Listing 1:

Listing 0.1: Updated Tasks gemspec file 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

14.2.4. Remove the test/ folder

Since the modular_engine gem created our test folders, we have to remove them manually:

  1. We already removed the line that loads the test directory from the gemspec file.
  2. Remove the test directory:
    rm -r engines/tasks/test/
    
  3. Update the 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'
    

14.2.5. Add the Core gem to the Tasks engine gemfile

Add the Core gem to the Tasks gemfile, as shown below:

Listing 0.2: Added Core gem to Tasks Gemfile blast_crm/engines/tasks/Gemfile
.
.
.
gem 'blast_core', path: '../core'

14.2.6. Update the routes file

Update the Tasks engine route file to extend the parent application’s route file:

Listing 0.3: Updating Tasks routes.rb file engines/tasks/config/routes.rb
Blast::Core::Engine.routes.draw do
end

14.2.7. Add the new engine to the Parent Gemfile

Add the Tasks engine to the parent application’s Gemfile, as shown below:

Listing 0.4: Adding Tasks engine to parent Gemfile blast_crm/Gemfile
.
.
.
gem 'blast_core', path: './engines/core'
gem 'blast_contacts', path: './engines/contacts'
gem 'blast_tasks', path: './engines/tasks'

14.2.8. Run bundle install

Run the bundle install command from the parent app folder, and restart your server.

14.3. Task Model

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:

Listing 0.5: Optional contact in Task model 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.

14.4. Pushing Our Changes

Once again,

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

14.5. Wrap Up

We have now created our second business module, the Tasks module.

14.5.1. Next Step

In the next chapter we will create our views and controllers for this module.