I'm writing a little piece of code in Ruby (1.9.3), and I use a pair of simple "enum-like" classes, that define some constants with const_set and some behavior of these constants (e.g. the class Days may have the constants MON, TUE... and Days::MON.succ should evaluate to TUE).
I'm really comfortable with these classes. However, while growing my code, i sometimes need to add more of them, and I don't like the idea of having five or more classes that share 99% of source code, e.g.:
class Days
NAMES = %w( MON TUE ... )
INSTANCES = []
def initialize(num)
#num = num
end
# An example operation
def +(n)
INSTANCES[(#num + n) % INSTANCES.length]
end
# Another example operation
def succ
self + 1
end
def to_s
NAMES[#num]
end
NAMES.each_with_index do |name, idx|
instance = new(idx)
INSTANCES[idx] = instance
const_set name, instance
end
end
class Months
NAMES = %w( JAN FEB ... )
...
end
I was wondering if Ruby's metaprogramming capabilities could be used to generate these classes. However, I'm having an hard time creating NAMES, INSTANCES and the "enum-named" constants (e.g. MON, TUE, ...).
Being const_set a class method of Class, in this code it's context (the value of self) is respectively Days and Months.
When creating a factory method, I'm compelled to do something like this:
def enum_new(names_array)
Class.new do
const_set "NAMES" []
names_array.each_with_index do |name, idx|
NAMES[idx] = name
end
...
end
end
Days = enum_new(%w| MON TUE ... |)
Months = enum_new(%w| JAN FEB ... |)
but this won't work (at least, not like i hoped it to), because const_set won't be called in the context of the class whose name is magically set (i.e. Days and Months), but apparently in the context of Class; therefore, not only it won't be accessible from the instance methods, but it will be overwrited every time enum_new is called with a new array of names as argument. A similar problem shows up when using class variables, because they'll be shared between any class generated with the method (because they'll become class variables of Class, i guess).
Is there any way to create constants in a class generated with Class.new, obtaining this way classes identical in everything to the original Days and Months classes, without having to pollute my code with almost identical classes?
Thanks for your attention and patience! :)
Yes. Do your initialization in a class_exec block, into which you can pass your name data and where self refers to the right class:
theClass=Class.new
theClass.class_exec(names) do |names|
#initialize constants here...
end
Related
How come if I have a class like:
class Thing
def number
10
end
end
And I inherit from it like this:
class OtherThing < Thing
CONSTANT = number / 2
end
I get undefined local variable or method 'number' when I try to instantiate the class, but if I do this:
class OtherThing < Thing
def method_instead_of_constant
number / 2
end
end
It works?
EDIT
I'm not necessarily looking for a hack to make this work, but an understanding as to why it doesn't. mudasobwa's answer below helped the most; constants are assigned at the class level, not on instances.
You need a class method to achieve the functionality you are looking for:
class Thing
# ⇓⇓⇓⇓ HERE
def self.number
10
end
end
class OtherThing < Thing
CONSTANT = number / 2
end
CONSTANT assignment is happening on the class level, hence it has an access to class methods of Thing, but not to the instance methods of it.
On the other hand, you might instantiate Thing and then call the instance method on it:
class Thing
def number
10
end
end
class OtherThing < Thing
# ⇓⇓⇓⇓⇓⇓⇓⇓⇓ HERE
CONSTANT = Thing.new.number / 2
end
Because number is an instance method on Thing. The scope of the class definition of OtherThing is an instance of Class, which means it isn't an instance of Thing or an instance of OtherThing at definition.
That said, you shouldn't be defining constants by executing methods. You could presumably have a calculated class variable that you call freeze on to prevent editing post-startup, but even that's not a very common pattern.
Because of scope.
Methods calls are evaluated based on the dynamic scope and distinguish between class and instance scope. Constants are resolved in lexical scope and do not distinguish between class and instance scope like methods do.
class A
# expressions are evaluated in scope of class A
def m
# expressions are evaluated in scope of an instance of A
return 42
end
end
Class and instance are not the same.
A
a = A.new
A.class # => Class
a.class # => A
A.respond_to?(:m) # => false
a.respond_to?(:m) # => true
A.m # => raises NoMethodError
a.m # => 42
Hence in the class body you cannot call instance methods.
Constants however are looked up using lexical scope, that is the surrounding context of classes and modules in the source file.
module M
# can access global constants and those defined in M
class A
# can access global constants and those defined in M or A
def m
# can access global constants and those defined in M or A
end
end
end
You can inspect the current constant lookup path with Module.nesting
I'm not necessarily looking for a hack to make this work, but and [sic]
understanding as to why it doesn't.
Here's the error message:
undefined local variable or method 'number'
When the Ruby interpreter sees number it looks for a local variable or a method named number. In the context of a method, the interpreter reads number as self.number. So this line:
CONSTANT = number / 2
is actually considered as:
CONSTANT = self.number / 2
So what's self?
Well that depends on where you've defined it (explicitly or implicitly). Within a class block, self is the class itself i.e. OtherThing. Since OtherThing nor any of its ancestors has a class-method number defined, nor does there exist a variable number, Ruby throws the error message.
self defined within an instance method definition is the current object. But this is awfully abstract without examples and some more theory. Other relevant topics are Singleton-Classes and Inheritance. If you like books, then I recommend The Well-Grounded Rubyist 2nd Ed, Chapter 5 by David A. Black. On second thoughts, read/study the whole book.
I have the following Module which has 1 variable which contains a string for the first day of a hypothetical year, 1 method which outputs a string and another method which also outputs a string:
module Week
first_day = "Sunday"
def weeks_in_month
puts "There are 4 weeks in a month"
end
def weeks_in_year
puts "There are 52 weeks in a year"
end
end
I now have a class who's only purpose is to print out the variable located in the module.(this is just for testing purposes)
class Decade
include Week
def firstday
puts Week::first_day
end
end
I now instantiate Decade and access the methods located in the module using Decades object. My program runs into a problem when calling the firstday method
z = Decade.new
z.weeks_in_month
z.weeks_in_year
z.firstday #Errors here
The error I get is:
undefined method `first_day' for Week:Module (NoMethodError)
I am new to Ruby and am just getting used to Modules, so any help would be appreciated.
When writing a module the convention is to declare constants like this:
module Week
FIRST_DAY = 'Sunday'
end
Note that they're in ALL_CAPS. Anything that begins with a capital letter is treated as a constant. Lower-case names of that sort are treated as local variables.
Generally it's bad form to access the constants of another module, it limits your ability to refactor how those are stored. Instead define a public accessor method:
module Week
def first_day
FIRST_DAY
end
end
Now you can call that externally:
Week.first_day
Note you can also change how that's implemented:
module Week
DAYS = %w[
Sunday
Monday
Tuesday
...
Saturday
]
def first_day
DAYS.first
end
extend self # Makes methods callable like Week.first_day
end
The nice thing about that is the first_day method does exactly the same thing, no other code has to change. This makes refactoring significantly easier. Imagine if you had to track down and replace all those instances to Week::FIRST_DAY.
There's some other things to note here. The first is that any time you call include on a module then you get the methods and constants loaded in locally. The second thing is when you define a mix-in module, be careful with your names to avoid potential conflict with the target class.
Since you've mixed it in, you don't need the namespace prefix, just calling first_day should do it.
Is there a practical application to the "crazy-ness" below?
It seems like this is a way for ted to always be able to return himself to the world and people will think they are talking to ted who they expect to act a certain way and be a certain age... but he isn't acting the way he portrays himself and is lying about his age to someone.
What 'trickery' is possible when an object is returned and you check on what that object represents and is capable of... but really that object was acting another way and capable of other things before returning.
class Person
def age
21
end
def who_am_i?
puts "I am #{self} / #{object_id} and I am #{age} years old"
self
end
end
ted = Person.new
def ted.singleton_who_am_i?
class << self
def age
0
end
end
puts "I am #{self} / #{object_id} and I am #{age} years old"
self
end
puts ted.who_am_i? == ted.singleton_who_am_i?
>> I am #<Person:0x100138340> / 2148123040 and I am 21 years old
>> I am #<Person:0x100138340> / 2148123040 and I am 0 years old
>> true
http://andrzejonsoftware.blogspot.ca/2011/02/dci-and-rails.html
in DCI, your data model gets different type of behavior based on the context it is used it. Usually it is done with object.extend, but it is pretty much what you are doing above -- taking advantage of the metaclass.
Another example (and probably why things work that way) is the way classes work in ruby. If you say
class Foo
end
that is the same thing as saying
Foo = Class.new
end
meaning that what you are doing is assigning a new instance of class Class to a constant. When you define a method on that class, you don't want it applied to all instance of class Class, you only want it on the class you are defining. So when you say
class Foo
def self.bar
end
end
it is the exact thing as saying
class Foo
end
def Foo.bar
end
which is exactly the same principal as you are talking about in your question
(sorry if that was unclear)
Ruby is a very dynamic language letting you inject code into objects at runtime. There are some good uses for it but it can also make code very hard to debug and understand.
It's totally counter-intuitive for a method that queries an object to modify that object. Nobody would expect a call to who_am_i to modify the object.
On the other hand replacing methods like that can make unit testing classes really straight forward.
If you want to test how the class behaves with different ages you can inject code like that before your tests.
I'm thinking in:
class X
def new()
#a = 1
end
def m( other )
#a == other.#a
end
end
x = X.new()
y = X.new()
x.m( y )
But it doesn't works.
The error message is:
syntax error, unexpected tIVAR
How can I compare two private attributes from the same class then?
There have already been several good answers to your immediate problem, but I have noticed some other pieces of your code that warrant a comment. (Most of them trivial, though.)
Here's four trivial ones, all of them related to coding style:
Indentation: you are mixing 4 spaces for indentation and 5 spaces. It is generally better to stick to just one style of indentation, and in Ruby that is generally 2 spaces.
If a method doesn't take any parameters, it is customary to leave off the parantheses in the method definition.
Likewise, if you send a message without arguments, the parantheses are left off.
No whitespace after an opening paranthesis and before a closing one, except in blocks.
Anyway, that's just the small stuff. The big stuff is this:
def new
#a = 1
end
This does not do what you think it does! This defines an instance method called X#new and not a class method called X.new!
What you are calling here:
x = X.new
is a class method called new, which you have inherited from the Class class. So, you never call your new method, which means #a = 1 never gets executed, which means #a is always undefined, which means it will always evaluate to nil which means the #a of self and the #a of other will always be the same which means m will always be true!
What you probably want to do is provide a constructor, except Ruby doesn't have constructors. Ruby only uses factory methods.
The method you really wanted to override is the instance method initialize. Now you are probably asking yourself: "why do I have to override an instance method called initialize when I'm actually calling a class method called new?"
Well, object construction in Ruby works like this: object construction is split into two phases, allocation and initialization. Allocation is done by a public class method called allocate, which is defined as an instance method of class Class and is generally never overriden. It just allocates the memory space for the object and sets up a few pointers, however, the object is not really usable at this point.
That's where the initializer comes in: it is an instance method called initialize, which sets up the object's internal state and brings it into a consistent, fully defined state which can be used by other objects.
So, in order to fully create a new object, what you need to do is this:
x = X.allocate
x.initialize
[Note: Objective-C programmers may recognize this.]
However, because it is too easy to forget to call initialize and as a general rule an object should be fully valid after construction, there is a convenience factory method called Class#new, which does all that work for you and looks something like this:
class Class
def new(*args, &block)
obj = alloc
obj.initialize(*args, &block)
return obj
end
end
[Note: actually, initialize is private, so reflection has to be used to circumvent the access restrictions like this: obj.send(:initialize, *args, &block)]
Lastly, let me explain what's going wrong in your m method. (The others have already explained how to solve it.)
In Ruby, there is no way (note: in Ruby, "there is no way" actually translates to "there is always a way involving reflection") to access an instance variable from outside the instance. That's why it's called an instance variable after all, because it belongs to the instance. This is a legacy from Smalltalk: in Smalltalk there are no visibility restrictions, all methods are public. Thus, instance variables are the only way to do encapsulation in Smalltalk, and, after all, encapsulation is one of the pillars of OO. In Ruby, there are visibility restrictions (as we have seen above, for example), so it is not strictly necessary to hide instance variables for that reason. There is another reason, however: the Uniform Access Principle.
The UAP states that how to use a feature should be independent from how the feature is implemented. So, accessing a feature should always be the same, i.e. uniform. The reason for this is that the author of the feature is free to change how the feature works internally, without breaking the users of the feature. In other words, it's basic modularity.
This means for example that getting the size of a collection should always be the same, regardless of whether the size is stored in a variable, computed dynamically every time, lazily computed the first time and then stored in a variable, memoized or whatever. Sounds obvious, but e.g. Java gets this wrong:
obj.size # stored in a field
vs.
obj.getSize() # computed
Ruby takes the easy way out. In Ruby, there is only one way to use a feature: sending a message. Since there is only one way, access is trivially uniform.
So, to make a long story short: you simply can't access another instance's instance variable. you can only interact with that instance via message sending. Which means that the other object has to either provide you with a method (in this case at least of protected visibility) to access its instance variable, or you have to violate that object's encapsulation (and thus lose Uniform Access, increase coupling and risk future breakage) by using reflection (in this case instance_variable_get).
Here it is, in all its glory:
#!/usr/bin/env ruby
class X
def initialize(a=1)
#a = a
end
def m(other)
#a == other.a
end
protected
attr_reader :a
end
require 'test/unit'
class TestX < Test::Unit::TestCase
def test_that_m_evaluates_to_true_when_passed_two_empty_xs
x, y = X.new, X.new
assert x.m(y)
end
def test_that_m_evaluates_to_true_when_passed_two_xs_with_equal_attributes
assert X.new('foo').m(X.new('foo'))
end
end
Or alternatively:
class X
def m(other)
#a == other.instance_variable_get(:#a)
end
end
Which one of those two you chose is a matter of personly taste, I would say. The Set class in the standard library uses the reflection version, although it uses instance_eval instead:
class X
def m(other)
#a == other.instance_eval { #a }
end
end
(I have no idea why. Maybe instance_variable_get simply didn't exist when Set was written. Ruby is going to be 17 years old in February, some of the stuff in the stdlib is from the very early days.)
There are several methods
Getter:
class X
attr_reader :a
def m( other )
a == other.a
end
end
instance_eval:
class X
def m( other )
#a == other.instance_eval { #a }
end
end
instance_variable_get:
class X
def m( other )
#a == other.instance_variable_get :#a
end
end
I don't think ruby has a concept of "friend" or "protected" access, and even "private" is easily hacked around. Using a getter creates a read-only property, and instance_eval means you have to know the name of the instance variable, so the connotation is similar.
If you don't use the instance_eval option (as #jleedev posted), and choose to use a getter method, you can still keep it protected
If you want a protected method in Ruby, just do the following to create a getter that can only be read from objects of the same class:
class X
def new()
#a = 1
end
def m( other )
#a == other.a
end
protected
def a
#a
end
end
x = X.new()
y = X.new()
x.m( y ) # Returns true
x.a # Throws error
Not sure, but this might help:
Outside of the class, it's a little bit harder:
# Doesn't work:
irb -> a.#foo
SyntaxError: compile error
(irb):9: syntax error, unexpected tIVAR
from (irb):9
# But you can access it this way:
irb -> a.instance_variable_get(:#foo)
=> []
http://whynotwiki.com/Ruby_/_Variables_and_constants#Variable_scope.2Faccessibility
Foo = Class.new
Foo.instance_eval do
def instance_bar
"instance_bar"
end
end
puts Foo.instance_bar #=> "instance_bar"
puts Foo.new.instance_bar #=> undefined method ‘instance_bar’
My understanding is that calling instance_eval on an object is supposed to allow you to define an instance variable or method for that object.
But in the example above, when you call it on class Foo to define the instance_bar method, instance_bar becomes a class method that can be invoked with "Foo.instance_bar". It is clear that this code has not created an instance method because Foo.new.instance_bar results in "undefined method ‘instance_bar’".
Why does instance_eval define a class method rather than an instance method in this context?
x.instance_eval changes your context so self evaluates to x.
This allows you to do many things, including defining instance variables and instance methods but only for x.
x = Object.new
y = Object.new
# define instance variables for x and y
x.instance_eval { #var = 1 }
y.instance_eval { #var = 2 }
# define an instance method for all Objects
class Object
def var
#var
end
end
x.var #=> 1
y.var #=> 2
Ruby lets you define instance methods for an object in a couple places. Normally,
one defines them in a class, and those instance methods are shared among all instances
of that class (like def var above).
However, we can also define an instance method for just a single object:
# here's one way to do it
def x.foo
"foo!"
end
# here's another
x.instance_eval do
# remember, in here self is x, so bar is attached to x.
def bar
"bar!"
end
end
Even though x and y have the same class, they don't share these methods, since they were only defined for x.
x.foo #=> "foo!"
x.bar #=> "bar!"
y.foo #=> raises NoMethodError
y.bar #=> raises NoMethodError
Now in ruby, everything's an object, even classes. Class methods are just instance methods
for that class object.
# we have two ways of creating a class:
class A
end
# the former is just syntatic sugar for the latter
B = Class.new
# we have do ways of defining class methods:
# the first two are the same as for any other object
def A.baz
"baz!"
end
A.instance_eval do
def frog
"frog!"
end
end
# the others are in the class context, which is slightly different
class A
def self.marco
"polo!"
end
# since A == self in here, this is the same as the last one.
def A.red_light
"green light!"
end
# unlike instance_eval, class context is special in that methods that
# aren't attached to a specific object are taken as instance methods for instances
# of the class
def example
"I'm an instance of A, not A itself"
end
end
# class_eval opens up the class context in the same way
A.class_eval do
def self.telegram
"not a land shark"
end
end
Note again, that all these methods are A-specific, B doesn't get access to any of them:
A.baz #=> "baz!"
B.telegram #=> raises NoMethodError
The important thing to take away from here is that
class methods are just instance methods of an object of class Class
The purpose of 'instance_eval' is to extend objects, but the purpose of 'class_eval' is to extend classes. And because classes are also objects, you can apply instance_eval on classes.
I guess that extending of classes is just more understandable in classic OOP. Dynamic languages allow us to easily specify behaviour of particular objects. Fact that each object can have own behaviour adds a lot of flexibility designing an application. Not only data can vary for objects of the same class. Two humans differ not only because they were born in different years, not only because they have different parents but they can think different and thus behave different.
Ability to change the behaviour of each object is fundamental. It exists in many languages.
Thinking about instance_eval think about objects first. Then you'll realize that classes are also objects - objects which additional purpose to create new objects, to hold description of common behaviour (methods). You can not only use definition of a class, but can also assign class to a variable, pass class as an argument, call method on a class, program a class!
I would recommend articles written by Yehuda Katz and Yugui to dive deeper into it:
http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/
http://yugui.jp/articles/846