From: Brian Candler Date: 2008-10-11T04:56:10+09:00 Subject: Re: Help me to better understand modules Zouplaz wrote: > Hence, my question : > - is it 'good design' to use the modules like this ? May I have some > (bad) surprises later ? > - are the modules 'motchable / stubbable' ? > - is it different to unit test modules compared to classes ? In principle it should be fine. Modules are just objects, and I believe you should be able to mock/stub its methods just fine. Anyway, a 5 minute test will prove it one way or the other :-) > why did I need to use the 'self' keyword with self.my_stuff or > self.method_payment ? (without self. the methods are unknow under > SpecificPayment module) Effectively there are two different ways to use module methods, and you have attempted to mix them both. (1) You can have what I would call "pure" module methods. They are not attached to any object, but only operate on the arguments given. module Math def self.double(x) x*2 end end p Math.double(9) (2) You can have a module which is "mixed in" to a real class or a real object just like inheritance. It can invoke methods on those objects, or even access instance variables directly (although I wouldn't recommend the latter, it's a very low-level coupling) These are defined without 'self' and become methods on the class into which they are mixed (using 'include M' within the class) or in an object's singleton class (using 'obj.extend M') module Foo def double val * 2 # or: @val * 2 end end class Bar include Foo attr_reader :val def initialize(v) @val = v end end a = Bar.new(9) p a.double Your code is trying to do both. To change it into the mixin style you would write: module Payment def method_payment p 'here again' end end module SpecificPayment def my_stuff p 'here' method_payment end end class AClass include Payment include SpecificPayment def go my_stuff method_payment end end a = AClass.new a.go Note that I have removed your class variables (@@...). These are messy at the best of times, but if you write mixed-in modules which use class variables, I really wouldn't have a clue as to where the values were stored, or which objects or classes would be able to access them. It's a real recipe for trouble. Much safer to use class instance variables, or just regular objects, to store such state. > Any advice ? Modules will probably work just fine for you, but it's a matter of personal preference as to how to use them, depending on the specifics of your problem. I typically find that object composition ("has-a") rather than subclassing ("is-a" or mixin) is the most flexible way to compose systems. Obviously the component classes are easy to test in isolation, and they tend to be easier to re-use. Looking at your example, obviously without understanding your problem domain fully I would still be inclined to use concrete classes rather than modules. That is, something like this: class PaymentHandler def method_payment p 'here again' end end class SpecificPaymentHandler < PaymentHandler def my_stuff p 'here' method_payment end end class AClass def initialize @payment = SpecificPaymentHandler.new end def go @payment.my_stuff @payment.method_payment end end AClass.new.go Some people's immediate reaction would be: "but that creates a new object for every controller action invocation!". Ignore them. That is premature optimisation; object creation is cheap in Ruby. Consider: 10.times { puts "hello" } This creates 10 separate string objects, but nobody complains (much) about that. If the right conceptual model is for a payment-handler class, which carries its own state, then do that. Of course, if SpecificPayment.my_stuff() and Payment.method_payment() are "pure" module methods, depending only on values passed as arguments, then by all means keep them that way. Hope that gives you something to think about :-) Brian. -- Posted via http://www.ruby-forum.com/.