Skip to main content
Ask A Question
Notifications
You’re not receiving notifications from this thread.
Subscribe

store_accessor with jsonb nesting

Rails • Asked by Francisco Quinones

So Im building a user permits column with jsonb.

jsonb column

    bar_permit: {
       key1:      [],
       key2:       "Bar",
       key3:       false,
       key4:       false,
       key5:       false
      },
    foobar_permit: {
      key1:      [],
      key2:       "Bar",
      key3:       false,
      key4:       false,
      key5:       false
      }
    }
class Foo < ActiveRecord::Base
  serialize :permits, HashSerializer
  store_accessor  :permits, :bar_permit, :foobar_permit
end

with the HashSerializer I can call the first hash keys as table columns

example:

 foo.bar_permit
 or 
 foo.foobar_permit
class HashSerializer
  def self.dump(hash)
    hash.to_json
  end

  def self.load(hash)
    (hash || {}).with_indifferent_access
  end
end

now I need to make the bar_permit and the foobar_permit to a hash with preset keys

I add these methods to the model so I can have a {key:value} for bar_permits

def bar_permit=(attributes)
    super
    value = []
    attributes.each do |index, attrs|
      value << attrs
    end
    write_attribute(self.permits[:bar_permit], value)
  end

  def bar_permit
    super
    self.permits[:bar_permit]= {
      key1:      [],
      key2:       "Bar",
      key3:       false,
      key4:       false,
      key5:       false
    }
  end

Is this the correct way of making the attributes behave as hash?
How can I make the form helpers work with the keys of the virtual attributes as fields.


Hello,

I will test your script to get a better understand about your first question (is this the correct way),

but, for the second question (How can I make the form helpers work with the keys of the virtual attributes as fields.)

You can use OpenStruct for this,

ex:

<%= form_for @foo do |f| %>
    <%= f.fields_for :permits, OpenStruct.new(@foo.permits) do |permit_fields| %>
          <%= permit_fields.text_field :key2 %>
    <% end %>
<% end %>

Honestly, I think you're better off using this than trying to build your own implementation. It'll let you set defaults and types for each attribute which will make it significantly easier to work with: https://github.com/devmynd/jsonb_accessor

The types are going to be a problem when you start using forms because they won't automatically be cast to booleans, etc. That makes for a mess.

You shouldn't need the HashSerializer because that's what store_accessor implements for you. It doesn't do with indifferent access, but it will convert back and forth from jsonb to a hash.

To set defaults, just setup a default value after initialize:

class Foo < ApplicationRecord
  store_accessor :permits, :bar_permit, :baz_permit

  after_initialize :set_defaults

  def set_defaults
    self.permits ||= {
      bar_permit: {
        key1:      [],
        key2:       "Bar",
        key3:       false,
        key4:       false,
        key5:       false
      },
      foobar_permit: {
        key1:      [],
        key2:       "Bar",
        key3:       false,
        key4:       false,
        key5:       false
      }
    }
  end
end

<%= f.fields_for :foo_permit, OpenStruct.new(f.object.foo_permit || {}) do |permits| %>


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 30,005+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.