Forum: Ruby Help me to better understand modules

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Zouplaz (Guest)
on 2008-10-10 12:05
(Received via mailing list)
Hello, I would like to use ruby modules for the first time. I understand
theirs principle for a basic usage but get quite unsure for the present
situation :

- I want to put some code in a library (railsapp/lib) to
    1) lighten the controller that will use it
    2) test unit the code on one side, and test unit the controller on
the other side
- So, the lib is like this
    1) class BaseModule
    2) class SpecificModule < BaseModule
- The controller SomeController
    1) creates an instance sm = SpecificModule.new
    2) sm.doSomething

I don't like the idea of creating an instance - Because it seems (to me)
  too much Java/C++ oriented

Instead I would like

module BaseModule
module SpecificModule
   include BaseModule

class SomeController
   include SpecificModule

Then, I could call doIt() from the controller


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 ?

I did few tries with modules (see above) and I have a question more :
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)
I had a look to the rails code base and it seems that most methods
defined in modules don't use it... In my case it seems that I should use
'self' anywhere...

Any advice ?

Thank for the attention ;-)

module Payment

   @@attribut = "payment"

   def self.method_payment()
     p 'here again'
   end

end

module SpecificPayment

   include Payment

   @@attribut_specific = "foo"

   def self.my_stuff()
     p 'here'
     Payment.method_payment()
   end

end

class AClass
   include SpecificPayment

   def go()
     SpecificPayment.my_stuff()
     Payment.method_payment()
   end

end

a = AClass.new
a.go()
Brian C. (Guest)
on 2008-10-10 23:58
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.
Zouplaz (Guest)
on 2008-10-11 10:25
(Received via mailing list)
le 10/10/2008 21:56, Brian C. nous a dit:

> module SpecificPayment
>    def go
>      my_stuff
>      method_payment
>    end
> end
>
> a = AClass.new
> a.go
>

Thank you Brian, for this very valuable explanation - It's very clear to
me now. I've noticed that, with the self. version I need to prefix them
with the module's name, so having namespacing which tends to make the
code more readable.

module Payment
   def self.method_payment
   end
end

class AClass
   include Payment

   def go
     Payment.method_payment()
   end
end

 >
 > 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
inisolation,
 > and they tend to be easier to re-use.
 >

Yes, I agree - May be very conventional too but easy to understand, to
read and furthermore to isolate as you point out

>
> 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.

You win one dollar ;-) This is the first reason that pushed me to have a
look to modules for my current project, instead of using composite
objects (composite controller class in that case)

>
> 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.
>

I've switched from Class to Module - was quite interesting to compare
the two solutions but I prefer the class method and will switch back to
that because I feel more confortable with it

Now I see situations where modules will be very usefull but for the
current case it give nothing more compared to the class approach.

Btw, the modules methods proved to be stubbable very easily...

> Hope that gives you something to think about :-)

I learned a lot today ;-) Thank you
Brian C. (Guest)
on 2008-10-11 19:30
Zouplaz wrote:
> I've noticed that, with the self. version I need to prefix them
> with the module's name, so having namespacing which tends to make the
> code more readable.

That's right - you are explicitly calling a method which belongs to the
module itself, not to any object which mixes in the module.

> module Payment
>    def self.method_payment
>    end
> end
>
> class AClass
>    include Payment
>
>    def go
>      Payment.method_payment()
>    end
> end

You do not need "include Payment" in class AClass. You can call
Payment.method_payment from anywhere. The module "Payment" itself is the
receiver of the method.

You only need "include Payment" if you want to mix in *instance methods*
(those which are defined in the module without "self") - they then
become instance methods of class AClass.

Regards,

Brian.
This topic is locked and can not be replied to.