Skip to content

Instantly share code, notes, and snippets.

@sufleR
Last active August 29, 2015 14:07
Show Gist options
  • Select an option

  • Save sufleR/aaccb5b81705b08558e7 to your computer and use it in GitHub Desktop.

Select an option

Save sufleR/aaccb5b81705b08558e7 to your computer and use it in GitHub Desktop.
Example of embeding model in ActiveRecord using JSON and postgreSQL
2.1.1 :052 > t = Test.new()
=> #<Test id: nil, data: nil, created_at: nil, updated_at: nil>
2.1.1 :053 > t.virt = Virt.new(address: { street: 'pięknie jest', nr: 5 })
=> #<Virt:0x00000106100458 @address={:street=>"pięknie jest", :nr=>5}, @name=nil, @children=[]>
2.1.1 :054 > t.save!
(0.4ms) BEGIN
SQL (0.4ms) INSERT INTO "tests" ("created_at", "data", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", "2014-10-07 21:56:49.991978"], ["data", "{\"name\":null,\"address\":{\"street\":\"pięknie jest\",\"nr\":5},\"children\":[]}"], ["updated_at", "2014-10-07 21:56:49.991978"]]
(2.0ms) COMMIT
=> true
2.1.1 :055 > t.reload
Test Load (0.4ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" = $1 LIMIT 1 [["id", 10004]]
=> #<Test id: 10004, data: {"name"=>nil, "address"=>{"street"=>"pięknie jest", "nr"=>5}, "children"=>[]}, created_at: "2014-10-07 21:56:49", updated_at: "2014-10-07 21:56:49">
2.1.1 :056 > t.virt
=> #<Virt:0x00000106119fc0 @address={"street"=>"pięknie jest", "nr"=>5}, @name=nil, @children=[]>
2.1.1 :057 > t.virt.address
=> {"street"=>"pięknie jest", "nr"=>5}
2.1.1 :058 > t.virt.address.class
=> Hash
2.1.1 :059 > t.virt.class
=> Virt
2.1.1 :060 > t.virt.parent == t
=> true
2.1.1 :061 > t.virt.parent.virt == t.virt
=> true
class CreateTests < ActiveRecord::Migration
def change
create_table :tests do |t|
t.json :data
t.timestamps
end
end
end
module EmbeddedModel
def embeds_one(name, options)
column_name = options.fetch(:column_name, nil) || name
define_method("#{name}=") do |arg|
write_attribute(column_name, arg.is_a?(Hash) ? arg : arg.to_h)
end
define_method(name) do
if instance_variable_get("@#{name}").nil?
model = name.to_s.classify.constantize.new(read_attribute(column_name))
inverse_of = options.fetch(:inverse_of, nil) || self.class.name.downcase
model.public_send("#{inverse_of}=", self)
instance_variable_set("@#{name}", model)
end
instance_variable_get("@#{name}")
end
end
def embedded_in(name, options = {})
klass_name = options.fetch(:class_name, nil) || name.classify
self.attribute name, klass_name.to_s.constantize
end
end
class Test < ActiveRecord::Base
extend EmbeddedModel
embeds_one :virt, column_name: :data, inverse_of: :parent
end
class Virt
include Virtus.model
attribute :name, String
attribute :address, Hash
attribute :children, Array
embedded_in :parent, class_name: Test
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment