Ilias Lazaridis <ilias / lazaridis.com> wrote:
> 
> The above thread showed finally the need to split the evaluation down
> into smaller chunks.
> 
> Here is a simple evaluation template (first part) which can be applied 
> to the Ruby language:
> 
> http://lazaridis.com/case/lang/index.html

Okay, that's a neat bit of work. Here's a quick solution (ruby makes
most of this fairly trivial):

# here's your basic talker class

class Talker
  def sayHello
    puts "Hello world"
  end
end

# __FILE__ == $0 means that the program is being run directly
# rather than 'require'd from another file

if __FILE__ == $0
  talker = Talker.new
  talker.sayHello
end

# one-pass interpreter, and you can reopen classes
# so let's just continue

# here are the two instance variables added to the class, complete with
# autogenerated getters and setters

class Talker
  attr_accessor :name, :age

  def initialize(name, age)
    @name, @age = name, age
  end

  # following the spec, though say_name is more rubyish
  def sayYourName
    puts @name
  end

  def sayYourAge
    puts @age
  end
end

# now note that our object 'talker' has access to the new methods in its
# class

if __FILE__ == $0
  # note that we aren't breaking encapsulation and accessing the vars directly;
  # the setter methods are called name= and age=, and the getters are called
  # name and age
  talker.name = "Just another talker"
  talker.age = 1
  talker.sayYourName
  talker.sayYourAge
end

# reflection
class Talker

  # simple
  
  def sayYourClassName
    puts self.class.name # the 'self' is optional here
  end

  # objects know nothing about the variables that are bound to them
  # (a variable name is not the name of the instance anyway). The
  # closest you can come to the "name" of an object is it's object id, so...
  def sayYourInstanceName
    puts object_id # again, you could say self.object_id if you prefer
  end

  # advanced
  
  # caller returns a stack (array) of strings of the form 
  #   file:linenumber in `method'
  # so we extract the most recent one and parse the method name out
  # code from PLEAC
  def thisMethodName
    caller[0] =~ /in `([^']+)'/ ? $1 : '(anonymous)';
  end

  # string interpolation in action - the bit between the #{} can be
  # any valid expression; to_s will be called on the result and 
  # interpolated into the string
  def sayHelloAdvanced
    puts "#{thisMethodName}: Hello World"
  end
  
  # expert
  def sayYourClassDefinition
    puts "Class:"
    sayYourClassName
    
    # %{} is another way to write a string literal
    # (looks neat for multiline strings)
    # we use the standard 'inspect' method to print out arrays of 
    # method names in a ["meth1", "meth2", ...] format
    puts %{
Methods:
  public:
    #{public_methods.inspect}
  protected
    #{protected_methods.inspect}
  private:
    #{private_methods.inspect}
  non-inherited:
    #{(methods - self.class.superclass.instance_methods).inspect}
      
  Instance Variables:
    #{instance_variables.inspect}
    }

    # note that the instance variables belong to the *instance*, not
    # to the class, so they're not technically part of the class
    # definition. the following code is illustrative:
    #
    # class A
    #   attr_accessor :foo, :bar # defines getters and setters
    # end
    #
    # a = A.new
    # p a.instance_variables # => []
    # a.foo = 10
    # p a.instance_variables # => ["@foo"]
    # b = A.new
    # p b.instance_variables # => []
  end

  def sayYourClassCode
    puts "Sadly, you cannot introspect the source code from within a program"
    # though see http://rubyforge.org/projects/parsetree/
    # for a way to get at the parsed AST
  end 
end

# testing it out

if __FILE__ == $0
  talker.sayHelloAdvanced
  talker.sayYourClassName
  talker.sayYourClassDefinition
  talker.sayYourClassCode
end

Hope this helps.

martin