Skip to content

Storing templates in the database

Bryce Thornton edited this page Jul 22, 2013 · 3 revisions

Say you allow your users to have a custom template for posts/show. Prepend a template resolver to your view path using prepend_view_path:

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  prepend_view_path CustomTemplateResolver.new

  def show
    # It's probably a good idea to have a default template at
    # `app/views/posts/show.html.curly`.
    @post = Post.find(params[:id])
  end

  protected

  # This is the tricky part - in order to get data into your
  # template resolver, you need to register a template detail.
  # This needs to be an array. The block returns a default value.
  ActionView::LookupContext.register_detail(:accounts) { [] }

  # This is called by Rails to get the actual details:
  def details_for_lookup
    # You could also use e.g. `themes`.
    { accounts: [current_account] }
  end
end

Make sure there's a presenter with a matching name:

# app/presenters/posts/show_presenter.rb
class Posts::ShowPresenter < Curly::Presenter
  presents :post

  def title
    @post.title
  end
end

The resolver is the final part, and can be a bit tricky:

# app/models/custom_template_resolver.rb
class CustomTemplateResolver < ActionView::Resolver
  def find_templates(name, prefix, partial, details)
    account = details.fetch(:accounts).first

    source = account.template

    name = "_#{name}" if partial
    path = [prefix, name].compact.join("/")

    # This needs to be different for each template. Perhaps
    # use a digest of the template source.
    identifier = ["template", account.id].join("-")

    # Get the Curly handler
    handler = ActionView::Template.handler_for_extension(:curly)

    details = {
      format: :html,
      updated_at: account.updated_at, # important if you want to
                                      # cache the compiled templates.
      virtual_path: path
    }

    # Return a new template object wrapped in an array
    [ActionView::Template.new(source, identifier, handler, details)]
  end
end

We found this to be a much better fit than using the Liquid style rendering. Since we're using Rails' own template resolution system, we can render partials inside the presenters, and caching just works.

Clone this wiki locally