Reputation: 1403
The goal is to initialize a class attribute using a class method that's overridden in sub-classes. Following is the definition of my ruby classes:
class A
class_attribute :query
self.query = self.generate_query
def self.generate_query
return "abcd"
end
end
class B < A
def self.generate_query
query_part_1 = "ab"
return query_part_1 + generate_query_part_2
end
def self.generate_query_part_2
return part_2
end
end
The reason I want to do this is because query
is a constant string per class and should not be created again on instantiation but it's a complex string which is generated in multiple independent parts. Separating this logic out in methods would make the code cleaner. However, with this code, I get the undefined method generate_query for class A
.
I have tried lazy initialization of the class attribute while instantiating the class like the following:
def initialize
query = self.class.get_query
end
def self.get_query
self.query = self.generate_query if self.query.nil?
end
However, this initializes the query to same value for both class A and B if A is instantiated first because self.query.nil?
would then return false for B also.
Upvotes: 0
Views: 705
Reputation: 1160
The solution to your problem is simple:
You are calling self.query = self.generate_query
before your generate_query
method has been defined! Remember - Ruby is interpreted top to bottom and your class body is no different. You cannot call a method before it is defined.
Simply changing the code around to
class A
class_attribute :query
def self.generate_query
return "abcd"
end
self.query = self.generate_query
end
will make it work, but then you will have another problem, as the line self.query = self.generate_query
will only get evaluated once in your class - B
will reference the "abcd"
query, not "ab2"
.
To achieve the behavior you want - you need to define a getter method yourself which acts as an attribute (class_attribute
does the same thing under the hood btw)
class A
def self.query
@query ||= self.generate_query
end
def self.generate_query
return "abcd"
end
end
class B < A
def self.generate_query
query_part_1 = "ab"
return query_part_1 + generate_query_part_2
end
def self.generate_query_part_2
return '2'
end
end
Upvotes: 1