Difference between Module#const_set and Module#module_eval - ruby

Aren't they supposed to do the same thing? Why is this happening? At this point, I'm using the module_eval in my code out of necessity, but the const_set seems more readable. Regardless, I'd really like to understand why this happens.
here's the code:
class A
def foo
FOO
end
def self.foo
FOO
end
end
module B
class C < A
end
end
B.const_set(:FOO,'asdf')
>> B::C.foo
NameError: uninitialized constant A::FOO
from ./foo.rb:6:in `foo'
from (irb):1
>> B.module_eval {FOO='asdf'}
=> "asdf"
>> B::C.foo
=> "asdf"

Your module_eval isn't actually putting the constant in the module. Then, you're just accessing it from main:
module B;end
B.module_eval { FOO = 'asdf' }
>> FOO
=> "asdf"
You can fix that with self::FOO = 'asdf', then it's the same as B.const_set(:FOO,'asdf'). You can also do it more directly like this:
B::FOO = 'asdf'
The main problem with your code is that you can't access constants from other modules like that. If they're in an outer module, you need to specify the scope of the constant with the :: prefix:
def foo
B::FOO
end

Related

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

I am working on a Rails application where in one class, constants are defined using const_set constant_name, value.
I know we can define constants directly in class by just specifying the CAPITALIZE name of the constant with its value like following
class A
RANDOM_CONSTANT = 1
end
so what is the difference between defining constants using const_set and simply the way I have declared in class A ?
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.
I can add to the answer from #amadan that this method, like an others, for example const_get, method_missing, define_method etc is part of tools for meta-programming in ruby.
For example:
#const_get
Let's say we have such module Foo with list of constants: BOO, BAR, BAZ
module Foo
BAR = 1
BAZ = 2
BOO = 3
end
now we need to print all of it, we can do it dynamically or manually:
def print_dynamically
Foo.constants.each do |const|
p Foo.const_get(const)
end
end
def print_manually
p Foo::BAR
p Foo::BAZ
p Foo::BOO
end
> print_dynamically
=> 1
=> 2
=> 3
> print_manually
=> 1
=> 2
=> 3
#method_missing
Let's say we have such class Foo and we want to add some friendly error catching for NoMethodError
class Foo
def boo
puts 'boo'
end
def method_missing(method_name)
puts "sorry, method: ##{method_name} not defined."
puts "Here is a list of public methods:"
# here we take all defined public methods from class Foo except `#method_missing`
p public_methods(false).reject { |n| n == :method_missing }
end
end
> foo = Foo.new
> foo.boo
=> boo
> foo.bar
=> sorry, method: #bar not defined.
=> Here is a list of public methods:
=> [:boo]
#define_method
Let's say we need to define some list of simple(or not) methods, and we don't want to just copy-paste it:
instead of:
class Foo
def foo
puts "foo"
end
def boo
puts "boo"
end
def bar
puts "bar"
end
end
> foo.foo
=> foo
> foo.boo
=> boo
> foo.bar
=> bar
you can simply write it in his way:
class Foo
%i[foo boo bar].each do |method_name|
define_method(method_name) { puts "#{method_name}" }
end
end
> foo.foo
=> foo
> foo.boo
=> boo
> foo.bar
=> bar
Summary:
From examples below you can see that Ruby provide us a lot of tools for dynamically creation of methods, getting of it, getting of constants and setting of it.
P.S.
These is far not all the methods, it's just a few of them, but I think it's enough to show that you can do with meta-programming tools in ruby.
As far as I know, there's no difference, so this:
class A; end
A.const_set('RANDOM_CONSTANT', 1)
is equivalent to:
class A
RANDOM_CONSTANT = 1
end

How to create an instance with part of the methods of a class in Ruby?

I have class A with methods X and Y. Now I want to create an instance but only want it to have method X from class A.
How should I do it? Should it be by deleting method Y for the instance when creating it? Your help is appreciated!
You should not do this. You should instead share the problem you're solving and find a better pattern for solving it.
An example for solving this problem a little differently:
class A
def x; end
end
module Foo
def y; end
end
instance_with_y = A.new
instance_with_y.send :include, Foo
instance_with_y.respond_to? :y #=> true
Here is one way to solve the problem :
class X
def a
11
end
def b
12
end
end
ob1 = X.new
ob1.b # => 12
ob1.singleton_class.class_eval { undef b }
ob1.b
# undefined method `b' for #<X:0x9966e60> (NoMethodError)
or, you could write as ( above and below both are same ) :
class << ob1
undef b
end
ob1.b
# undefined method `b' for #<X:0x93a3b54> (NoMethodError)
It's possible to do what you want with ruby, as ruby can be very malleable like that, but there are much better ways. What you want to achieve seems like a really bad idea.
The problem you just described a problem inheritance is designed to solve. So really, you have two classes. Class A and also class B which inherits from class A.
class A
def foo
'foo'
end
end
# B inherits all functionality from A, plus adds it's own
class B < A
def bar
'bar'
end
end
# an instance of A only has the method "foo"
a = A.new
a.foo #=> 'foo'
a.bar #=> NoMethodError undefined method `bar' for #<A:0x007fdf549dee88>
# an instance of B has the methods "foo" and "bar"
b = B.new
b.foo #=> 'foo'
b.bar #=> 'bar'

Undefine class method and back to define it later

I need to disable a class method for some time, then enable it again. How could I do that? I know that I can remove a method:
class Foo
def Foo.bar
puts "bar"
end
end
Foo.bar # => bar
class <<Foo
remove_method :bar
end
Foo.bar # => undefined method `bar' for Foo:Class (NoMethodError)
Now I need Foo.bar again. How could I do that? I tried to save the method in a proc
m = Proc.new { Foo.bar }
and then define it again:
class Foo
define_method(:bar, &m)
end
but I get
NameError: undefined local variable or method 'm' for...
So I flattened the scope
P = Class.new(Foo) do
define_method(:bar, &m)
end
but I get undefined method if I run it
P.bar
Foo.bar
Is it possible to save a method, undefine it, and then define it back?
Instead of keeping a method body in a proc, you should keep methods as methods. Constantly keep it defined under some different name, and switch Foo.bar between alias of it or not.
class Foo
def Foo.temporal_bar
puts "bar"
end
end
Foo.singleton_class.class_eval{alias bar temporal_bar}
Foo.bar # => bar
Foo.singleton_class.class_eval{remove_method bar}
Foo.bar # => Undefined local variable or method `bar' for #<Class:Foo>
Foo.singleton_class.class_eval{alias bar temporal_bar}
Foo.bar # => bar
You can try out instance_eval:
(class << Foo; self; end).instance_eval do
define_method(:bar, &m)
end

Inheritence & Scope Resolution in Ruby

I'm trying to understand why I need the scope resolution operator to access constants in an inherited module but not methods. Assuming:
module Foo
SOMETHING = {:one => 1, :two => 2}
def showit
p SOMETHING
end
end
and
class Bar
include Foo
def initialize
# why doesn't method name need scope resolution but const does?
showit
p Foo::SOMETHING
end
Also related, can you access instance or local variables declared in Foo from Bar? I tried something like this:
module Foo
#myar = "some string"
myvar2 = "some other string"
def showit
p #myvar
p myvar2
end
end
and calling showit from within the Bar class...printing #myvar resulted in nil and myvar2 was undefined. I would expect both strings to print...what's going on?
You actually don't have to scope the constant.
This should run just fine.
class Bar
include Foo
def initialize
showit
p SOMETHING # instead of Foo::SOMETHING
end
end
Bar.new

How to reference global variables and class variables?

I'm new to programming. Right now I'm studying Ruby. To my understanding, global variables are defined in the global namespace (so outside of any classes or functions). I'm reading something and it says global variables have a $ sign before them. What does that mean? Does it mean when I define a function or class and want to reference my global variable (let's say it is edmund = 123) I would have to reference it like this: $edmund?
so:
edmund = 123
def my_function()
456 + $edmund
end
Also are class variables (the ones that begin with ##) like instance variables (#) where you can access them by calling them through Class.classvariable? What is their purpose?
Global scope is scope that covers the entire program. Global scope is enjoyed by global variables, which are recognizable by their initial dollar-sign ($) character. They’re available everywhere and creating your own global variables can be tempting, especially for beginning programmers. But they’re not always a good idea.
$gvar = "I'm a global!"
class C
def examine_global
puts $gvar
end
end
c = C.new
c.examine_global # I'm a global!
Class variables begin with two at signs: ##var, for example. Despite their name, class variables aren’t class scoped. Rather, they’re class-hierarchy scoped. At its simplest, the idea behind a class variable is that it provides a storage mechanism that’s shared between a class and instances of that class, and that’s not visible to any other objects.
class Parent
##value = 100
end
class Child < Parent
##value = 200
end
class Parent
puts ##value
end
What gets printed is 200. The Child class is a subclass of Parent, and that means Parent and Child share the same class variables—not different class variables with the same names, but the same actual variables. When you assign to ##value in Child, you’re setting the one and only ##value variable that’s shared throughout the hierarchy—
that is, by Parent and Child and any other descendant classes of either of them.
And to give credit where its due - This explanation comes from "The Well Grounded Rubyist" by David A Black, one of the best resources to learn about Ruby.
Excellent question. Unfortunately, you just jumped down a rabbit hole, but it's one that you have to fall through eventually in ruby to start understanding the real intricacies.
For your first question, regarding the $-prefixed global variables. They are truly global:
def mk_foo() $foo ||= "foo"; end
$foo # => nil
mk_foo # => "foo"
$foo # => "foo"
mk_foo.object_id # => 70299647799620
$foo.object_id # => 70299647799620
As you can see, when $foo is defined within the mk_foo method, it is defined in the global space, and you can access it anywhere:
class CanSeeFoo
def see_foo() $foo; end
end
CanSeeFoo.new.can_see_foo
# => "foo"
CanSeeFoo.new.can_see_foo.object_id
# => 70299647799620
As for the class variable question, this is where the rabbit-hole begins. First, you are correct that ##-prefixed variables are referred to as "class variables" and #-prefixed variables are referred to as "instance variables".
Class variables are static across all subclasses (at all sub-levels of the inheritance tree) of the defining class. The implication here is that if any subclass changes the class variable, it will change in all related subclasses and up to the defining class.
class A; end
class B < A; ##foo = "foo"; end
B.class_variable_get(:##foo) # => "foo"
A.class_variable_get(:##foo)
# => raises NameError "uninitialized class variable ##foo in A"
class C < B; end
C.class_variable_get(:##foo) # => "foo"
class D < C
def self.change_foo(); ##foo = "bar"; end
def change_foo(); ##foo = "baz"; end
end
D.class_variable_get(:##foo) # => "foo"
class E < D; end
E.class_variable_get(:##foo) # => "foo"
D.change_foo # => "bar"
D.class_variable_get(:##foo) # => "bar"
E.class_variable_get(:##foo) # => "bar"
C.class_variable_get(:##foo) # => "bar"
B.class_variable_get(:##foo) # => "bar"
D.new.change_foo # => "baz"
D.class_variable_get(:##foo) # => "baz"
E.class_variable_get(:##foo) # => "baz"
C.class_variable_get(:##foo) # => "baz"
B.class_variable_get(:##foo) # => "baz"
A.class_variable_get(:##foo)
# => raises NameError "uninitialized class variable ##foo in A"
As for accessing class and instance variables, neither is accessible without the use of #instance_variable_get or ::class_variable_get until an accessor is defined. At present, ruby only has methods for defining accessors on instance variables, but it is simple enough to define the appropriate methods for the class variables:
class A
##foo = "foo"
# the second argument `true` adds the writer method `#bar=`
attr :bar, true
def self.foo(); ##foo; end
def self.foo=(v); ##foo = v; end
def initialize()
#bar = "bar"
end
end
class B < A; end
A.foo # => "foo"
B.foo = "foobar"
A.foo # => "foobar"
B.foo # => "foobar"
a = A.new
a.bar # => "bar"
a.bar = "baz"
a.bar # => "baz"
a.foo
# => raises NoMethodError: undefined method `foo' for #<A:0x ...
You can see the attribute accessor methods here in the ruby core docs: http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr. Also, ActiveSupport (http://rubygems.org/gems/activesupport) has "cattr" methods for defining class variable accessors http://api.rubyonrails.org/v3.2.5/classes/Class.html#method-i-cattr_accessor.
That's the simple stuff. The next step is understanding the "singleton class" also known as the "eigenclass" or "metaclass" (Wikipedia: Metaclass) (remember, everything in ruby is an Object, including the Class and Module constructs). Here I will point you to an excellent post by Yehuda Katz: Metaprogramming in Ruby: It’s All About the Self, and another Stack Overflow question: class << self idiom in Ruby.
As a preview: The singleton class (not to be confused with the singleton design pattern) allows you to access methods and instance data for a specific class or module. For some related documentation, see the core docs: http://www.ruby-doc.org/core-1.9.3/Object.html#method-i-singleton_class
class A; end
class B < A;
class << self
def foo() #foo end
def foo=(v) #foo = v; end
end
end
B.foo = "foo"
class C < B; end
A.foo
# => raises NoMethodError: undefined method `foo' for A:Class
B.foo # => "foo"
C.foo # => nil
B.foo = "baz"
B.foo # => "baz"
C.foo # => nil
C.foo = "foo"
C.foo # => "foo"
B.foo # => "baz"
Lastly, remember to make use of the Ruby-Core docs. Most useful for understanding the above are:
http://www.ruby-doc.org/core-1.9.3/Object.html
http://www.ruby-doc.org/core-1.9.3/Module.html
http://www.ruby-doc.org/core-1.9.3/Class.html
The dollar sign is part of the variable name, so it has to be declared like this:
$edmund = 123
This is the same things for the instance and class variables: their names begin with # or ##.

Resources