Thread: 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 |
From: John R. P. <jo...@pi...> - 2003-07-22 03:23:11
|
Got it. I agree -- this is interesting, but lower on my priority list as well. I was discussing this topic today with my colleague, Ben Schroeder, and we both felt, that while it is interesting and more complete to let .NET classes call out to Ruby classes, we see a ton more value in our day-to-day activities to let Ruby script control .NET classes. At least this is how we've approached our development of our Ruby.NET bridge to date. Now what I think would be interesting is to develop some sort of runtime wrapper technology for .NET to be able to call ruby classes that implement a certain interface. This would be less an exercise in annotating a ruby class and generating an assembly at "compile time" and more an issue of generating a .net wrapper of the Ruby class at runtime (emit the IL) based upon interface signatures. For example, imagine you have a .net class that collects classes that implement ILoggable. So you want your Ruby class to implement ILoggable at runtime so you can slip it into the .NET collection. Under this scenario, the interop would generate a wrapper at runtime that calls back to the ruby methods that implement that interface (ILoggable). All of this could be done without any annotation of Ruby code. We do the same thing right now in our Ruby.Net bridge, but only for delegates (which is essentially an interface with one method). Regards, John With that said, this doesn't mean that we don't want to go the other way On Tue, 22 Jul 2003 03:57:09 +0100, "Thomas Sondergaard" wrote: > > The type declarations would only be necessary if you wanted to define new > types in ruby and make them available to other .net classes. With the type > annotations you could run a utility, lets call it genWrapperAssembly.rb, that > loads your ruby library and generates a .net assembly defining .net wrappers > for all your the ruby types. These wrappers would then call into ruby. This > would be a useful addition to the interop/bridge at some point, but it is not > high on my own priority list. After all, it is possible to do subclassing and > interface implementation without doing any of this - it is only if you want > to define new types in ruby. > > Cheers > > Thomas > > > ---------- Original Message ----------- > From: "John R. Pierce" <jo...@pi...> > To: rub...@li... > Sent: Mon, 21 Jul 2003 08:29:06 -0700 (PDT) > Subject: Re: [Rubydotnet-developer] Getting necessary metadata on Ruby > classes for externalization... > > > 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 > > > > ------------------------------------------------------- > > 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 ------- > > > > ------------------------------------------------------- > 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 |
From: Ben S. <bsc...@pr...> - 2003-07-22 13:20:18
|
Hi John, all, > At least this is how we've approached our development of our=20 > Ruby.NET bridge to > date. Now what I think would be interesting is to develop=20 > some sort of runtime > wrapper technology for .NET to be able to call ruby classes=20 > that implement a > certain interface. This would be less an exercise in=20 > annotating a ruby class > and generating an assembly at "compile time" and more an=20 > issue of generating a > .net wrapper of the Ruby class at runtime (emit the IL) based=20 > upon interface > signatures. It occurs to me that, if we had this technology, we could use it to = create new Ruby types for people to consume, if we were willing to do = separate compilation. We would just create the .NET interface in its = own assembly and then write a Ruby class to implement it. Over the long run, or if I was doing a lot of consuming of new Ruby = types from .NET, I think I would prefer something like Richard = describes, as it keeps the declarations near the code. The = implementation-from-Ruby code could be extended to create the new = interface assembly from the extern declarations. Regards, Ben Schroeder |
From: Thomas S. <th...@th...> - 2003-07-22 02:57:25
|
The type declarations would only be necessary if you wanted to define new types in ruby and make them available to other .net classes. With the type annotations you could run a utility, lets call it genWrapperAssembly.rb, that loads your ruby library and generates a .net assembly defining .net wrappers for all your the ruby types. These wrappers would then call into ruby. This would be a useful addition to the interop/bridge at some point, but it is not high on my own priority list. After all, it is possible to do subclassing and interface implementation without doing any of this - it is only if you want to define new types in ruby. Cheers Thomas ---------- Original Message ----------- From: "John R. Pierce" <jo...@pi...> To: rub...@li... Sent: Mon, 21 Jul 2003 08:29:06 -0700 (PDT) Subject: Re: [Rubydotnet-developer] Getting necessary metadata on Ruby classes for externalization... > 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 > > ------------------------------------------------------- > 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 ------- |
From: Richard K. <ri...@in...> - 2003-07-22 03:44:29
|
I would agree that the priority is Ruby scripting .NET classes, in the same way that Ruby driving a WSDL/SOAP services has high value. The question becomes when will we be able to have Ruby produce services (webservices or Types) to be consumed by others. Again, this is less important right now, but is important in the long run IMHO. -rich On Monday, July 21, 2003, at 10:57 PM, Thomas Sondergaard wrote: > The type declarations would only be necessary if you wanted to define > new > types in ruby and make them available to other .net classes. With the > type |