By Ed Jones, on Feb 23, 2015

ContentfulModel and ContentfulRails Ruby gems help building Ruby apps faster

We are continuing the series of features of our partners contributions to the Contentful developer ecosystem. Today our guest author is Ed Jones, director and co-owner of Error, a Liverpool-based digital creative studio. Ed and his team have created two Ruby gems that simplify and quicken developing Ruby apps with Contentful.

At Error we’ve developed websites, applications and interactive content for lots of interesting organisations. One thing we’ve learned over the years is that building bespoke content management systems is difficult! That’s why we’re so pleased to be a Contentful partner, and let them take care of content management complexity.

We’re always working to standardise our processes and workflow, so we don’t have to keep reinventing the wheel for each client. We can develop faster; clients and partners get to see their project come together more quickly; support and training effort is reduced to a minimum.

As we develop a lot of projects in Ruby, I thought it would be nice to share with you a couple of gems we’ve developed to simplify integration with Contentful.

ContentfulModel: access Contentful like a local data model

It’s great to abstract complexity away when you’re implementing similar projects several times. So we’ve built a wrapper around Contentful’s Ruby client, which we’ve called ContentfulModel. You can include it in any Ruby project, and with a simple declaration in the model you can access remote attributes as if they were database-backed.

The Readme has most of the details to get you started, so I won’t repeat them all here. But here’s an example of how you set up your Contentful connection, create a model, and call some methods:

ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
require 'contentful'
require 'contentful_model'

#Configure your access to Contentful: demo account shown here
ContentfulModel.configure do |config|
  config.access_token = "b4c0n73n7fu1" #replace with your access token
  config.space = "cfexampleapi" #replace with your space ID
  config.options = {
    #extra options to send to the Contentful::Client
  }
end

# Inherit from ContentfulModel::Base and specify a content type ID
# to access the attributes for your model
# This is your model definition. Short, isn't it? :-)
class Cat < ContentfulModel::Base
   self.content_type_id = "cat" #note that your content type will actually be a long string
   
   #optionally coerce fields to a particular type
   coerce_field birthday: :date
end

# Add entry mappings for your classes, so you get objects of the type you expect
ContentfulModel::Base.descendents.each do |klass|
  klass.send(:add_entry_mapping)
end

# Call your new Cat model with an ActiveRecord-like interface
c = Cat.first
 # => #<Cat: @fields={:name=>"Happy Cat", etc. etc.}>
c.name
 # => "Happy Cat"
Cat.first.birthday
 # => Tue, 28 Oct 2003 23:00:00 +0000
c.best_friend
 # => #<Cat: @fields={:name=>"Nyan Cat", :likes=>["rainbows", "fish"], etc. etc. >
c.best_friend.likes
 # => ["rainbows", "fish"]
 Cat.offset(1).first
 # => #<Cat: @fields={:name=>"Garfield", :likes=>["lasagna"], etc. etc. >

This can be used independently, as part of a framework like Sinatra, or in a Rails app. The only dependencies are the Contentful Ruby client and the ActiveSupport gem for some string manipulation. It’s ideal if you’re planning to develop something really lightweight but need the power of a content management system.

ContentfulRails: webhooks, caching and more

Of course you might well be planning to use the Contentful CMaaS as the backend for a Rails app. That’s how we use it most often. We’ve developed a ContentfulRails gem to help you integrate with a Rails app, and promptly get all the power of the Contentful backend.

Here is what ContentfulRails does:

  • Registers all your models so you get the right classes returned when calling them. (You need to do this manually without ContentfulRails).
  • Provides a mountable Rails Engine to receive webhook calls from Contentful when your content changes
  • Caches timestamps for Contentful content, so you can use normal ‘Russian Doll’ caching techniques without incurring heavy page load penalties.
  • Provides a view helper to parse Markdown

As with ContentfulModel, the gem’s Readme has most of what you need to get started. Since we’re using ContentfulModel as a dependency, you’ll need to configure both gems in an initializer.

Caching

Everyone loves a fast website: that’s why caching is so important for content delivery. While Contentful’s CDN is pretty fast, the speed can always be improved by delivering from application server memory.

One of the issues with Rails normal view caching is that it relies on a database hit to determine the timestamp of the model. That’s fine if it’s local – most likely, the query will be cached in your database server and, provided the data hasn’t changed, that’s the only query required to render the page.

However, if the timestamp of the model is at the other end of an expensive, slow API call, it makes a lot of sense to cache it locally. That’s what we do in this gem. If you’ve switched on caching in your environment file (usually off for development, and on for production environments), every call to a Contentful-backed model updated_at() method will cache the response, meaning that it’s really quick for Rails to check the timestamp.

In the ContentfulModel::Base class, we have a method called cache_key() which is used by Rails to determine the key to use to cache the document.

Clearing the timestamp cache with Contentful webhooks

If you dive into the code (and I encourage you to do so!) you’ll see that there is a simple controller which you can mount into routes.rb as follows:

ruby
1
2
3
# Mount the ContentfulRails Engine at /contentful
# Feel free to choose a different endpoint name
mount ContentfulRails::Engine => '/contentful'

You’ll get two new routes added to the Rails project, one of which is just for debugging, and only works in the development environment. Just configure your Contentful space’s webhook callbacks with your URL. We’re big fans of Ngrok for local webhooks development.

ruby
1
2
3
4
5
6
7
8
9
# On the command line you can see the new routes:
bundle exec rake routes

#Which returns:
#   contentful_rails      /contentful               ContentfulRails::Engine

# Routes for ContentfulRails::Engine:
#   debug_webhooks GET  /webhooks/debug(.:format) contentful_rails/webhooks#debug {:format=>:json}webhooks POST /webhooks(.:format)
#   contentful_rails/webhooks#create {:format=>:json}

At the moment, this webhook only deletes the cached timestamp for the entry which has been updated. In future iterations, it would be nice to expose this callback so you can add custom code. It might be a valid case for using Rails’ instrumentation hooks.

Rails Helpers

Because a lot of Contentful content is stored as Markdown, it makes sense to provide a helper to decode this and render in Rails as HTML. We’ve added a helper built around Redcarpet to simplify this conversion:

ruby
1
2
3
4
5
6
7
8
class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception

  #Include the ContentfulRails Markdown helper.
  helper ContentfulRails::MarkdownHelper
end

What’s next

While we’re using these gems in production, they’re only at the beginning of their life. I’d be delighted if you could join us in developing them. Our to-do list includes:

  • mimicking a parent-child relationship in a local cache, so we can resolve links between content types quickly;
  • optionally caching the whole object instead of just the timestamp;
  • some tests.

If you’ve got ideas, please feel free to raise an issue on either project – or even better, a pull request! If you’re keen, we’ll happily give you maintainer / commit status on the project. The gems are MIT-licensed, so have fun!

Thank you for reading. We hope our gems will help your projects shine.

About Error

We create digital products and brands: websites, software as a service, and mobile applications. We’re connoisseurs of design, champions for the user, rather nerdy about technology, and great at helping our clients solve tricky problems.

Error works best with people who intend to change an industry, market, society or culture.

Whether it’s a time saving application, a mission-critical at-a-glance dashboard, or a disruptive startup carving out a completely new market with a digital product, our work must empower both our clients and their customers or users.

About Ed Jones

Ed is passionate about anything technical. He has worked for companies as diverse as UK national radio broadcasters, recording studios, web consultancies and even an online vegetable retailer.

With a degree in education, a background in project management and business analysis, and a strong set of technical skills, you can get him excited about almost everything!

Ed is a director and co-owner of Error, with responsibility for business operations and consultancy. He can be contacted via the studio website.

Ed Jones

Director of Error Creative Studio. You can follow Ed on Twitter.