Re: [Rubydotnet-developer] Getting necessary metadata on Ruby classes for externalization...
Status: Alpha
Brought to you by:
thomas
From: John R. P. <jo...@pi...> - 2003-07-21 15:29:13
|
I'd have to see the .NET calling code to help me see how annotating the parameter types enables the bridge to work or work better than not annotating param types. I can see how one would need to annotate the return type so that as the method returns out of Ruby I don't always have to downcast the return type from object to whatever, but, when it comes to sending parameters, ruby doesn't care if it gets an object type and, generally, I don't see the need to enforce a compiler check on parameter types. That's what unit tests are for. Regards, John On Fri, 18 Jul 2003 23:54:29 -0400, Richard Kilmer wrote: > > 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 |