All threads / store_accessor with jsonb nesting

Ask A Question

Notifications

You’re not receiving notifications from this thread.

store_accessor with jsonb nesting

Francisco Quinones asked in Rails

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| %>

Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 35,699+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

    © 2020 GoRails, LLC. All rights reserved.