Confusing behaviour of const_get in Ruby?
You are correct to be confused... The doc didn't state that Ruby makes a special case for lookup of constants in Modules
and has been modified to state this explicitly. If the constant has not been found in the normal hierarchy, Ruby restarts the lookup from Object
, as can be found in the source.
Constant lookup by itself can be bit confusing. Take the following example:
module M
Foo = :bar
module N
# Accessing Foo here is fine:
p Foo # => bar
end
end
module M::N
# Accessing Foo here isn't
p Foo # => uninitialized constant M::N::Foo
end
p M::N.const_get :Foo # => uninitialized constant M::N::Foo
In both places, though, accessing Object
level constants like Array
is fine (thank god!). What's going on is that Ruby maintains a list of "opened Module definitions". If a constant has an explicit scope, say LookHereOnly::Foo
, then only LookHereOnly
and its included modules will be searched. If no scope is specified (like Foo
in the example above), Ruby will look through the opened module definitions to find the constant Foo
: M::N
, then M
and finally Object
. The topmost opened module definition is always Object
.
So M::N.const_get :Foo
is equivalent to accessing Foo
when the opened classes are only M::N
and Object
, like in the last part of my example.
I hope I got this right, coz I'm still confused by constant lookups myself :-)
Confusing behaviour between class and object
Here is a little different way to define a classes/objects:
klass = Class.new {
puts self
def bar
puts self
end
}
# #<Class:0x3fbdbb8>
As you can see, it outputs #<Class:0x3fbdbb8>
. So its class is Class
. You can check it via class
method:
klass.class
# => Class
When you name your class with uppercase letter (for example Example
) for the first time, it use it as output instead of cryptic name like: #<Class:0x3fbdbb8>
.
Foo = Class.new {
puts self
def bar
puts self
end
}
Still outputs cryptic name because Foo =
's part hasn't been evaluated yet.
puts Foo
Outputs correct name - Foo
What if I name it again?
Qux = Foo
# => Foo
Nothing. Foo
will be Foo
forever.
Strange ruby behaviour with __FILE__ constant?
What you want is to expand the path properly:
# Affected by the current working directory, etc.
puts __FILE__
# Always an absolute path
puts File.expand_path(__FILE__, Dir.getwd)
This takes your current working directory into account.
How to judge whether a Constant has been defined within a module namespace, not a global one?
Indeed, const_defined?
and const_get
go through the hierarchy, which for modules include (artificially) the Object
class. You can use Module#constants
to avoid this, though:
module Admin
A = 'A Const with in the Admin namespace' unless constants.include?(:A)
end
Note: In Ruby 1.8, you check against "A"
, not :A
Confusing behavior of File.dirname
File.dirname
should return an absolute path if given an absolute path, and a relative path if given a relative path:
File.dirname('/home/jon/test.rb') # => '/home/jon'
File.dirname('test.rb') # => '.'
__FILE__
returns the name of the current script, which is therefore a relative path from the current directory. That means you should always use expand_path
if you want to get the absolute path with File.dirname(__FILE__)
.
NB Ruby 2.0.0 introduces the __dir__
constant
Why doesn't the Digest gem show all the class as constants?
The magic is in the const_missing
singleton method in the Digest module. @knugje has pointed out a very useful link in his answer which makes in clearer.
ri documentation about const_missing
says:
Invoked when a reference is made to an undefined constant in mod
So here's the trick:
module X
def self.const_missing(name)
name
end
end
p X.constants # => []
p X::Y # => :Y
p X.constants # => []
So the code doesn't define the constant in the module. So we can use a fairly simple trick:
module X
define_singleton_method(:const_missing) { |n| const_set(n, n.id2name) }
end
p X.constants # => []
p X::Y # => "Y"
p X::Z # => "Z"
p X.constants # => [:Y, :Z]
p X::Z # "Z"
That's how const_missing
is used here. But Instead of const_set
, what digest does is loading up a file with the name and do some exception handling and checking if the constants exist on the file.
Personally speaking, this may create confusion. For example, when you have no document to offline and online documents, you can't just simply run Digest.constants
to list the available constants.
As knugie pointed out, you can read about the const_missing
implementation in the Digest module here.
Why do Global Variables exist in Ruby?
Global variables are not bad. They're not evil. They're just incredibly, incredibly powerful. Which is why you shouldn't use them.
Global variables are global- they can be accessed and modified anywhere in the code. A single global variable has the potential to affect all of your classes, all of your functions, all of the classes and functions of every single library or dependency you load into your project, and all of the classes and functions of every single project which loads your project as a dependency, as well as the projects that load those projects, and so and and so forth, for ever and always, for the rest of time.
The second people start feeling comfortable using global variables, the namespace gets insanely cluttered and we get conflicts left and right and the stability of the programming language itself is threatened. Which is why the use of global variables is emphatically and repeatedly discouraged.
But global variables are not bad. They're like the highway lanes labeled "for emergency vehicles only," or like those fire-axes behind glass labeled "break glass in case of emergency."
It's entirely possible that at some point, in the distant future, you will have an incredibly unusual situation which merits the use of a single global variable. But that day is not today. And it is probably not tomorrow, or a month from now, or a year from now. Daily life, daily code- it just doesn't call for the unbridled power of a global variable.
$stdout
is a great example of why global variables are sometimes important. $stdout
is the default stream in ruby- the one where things will print if no other stream is specified. $stdout
should be accessible from every class and every function in every library because it acts like a giant funnel, shoveling all output to a single location. The whole world knows and agrees that $stdout
exists in ruby, and its uses are well-documented, so its power is well-managed.
This isn't to be confused with STDOUT
which is a constant representing the actual pipe which sets up a stream between ruby and its parent program (usually a terminal). $stdout = STDOUT
by default, but $stdout
can be changed to anything. If you want your program to print to a file, you can change $stdout
to a file stream.
I don't think this name choice is confusing for a seasoned rubyist. A variable is designed to be modified and a constant is designed to be constant. The difference between $stdout and STDOUT is that the former can be modified to change the standard output location of your program, and the latter is a constant, always pointing to the stdout stream. The capitalization makes a world of difference and conveys very different meanings.
As for why global constants are uninitialized and global variables are nil
, that actually has nothing to do with globals. Ruby automatically initializes all variables as nil
. You can easily see this with instance variables such as @foo
or @@foo
. In almost every situation, an undefined local variable will throw a NameError
because ruby cannot tell whether it is a variable or a method. But in strange situations, they too are initialized as nil
:
puts foo # => NameError: undefined local variable or method 'foo'
foo = 42 if false
puts foo # => nil
puts bar # => NameError
bar = bar
puts bar # => nil
It was a conscious design choice in Ruby not to automatically initialize constants. Because a constant is, by definition, something which is initialized once and then never changed, it would break the definition for a constant to be nil
at first and then a different value later in the code.
I should also mention that global constants are considered acceptable, even among people who tout global variables as bad. The difference is that constants can only be assigned once and generally throw a warning or error if they get assigned again. This protects programmers from situations where conflicting global constants might cause problems.
Related Topics
Ruby: How to Count the Number of Times a String Appears in Another String
Multiple Robots.Txt for Subdomains in Rails
Testing Whether String Starts with or End with Another String
Rendering Partials/View in a Rake Task/Background Job/Model in Rails 4
Does Scala Scale Better Than Other Jvm Languages
How to Do Sane "Set-Difference" in Ruby
Best Way to Monitor for Completion of a Sidekiq Job
Paperclip :Style Depending on Model (Has_Many Polymorphic Images)
Run Ruby Script in the Background
No Implicit Conversion of String into Integer (Typeerror)
Most Concise Way to Test String Equality (Not Object Equality) for Ruby Strings or Symbols
Where/How to Include Helper Methods for Capybara Integration Tests
Configuring Jekyll for Github Project Pages