Spellbook

by Drew Barontini

We’re frequently building sites or applications that don’t quite lend themselves to being built with a JavaScript framework, such as Angular, Ember, Backbone, React, etc. Sometimes we only need a collection of components and architectural conventions that can make our application more manageable. That’s where Spellbook comes in.

What is it?

A compilation of CoffeeScript structure, functions, classes, and modules.

At Code School, our front-end team writes and builds JavaScript, frequently without the use of a JavaScript framework. In these instances, we’ve found it helpful to, much like with our CSS/Sass, establish conventions and common patterns and components for writing JavaScript.

Spellbook is a collection of functions, classes, and modules, written in CoffeeScript, combined with a set of architectural conventions for organizing our JavaScript. It is not a framework, and, because each component is independent from the next, you can selectively choose what to include in your project.

The structure is composed of the Compendium and Components, with each section broken down into individual sub-sections. Let’s take a look.

Compendium

The Compendium contains three parts:

  1. application.coffee - the manifest
  2. spellbook.coffee - the namespace
  3. domready.coffee - code run when the document is ready

Manifest

The Manifest file is where we load the files, and it generally uses Sprockets to require the individual files in a particular order.

#= require spellbook
# ...

If you’re working in an environment where this isn’t available, you can concatenate scripts using a build tool like Gulp, or manually include them in your HTML file.

Namespace

The Namespace file is where, as you’d expect, you set up your namespaces for your application. There is a set of defaults, but you can easily change and add to them, as you see fit.

@Spellbook          = {}
@Spellbook.Globals  = {}
@Spellbook.Classes  = {}
@Spellbook.Helpers  = {}
@Spellbook.Modules  = {}
@Spellbook.Services = {}
@Spellbook.Inbox    = {}

These are the default Spellbook namespaces, but you will change these to fit your project, like so:

@ProjectName          = {}
@ProjectName.Globals  = {}
@ProjectName.Classes  = {}
@ProjectName.Helpers  = {}
@ProjectName.Modules  = {}
@ProjectName.Services = {}
@ProjectName.Inbox    = {}

Document Ready

If you’ve written JavaScript where the DOM is being manipulated, you’ve most likely put that code inside of a “document ready,” which is essentially a function that will run once the document is ready to be manipulated. This file in the Compendium is where you will add your code that does just that.

jQuery ($) ->

  # ...

Yes, there is a jQuery dependency in Spellbook. We don’t mind the inclusion of additional page weight because of all the niceties that jQuery provides. However, it can be swapped out for a more lightweight library, or a home-grown solution.

Components

Components are either a Function, Class, or Module.

Function

A function is classified as either a Helper or a Service.

Helper

A Helper is a one-off utility function that performs a single action. Some examples:

  • Spellbook.sanitize() - Sanitizes a string to make it safe
  • Spellbook.slugify() - Converts a string to a URL-friendly slug
# *************************************
#
#   Helper Name
#   -> Description
#
# *************************************
#
# @param item { string }
#
# *************************************

@Spellbook.Helpers.helperName = ( item ) ->
  # ...

# -------------------------------------
#   Usage
# -------------------------------------
#
# Spellbook.Helpers.helperName()
#

A template for a Helper.

Service

A Service is a function that can perform multiple actions, but a single service, and it is written neither as a class or module. For example:

  • Spellbook.autoSubmit() - Submits form on input update
  • Spellbook.scrollTo() - Animates scrolling to element

There is some confusion with the name “Service” as it relates to that name in frameworks like Angular. Although it’s applicable here, the name might change in the future to avoid any confusion.

# *************************************
#
#   Service Name
#   -> Description
#
# *************************************
#
# @param $element  { jQuery object }
#
# *************************************

@Spellbook.Services.serviceName = ( options ) ->
  settings = $.extend
    $element: $( '.js-element' )
  , options

  # ...

# -------------------------------------
#   Usage
# -------------------------------------
#
# Spellbook.Services.serviceName
#   # ...
#

A template for a Service.

Class

A Class is, as you’d expect, a CoffeeScript class. Classes are created when you need an instance of the Class (object) that you can then call methods and properties on, or when you have a parent class with shared functionality. However, the latter doesn’t apply in Spellbook (at least for now) because of the independent nature of the components.

# *************************************
#
#   Class Name
#   -> Description
#
# *************************************
#
# @param $element  { jQuery object }
# @param className { string }
#
# *************************************

class @Spellbook.Classes.ClassName

  # -------------------------------------
  #   Private Variables
  # -------------------------------------

  _settings : {}

  # -------------------------------------
  #   Constructor
  # -------------------------------------

  constructor: ( @options ) -> @init()

  # -------------------------------------
  #   Initialize
  # -------------------------------------

  init: ->
    @_settings = $.extend
      $element : $( '.js-element' )
    , @options

    @_setEventHandlers()

  # -------------------------------------
  #   Set Event Handlers
  # -------------------------------------

  _setEventHandlers: ->
    # ...

# -------------------------------------
#   Usage
# -------------------------------------
#
# new Spellbook.Classes.ClassName()
#

A template for a Class.

Module

A Module is written in the Revealing Module Pattern. Modules are created when you don’t specifically need to operate on an object, but need shared properties and methods with an explicit privacy interface.

# *************************************
#
#   Module Name
#   -> Description
#
# *************************************
#
# @param $element  { jQuery object }
#
# *************************************

@Spellbook.Modules.ModuleName = do ->

  # -------------------------------------
  #   Private Variables
  # -------------------------------------

  _settings = {}

  # -------------------------------------
  #   Initialize
  # -------------------------------------

  init = ( options ) ->
    _settings = $.extend
      $element : $( '.js-element' )
    , options

    _setEventHandlers()

  # -------------------------------------
  #   Set Event Handlers
  # -------------------------------------

  _setEventHandlers = ->
    # ...

  # -------------------------------------
  #   Public Methods
  # -------------------------------------

  init : init

# -------------------------------------
#   Usage
# -------------------------------------
#
# Spellbook.Modules.ModuleName.init()
#

A template for a Module.

Why use it?

So when would Spellbook be something you might use?

  • You only want functionality that your project needs (keep things lightweight)
  • You want namespaced components that can co-exist with other libraries
  • You need to easily modifiy core functionality of components

Testing

In addition to the conventions and usefulness of having a library of components for projects, we also wanted to make sure that our JavaScript was properly tested. With this, we included Jasmine (and Jasmine jQuery) for writing tests for the individual components. There isn’t 100% test coverage due to weirdness with testing particular functionalities in JavaScript, but we, at the time of writing this, have 97 passing specs. We’ll take that for now.

CoffeeScript Styleguide

As you’ve most likely seen, we’re big fans of CoffeeScript. With the rise of ES6, and its implementation of several CoffeeScript-style features natively, CoffeeScript will have to evolve. However, for now, we’re still fans, and we’ve started the documentation for how we like to write JavaScript and, more particularly, CoffeeScript. Although some of the conventions are strictly specific to CoffeeScript, there are several others that apply to JavaScript more universally. It’s a work-in-progress.

Moving Forward

So that’s where we’re at now. We’re constantly refining and improving Spellbook as we work on new projects. It’s a good start, but there is still room for improvement.