Class Method VS Constant in Ruby/Rails

Class method vs constant in Ruby/Rails

The latter is better. If it were a method, a new array and new strings will be created every time it is called, which is a waste of resource.

Ruby Class method or Constant, best practice

This is the most common idiom:

class Product 
def xml_doc
@@xml_doc ||= Nokogiri::XML(open("#{Rails.root}/myxmlfile.xml"))
return @@xml_doc
end
end

The ||= operator says "if the variable is nil, calculate the result of the expresion and store it, otherwise do nothing". This idiom is called "memoization".

Do no think of constants as a way to optimize your code, in Ruby they are not really constant anyway.

Constants or class variables in ruby?

The main thing is that by using the CONSTANT notation, you're making it clear to the reader. the lower case, frozen string gives the impression is might be settable, forcing someone to go back and read the RDoc.

In Ruby, in a method defined in class self, why can't a constant defined on the superclass be access without self?

You have encountered a common Ruby gotcha - constant lookup.

The most important concept in constant lookup is Module.nesting (unlike in method lookup, where the primary starting point is self). This method gives you the current module nesting which is directly used by the Ruby interpreter when resolving the constant token. The only way to modify the nesting is to use keywords class and module and it only includes modules and classes for which you used that keyword:

class A
Module.nesting #=> [A]

class B
Module.nesting #=> [A::B, A]
end
end

class A::B
Module.nesting #=> [A::B] sic! no A
end

In meta programming, a module or class can be defined dynamically using Class.new or Module.new - this does not affect nesting and is an extremely common cause of bugs (ah, also worth mentioning - constants are defined on the first module of Module.nesting):

module A
B = Class.new do
VALUE = 1
end

C = Class.new do
VALUE = 2
end
end

A::B::VALUE #=> uninitialized constant A::B::VALUE
A::VALUE #=> 2

The above code will generate two warnings: one for double initialization of constant A::VALUE and a second for reassigning the constant.

If it looks like "I'd never do that" - this also applies to all the constants defined within RSpec.describe (which internally calls Class.new), so if you define a constant within your rspec tests, they are most certainly global (unless you explicitly stated the module it is to be defined in with self::)

Now let's get back to your code:

class SubExample < SuperExample
puts Module.nesting.inspect #=> [SubExample]

class << self
puts Module.nesting.inspect #=> [#<Class:SubExample>, SubExample]
end
end

When resolving the constant, the interpreter first iterates over all the modules in Module.nesting and searches this constant within that module. So if nesting is [A::B, A] and we're looking for the constant with token C, the interpreter will look for A::B::C first and then A::C.

However, in your example, that will fail in both cases :). Then the interpreter starts searching ancestors of the first (and only first) module in Module.nesting. SubrExample.singleton_class.ancestors gives you:

[
#<Class:SubExample>,
#<Class:SuperExample>,
#<Class:Object>,
#<Class:BasicObject>,
Class,
Module,
Object,
Kernel,
BasicObject
]

As you can see - there is no SuperExample module, only its singleton class - which is why constant lookup within class << self fails (print_constant_fails).

The ancestors of Subclass are:

[
SubExample,
SuperExample,
Object,
Kernel,
BasicObject
]

We have SuperExample there, so the interpreter will manage to find SuperExample::A_CONSTANT within this nesting.

We're left with print_constant_works_2. This is an instance method on a singleton class, so self within this method is just SubExample. So, we're looking for SubExample::A_CONSTANT - constant lookup firstly searches on SubExample and, when that fails, on all its ancestors, including SuperExample.

Constant vs. instance variable vs. instance method

Last time I was evil to a female newbie. This time over, I'll try to be nice.

Way one, using a constant:

A = Class.new

class B < A
FOO = [ :hello, :world ] # this is your array
end

# Access different constants defined in different descendants is tricky, like this:

class A
def foo_user
puts self.class.const_get :FOO
end
end

B.new.foo_user #=> [ :hello, :world ]

# Note theat you can't do this:

class A
def foo_user
puts FOO # this would look for FOO in A mother class only
end
end

B.new.foo_user #=> error

Way two, using an instance variable belonging to the subclasses of A:

A = Class.new

class B < A
@foo = [ "hello", "world" ]
end

# This way is more usable. I would go about it like this:

class A
class << self
attr_reader :foo # defining a reader class method
end

def foo
self.class.foo # delegating foo calls on the instances to the class
end

def foo_user
puts foo
end
end

B.new.foo_user #=> [ :hello, :world ]

Way three, using an instance method defined on the descendants:

A = Class.new

class B < A
def foo
[ "hello", "world" ]
end
end

# This way is also usable.

class A
def foo_user
puts foo
end
end

The choice between the way 2 (instance variable belonging to a descendant class) and 3 (method defined on a descendant class) depends on how flexible the value (the array) needs to be. Way 2 is most flexible, but way 3 takes less code.

Ruby constant within a class method

Not exactly what you wanted, but you simply haven't defined CONST inside class A but in its metaclass, which I have therefore saved a reference to...

class A
class << self
::AA = self
CONST = 1
end
end
puts AA::CONST


Related Topics



Leave a reply



Submit