How to define and test a hash attribute in a Rails ActiveRecord model

- 3 mins

To persist a hash in an ActiveRecord model, you can utilize the hstore extension for PostgreSQL. Rails 4 allows you to define columns of type hstore which utilize the hstore extension.

hstore is supported by PostgreSQL 9.2+.

To effectively define and test a model with a hash attribute, we can follow the steps outlined below:

  1. Enable the hstore extension
  2. Add the hash column to the model
  3. Configure model to use hstore extension
  4. Create spec and define assertions for the attribute
  5. Add validation for attribute to model

Enable the hstore extension

To enable hstore, you must issue the CREATE EXTENSION command.

In a PostgreSQL script or shell:

CREATE EXTENSION IF NOT EXISTS hstore;

This command can also be executed via a Rails migration:

$ rails g migration enable_hstore_extension
class EnableHstoreExtension < ActiveRecord::Migration
  def change
    enable_extension "hstore"
  end
end

Add the hash column to the model

Create a migration to add the hash column to your model. In this example, we’ll be defining the meta column of the post model as a hash:

$ rails g migration add_meta_to_articles meta:hstore
class AddMetaToArticles < ActiveRecord::Migration
  def change
    add_column :articles, :meta, :hstore, default: {}, null: false
  end
end

Note the use of hstore as the column type.

Configure the model to use the hstore extension

We have to identify the meta column on the Article model with ActiveRecord::Store::ClassMethods#store_accessor.

class Article < ActiveRecord::Base
  store_accessor :meta
end

We can test the meta column in the Rails console:

$ rails console
sample_article = Article.new

sample_article.meta.class
#=> Hash

sample_article.meta = { color: 'red' }

sample_article.save!
sample_article.reload

sample_article.meta[:color]
#=> "red"

# Create spec and define assertions for the attribute We need to provide test coverage for the meta attribute.

The factory makes use of the factory_girl gem:

FactoryGirl.define do
  factory :article do
    meta { {} }
  end
end

The spec file makes use of the rspec-rails and shoulda-matchers gems:

require 'rails_helper'

RSpec.describe Post, type: :model do
  before { @article = FactoryGirl.build(:article) }

  subject { @article }

  # one line assertions with shoulda-matchers
  it { should respond_to(:meta) }

  # validations
  it { should validate_presence_of(:meta) }
end

If test the model, we should receive a failure message:

$ rspec spec/models/article_spec.rb
Failures:

  1) Post should validate that :meta cannot be empty/falsy
     Failure/Error: it { should validate_presence_of(:meta) }
       Post did not properly validate that :meta cannot be
       empty/falsy.
         After setting :meta to ‹nil›, the matcher expected the
         Post to be invalid, but it was valid instead.

This happens because we have not added the necessary validation to the Post model. We’ll do that in the next step.

Add validation for attribute to model

To enable our model test pass, we add the required validation:

class Post < ActiveRecord::Base
  store_accessor :meta

  # validations
  validates_presence_of :meta
end

The tests should pass now.

Wrapping up

We should note that hstore stores all values as strings. The values should be casted to their appropriate types before use.

What does this have to do with robots?

Not much, really.

Ifeanyi Oraelosi

Ifeanyi Oraelosi

Making stuff to facilitate learning and creativity. Also, video game experiments.

comments powered by Disqus
rss facebook twitter github youtube mail spotify instagram linkedin google pinterest medium vimeo