Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

const_defined? vs. defined? in Ruby

Tags:

ruby

To test, whether a constant (say: a class) is known at a certain point in the code, I can write for instance:

if defined? :String

or I can write

if self.class.const_defined? :String

Is there a situation where I these two ways of testing would make a difference? Note that I don't ask about the case where I have an explicit receiver, such as MyModule.const_defined? :Something, but only for the case where I want to test whether a certain constant (which in my case happens to be a constant denoting a class) is already defined.

like image 423
user1934428 Avatar asked Dec 13 '22 08:12

user1934428


2 Answers

First things first, defined? is a keyword which behaves a bit similar similar to a method. It receives the name of the thing (variable, constant, ...) to check. What makes this method different from all others is that the parser will not resolve the value of the given name but rather check directly for whether it is defined (hence the keyword property). To check if a constant is defined, you thus have to pass the actual name (rather than a Symbol):

if defined?(String)

The const_defined? on the oither hand is more regular. It expects a Symbol or String with the name of a constant and checks whether it is defined on the receiver.

Now as for the differences between the two (when used correctly): if you use them both within the context of an instance method to check for the existence of a constant, they work the same.

When running e.g. in a class definition (such that self is e.g. a class), you need to make sure to use the correct receiver for your const_defined method, e.g. if self.const_defined? :String.

Also, defined? can check for a lot more than just constants (e.g. methods, expressions, variables, ...)

If you want to use this to make sure you actually have the name of a constant at hand in a given variable, you need to use const_defined?. If you want to "statically" check whether an constant was defined, you can use defined?.

like image 160
Holger Just Avatar answered Dec 26 '22 02:12

Holger Just


defined? is a keyword that will check if an expression exists in the current scope.

const_defined? is a method that will check if a constant exists through the ancestor chain of the receiver.

planet = "Earth"

class Experiment
  def diff
    ""
  end

  def show
    puts "defined" if defined?(diff)
    puts "Earth not found" if !defined?(planet)
    puts "String constant defined" if self.class.const_defined?(:String)
  end
end

Experiment.new.show
#=> defined
#=> Earth not found
#=> String constant defined

p Experiment.ancestors #=> [Experiment, Object, Kernel, BasicObject]
p String.superclass #=> Object

Here's an example of situations where this will make a difference:

Using defined?(Nothing's printed)

class Lab
  class Coco
  end
end

class Experiment

  def diff
    ""
  end

  def show
    puts "defined" if defined?(Coco) #=> Nothing printed
  end
end

Experiment.new.show

Using self.class.const_defined? (Something's printed)

class Lab
  class Coco
  end
end

class Experiment < Lab

  def diff
    ""
  end

  def show
    puts "defined" if self.class.const_defined? :Coco #=> defined
  end
end

Experiment.new.show
p Experiment.ancestors #=> [Experiment, Lab, Object, Kernel, BasicObject] We find 'Lab' class in the ancestor chain.
like image 45
emi Avatar answered Dec 26 '22 02:12

emi