hstore-properties

Setup

Simply add following to your gemfile

gem 'hstore-properties'

Create properties column in the model you would like to use properties within, i.e.

rails g migration AddPropertiesToUsers properties:hstore

Apply your migration

rake db:migrate

Finally, describe your properties in your model file

class User < ActiveRecord::Base
  include ActiveRecord::HstoreProperties
  properties 'third_name', 
             'some_cool_feature' => :boolean, 
             'comments' => :counter,
             'age' => :number
end

Usage

Retrieving values

By default, all your properties are of type String. There are number of other property types available though...

  • string
  • boolean
  • number
  • counter
  • translation

More will come in near future...

All properties can be retrieved just as they are written into hstore column, by suffixing them with _property, i.e.

User.last.third_name_property #=> "Jack"

Booleans

Boolean properties, can be additionaly retrieved by using _enabled? and ? suffixes, that will cast them to boolean value, i.e.

User.last.some_cool_feature_enabled? #=> true
User.last.some_cool_feature? #=> true

What is more, you can toggle value of boolean property using _enable! / _raise! and _disable! / _lower! suffixes, e.g.

User.last.some_cool_feature_enable! #=> Changes property to true
User.last.some_cool_feature_raise! #=> Changes property to true
User.last.some_cool_feature_disable! #=> Changes property to false
User.last.some_cool_feature_lower! #=> Changes property to false

Counters

Counter properties, can be retrieved by using _count suffix, that will cast them to integer value, i.e.

User.last.comments_count #=> 10

What is more, it is possible to bump counter properties, i.e. following line will increment comments property by 1

User.last.comments_bump!

Translations

Translation kind of property allows you to store translated strings in properties column. The simplest way to store translation is just assign it a value, and it will save it in current locale, e.g.

class Category < ActiveRecord::Base
  include ActiveRecord::HstoreProperties
  properties 'caption' => :translation            
end

c = Category.last
c.caption = "Nice product" #this will save 'Nice product' into caption_en property 
c.save
c.caption_property #this will retrieve 'Nice product' from caption_en property 

You can always enforce in which locale you would like to store property, by suffixing it with any locale code available in I18n.available_locales

c = Category.last

c.caption_en = "Nice product" 
c.caption_nb_no = "Fint produkt" 

c.save

c.caption_property #=> "Nice product"
c.caption_nb_no #=> "Fint produkt" 

I18n.locale = :nb-NO'
c.caption_property #=> "Fint produkt" 

Updating through forms

You obviously need to add :properties to yours attr_accessible

Below is an example of building appropriate fields dynamically with formtastic

<%= semantic_form_for @user do |f| %>
    <%= f.first_name %>
    <%= f.fields_for :properties, OpenStruct.new(@user.properties) do |builder| %>
        <% User.properties.each do |property| %>
            <%= builder.input property.name, property.formtastic_options %>
        <% end %>
    <% end %>
    <%= f.submit %>
<% end %>

Further customization

If most of your properties are of the same type, but other than string, you can overwrite default_property_klass to make other type default, i.e.

class User < ActiveRecord::Base
#...
  def self.default_property_klass
    ActiveRecord::Properties::BooleanProperty
  end
end

When to use?

  • If you consider adding redundant column to your table, that will only sometimes store any data
  • If you would like to make particular model "configurable"
  • If you will not likely perform queries on specific field but mostly read from it directly