Adding Variables and Methods

You can also use the module_eval and class_eval methods to retrieve the values of class variables (but bear in mind that the more you do this, the more your code becomes dependent on the implementation details of a class, thereby compromising encapsulation):

Y.class_eval( "@@x" )

In fact, class_eval can evaluate expressions of arbitrary complexity. You could, for example, use it to add new methods to a class by evaluating a string:

ob = X.new
X.class_eval( 'def hi;puts("hello");end' )
ob.hi        #=> hello

Returning to the earlier example of adding and retrieving class variables from outside a class (using class_eval), it turns out that there are also methods designed to do this from inside a class. The methods are called class_variable_get (this takes a symbol argument representing the variable name, and it returns the variable’s value) and class_variable_set (this takes a symbol argument representing a variable name and a second argument that is the value to be assigned to the variable).

Here is an example of these methods in use:

classvar_getset.rb

class X
    def self.addvar( aSymbol, aValue )
        class_variable_set( aSymbol, aValue )
    end

    def self.getvar( aSymbol )
        return class_variable_get( aSymbol )
    end
end

X.addvar( :@@newvar, 2000 )
puts( X.getvar( :@@newvar ) )    #=> 2000

To obtain a list of class variable names as an array of strings, use the class_variables method:

p( X.class_variables )    #=> ["@@abc", "@@newvar"]

You can also add instance variables to classes and objects after they have been created using instance_variable_set:

dynamic.rb

ob = X.new
ob.instance_variable_set("@aname", "Bert")

By combining this with the ability to add methods, the bold (or maybe reckless?) programmer can completely alter the internals of a class “from the outside.” Here I have implemented this in the form of a method called addMethod in class X, which uses the send method to create the new method m using define_method with the method body, defined by &block:

def addMethod( m, &block )
    self.class.send( :define_method, m , &block )
end

Note

The send method invokes the method identified by the first argument (a symbol), passing to it any arguments specified.

Now, an X object can call addMethod to insert a new method into the X class:

ob.addMethod( :xyz ) { puts("My name is #{@aname}") }

Although this method is called from a specific instance of the class (here ob), it affects the class itself, so the newly defined method will also be available to any subsequent instances (here ob2) created from the X class:

ob2 = X.new
ob2.instance_variable_set("@aname", "Mary")
ob2.xyz

If you don’t care about the encapsulation of data in your objects (my definition of encapsulation assumes the hiding of internal data, though some people have less rigorous definitions), you can also retrieve the value of instance variables using the instance_variable_get method:

ob2.instance_variable_get( :@aname )

You can similarly set and get constants:

X::const_set( :NUM, 500 )
puts( X::const_get( :NUM ) )

Because const_get returns the value of a constant, you could use this method to get the value of a class name, which is itself a constant, and then append the new method to create a new object from that class. This could even give you a way of creating objects at runtime by prompting the user to enter class names and method names. Try this by running this program:

dynamic2.rb

class X
    def y
        puts( "ymethod" )
   end
end

print( "Enter a class name: ")                  #<= Enter: X
cname = gets().chomp
ob = Object.const_get(cname).new
p( ob )                                         #=> #<X:0x2bafdc0>
print( "Enter a method to be called: " )        #<= Enter: y
mname = gets().chomp
ob.method(mname).call                           #=> ymethod
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.74.25