I would like to extend the functionality of variables in Ruby. The reason is I am working on something similar of a type system or value checker (this sounds a bit crazy but the whole idea is to long to explain, just the reason I would like to extend default variables).
I have read that in Ruby, everything is an object; so variables are objects. And Ruby is supposed to be quite liberal concerning what can be changed via meta-programming.
Is there some kind of 'Class' associated with local variables that I could extend?
I would like to associate a string-variable for each variable that holds a string representation of a type. Further I would like to intercept variable assignments and execute a method each time a new value is assigned to a variable. This is so I can check, if the new value is correct according to the type (stored as string in the Variable).
If local variables in Ruby are defined as object of a class, I can extend that class or modify it via a ruby mixin.
The workaround would be to create a new class for my Variables (and not use the build in local variables of Ruby). This class can have a value attribute, a attribute (stored as string) for the type and a get- and set-method. This way I can solve my problem, but I would like to extend the built in variables in ruby, if that is possible.
current work in progress
class Fixnum
attr_accessor :tp
@tp
def mytype ( type )
@tp = type
end
def typecheck
#call typechecker
puts "checked"
end
end
Test Code:
a = 3
a.mytype("nat")
puts a.tp
a.typecheck
There are still two problems.
First, I think it is not possible to add a new constructor to Fixnum. Second, I would like to intercept the variable access, i.e. "b = a" calls the method 'typecheck' of a. But this would require something similar to Aspect Oriented Programming and I am not sure if this can be solved with Ruby's meta-programming facilitates.
I have read that in Ruby, everything is an object
That depends on your definition of "object" and every-"thing". "Object" can mean "entity that can be manipulated by the program" (which I will call object from now on), or "value that is a member of the object system" (which I will call Object
from now on).
In Ruby, everything that can be manipulated by the program (i.e. every object) is also an Object
, i.e. an instance of a class. This is unlike Java, for example, where primitives can be manipulated by the program (i.e. are objects in that sense of the word), but aren't Objects
. In Ruby, this distinction doesn't exist: every object is an Object
and every Object
is also an object.
However, there are things in the language, which cannot be manipulated by the program and which aren't instances of a class, i.e. they are neither object s nor Object
s. These are, for example, methods, variables, syntax, parameter lists, arguments lists, keywords.
Note: you can use Ruby's reflection API to give you an object that represents a method or a parameter list, but that object is only a proxy, it is not the real thing.
So, when we say "everything is an object", what we really mean is that "every object is an Object
", i.e. that everything which can be manipulated by the program is also a member of the object system, or in other words, there are no values outside of the object system (unlike primitives in Java). We do not mean that everything that exists in the language can also be manipulated at runtime by the program.
so variables are objects
No, unfortunately, they are neither object s nor Object
s.
This is also clearly stated in the Ruby Language Specification (emphasis added by me):
6.2 Variables
6.2.1 General description
A variable is denoted by a name, and refers to an object, which is called the value of the variable.
A variable itself is not an object.
In the book The Ruby Programming Language by Matz and David Flanagan it says on page 2:
every value is an object
Note, it doesn't say every-thing, only every value.
See also the question Is variable is object in ruby?
There are a couple things you can do. For starters, all (or very nearly all) Ruby classes (including "primitives" such as numbers) support a to_s method which returns a string representation of the object. For numbers, to_s will just return the string representation of that number (e.g., "42" for 42). The string value returned for other classes will vary. The nice thing is that you can override a class's methods via "monkey patching". Here's an extremely contrived example:
class Array
def to_s
return "An array of size #{self.size}."
end
end
a = [1, 2, 3, 4]
puts a.to_s
# => "An array of size 4."
For your other question regarding executing a method every time a variable's value is set, the way to handle this is to always interact with the variable through its accessor methods. This way you can implement custom code inside a property's getter and setter methods (or simply call another method from inside the accessor). Like this:
class MyClass
# Use the default getter for var.
attr_reader :var
def initialize
@var = 1
end
# Custom setter for var.
def var= v
@var = v
puts "var changed to #{v}"
end
end
mc = MyClass.new
mc.var = 9
# => "var chaged to 9"
You could do something like this, but it only works for globals:
$type_checked = {:$a => String, :$b => Array}
$type_checked.keys.each do |var|
trace_var(var) do |obj|
puts "hey, don't assign #{var} to a #{obj.class}" unless $type_checked[var] == obj.class
#or raise an Error
end
end
$a = 1
$a = "a"
$b = 1
#output:
#hey, don't assign $a to a Fixnum
#hey, don't assign $b to a Fixnum
But this clearly goes against the grain of the language.