Goal: I want to initialize a record like ActiveRecord does under the hood.
Secondary goal: I also want to understand ActiveRecord better.
Why: I want an ActiveRecord object with values that I selected myself using hard-written SQL.
Analogy: I can do
Foo.first
and that will give me a regular Foo instance with all of its columns Foo<id: 1, name: "ABC">
I can also do
Foo.select('1 AS bar').first
and I'll get an instance of Foo that I can then call foo.bar and it will return 1, EVEN if the Foo table doesn't have a 'bar' column. Nice!
Background: I have a hash of properties, {bar 1, id: 1, name: "ABC"}. I want to initialize an instance of Foo that will have a bar property even though the Foo table doesn't have a bar column.
Error: If I try to initialize Foo with those properties, I'll get the error ActiveModel::UnknownAttributeError: unknown attribute 'bar' for Foo
Condition: I don't want to have an attribute :bar in the Foo class, nor do I want an attr_accessor :bar either.
Reasoning: I know that ActiveRecord is doing something under the hood when I select columns that it doesn't have so that in the end I get an object with those properties. What is it doing?
Suppose you have an active-record model Foo:
class Foo < ApplicationRecord
end
You may instantiate a new instance of that model with arbitrary attribute values without fetching them through SQL using the ActiveRecord::Persistence.instantiate method:
attributes = {
"id" => 1,
"foo" => 1,
"name" => "ABC"
}
foo = Foo.instantiate(attributes)
# => #<Foo:0x00007fcae44fee88 id: 1>
Please note that the attributes hash is required provide the keys as String, not as Symbol. So, you need to have attributes = {"id" => 1, "foo" => 1, "name" => "ABC"} rather than attributes = {id: 1, foo: 1, name: "ABC"}.
If you already have a hash with Symbol keys, you may convert it using Hash#stringify_keys:
attributes = {id: 1, foo: 1, name: "ABC"}
attributes.stringify_keys # => {"id" => 1, "foo" => 1, "name" => "ABC"}
foo = Foo.instantiate(attributes.stringify_keys)
From the instance foo, you may retrieve the attributes like this:
foo.attributes
# => {"id"=>1, "bar"=>1, "name"=>"ABC"}
# or:
foo.bar
# => 1
ActiveRecord::Persistence.instantiateIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With