Chapter 11

The Contacts Module: Extending Views

11.1. Extending the Core view

So how are we going to extend the views that we wrote in the Core engine from the Contacts engine? We’re going to use a neat little gem called Deface, created by Spree.

Thanks to this gem, we can define hooks in our views and easily extend them. It’s really easy to extend views, as you’re about to see.

But first things first:

git checkout -b Chapter-11

Step 1: The deface Gem

We need to make sure the deface gem is in our engine gemspec.

Box 11.1. We already added deface

Don’t forget that we already added the deface gem in our Gemfiles in Chapter cha:03_09_contacts_setup, Section sec:03_09_adding_deface_gem, when we were setting up our engine.

Listing 0.1: Deface gem in blastcontacts.gemspec contacts/blast_contacts.gemspec
s.add_dependency "deface", '~> 1.4.0'

Step 2: Require Deface

If you remember correctly, we need to require this gem in blast_crm/engines/contacts/lib/blast/contacts.rb:

Listing 0.2: Requiring deface gem contacts/lib/blast/contacts.rb
require "deface"

module Blast
 module Contacts

Step 3: Bundle install

Let’s run bundle install from the parent app to get Deface and restart the server to load our new gem.

Step 4: Add your first hook

Now, we can start on the interesting stuff.

First, what’s a hook? In Deface terms, it’s an element in your view that you can use to add some code, either before, after, inside and so on. Theory sucks so let’s create a hook right now to understand what it really is!

We’re going to add a hook inside the Core engine in the layout file.

Locate the piece of code in Listing 3:

Listing 0.3: Adding hook in Core layout core/app/views/layouts/blast/application.html.erb
<!-- ... -->

<ul class="navbar-nav mr-auto">
  <li data-blast-hook='main_nav' class="nav-item <%= active(blast.root_path) %>">
    <%= link_to 'Dashboard', blast.root_path, class: 'nav-link' %>

<!-- ... -->

Did you notice what we added? Did you see the data-blast-hook='main_nav'?

With this, we basically defined this list element as a hook. With Deface, we can now add some code before, or after, it very easily. Note that it doesn’t have to be a data-attribute. It can be literally anything: an ID, a class or any kind of selectable element. To keep the code clean we used a unique data-attribute with different values. It makes it easy to find your hooks.

Now that we have a hook, we can create an override!

Step 5: Create an override

A Deface override is a small piece of code that will detect a hook and insert the code we tell it to. There is actually another way to use Deface, using their custom DSL, but we prefer the solution we’re going to show you. You can learn more about the other technique on the Deface GitHub page.

If you didn’t see it yet, there is a folder named “overrides” in blast_crm/engines/contacts/app. It was generated by modular_engine and will be used to store our overrides.

Let’s create an override file named add_contacts_link_to_nav.rb and put the contents of Listing 4 inside it (run the command from the Contacts engine):

touch app/overrides/add_contacts_link_to_nav.rb

Basically, we’re instantiating the Deface::Override class with a set of options. Wondering what each option means? The following is from the Deface GitHub page:

  • virtual_path

    The template / partial / layout where the override should take effect eg: shared/_person, admin/posts/new this will apply to all controller actions that use the specified template.

  • name

    Unique name for override so it can be identified and modified later. This needs to be unique within the same :virtual_path.

  • insert_after

    Inserts after all elements that match the supplied selector. One of the many options available like insert_before, insert_top and so on. Check the documentation for more options!

  • partial

    Relative path to a partial.

  • namespaced

    Namespace the override to avoid conflicts with other engines.

  • original

    String containing original markup that is being overridden. If supplied Deface will log when the original markup changes, which helps highlight overrides that need attention when upgrading versions of the source application. Only really warranted for :replace overrides. NB: All whitespace is stripped before comparison.

These are just the options we’ll be using in this book. Don’t hesitate to check what else you can do and adapt what we’re showing you to match your needs.

Now, let’s see how it looks by navigating to http://localhost:3000/contacts:
Figure 1

Oops… We didn’t create the override view that’s supposed to be inserted.

Step 6: Create a view for the override

We can create the override view in app/views/blast/contacts/app/overrides/. The name needs to be the same as the one we defined in the previous step in :partial. In our case, the file should be named _contacts_link.html.erb, with the contents of Listing 5:

touch app/views/blast/contacts/overrides/_contacts_link.html.erb

And after a quick refresh, we get:
Figure 2

Wow, isn’t that great? Feel free to play with it… try removing the Contacts engine from the Gemfile, run bundle install, restart the server and refresh the page. No contacts link anymore! Now you know how easy it is to extend a view in another engine. Just to be sure, we’ll do it a few more times in the next chapters.

How about we extend some models now?

11.2. Pushing Our Changes

Once again,

  1. Check the changes:
    git status
  2. Stage them:
    git add .
  3. Commit them:
    git commit -m "Extending Contacts Views"
  4. Push to your GitHub repo if you’ve configured it:
    git push origin Chapter-11

11.3. Wrap Up

In this chapter we extended the Core module views so that our Contacts views are plug-and-playable.

11.3.1. What did we learn?

  • Implemented the deface gem to allow for extending views using overrides.

11.3.2. Next Step

In the next chapter we will learn how to extend Models and Controllers.