Understanding modules/classes

I’ve written a test harness (in Perl, but I may convert it to Ruby, if
I’m happy with the language after this step), and I’d like to replace
the part that displays the results in a web browser with a Ruby-based
CGI program. Currently I’m modeling it in a MVC pattern, with the
Model (which varies, depending on what sort of results you’re after)
containing the interface to my test database, and the View asking the
Model for the data to display in HTML.

The actual writing of classes is going well, though I’m torn on
whether to have one monolithic Model class, or have several smaller
ones for each View they’re meant to support. My question is on code
organization.

Initially, I tried to just put a class definition (or two) in each
file, and use “require ‘filename’” to fetch it, but that wouldn’t work
until I surrounded the class definition with a "module TestHarness/end"
pair. Later, I figured out it must’ve been a typo of some sort, but
by then, I’d started defining module-level globals, and didn’t want to
give up the namespace.

Okay, that’s not a problem. But I can’t figure out how to
export classes from my module into the global namespace. In Perl, I’d
just add the functions to @EXPORT, and be done with it. Can this be
done in Ruby as well, or am I just thinking about it the wrong way?
Should I be creating my CGI executable in that same module as well?
Or am I just over-organizing here?

Comments welcome.

-=Eric

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

[…]

Okay, that’s not a problem. But I can’t figure out how to
export classes from my module into the global namespace. […]

Comments welcome.

-=Eric

How about importing them into the global namespace?

irb(main):001:0> module Foo
irb(main):002:1> class Bar
irb(main):003:2> end
irb(main):004:1> end
nil
irb(main):010:0* include Foo
Object
irb(main):011:0> Bar
Foo::Bar

Of course, you can create shortcuts to classes:

require “test/unit”

TestCase = Test::Unit::TestCase

class TC_Foo < TestCase

Hope this helps,
Gavin

···

From: “Eric Schwartz” emschwar@fc.hp.com

Easily enough to do:

module Foo
class Bar
def Bar.baz
puts “Foo::Bar::baz”
end
end
end

Foo::Bar.baz

include Foo

Bar.baz

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.06 at 21.52.58

···

On Thu, 7 Nov 2002 10:27:02 +0900, Eric Schwartz wrote:

Okay, that’s not a problem. But I can’t figure out how to export
classes from my module into the global namespace. In Perl, I’d
just add the functions to @EXPORT, and be done with it. Can this
be done in Ruby as well, or am I just thinking about it the wrong
way? Should I be creating my CGI executable in that same module as
well? Or am I just over-organizing here?

Austin Ziegler austin@halostatue.ca writes:

···

On Thu, 7 Nov 2002 10:27:02 +0900, Eric Schwartz wrote:

Okay, that’s not a problem. But I can’t figure out how to export
classes from my module into the global namespace. In Perl, I’d
just add the functions to @EXPORT, and be done with it. Can this
be done in Ruby as well, or am I just thinking about it the wrong
way? Should I be creating my CGI executable in that same module as
well? Or am I just over-organizing here?

Easily enough to do:

Okay, thanks to you and Gavin. One last question: is it good style to
put that in the class files? That is, do I put the ‘include Foo’ in
bar.rb, or in the file that 'require’s it? I understand it doesn’t
matter; I’m trying to get an idea for good Ruby style.

-=Eric

Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

Austin Ziegler austin@halostatue.ca writes:

Okay, that’s not a problem. But I can’t figure out how to export
classes from my module into the global namespace. In Perl, I’d
just add the functions to @EXPORT, and be done with it. Can this
be done in Ruby as well, or am I just thinking about it the wrong
way? Should I be creating my CGI executable in that same module as
well? Or am I just over-organizing here?

Easily enough to do:

Okay, thanks to you and Gavin. One last question: is it good style to
put that in the class files? That is, do I put the ‘include Foo’ in
bar.rb, or in the file that 'require’s it? I understand it doesn’t
matter; I’m trying to get an idea for good Ruby style.

-=Eric

It is perfectly good style to include a module in a class. That is one of the
primary aims of modules: in this sense, they are called mix-ins.

BTW, since you expressed thanks, can I ask a favour in return? Can you write a
post, with “[FAQ]” in the title, summarising the problem and solution? Then
someone can easily find this information later and add it to the Ruby FAQ.
Thanks!

Gavin

···

From: “Eric Schwartz” emschwar@fc.hp.com

On Thu, 7 Nov 2002 10:27:02 +0900, Eric Schwartz wrote:

It does matter where you put it, to a degree. “include” simply
includes the methods and classes from a module into the current
object scope. The “standard” object scope is (more or less) the
Object object.

Thus, when I do:

include MIME::Types

Then I have made it so that I can call the MIME::Types functions
without any specifiers (although it may not be a good idea to do
so).

On the other hand, when I do:

class Foo
include MIME::Types
end

I have made it so that I can call the MIME::Types functions within
the context of Foo, and would automagically gain Foo# behaving
like MIME::Types::. More or less.

I’ve probably goofed on some of the specifics, but the gist is
there.

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.07 at 18.42.38

···

On Fri, 8 Nov 2002 05:49:14 +0900, Eric Schwartz wrote:

Austin Ziegler austin@halostatue.ca writes:

On Thu, 7 Nov 2002 10:27:02 +0900, Eric Schwartz wrote:

Okay, that’s not a problem. But I can’t figure out how to export
classes from my module into the global namespace. In Perl, I’d
just add the functions to @EXPORT, and be done with it. Can this
be done in Ruby as well, or am I just thinking about it the
wrong way? Should I be creating my CGI executable in that same
module as well? Or am I just over-organizing here?
Easily enough to do:

Okay, thanks to you and Gavin. One last question: is it good style
to put that in the class files? That is, do I put the ‘include
Foo’ in bar.rb, or in the file that 'require’s it? I understand it
doesn’t matter; I’m trying to get an idea for good Ruby style.

“Gavin Sinclair” gsinclair@soyabean.com.au writes:

It is perfectly good style to include a module in a class. That is one of the
primary aims of modules: in this sense, they are called mix-ins.

I’m afraid you’ve confused me further. Let me illustrate.

module TestHarness
class TestTableModel
def initialize(project)
@project = project
# stuff happens
end

 def MachineList
    # return a list of machines that have test results for the
    # current project
 end

 #... other methods defined, too.

end
end

This is the Model of an MVC pattern I’m using to display test results
from a CGI program.

Now, say I’m writing the CGI itself. Inside it, do I say:

require ‘testharness/testtablemodel’
require 'testharness/testtableview
include TestHarness

model = TestTableModel.new(‘projname’)
view = TestTableView.new(model, cgipath)

view.display()

Or is it better to put the ‘include TestHarness’ line in testtablemodel.rb
itself, so that the client program just has to say:

require ‘testharness/testtablemodel’
require 'testharness/testtableview

model = TestTableModel.new(‘projname’)
view = TestTableView.new(model, cgipath)

view.display()

BTW, since you expressed thanks, can I ask a favour in return? Can you write a
post, with “[FAQ]” in the title, summarising the problem and solution? Then
someone can easily find this information later and add it to the Ruby FAQ.
Thanks!

Will do, once I understand it. :slight_smile:

-=Eric

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

Austin Ziegler austin@halostatue.ca writes:

It does matter where you put it, to a degree. “include” simply
includes the methods and classes from a module into the current
object scope. The “standard” object scope is (more or less) the
Object object.

Thus, when I do:

include MIME::Types

Then I have made it so that I can call the MIME::Types functions
without any specifiers (although it may not be a good idea to do
so).

Okay, I’m still confused. I get the feeling that I just walked right
past the point without even noticing it. You seem to be talking about
Modules as if they’re just collections of functions. I see them as a
way of implementing namespaces for Ruby objects, which I see as
necessary.

Otherwise, as I understand it, if I defined a TestTableModel class,
and you do too, you will silently extend mine, instead of creating an
entirely new class (or better yet, warning you of the problem and
falling over and dying). Whereas if I hide them inside a TestHarness
Module, you’d have to explicitly “include” it to get that problem, and
at least at that point you can be presumed to know what you’re doing.

-=Eric

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

IMO, this is what you want. I may prefer to do:

foo = TestHarness::TestTableModel.new

That is, I might have a TestTableModel in my own code.

I could also choose to do:

require ‘testharness/testtablemodel’

class Tester
include TestHarness

end

Then Tester has become an alias – of sorts – for TestHarness.

module Foo
class Bar
def baz
puts “in Foo::Bar::baz”
end
end
end

class Bag
include Foo
def initialize
@bar = Bar.new
end
def baz
puts “in Bag::baz”
@bar.baz
end
end

t = Foo::Bar.new
t.baz

in Foo::Bar::baz

b = Bag.new
b.baz

in Bag::baz

in Foo::Bar::baz

z = Bag::Bar.new
z.baz

in Foo::Bar::Baz

include Foo
c = Bar.new
c.baz

in Foo::Bar::Baz

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.08 at 13.38.27

···

On Sat, 9 Nov 2002 02:53:30 +0900, Eric Schwartz wrote:

“Gavin Sinclair” gsinclair@soyabean.com.au writes:

It is perfectly good style to include a module in a class. That
is one of the primary aims of modules: in this sense, they are
called mix-ins.
I’m afraid you’ve confused me further. Let me illustrate.

module TestHarness
class TestTableModel
def initialize(project)
@project = project
# stuff happens
end
def MachineList
# return a list of machines that have test results for the
# current project
end
#… other methods defined, too.
end
end

This is the Model of an MVC pattern I’m using to display test
results from a CGI program.

Now, say I’m writing the CGI itself. Inside it, do I say:

require ‘testharness/testtablemodel’
require ‘testharness/testtableview’
include TestHarness

Austin Ziegler austin@halostatue.ca writes:

It does matter where you put it, to a degree. “include” simply
includes the methods and classes from a module into the current
object scope. The “standard” object scope is (more or less) the
Object object.

Thus, when I do:

include MIME::Types

Then I have made it so that I can call the MIME::Types functions
without any specifiers (although it may not be a good idea to do
so).
Okay, I’m still confused. I get the feeling that I just walked
right past the point without even noticing it. You seem to be
talking about Modules as if they’re just collections of functions.
I see them as a way of implementing namespaces for Ruby objects,
which I see as necessary.

It’s both. ‘include’ effectively removes the namespace. See my most
recent response to you on this. It works with classes, other
modules, and functions, too.

Otherwise, as I understand it, if I defined a TestTableModel
class, and you do too, you will silently extend mine, instead of
creating an entirely new class (or better yet, warning you of the
problem and falling over and dying). Whereas if I hide them inside
a TestHarness Module, you’d have to explicitly “include” it to get
that problem, and at least at that point you can be presumed to
know what you’re doing.

This is correct, and that’s why you want to leave it up to the user
to do the include where they want to do it.

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.08 at 13.50.53

···

On Sat, 9 Nov 2002 03:33:40 +0900, Eric Schwartz wrote: