Alchemy CMS: great for managing websites integrated with Ruby on Rails
July 13, 2017 · Chris Peters
After learning Ruby on Rails, I played around a little with a few of the open source content management system options available, and one in particular stood out for what I typically need: Alchemy CMS.
After learning Ruby on Rails, I played around a little with a few of the open source content management system options available, and one in particular stood out for what I typically need: Alchemy CMS.
I was happy to learn that Alchemy CMS installs as a Rails engine, meaning that you can integrate it into your existing Rails application and use it alongside the Rails features that you know and love.
Here’s a little tour of Alchemy and some of the features that I’ve found to be useful in building websites for small- and medium-sized organizations.
Key feature: keep the integrity of your design and layout with elements
Why not just install WordPress, Chris? Like 250% of the web runs on WordPress anyway, amirite?
The most dreaded part of WordPress as a CMS for websites is the single giant text editor per page. I hate nothing more than fixing a page layout wrecked by a content author who messed up some HTML in the WordPress Editor of Doom™. I know that there are advanced ways around this with custom fields, widgets, short codes, etc., but then you end up trying to force the content author to understand minutiae that they likely won’t remember 2 months later when they need to change something real, outside the scope of your demo.
Alchemy comes to the rescue with elements
An element in Alchemy CMS lets the web designer define a block of content comprised of different types of content (called essences: see the chemistry theme emerging yet?).
Let’s say that you have a heavily-styled content element, all rocked out with
media queries and assumptions about the right-sized image. Rather than leaving
behind a nested div
structure for a future author to mess around with, you
configure an element in config/alchemy/elements.yml
with the content expected:
- name: call_to_action | |
hint: 'Call to action with headline, description, button, and secondary action.' | |
contents: | |
- name: headline | |
type: EssenceText | |
- name: description | |
type: EssenceRichtext | |
- name: button_link | |
type: EssenceLink | |
hint: Where to link the button. | |
- name: button_icon | |
type: EssenceText | |
hint: Icon to use within button. See www.fontawesome.io for a list of icons. Refer to the icon name without the "fa-" in front of it. | |
- name: button_text | |
type: EssenceText | |
- name: secondary | |
type: EssenceRichtext | |
hint: Secondary call to action. Place phone number in bold formatting. |
After running rails g alchemy:elements --skip
, you can edit
app/views/alchemy/elements/_call_to_action_view.html.erb
to define the HTML
structure for the content, force image sizes, etc.
Your code may end up looking something like this:
<%- cache(element) do -%> | |
<%= element_view_for(element) do |el| -%> | |
<div class="cta"> | |
<h3 class="cta-heading"> | |
<%= el.render :headline %> | |
</h3> | |
<%= el.render :description %> | |
<div class="cta-actions"> | |
<div class="cta-primary"> | |
<%= link_to el.ingredient(:button_link), class: 'button cta-button' do %> | |
<%= fa_icon(el.ingredient(:button_icon)) if el.ingredient(:button_icon).present? %> | |
<%= el.render :button_text %> | |
<% end %> | |
</div> | |
<div class="cta-secondary"> | |
<%= el.render :secondary %> | |
</div> | |
</div> | |
</div> | |
<%- end -%> | |
<%- end -%> |
I really like this style because it encourages modular design. I usually end up
with a matching Sass file somewhere like
app/assets/stylesheets/modules/_ctas.scss
.
I’ve found that I need to create some “general-purpose” elements for each site
like title
, rich_text
, image
, video
, lead
, button
, etc. That’s
probably the topic of a separate post. More of us should be sharing these sorts
of details with the rest of the community.
Content editing experience with elements
The content author can then add one or many elements to any page, formatted exactly how you want. It ends up looking something like this in the Alchemy admin:
The overall editing experience reminds me of the increasingly popular Craft CMS, with the page content visible in context and content editing controls off to the side.
Notice the little question mark bubbles next to elements in the editor:
Hint bubbles with question marks appear next to fields defined with a hint in Alchemy CMS.
Those are hints that are revealed on hover, populated from the elements.yml
example earlier in this post. You can help your content authors by providing
explanations for elements and fields that may need some extra consideration. I
find this to be a very nice touch, requiring a minimal amount of extra effort
while authoring the elements.yml
file.
Other features that make Alchemy worth trying out
Elements really are the flagship feature of Alchemy CMS in my mind, but here are a few other niceties.
Page layouts
Each page created in Alchemy CMS must have a defined page layout. Of
course, the layouts that you create will depend on the requirements of the site.
I typically create layouts for home
, one_column
, two_column
, subpage
,
and narrow
, give or take others.
The nice things about layouts:
- You can define which elements are allowed in each layout. Have an element
that’s only meant to be used on the home page? Only list it in the
home
layout. - Each layout can have one or many cells. These are basically different regions that elements can be added to. You can get granular and restrict which elements are allowed to be added to each cell.
- You can indicate that a layout must be
unique
, i.e., it can only be used for one page. (There can only be one home page!) - Each layout can be configured to auto-generate new content items based on
elements that you list. That way an editor can be presented with a
title
to fill in as a starter on each page, for example.
Here is a quick sample page_layouts.yml
file for you to glance at:
- name: home | |
unique: true | |
elements: | |
- home_hero | |
- location_search_box | |
- lead | |
- rich_text | |
- image | |
- definition_list | |
- cta | |
cells: | |
- hero | |
- main | |
- sidebar | |
- name: single_column | |
elements: | |
- title | |
- lead | |
- rich_text | |
- image | |
- video | |
- button | |
- definition_list | |
autogenerate: | |
- title | |
- name: two_column | |
elements: | |
- title | |
- lead | |
- rich_text | |
- image | |
- video | |
- button | |
- definition_list | |
- event_sidebar | |
- contact_form | |
cells: | |
- heading | |
- sidebar |
Powerful image processing
Alchemy ships with the Dragonfly gem, a nice API and configuration point for transforming and storing images. When you utilize this, you enter a nice scenario where content authors can upload a “master image” that can be reused and transformed in one or many places within the content.
As a quick example, you can do things like this to resize images to display thumbnail versions with a forced image format, etc.:
<%- cache(element) do -%> | |
<%= element_view_for(element, class: 'product-listing') do |el| -%> | |
<%# Force thumbail to be rendered at 372px height, and HTML tag to force height to 186px | |
# for higher DPI goodness. %> | |
<%= image_tag el.ingredient(:thumbnail).url(size: 'x372', format: 'jpg'), height: 186, | |
style: 'height: 186px;', class: 'product-listing-thumb' %> | |
<%- end -%> | |
<%- end -%> |
Nestable elements
OK, one more thing about elements. You can have nestable elements: elements within elements. Let’s say I wanted to create my own definition list element, which most WYSIWYG editors generally don’t do so well at providing.
I could define something like this in elements.yml
:
- name: definition_list | |
nestable_elements: | |
- definition_title | |
- definition | |
- name: definition_title | |
contents: | |
- name: title | |
type: EssenceText | |
- name: definition | |
contents: | |
- name: definition | |
type: EssenceText # Or EssenceRichText, depending on what you want to do. |
Then I would define very simple views for the nested definition_title
and
definition
elements:
<%- cache(element) do -%> | |
<%= element_view_for(element, tag: :dl) do |el| -%> | |
<% element.nested_elements.available.each do |nested_element| %> | |
<%= render_element(nested_element) %> | |
<% end %> | |
<%- end -%> | |
<%- end -> | |
<%- cache(element) do -%> | |
<%= element_view_for(element, tag: :dt) do |el| -%> | |
<%= el.render :title %> | |
<%- end -%> | |
<%- end -%> |
<%- cache(element) do -%> | |
<%= element_view_for(element, tag: :dd) do |el| -%> | |
<%= el.render :definition %> | |
<%- end -%> | |
<%- end -%> |
As you can see, I customized the tag
used to contain each element (which
defaults to div
). You can also customize other things like the class
attribute.
This then provides a nice UI for adding, removing, and drag-and-drop sorting
child definition_title
and definition
elements within the parent
definition_list
:
Rails workflow
I’ve mentioned this before, but I think it’s worth repeating. With Alchemy CMS, you can have a content management system sitting alongside a full-blown Rails application with its own controllers, models, etc.
That means that you can use other gems, the built-in Asset Pipeline (or soon the Webpack stuff in Rails 5.1), built-in caching strategies, integrate special objects in your elements (or even custom essences), etc.
The key is really in where you mount the Alchemy engine in config/routes.rb
.
Typically, your routes.rb
file will end up looking like this:
Rails.application.routes.draw do | |
devise_for :users | |
# Your own custom logic is listed first. | |
resources :products | |
get :events, to: 'events#index' | |
namespace :system do | |
resources :products | |
end | |
# Alchemy takes it from here for URLs that don't match anything listed above. | |
# Keep in mind that Alchemy's admin is mounted at the `/admin` folder though. | |
mount Alchemy::Engine => '/' | |
end |
Read more about Alchemy’s architecture.
Caveats, limitations, and bad news
Of course, no content management solution is going to be perfect. They all have their own strengths and weaknesses. Here are some of the negative things that I’ve noticed.
Documentation
In my opinion, the documentation is good enough to get started and gain a moderate understanding of how to build a solution with Alchemy. Unfortunately, it kind of ends there.
Because this is open source, you’re probably not surprised. Because it’s open source, maybe we should all be helping out with that (myself included).
No good way to reuse content
When you add a content item to a page, there is no real good way of sharing it with other pages. So if you have the same block of text on 15 different pages, you need to edit each content item individually.
It’s niche
The team behind Alchemy is great, but there isn’t that large of a community behind it, especially when you compare it to the popular open source PHP CMSes.
However, I’ve been able to use that to my advantage at times. Whenever I’ve needed help, the developers of Alchemy have been very eager to answer questions or even fix bugs. The Slack channel is especially useful for having direct access to the developers.
This is somewhat of a non-point because the overall Rails community is very large. Oftentimes, you can find a Ruby gem that does most of what a similar WordPress plugin would do anyway. However, you’ll end up needing to train content authors on how to use the system because there aren’t any completed manuals for them as of this writing.
The concept of “navigation” doesn’t provide the flexibility that I’d like to see
A page in Alchemy has a few options:
By checking that “visible in navigation” check box, you have the ability to use some helpers provided by Alchemy CMS to build out a nested menu structure for the site. Really cool!
However, you are forcing that page to include a nested URL structure. That’s
usually fine. (In this case, the URL of the page will be
/products/widget-plus
, which I like.)
The problem comes in when you want a nested URL structure but don’t want the
page to be included in a navigation menu. What ends up happening is that even a
page in the tree at Products > Widget Plus will only have a URL of
/widgets-plus
unless you check that “visible in navigation” check box.
In other words, you lose quite a bit of flexibility in URL structure if you want to build dynamic menus based on the value of that check box.
Give it a try (especially if you’re interested in Rails)
All-in-all, Alchemy CMS is the best open source Rails CMS that I’ve found. It is especially useful for those who are already proficient in Rails or want to learn. In other words, this is a great solution if a key part of your CMS decision is based on a desire to implement it in Ruby on Rails and not something like .NET or PHP.
I hope to share some more details of particular implementations and lessons learned in the coming months.