Read @webhouse/cms content from a Ruby on Rails application. Helper module, controllers, ERB views.
Setup
my-project/
cms.config.ts
content/
public/uploads/
app/
controllers/
views/
helpers/Helper module
Create app/lib/webhouse.rb:
require 'json'
module Webhouse
CONTENT_DIR = Rails.root.join('content').freeze
def self.collection(name, locale: nil)
folder = CONTENT_DIR.join(name.to_s)
return [] unless Dir.exist?(folder)
Dir.glob(folder.join('*.json'))
.map { |f| JSON.parse(File.read(f)) }
.select { |d| d['status'] == 'published' }
.then { |docs| locale ? docs.select { |d| d['locale'] == locale } : docs }
.sort_by { |d| d.dig('data', 'date') || '' }
.reverse
end
def self.document(collection, slug)
path = CONTENT_DIR.join(collection.to_s, "#{slug}.json")
return nil unless File.exist?(path)
doc = JSON.parse(File.read(path))
doc['status'] == 'published' ? doc : nil
end
def self.find_translation(doc, collection)
tg = doc['translationGroup']
return nil unless tg
collection(collection).find do |other|
other['translationGroup'] == tg && other['locale'] != doc['locale']
end
end
endController
# app/controllers/blog_controller.rb
class BlogController < ApplicationController
def index
@posts = Webhouse.collection('posts', locale: 'en')
end
def show
@post = Webhouse.document('posts', params[:slug])
raise ActionController::RoutingError, 'Not Found' unless @post
end
endRoutes
# config/routes.rb
Rails.application.routes.draw do
root 'blog#index'
get '/blog/:slug', to: 'blog#show', as: 'post'
endERB view
<%# app/views/blog/show.html.erb %>
<article>
<h1><%= @post.dig('data', 'title') %></h1>
<time><%= @post.dig('data', 'date') %></time>
<div class="prose">
<%= markdown(@post.dig('data', 'content') || '') %>
</div>
<% (@post.dig('data', 'tags') || []).each do |tag| %>
<%= link_to "##{tag}", "/tags/#{tag}", class: 'tag' %>
<% end %>
</article>Add a markdown helper in app/helpers/application_helper.rb:
require 'redcarpet'
module ApplicationHelper
def markdown(text)
renderer = Redcarpet::Render::HTML.new(hard_wrap: true)
Redcarpet::Markdown.new(renderer, fenced_code_blocks: true).render(text).html_safe
end
endAdd to Gemfile: gem 'redcarpet'
Serving media
Add public/uploads/ to the Rails public directory — it's served automatically by Rails in development and by your web server in production.
i18n
# Show a post with a language switcher
post = Webhouse.document('posts', params[:slug])
translation = Webhouse.find_translation(post, 'posts')
# In the view
if translation
link_to "Read in #{translation['locale']}", blog_post_path(translation['slug'])
endCaching
def self.collection_cached(name, locale: nil)
Rails.cache.fetch("webhouse:#{name}:#{locale || 'all'}", expires_in: 1.minute) do
collection(name, locale: locale)
end
endJekyll alternative
For static Jekyll sites, read the same JSON files in a _plugins/webhouse.rb generator:
module Jekyll
class WebhouseGenerator < Generator
def generate(site)
Dir.glob('content/posts/*.json').each do |f|
doc = JSON.parse(File.read(f))
next unless doc['status'] == 'published'
site.pages << PageWithoutAFile.new(site, site.source, 'blog', "#{doc['slug']}.html").tap do |page|
page.content = doc.dig('data', 'content')
page.data['title'] = doc.dig('data', 'title')
page.data['layout'] = 'post'
end
end
end
end
endNext steps
- See the Rails example
- Learn about Framework-Agnostic Architecture