Prev: * operator, Float
Next: soap with ruby 1.9 ?
From: Rolf Pedersen on 25 Jul 2010 15:14 [Note: parts of this message were removed to make it a legal post.] Hi I was reading the book "Metaprogramming Ruby" which I found quite good for a guy trying to improve on his novice Ruby skills. Parts of the book describes the creation of Class macros, and I got intrigued by this, and I tried to create a small project to practice a little bit. Not that I know that this is a particular good idea in Ruby, I started to make a small framework to enforce the creation of certain methods. I call it interfaces, since that's what I know from C#. Anyway, I soon stumbled into a dead end... as expected ;o) Here is the code: module Interface module Base def self.included(base) base.extend ClassMethods end module ClassMethods def add_interface_methods(*methods) methods.each do |method| (@interface_methods ||= []) << method.to_s end end def implement_interfaces(*interfaces) (@interfaces = interfaces).each do |interface| puts "self: " + self.inspect.to_s end end def enforce_interfaces if not (not_implemented_interface_methods = @interface_methods - self.instance_methods).empty? raise RuntimeError, "The following interface methods are not implemented in class #{self.to_s}:\n " + not_implemented_interface_methods.inspect end end end end end module Interface module ILockable add_interface_methods :lock, :unlock end module IDrivable add_interface_methods :drive, :break end end class MyClass include Interface::Base implement_interfaces Interface::ILockable, Interface::IDrivable # methods and other stuff enforce_interfaces end When I run this I get: interface.rb:28: undefined method `add_interface_methods' for Interface::ILockable:Module (NoMethodError) I do understand why I get this error, but I can't see how to go about fixing it given that I want it structurally to be along the lines already laid out. Do not bother to much about the code being bloated or otherwise not in accordance with "standard Ruby thinking"... :o) And I do realize I didn't have to use the included hook script since I'm only adding class methods, but it's part of a learning process :o) So, where did I go wrong? Any input/solution/discussion on this issue is most welcome! Kind regards, Rolf
From: Paolo Perrotta on 25 Jul 2010 18:40 Hello, Rolf. Let's wrap up what happened and why. The issue comes from these lines: > module Interface > module ILockable > add_interface_methods :lock, :unlock > end > > # ... > end Coming from C#, you're probably tempted to assume that the code inside ILockable (and IDrivable) is executed only when a class/module is "loaded". Actually, that code is executed immediately as Ruby goes through the definition. So, at this point (and independently of all the other code in your example), Ruby tries to execute add_interface_methods() in the scope of Interface::ILockable. Inside that scope, the role of self is taken by Interface::ILockable itself - so that's the implicit receiver of any method call. So Ruby tries to call Interface::ILockable.add_interface_methods(), doesn't find that method, and fails. In the real world, I'd go for something simpler (and I'd be a real prick in questioning the notion of strongly typed interfaces in Ruby ;) ). However, because this is a great learning exercise, here's something along your own lines that probably works as you expect: --- module Interface module Base def self.included(base) base.extend ClassMethods end module ClassMethods def implement_interfaces(*interfaces) interfaces.each do |interface| @interface_methods = (@interface_methods || []) + interface.instance_methods puts "self: " + self.inspect.to_s end end def enforce_interfaces if not (not_implemented_interface_methods = @interface_methods - self.instance_methods).empty? raise RuntimeError, "The following interface methods are not implemented in class #{self.to_s}:\n " +not_implemented_interface_methods.inspect end end end end end module Interface module ILockable def lock; end def unlock; end end module IDrivable def drive; end def end; end end end class MyClass include Interface::Base implement_interfaces Interface::ILockable, Interface::IDrivable # methods and other stuff enforce_interfaces end --- Paolo "Nusco" Perrotta Metaprogramming Ruby (http://www.pragprog.com/titles/ppmetr)
From: Xavier Noëlle on 27 Jul 2010 05:42 2010/7/25 Rolf Pedersen <rolfhsp(a)gmail.com>: > module Interface > module ILockable > add_interface_methods :lock, :unlock > end > > module IDrivable > add_interface_methods :drive, :break > end > end add_interface_methods is not defined for these modules. "include Base" in both should remove this error, but I'm not sure that's what you want in the end. I take the opportunity of this topic to provide my own version of interface implementation and to ask a question about it (if it's off topic, don't hesitate to tell me). My goal is to use your code to perform an automatic interface check and not bother with calling enforce_interface everytime: ===================================== module Interface def inherited(base) puts "#{base} inherited from me" enforce_interface(base) end def enforce_interface(base) instanceMethods = base.public_instance_methods() if (!@interface_methods.nil?()) @interface_methods.each() do |method| throw "No method #{method}" unless instanceMethods.include?(method) end end end def add_interface_methods(*methods) methods.each do |method| (@interface_methods ||= []) << method.to_s end end end class Mother extend Interface add_interface_methods :pwet, :ziou end class Child < Mother def pwet() puts "Method pwet" end def initialize() puts "Child ctor" end end ===================================== This piece of code almost works (so, doesn't work :-)), but ends up (logically) with a "No method pwet" exception. I suppose that it's due to the fact that Child inherits from Mother before to be able to define any method. Is there a way around this problem ? I like Paolo's proposal (which could maybe be extended to allow to check methods arity as well), but dislike the need to call explicitely enforce_interface. -- Xavier NOELLE
From: Paolo Perrotta on 28 Jul 2010 11:01 On Jul 27, 9:29 pm, Rolf Pedersen <rolf...(a)gmail.com> wrote: > So is this possible to accomplish? You might add add_interface_methods to the Module class. It would then be available to all modules. --- Paolo "Nusco" Perrotta Metaprogramming Ruby (http://www.pragprog.com/titles/ppmetr)
|
Pages: 1 Prev: * operator, Float Next: soap with ruby 1.9 ? |