Initializing class attribute using a method - ruby

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.

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)
Solution
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

Related

How create Ruby Class with same object id

I need to create a class where if the attribute value is the same it does not generate a new object id, example:
result:
described_class.new('01201201202')
<PixKey:0x00007eff5eab1ff8 #key="01201201202">
if i run it again with the same value it should keep the same object id
0x00007eff5eab1ff8
is similar behavior with the symbol
test:
describe '#==' do
let(:cpf) { described_class.new('01201201202') }
it 'verifies the key equality' do
expect(cpf).to eq described_class.new('01201201202')
end
end
Running the test shows an error, because the obejct id changes:
expected: #<PixKey:0x00007eff5eab1ff8 #key="01201201202">
got: #<PixKey:0x00007eff5eab2070 #key="01201201202">
Class:
class PixKey
def init(key)
#key = key
end
end
The other answers are fine, but they are a little more verbose than needed and they use class variables, which I find to be a confusing concept because of how they are shared among various classes.
class PixKey
#instances = {}
def self.new(id)
#instances[id] ||= super(id)
end
def initialize(id)
#key = id
end
end
p PixKey.new(1)
p PixKey.new(2)
p PixKey.new(2)
p PixKey.new(1)
Running the test shows an error, because the object id changes
Not quite. It shows an error because the objects are not equal. And the error message prints both objects including their id. But the object id is not what's causing the test to fail.
I need to create a class where if the attribute value is the same it does not generate a new object id
That would probably work, but you're likely approaching the problem from the wrong side. In Ruby, equality doesn't mean object identity. Two objects can be equal without being the same object, e.g.
a = 'foo'
b = 'foo'
a.object_id == b.object_id
#=> false
a == b
#=> true
There's no need to tinker with object ids to get your test passing. You just have to implement a custom == method, e.g.:
class PixKey
attr_reader :key
def initialize(key) # <- not "init"
#key = key
end
def ==(other)
self.class == other.class && self.key == other.key
end
end
The == method checks if both objects have the same class (i.e. if both are PixKey instances) and if their key's are equal.
This gives:
a = PixKey.new('01201201202')
b = PixKey.new('01201201202')
a == b
#=> true
Create a class method to create instances and have it look up a hash.
class PixKey
##instances = {}
def PixKey.create(id)
if not ##instances.has_key?(id)
##instances[id] = PixKey.new(id)
end
return ##instances[id]
end
def initialize(id)
#key = id
end
end
a = PixKey.new(123)
b = PixKey.new(123)
c = PixKey.create(123)
d = PixKey.create(123)
puts a
puts b
puts c
puts d
Output:
#<PixKey:0x000000010bc39900>
#<PixKey:0x000000010bc38078>
#<PixKey:0x000000010bc33eb0>
#<PixKey:0x000000010bc33eb0>
Notice the last two instances created with the PixKey.create(id) method return the same instance.
Note that Ruby's new method is just a method on Class and can be overridden like any other. The docs describe the default implementation.
Calls allocate to create a new object of class's class, then invokes that object's initialize method, passing it args. This is the method that ends up getting called whenever an object is constructed using .new.
So, if you want to keep the .new syntax and still get the same objects back, we can override new on the class and call super. This is exactly what OscarRyz' answer does, just with .new and super rather than a separate helper function.
class PixKey
##instances = {}
def PixKey.new(id)
if not ##instances.has_key?(id)
##instances[id] = super(id)
end
return ##instances[id]
end
def initialize(id)
#key = id
end
end
a = PixKey.new(123)
b = PixKey.new(123)
puts a
puts b

Accessing a variable from a lower class that was NOT created as a child class (ie: using the "<" operator) in Ruby

I have a ToDo list program I'm writing for practice. My problem is that by separating concerns and making each list be a class while each task is also a class, I would like to be able to call the name of the list which the new task is being added to without having to pass the list name into the task class (either upon creation or later):
class HigherClass
def initialize
#higher_class_variable = unique_value
#instance_of_lower_class #keep variable empty for now
end
end
class LowerClass
def intitialize
#lower_class_variable = unique_value #this should be the same unique value as above
end
end
instance_of_higher_class = HigherClass.new
instance_of_higher_class.#instance_of_lower_class = LowerClass.new
instance_of_higher_class.#instance_of_lower_class.#lower_class_variable
#this should return the unique_value from the HigherClass instance
If you want to access attributes:
attr_reader :lower_class_variable
Then you can just do this:
#instance_of_lower_class.lower_class_variable
Same principle applies here for higher class:
class HigherClass
attr_writer :instance_of_lower_class
end
Then:
instance_of_higher_class.instance_of_lower_class = LowerClass.new
That all seems rather clunky considering you can do this:
class HigherClass
attr_accessor :unique
def initialize
#unique = unique_value
end
end
class LowerClass < HigherClass
def initialize
# Call superclass initialization
super
# Anything else you want here.
end
def something_using_unique
call_method(unique)
end
end

Where do methods defined at the top-level exist?

I am learning some metaprogramming and I was stuck trying to find a method. Let's say I have the following class:
class MyClass
def self.my_method
end
def your_method
end
end
With the following code I can search for each method in the object space:
type = Class
name = /^my_method$/
result = ObjectSpace.each_object(type).select do |o|
o.instance_methods(false).grep(name).size > 0 || o.methods.grep(name).size > 0
end
p result
And it finds it showing the following output:
[MyClass]
As the searcher code also searches for instance methods, it shows the same output when looking for your_method.
Even with if I add a singleton method to an object:
mc = MyClass.new
def mc.our_method
end
Just changing the searcher like this:
type = Object
name = /^our_method$/
result = ObjectSpace.each_object(type).select do |o|
o.methods.grep(name).size > 0
end
p result
It also finds it:
[#<MyClass:0x8f86760>]
The question is, how do I find a method defined in the top level object? This method:
def hidden
end
Besides, which is the current class when defining a method like this?
Which is the current class when defining a method like this?
We can easily figure out what object we’re in by inspecting self in this top level scope:
self #=> main
self.class #=> Object
So we’re not in a Class, but an instance of Object which is dubbed “main”.
How do I find a method defined in the top level object?
This is where it gets interesting. The top-level scope object in Ruby behaves specially, but it’s relatively easy to discover where a method here defined lives:
def foo; :bar; end
method(:foo).owner #=> Object
Object.new.foo #=> NoMethodError: private method `foo' called
Object.new.send(:foo) #=> :bar
So methods defined at the top-level are made (private*) instance methods of Object. The reason your ”searcher” cannot find it is because these methods are private, and neither methods nor instance_methods include private methods, instead you need private_methods and private_instance_methods:
Object.instance_methods.include?(:foo) #=> false
Object.private_instance_methods.include?(:foo) #=> true
* Note that Pry (at least v0.10.1) alters this to make methods defined at top-level in its REPL public.
If you had this:
def my_method() end
class A
def self.my_method() end
end
class B < A
def my_method() end
end
class C
def my_method() end
end
and wanted to find methods named 'my_method' that you've created, you could do this:
ObjectSpace.each_object(Class).select do |o|
o.instance_methods(false).include?(:my_method)
end
#=> [C, B]
ObjectSpace.each_object(Class).select do |o|
o.methods(false).include?(:my_method)
end
#=> [A]
ObjectSpace.each_object(Class).select do |o|
o.private_instance_methods(false).include?(:my_method)
end
#=> [Object]

Parent class definition with arguments

I was browsing the camping documentation, and I ran into this example for defining a controller:
class Digits < R '/nuts/(\d+)'
def get(number)
"You got here by: /nuts/#{number}"
end
end
It looks like what this class definition is doing is that it's passing a string argument to the R superclass. However, I looked through the camping codebase and I didn't see R defined as a class anymore. It was defined as a method like this:
def R(c,*g)
p,h=/\(.+?\)/,g.grep(Hash)
g-=h
raise "bad route" if !u = c.urls.find{|x|
break x if x.scan(p).size == g.size &&
/^#{x}\/?$/ =~ (x=g.inject(x){|x,a|
x.sub p,U.escape((a.to_param rescue a))}.gsub(/\\(.)/){$1})
}
h.any?? u+"?"+U.build_query(h[0]) : u
end
and the method to actually handle the route:
def /(p); p[0] == ?/ ? #root + p : p end
I don't understand exactly how this works, because when I tried to make a class and define a method as a superclass, like this:
def doSomething(boo)
puts boo
end
class Someclass < doSomething 'boo'
end
I get this error:
(eval):60: (eval):60: superclass must be a Class (NilClass given) (TypeError)
Can someone point me to where in the ruby documentation this feature (using a method as a superclass) is covered? I don't know what to call this feature, so my googling efforts couldn't really find me anything.
You'll have to return a class from your method:
def doSomething(boo)
Class.new { define_method(:boo) { boo } }
end
class SomeClass < doSomething 'boo'
end
SomeClass.new.boo # => 'boo'
You're also looking at the wrong method. Camping has a class method on Controllers called R (that's the one used when defining controllers) and an instance method on Base called R (for generating routes). This is the actual definition: https://github.com/camping/camping/blob/ae5a9fabfbd02ba2361ad8831c15d723d3740b7e/lib/camping-unabridged.rb#L551

What's the difference between self.generate and Invoice.generate?

class Invoice
def Invoice.generate(order_id, charge_amount, credited_amount = 0.0)
Invoice.new(:order_id => order_id, :amount => charge_amount, :invoice_type => PURCHASE, :credited_amount => credited_amount)
end
end
Why would you create Invoice.generate inside Invoice class rather than self.generate?
self.generate is easier to work with, whereas Invoice.generate is arguably more explicit. Other than that, there's no difference between the two.
Explanation
You can define a method on any instance using this form
def receiver.method(args)
end
Check this out
class Foo
end
def Foo.bar
"bar"
end
Foo.bar # => "bar"
And yes, I mean any instance. It's absolutely possible that one instance has some method while another doesn't
f = Foo.new
def f.quux
'quux'
end
f2 = Foo.new
f.quux # => "quux"
f2.quux # => # ~> -:20:in `<main>': undefined method `quux' for #<Foo:0x007fe4e904a6c0> (NoMethodError)
A reminder: inside of class definition (but outside of method definitions) self points to that class.
class Foo
# self is Foo
end
So, armed with this knowledge, the difference between self.generate and Invoice.generate should be obvious.
Under normal circumstances, it would practically have no difference from def self.generate.
The only edge case I can think of is if you have a nested class with the same name, then the explicit version would apply only to the nested class.
class A
def self.x
name
end
def A.y
name
end
class A
# nested class A::A
end
def self.p
name
end
def A.q
name
end
end
> A.x # => "A"
> A.y # => "A"
> A.p # => "A"
> A.q # => NoMethodError: undefined method `q' for A:Class
> A::A.q # => "A::A"
As you see, after a nested class with the same name is defined, subsequent explicit class method definitions made with the class name refer to the nested class, but explicit definitions made beforehand refer to the original.
Implicit definitions made with self always refer to the base class.
You have 2 ways for defining a class method.
1) You can use the name of the class directly
class Test #Test is now an instance of a built-in class named Class
def Test.class_method
"I'm a class method."
end
end
2) You can use the self variable, which is always pointing to the current object
class Test
def self.class_method
"I'm a class method."
end
end
Once you understand that classes are objects, this use of the self variable to define a class method finally makes sense.
The value of self
Not too surprinsingly, when you are inside a class method, the value of self refers to the object that holds the class structure (the instance of class Class). This means that :
class Test
def self.class_method
self.x
end
end
is equivalent to :
class Test
def self.class_method
Test.x
end
end
When you are inside an instance method, the value of self still refers to the current object. This time however, the current object is an instance of class Test, not an instance of class Class.
More info. : http://www.jimmycuadra.com/posts/self-in-ruby

Resources