tirdadc

Web Development | Ruby on Rails | React.js

Practical use of JSONB attributes in Ruby on Rails forms

As you probably already know by now, Postgres 9.4 introduced JSONB as a new column type, effectively allowing you to use it both as a relational database and a full-fledged indexed and queryable document store when needed instead of trying to shoehorn everything into the latter and then dealing with the inevitable aftermath. Ruby on Rails has provided support for JSONB since 4.2.

There’s already a solid amount of blog posts out there on this topic, so this one aims to focus on a practical aspect of implementing it.

Start off by adding an indexed JSONB field to your table:

class AddWebsitesToUser < ActiveRecord::Migration
  def change
    add_column :users, :websites, :jsonb, default: '{}'
    add_index  :users, :websites, using: :gin
  end
end

Now you can set whatever data you want inside of that field using keys:

user.websites['primary_website'] = 'http://www.example.com'

Using a store accessor allows you to specify the keys you want to access directly without referencing the JSONB field:

# user.rb
store_accessor :websites, :primary_website, :secondary_website, :optional_website

Which allows you to do this:

user.primary_website
#=> 'http://wwww.example.com'

Your client _form.html.erb can then treat these as regular fields:

<div class="field">
  <%= f.label :primary_website %>
  <%= f.text_field :primary_website %>
</div>
<div class="field">
  <%= f.label :secondary_website %>
  <%= f.text_field :secondary_website %>
</div>
<div class="field">
  <%= f.label :optional_website %>
  <%= f.text_field :optional_website %>
</div>

Be sure to whitelist them as usual in the controller:

# users_controller.rb
def user_params
  params.require(:user).permit(
    :primary_website,
    :secondary_website,
    :optional_website
  )
end