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
# %{} 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}
# 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
> Okay, that's a neat bit of work. Here's a quick solution (ruby makes > most of this fairly trivial):
I apologize for the late answer.
> # here's your basic talker class
> class Talker > def sayHello > puts "Hello world" > end > end
ok
> # __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
Assuming I placethe code into the file "talker.rb".
from the command-line, i like to start it, e.g. with "ruby talker.rb"
I miss a "main" directive / function / object.
something like:
def main talker = Talker.new talker.sayHello end
> # one-pass interpreter, and you can reopen classes > # so let's just continue
[sidenote: I don't understand this]
> # here are the two instance variables added to the class, complete with > # autogenerated getters and setters
very nice!
> class Talker > attr_accessor :name, :age
can I write?:
attr_accessor :name attr_accessor :age
> def initialize(name, age) > @name, @age = name, age > end
Is this the constructor?
I assume I can write
def initialize(name, age) @name = name @age = age end
> # following the spec, though say_name is more rubyish > def sayYourName > puts @name > end
can I write?: def sayYourName puts @name end
> def sayYourAge > puts @age > end > end
ok
> # 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
very(!) nice!
> talker.name = "Just another talker" > talker.age = 1 > talker.sayYourName > talker.sayYourAge > end
> > # one-pass interpreter, and you can reopen classes > > # so let's just continue
> [sidenote: I don't understand this]
The ruby interpreter is one-pass insofar as it takes a single pass through the file, from top to bottom, building and executing an abstract syntax tree as it goes. Anything enclosed in a def...end block (and a few other delayed-evaluation constructs like lambda) gets converted into a 'callable' block of code, the rest is just executed. That's how attr_accessor works, incidentally - it's just a piece of code that runs inside a class's context and generates methods when it's executed. Try the following:
class Foo def say_hello puts "hello world" end
puts "hi, this is the reader" end
a = Foo.new a.say_hello
the first puts statement gets executed when say_hello is called; the second one is not inside a def statement, and so gets executed when the interpreter gets to it.
Hence the lack of a main() method - in a sense, the whole fine is a main(), since the entry point is the top of the file (comes in real handy when writing quick imperative scripts - you aren't forced to define a class just because Ruby is a pure OO language).
Hence the if __FILE__ == $0 blocks - if the file is not being run directly, the condition fails, and the whole block is skipped.
As for reopening classes, you can modify a class at runtime (add/delete methods, mix in a module), and any objects belonging to the class inherit those modifications (rather trivially, since they look up methods in the class). But, since this is a one pass interpreter, those changes come into effect at the time the reader reads them, and so the object "changes" in the middle of its lifecycle.
>> You can think of the whole file as "main". The code will be read from >> top to bottom. [...] > This means that ruby is _not_ strictly Object Oriented.
> And this means that I can drop the "if __FILE__ [...]" construct.
__FILE__ is the name of the current source file. $0 is the name of the top-level ruby program being executed.
So this construct just checks if this the source file is the file that is executed directly with e.g. 'ruby talker.rb', and only if this is the case, the codeblock will be executed.
>> And this means that I can drop the "if __FILE__ [...]" construct.
> __FILE__ is the name of the current source file.
ok, here i guessed right (intuitive naming).
> $0 is the name of the top-level ruby program being executed.
here i thought that "$0" represents "NULL".
$0 => main-file (or entry-file, or top-level file, or top-level program)
> So this construct just checks if this the source file is the file that > is executed directly with e.g. 'ruby talker.rb', and only if this is the > case, the codeblock will be executed.
> 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
I will make a slight addition:
The application main code will be alternatively entered in a seperate file, main.rb:
> talker = Talker.new > talker.sayHello
How does "main.rb" gets knowledge about "talker.rb" ?
-
[...]
> # 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
[...]
"#" is used as a comment marker _and_ partly within code.
Can I use another comment marker?
Can I write "puts "#{thisMethodName}: Hello World"" in an different manner, without the use of "#"?
On Thu, 24 Mar 2005 10:59:50 +0900, Ilias Lazaridis <il...@lazaridis.com> wrote: > Martin DeMello wrote: > > # here's your basic talker class
> > class Talker > > def sayHello > > puts "Hello world" > > end > > end
> I will make a slight addition:
> The application main code will be alternatively entered in a seperate > file, main.rb:
> > talker = Talker.new > > talker.sayHello
> How does "main.rb" gets knowledge about "talker.rb" ?
Put the code for the Talker class in a separate file then 'require' that file in main.rb. For example, if the Talker class were in talker.rb then I'd have:
require 'talker'
talker = Talker.new talker.sayHello
for my main.rb. Note that the require statement does not need the .rb suffix on the file containing the class.
> > # 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
> "#" is used as a comment marker _and_ partly within code.
> Can I use another comment marker?
No. To my knowledge there is only the one comment syntax in ruby.
> Can I write "puts "#{thisMethodName}: Hello World"" in an different > manner, without the use of "#"?
Sure. #{thisMethodName} in a string literal simply calls .to_s on the enclosed expression and inserts it at the designated location. So we can pull that out such:
puts thisMethodName.to_s + ": Hello World"
The + operator performs concatenation when its arguments are strings.
> def sayYourClassName > puts self.class.name # the 'self' is optional here > end
omitting "self" (puts class.name) leads to an error
> # 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
ok
> # 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
I understand the concept.
is there possibly a more direct solution available, with cleaner code and a stable/higher execution speed?
> # 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
> # (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}
Can I get somehow a more precise reflection of the class definition (output similar to the original class-def, excluding code)?
> # 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
ok
> 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
ok
> end
> # testing it out
> if __FILE__ == $0 > talker.sayHelloAdvanced > talker.sayYourClassName > talker.sayYourClassDefinition > talker.sayYourClassCode > end
[from an answer which showed up as a seperate thread]
James Edward Gray II wrote:
> Begin forwarded message:
>>> # reflection >>> class Talker >>> # simple >>> def sayYourClassName >>> puts self.class.name # the 'self' is optional here >>> end
>> omitting "self" (puts class.name) leads to an error
> I checked this one myself, because it surprised me when you said it. > You're right of course. I'm assuming it's because class is a method > name and a Ruby keyword.
>>> # 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
>> I understand the concept.
>> is there possibly a more direct solution available, with cleaner code >> and a stable/higher execution speed?
> Have you measured it and proven it too slow? Remember, premature > optimization is the root of all evil in programming. ;)
> I'm not sure what you consider "clean", but getting rid of the ternary > operator may make it a little more readable:
> if caller[0] =~ /in `([^']+)'/ then $1 else '(anonymous)' end
>> puts "Class #{self.class.name}" >> Class Talker
>> but
>> puts "Class #{sayYourClassName}" >> Talker Class >> puts "Class " + sayYourClassName.to_s >> Talker Class
>> why?
> In the first example, you're asking Ruby for the class name, which you > add to a string that gets printed by the local call to puts. In the > other two, you're calling a method that prints the class name > immediately.
Of course [I've overseen the print statements].
> Then the local puts prints "Class " and the return value > from the method call, which isn't meaningful in this context.
ok
>>> # %{} is another way to write a string literal
> No, these are not equivalent. #{...} is for interpolating Ruby code > inside a string. %{...} defined a double quoted string, without the > quotes:
> %{This is a string. I can use "quotes" in here. And #{"interpolate"} > values.}
ok, now I understand.
%{} => string #{} => string interpolation of code
>>> # (looks neat for multiline strings) [...] >>> }
>> Can I get somehow a more precise reflection of the class definition >> (output similar to the original class-def, excluding code)?
> I don't believe so, no. Remember that a Ruby class can be reopened > and definitions added to it. That means a class could be built up > from many places.
> Ruby does have a means to get it to store the source code it reads, > but I don't believe that's what you were asking for.
you're right.
-
I will try to work this out myself.
Where can I find the following information?:
* An UML diagramm (or another visual representation) of the ruby class-model.
* A deep (but compact) description of the reflection/introspection api's.
Note that it is quite large and not too pretty, though.
> * A deep (but compact) description of the reflection/introspection api's.
Well, the Pickaxe book (slightly outdated version is available online for free, new issue with lots of new material can be ordered online) contains that among much other information and is a pretty interesting read. See
>> * A deep (but compact) description of the reflection/introspection >> api's.
> Well, the Pickaxe book (slightly outdated version is available online > for free, new issue with lots of new material can be ordered online) > contains that among much other information and is a pretty interesting > read. See
> I'd recommend ordering the second issue from the pragmatic guys as it > contains detailed information that will be useful for evaluating Ruby in > depth.
thank you for the information.
I need at this point only reference of the reflection/introspection api.
Your comment has already hit on one solution. :) There may be many others, depending on how you intend to use this information...
> -
> "attr_accessor" does not work on class variables (e.g. @@count).
> Must I create @@var/getter/setter manually?
Not exactly. Here's a trick:
irb(main):001:0> class MyClass irb(main):002:1> class << self irb(main):003:2> attr_accessor :test irb(main):004:2> end irb(main):005:1> end => nil irb(main):006:0> MyClass.test = 501 => 501 irb(main):007:0> MyClass.test => 501
There is a gotcha though:
irb(main):008:0> class MyClass irb(main):009:1> def test_method irb(main):010:2> @@test irb(main):011:2> end irb(main):012:1> end => nil irb(main):013:0> MyClass.new.test_method NameError: uninitialized class variable @@test in MyClass from (irb):10:in `test_method' from (irb):14 from :0
You can see that this technique does not use @@test.
All of this is just playing around though. If you seriously need to define class accessors a lot and can't be bothered to type:
def self.test( ) @@test end def self.test=( value ) @@test = value end
Most editors I've ever seen will allow you to macro that and assign it to a single keystroke.
My opinion, feel free to completely ignore, is that you've strayed pretty far from "evaluating Ruby" when you worry about details like this. We can sit here and show you examples like the above indefinitely, exploring all corners of the language. There's no substitute for just learning the language and seeing what it can do for you though, and this is a poor way to go about that.
> Your comment has already hit on one solution. :) There may be many > others, depending on how you intend to use this information...
I extract: Ruby has not standard-mechanism for metadata/annotations.
ok
>> "attr_accessor" does not work on class variables (e.g. @@count).
>> Must I create @@var/getter/setter manually?
> Not exactly. Here's a trick:
[...] - (trick's and workarounds)
> Most editors I've ever seen will allow you to macro that and assign it > to a single keystroke.
I am aware about editors and macros.
-
Where can I find the implementation of "attr_accessor"?
I'm intrested in seeing how much effort it is to write an own "flex_attr_accessor".
[I will possibly include this in the evaluation template, section "language extension"]
> My opinion, feel free to completely ignore, is that you've strayed > pretty far from "evaluating Ruby" when you worry about details like > this. We can sit here and show you examples like the above > indefinitely, exploring all corners of the language. There's no > substitute for just learning the language and seeing what it can do for > you though, and this is a poor way to go about that.
> James Edward Gray II
I've an evaluation template, which I like to apply to several languages.
Ruby looks very nice till now, although I've hit already some limitations: