From: Freeman Pascal Date: 2001-03-16T17:27:49+09:00 Subject: [ruby-talk:12732] ModuleModifier.rb - An interesting idea... This is a multi-part message in MIME format. ------=_NextPart_000_0004_01C0ADAF.E020C560 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit Hello, While exploring Ruby and looking for answers to my recent questions (see [ruby-talk:12557]). I came up with an interesting use of append_features(). I'm looking at using Ruby in a virtual environs project and wanted a language that was expressive and simple as Smalltalk but was embeddable and could eventually support native compilation or running in an embeddable VM. Ruby looks like it has that potential and even has projects working in these directions. As a bonus Ruby is very expressive at the meta level and allows extending the language into interesting directions. One of the features I wanted with my virtual environs efforts was the ability for the language implementing environment and entity behavior to refer to feature sets though a common naming scheme and allow extending system and user classes by referring to a common feature name while hiding class specific specializations. Here is what I came up with using Ruby. It is a module that allows packaging extensions to a class or set of classes into a single module that can be referred to by a single name, and dynamically extends any classes as necessary to implement the desired behavior across the calling classes. I hate the name, ModuleModifier, if anyone can think of a better OO domain specific nomenclature I would happily change it. The comments below contain a functional example. Being new to Ruby and OO in general, I would be interested in any comments. Please forgive me if my explanation seems thin, I'm still learning the vocabulary necessary to communicate OO design and methodologies. -Freeman # ModuleModifier.rb # ModuleModifier is used to enable modules to package modifications # to including classes or modules under a common name # Example: # # require "ModuleModifier" # # module Foo # # include ModuleModifier # # def whoami # puts "#{self.class} = #{self}" # end # # module Bar # def hello # puts "Hello, World!" # end # end # # module Baz # def goodbye # puts "Goodbye cruel world!" # end # end # # end # # class Bar # include Foo # end # # class Baz # include Foo # end # # p Bar.ancestors # -> [Bar, Foo, Foo::Bar, Object, Kernel] # p Baz.ancestors # -> [Baz, Foo, Foo::Baz, Object, Kernel] # # bar = Bar.new # baz = Baz.new # # bar.whoami # -> Bar = # # baz.whoami # -> Baz = # # # bar.hello # -> Hello, World! # baz.goodbye # -> Goodbye cruel world! # module ModuleModifier # Override append_features to add a custom append_features() # to the class including ModuleModifier. def ModuleModifier.append_features (klass) klass.module_eval %Q{ def #{klass}.append_features (klass) klass.ancestors.each { |ancestor| if eval "defined? \#{self}::\#{ancestor}" klass.module_eval "include \#{self}::\#{ancestor}" break end } super end } # Neglicting to call super here keeps ModuleModifier from being # added as an ancestor to the class we are modifying. end end ------=_NextPart_000_0004_01C0ADAF.E020C560 Content-Type: application/octet-stream; name="ModuleModifier.rb" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="ModuleModifier.rb" # $Header$=0A= =0A= # ModuleModifier.rb=0A= =0A= # ModuleModifier is used to enable modules to package modifications=0A= # to including classes or modules under a common name=0A= =0A= # Example:=0A= #=0A= # require "ModuleModifier"=0A= #=0A= # module Foo=0A= #=0A= # include ModuleModifier=0A= #=0A= # def whoami=0A= # puts "#{self.class} =3D #{self}"=0A= # end=0A= #=0A= # module Bar=0A= # def hello=0A= # puts "Hello, World!"=0A= # end=0A= # end=0A= #=0A= # module Baz=0A= # def goodbye=0A= # puts "Goodbye cruel world!"=0A= # end=0A= # end=0A= #=0A= # end=0A= #=0A= # class Bar=0A= # include Foo=0A= # end=0A= #=0A= # class Baz=0A= # include Foo=0A= # end=0A= #=0A= # p Bar.ancestors # -> [Bar, Foo, Foo::Bar, Object, Kernel]=0A= # p Baz.ancestors # -> [Baz, Foo, Foo::Baz, Object, Kernel]=0A= #=0A= # bar =3D Bar.new=0A= # baz =3D Baz.new=0A= #=0A= # bar.whoami # -> Bar =3D #=0A= # baz.whoami # -> Baz =3D #=0A= #=0A= # bar.hello # -> Hello, World!=0A= # baz.goodbye # -> Goodbye cruel world!=0A= #=0A= =0A= module ModuleModifier=0A= =0A= # Override append_features to add a custom append_features()=0A= # to the class including ModuleModifier.=0A= =0A= def ModuleModifier.append_features (klass)=0A= klass.module_eval %Q{=0A= =0A= def #{klass}.append_features (klass)=0A= klass.ancestors.each { |ancestor|=0A= if eval "defined? \#{self}::\#{ancestor}"=0A= klass.module_eval "include \#{self}::\#{ancestor}"=0A= break=0A= end=0A= }=0A= super=0A= end=0A= =0A= }=0A= # Neglicting to call super here keeps ModuleModifier from being=0A= # added as an ancestor to the class we are modifying.=0A= end=0A= =0A= end=0A= =0A= # $Header$=0A= ------=_NextPart_000_0004_01C0ADAF.E020C560--