1

In my rails model I have a JSON column which stores some meta information. This is to be entered bu the user from a form.

Since the keys of the JSON column are not attributes of the model I cannot use them directly in form_for instead I need to define a virtual attribute.

Since this number of virtual attributes could grow to be arbitrarily lengthy I would like to use meta programming to define the attributes.

I did try the answer in this question however when I use the constant in my model I get an error saying that the constant is undefined. So I added the symbols for the keys in an array directly and iterate over them in the module. When I do this I get an error that says stack level too deep.

Please can someone help me out here?

Community
  • 1
  • 1

2 Answers2

2

I figured it out. I return the attribute as a key of the JSON column and it works fine now.

# lib/virtuals.rb
module Virtuals

  %W(key_1 key_2 key_3).each do |attr|
    define_method(attr) do
      self.my_json_column[attr]
    end

    define_method("#{attr}=") do |val|
      self.my_json_column[attr] = val
    end
  end
end

In my Model i just need to include that above module and it works fine in the form_for and updates correctly as well.

Roman Kiselenko
  • 43,210
  • 9
  • 91
  • 103
2

If you are using PostgreSQL specific columns like hstore or json simply use store_accessor instead to generate the accessor methods. Be aware that these columns use a string keyed hash and do not allow access using a symbol.

class Model < ActiveRecord::Base
  store_accessor :my_json_column, [ :key_1, :key_2, key_3 ]
end

What it doing under the hood? It has define write\read helper methods:

def store_accessor(store_attribute, *keys)
  keys = keys.flatten

  _store_accessors_module.module_eval do
    keys.each do |key|
      define_method("#{key}=") do |value|
        write_store_attribute(store_attribute, key, value)
      end

      define_method(key) do
        read_store_attribute(store_attribute, key)
      end
    end
  end

# .....

store

Roman Kiselenko
  • 43,210
  • 9
  • 91
  • 103
  • Thank you for your response. I did not know about this. If you do not mind, then please could you tell me which method should be preferred (`store_accessor` or using `define_method` in an iterator) and why? – Akshay Pardhanani Sep 23 '16 at 09:43
  • 1
    you should use methods and macros which provided by the Rails and Rails core lib to avoid any duplication of the core lib methods, the Rails code base has a good test coverage. But your answer also good for education purpose. + 1 – Roman Kiselenko Sep 23 '16 at 10:06
  • 2
    _The good coder can write new code, the ninja coder can make old code work again._ – Roman Kiselenko Sep 23 '16 at 10:08