I recently discovered the excellent ActiveRecord store method whilst researching the best-practice for storing optional, flexible metadata against a record. store lets you keep simple key/value data into a single text column on your model.
By declaring stored attributes on your model, ActiveRecord will automatically generate the appropriate setter/accessor methods, and validations work just as you'd expect.
# db/migrate/create_cars.rb # ... create_table do |t| t.references :model t.references :manufacturer t.text :metadata # Note metadata is just a text column t.timestamps end # app/models/car.rb class Car < ActiveRecord::Base belongs_to :model belongs_to :manufacturer # Manufacturer and Model are 'real', database-backed attributes attr_accessor :model_id, :manufacturer_id, :colour, :size, :notes, :product_url store :metadata, :accessors => [:colour, :size, :notes, :product_url] # Database-backed attributes validates :model, :presence, :presence => true validates :manufacturer, :presence => true # Metadata stored attributes validates :colour, :presence => true validates :size, :presence => true, :inclusion => { :in => %w(small medium large) } validates :product_url, :format => { :with => /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/ } end |
Note that the metadata is just a text column. Rails will automatically serialise the hash into this column.
With our Car model defined, and its metadata attributes declared with store, we can use them in just the same way as normal, database-backed attributes:
car = Car.new car.model = CarModel.first car.manufacturer = Manufacturer.first car.colour = "Red" car.size = "medium" car.product_url = "http://www.example.com/" car.save # => true car.colour # => "red" car.size # => "medium" |
Pros and Cons
The biggest advantage to this type is store is it provides a flexible data schema. This is perfect for storing non-indexed metadata about a model, and a use-case that often crops up when building apps. It provides a compromise between common relational databases (such as MySQL and PostgreSQL) and the flexibility of NoSQL databases such as MongoDB and CouchDB. In practical use, it means attributes can quickly be added to a model without the need to perform any migrations on the database schema.
A possible disadvantage is that stored attributes can not be indexed or used in queries (as they have no corresponding database column). For example, you could not call Car.where(:colour => "red"). However, as Garry Tan points out in his post, if this becomes a requirement in the future, you could always add a database-column to the schema at a later date, and just move the data at that time.
Helpful post? Let me know on Twitter.


If you like to test out the latest and greatest builds of