What Does Ruby Constant Mean

What does Ruby constant mean?

That's right, constants are just like variables in ruby, but you get a warning if you change them.

Also, there's one difference with mere variables: You can access constants even if they are defined inside another class or module, for example given this snippet:

module Constants
PI = 3,1415
other = "variable"
end

You can reach PI doing Constants::PI while Constants::other will not work.

Scope of Constants in Ruby Modules

The USER_KEY you declared (even conditionally) in Auth is globally known as Auth::USER_KEY. It doesn't get "mixed in" to including modules, though including modules can reference the key in a non-fully-qualified fashion.

If you want each including module (e.g. ApplicationController) to be able to define its own USER_KEY, try this:

module Auth
DEFAULT_USER_KEY = 'user'
def self.included(base)
unless base.const_defined?(:USER_KEY)
base.const_set :USER_KEY, Auth::DEFAULT_USER_KEY
end
end
def authorize
user_id = session[self.class.const_get(:USER_KEY)]
end
end

class ApplicationController < ActionController::Base
USER_KEY = 'my_user'
include Auth
end

If you're going to go to all this trouble, though, you might as well just make it a class method:

module Auth
DEFAULT_USER_KEY = 'user'
def self.included(base)
base.extend Auth::ClassMethods
base.send :include, Auth::InstanceMethods
end
module ClassMethods
def user_key
Auth::DEFAULT_USER_KEY
end
end
module InstanceMethods
def authorize
user_id = session[self.class.user_key]
end
end
end

class ApplicationController < ActionController::Base
def self.user_key
'my_user'
end
end

or a class-level accessor:

module Auth
DEFAULT_USER_KEY = 'user'
def self.included(base)
base.send :attr_accessor :user_key unless base.respond_to?(:user_key=)
base.user_key ||= Auth::DEFAULT_USER_KEY
end
def authorize
user_id = session[self.class.user_key]
end
end

class ApplicationController < ActionController::Base
include Auth
self.user_key = 'my_user'
end

Ruby CONSTANTS seem to be INVISIBLY ALTERABLE?

TL;DR

Short of monkey-patching Kernel#warn (see https://stackoverflow.com/a/662436/1301972) to raise an exception, you won't be able to prevent reassignment to the constant itself. This is generally not a pragmatic concern in idiomatic Ruby code where one expects to be able to do things like reopen classes, even though class names are also constants.

A Ruby constant isn't actually immutable, and you can't freeze a variable. However, you can get an exception to be raised when something attempts to modify the contents of a frozen object referenced by the constant.

Freezing Objects Deeply with Plain Ruby

Freezing an Array is easy:

CONSTANT_ONE = %w[one two three].freeze

but the strings stored in this Array are really references to String objects. So, while you can't modify this Array, you can still (for example) modify the String object referenced by index 0. To solve this problem, you need to freeze not just the Array, but the objects it holds, too. For example:

CONSTANT = %w[one two three].map(&:freeze).freeze

CONSTANT[2] << 'four'
# RuntimeError: can't modify frozen String

CONSTANT << 'five'
# RuntimeError: can't modify frozen Array

Freezing Objects Recursively with a Gem

Since freezing recursive references can be a bit unwieldy, it's good to know there's a gem for that. You can use ice_nine to deep-freeze most objects:

require 'ice_nine'
require 'ice_nine/core_ext/object'

OTHER_CONST = %w[a b c]
OTHER_CONST.deep_freeze

OTHER_CONST << 'd'
# RuntimeError: can't modify frozen Array

OTHER_CONST[2] = 'z'
# RuntimeError: can't modify frozen Array

A Better Way to Use Ruby Constants

Another option to consider is calling Object#dup when assigning the value of a constant to another variable, such as instance variables in your class initializers, in order to ensure you don't mutate your constant's references by accident. For example:

class Foo
CONSTANT = 'foo'
attr_accessor :variable

def initialize
@variable = CONSTANT.dup
end
end

foo = Foo.new
foo.variable << 'bar'
#=> "foobar"

Foo::CONSTANT
#=> "foo"

Ruby - What is difference between defining constant using 'const_set' and simple capitalize constant name in the class?

class A
RANDOM_CONSTANT = 1
end

is simpler to write and read. This should be the preferred way to set a constant.

constant_name = 'RANDOM_CONSTANT'
A.const_set(constant_name, 1)

will work when the constant name is dynamically generated, and thus more flexible. You'd typically use it only when you want to do some metaprogramming magic.

Other than that, they are equivalent.

When to use constants instead of instance variables in Ruby?

There's a few things to consider here:

  • Will the value change within the life-cycle of an object?
  • Will you need to override the value in sub-classes?
  • Do you need to configure the value at run-time?

The best kind of constants are those that don't really change short of updating the software:

class ExampleClass
STATES = %i[
off
on
broken
].freeze
end

Generally you use these constants internally in the class and avoid sharing them. When you share them you're limited in how they're used. For example, if another class referenced ExampleClass::STATES then you can't change that structure without changing other code.

You can make this more abstract by providing an interface:

class ExampleClass
def self.states
STATES
end
end

If you change the structure of that constant in the future you can always preserve the old behaviour:

class ExampleClass
STATES = {
on: 'On',
off: 'Off',
broken: 'Broken'
}.freeze

def self.states
STATES.keys
end
end

When you're talking about instance variables you mean things you can configure:

class ConfigurableClass
INITIAL_STATE_DEFAULT = :off

def self.initial_state
@initial_state || INITIAL_STATE_DEFAULT
end

def self.initial_state=(value)
@initial_state = value ? value.to_sym
end
end

Constants are great in that they're defined once and used for the duration of the process, so technically they're faster. Instance variables are still pretty quick, and are often a necessity as illustrated above.

How to write constant inside module?

I'm not sure I understand - did you mean this:

module Test1
module Test2
CONSTANT = 5
def self.included(base)
# Where can I declare constant ? How ?
base.extend ClassMethods
base.class_eval do
# named scopes
end
end

module ClassMethods
end
end
end

class A
include Test1::Test2
end

puts A::CONSTANT # => 5

What does it mean when a variable or method or constants has preceded with an underscore in Ruby?

There is no difference in bahavior. It is just convertion.

But let's have a closer look:

_('A string') is actually a method not a variable. The underscore method is defined by Gettext and translates a string.

@_foo is offen used to show other developers that there is something special about the variable, and therefore it should not be used. I saw that pattern a lot for variables that are used to cache values, like:

def expensive_operation
@_expensive_operation ||= begin
# long running code...
end
end

And the underscore is sometimes used to indicate that a variable is not used in a block. Like this:

a_hash.each do |_, value|
# do something with the value, not with the key
end

ruby constant scope inside a class self block

You must reference the correct class:

Thing.singleton_class::NUM #=> 3

There is no inconsistency between the way constants and methods in the singleton class are referenced:

Thing.methods.include?(:speak)                          #=> true 
Thing.singleton_class.methods.include?(:speak) #=> false
Thing.singleton_class.instance_methods.include?(:speak) #=> true

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