Re: [Rubydotnet-developer] Getting necessary metadata on Ruby classes for externalization...
Status: Alpha
Brought to you by:
thomas
From: Thomas S. <th...@th...> - 2003-07-21 14:29:07
|
> So what do folks think about the syntax? I'd prefer the syntax to be as terse as possible and with as little duplicate information as possible. In other words, I'd prefer extern :Account.open, Account, String, String def Account.open(first, last) end to extern :Account.open, :return[Account], :first[String], :last[String] def Account.open(first, last) end But I don't know how to get the names of the arguments? The Module methods to iterate over methods simply return strings. So I'm not sure we can do better, than what you suggest, and it's not bad at all, but maybe we should suggest to matz that it would be convenient if you could get more information at runtime about the signature of a method? Cheers, Thomas ---------- Original Message ----------- From: Richard Kilmer <ri...@in...> To: rub...@li... Sent: Fri, 18 Jul 2003 23:54:29 -0400 Subject: [Rubydotnet-developer] Getting necessary metadata on Ruby classes for externalization... > All, > > I did not quite know where to post this, and I hope this list is ok... > > I have been thinking a bit on how to allow a developer to annotate > their class's methods with parameter types when used externally by > statically typed languages (Java, C#, SOAP). I want to present a > syntax, and at the bottom have the code to back it up. > > Given the following class: > > class Account > def Account.open(first, last) > end > def close > end > def add(money) > end > def remove(money) > end > def transfer(money, account) > end > end > > And assuming you wanted to make that accessible for statically typed > systems you would just do this: > > require 'extern' > > class Account > extern :Account.open, :return[Account], :first[String], :last[String] > def Account.open(first, last) > end > > extern :close > def close > end > > extern :add, :money[Float] > def add(money) > end > > extern :remove, :money[Float] > def remove(money) > end > > extern :transfer, :money[Float], :account[Account] > def transfer(money, account) > end > end > > So, for each method you want to externalize you just include the > 'extern' call. > > Usage: extern <method symbol>, [ [<:return[Classname]>], > <:param[Classname]>, ... ] > > To reflect on this metadata you do: > > Account.each_externalized_method do | method | > puts method.to_s > end > > Which outputs: > > Account Account.open(String first, String last) > NilClass close() > NilClass add(Float money) > NilClass remove(Float money) > NilClass transfer(Float money, Account account) > > You can, of course, inspect the method (ExternalMethodDefinition > instance) instead of printing it out. So this creates a runtime > structure to store type information about methods, which again is > useful is you want to bridge .NET to Ruby at the level of having a > CLR type subclass a Ruby class, or generate a CLR type that > represents a Ruby class. > > Of course, since Ruby's classes are open, you can define this extern > metadata on existing classes: > > class ThreadGroup > extern :ThreadGroup.new, :return[ThreadGroup] > extern :add, :return[ThreadGroup], :thread[Thread] > extern :list, :return[Array] > end > > So what do folks think about the syntax? > > -rich > > PS...here is the magic code that makes this syntax work: > > ________ BEGIN 'extern.rb' _________ > > class ExternalMethodDefinition > attr_accessor :klass, :name, :return_type > Parameter = Struct.new(:name, :type) > @@definitions = Hash.new([]) > > def self.[](klass) > @@definitions[klass] > end > > def initialize(klass, name, *parameters) > list = @@definitions[klass] > unless list > list = [] > @@defintions[klass] = list > end > list << self > @klass = klass > @name = name.to_s > @name.gsub!(/__/, '.') > @parameters = [] > parameters.each do |param| > if param.name=="return" > @return_type = param.type > else > @parameters << param > end > end > @return_type = NilClass unless @return_type > end > > def add_parameter(name, type) > unless type.kind_of?(Class) > raise "Parameter #{name}'s type on method '#{@name}' of class > '#{@klass}' must be a Ruby class" > end > if name == :return > @return_type = type > else > @parameters << Parameter.new(name.to_s, type) > end > end > > def each_parameter > @parameters.each {|param| yield param} > end > > def to_s > params = (@parameters.collect {|param| param.type.to_s+' > '+param.name}).join(', ') > return "#{@return_type.to_s} #{@name}(#{params})" > end > end > > class Symbol > def [](klass=nil) > if klass > ExternalMethodDefinition::Parameter.new(self.to_s, klass) > else > (self.to_s+"__[]").intern > end > end > > def method_missing(method, *args, &block) > if (?A..?Z).member?(self.to_s[0]) > return (self.to_s+"__"+method.to_s).intern > end > super > end > end > > class Module > def extern(method, *parameters) > ExternalMethodDefinition.new(self, method, *parameters) > end > > def each_externalized_method > ExternalMethodDefinition[self].each { |exdef| yield exdef } > end > end > > ------ END 'extern.rb' ------ > > ------------------------------------------------------- > This SF.net email is sponsored by: VM Ware > With VMware you can run multiple operating systems on a single machine. > WITHOUT REBOOTING! Mix Linux / Windows / Novell virtual machines at the > same time. Free trial click here: > http://www.vmware.com/wl/offer/345/0 _______________________________________________ > Rubydotnet-developer mailing list > Rub...@li... > https://lists.sourceforge.net/lists/listinfo/rubydotnet-developer ------- End of Original Message ------- |