Reputation: 2200
My app creates a QuoteRequest
object and upon save of that object to the db I want it to create a Quote.new
object (QuoteRequest
has_one
Quote
). In a new Quote
instance I want to set some instance variables, which various methods related to a Watir crawler will use, then the result of that Watir crawl & scrape will #save thus persist the Quote object to the db.
I would like to set the Quote
instance variables in an initialize
method as soon as the object is created, but this is creating issues with the fact that it inherits from ApplicationRecord
this there's an initialization conflict.
What is the correct way to instantiate an ApplicationRecord
object with variables, without conflicting with the Rails library initialization code?
Upvotes: 4
Views: 1922
Reputation: 2575
If you just want to set one instance variable without any extra logic at initialize then Vijay's answer is a good one. However you can have more flexibility than just being restricted to attr
methods.
As with all Ruby, you can always take advantage of super
if you're maintaining the right interface to the super method. ActiveRecord::Base#initialize
only takes one optional argument, a hash
of attributes and values. It also yields itself to a block if a block is given. https://github.com/rails/rails/blob/master/activerecord/lib/active_record/core.rb#L313
So if you wanted to set an instance variable at initialization without also defining any public access methods, you can:
class Quote < ApplicationRecord
def initialize(attributes = nil, &block)
@my_var = attributes.delete(:my_var) if attributes
super
end
end
quote = Quote.new(my_var: 'value')
quote.instance_variable_get(:@my_var) # => "value"
You can also perform more complex operations. Assume you have User has_many :posts
and Post belongs_to :user
, User
has a :name
and Post
has a :title
.
class User < ApplicationRecord
def initialize(attributes = nil, &block)
title = attributes.delete(:first_post_title) if attributes
super
posts.build(title: "#{name}'s first post: #{title}") if title
end
end
user = User.new(first_post_title: 'Hello, world!', name: 'Matz')
user.posts.first # => #<Post:xxxxxxxxxxx title: "Matz's first post: Hello, world!">
Hopefully that demonstrates how flexible you can be even with ActiveRecord
objects, and sometimes callbacks are a little too complicated in implementation and restrictive in use when super
can work just fine and even better when used appropriately.
Upvotes: 4
Reputation: 1683
For the properties of Quote
you don't want to save in DB, create attr_reader
(or attr_accessor
). Suppose your Watir method needs var1
and var2
objects, then your Quote
should look like this:
class Quote < ApplicationRecord
attr_reader :var1, :var2
end
And then in after_create
callback of QuoteRequest
you should simply pass these variables as key-value pair like any other property.
obj1 = Var1.new
obj2 = Var2.new
quote = Quote.new(var1: obj1, var2: obj2, ....)`
Now you can access these by calling quote.var1
and quote.var2
. Calling save
on quote
would persist all the properties which have corresponding columns in db except var1
, var2
.
Upvotes: 1