Ruby - extend built-in variables in Ruby

2019-06-01 19:29发布

问题:

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.

回答1:

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 Objects. 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 Objects.

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?



回答2:

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"


回答3:

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.