July 11, 2016 · Ruby on Rails

Liquid Validation in Ruby on Rails

In the publishing application I'm working on at the moment, I've been using Liquid in many places in order to create templates. It's a nice markup language that lets a user create templates that can be safely rendered on a server. One issue I ran into was that users could potentially save Liquid templates that would cause errors later when the template is rendered. Instead of having to handle these rendering errors later on, I thought it would be more helpful if the template would be validated before being saved. The user can then be informed of the errors in their template and make appropriate fixes.

By default Liquid does not raise errors for templates. This could be a problem because the user is never notified of issues with their template. Liquid needs to be configured to use the strict error mode. In an initializer I added the line Liquid::Template.error_mode = :strict, so that Liquid uses strict mode whenever parsing templates.

In any models that store Liquid templates I added a validation method called syntax_errors. This method parses the template content. If any syntax errors are raised these are added to the content's errors array. For example if a particular tag is not closed in the template this will specified in the error message. These errors can then be displayed to the user in the view.

def syntax_errors  
  Liquid::Template.parse(self.content)
rescue Liquid::SyntaxError => e  
  errors.add(:content, e.message)
end  

This validation method can be tested with RSpec.

describe 'Liquid validation' do  
  it 'allows valid content' do
    liquid_template_valid = LiquidTemplate.new(content: '{% if products.size > 0 %}Hello{% endif %}')
    liquid_template_valid.valid?
    expect(liquid_template_valid).to be_valid
  end

  it 'adds an error for invalid content' do
    liquid_template_invalid = LiquidTemplate.new(content: '{{ name')
    liquid_template_invalid.valid?
    expect(liquid_template_invalid).not_to be_valid
    expect(liquid_template_invalid.errors).to have_key(:content)
  end
end  

Here's a sample app on GitHub.

Comments powered by Disqus