Welcome to Giant Robots Smashing Into Other Giant Robots — a weblog about development, business, design and technology — written by thoughtbot.

no need

Ruby’s modules give us opportunities to eliminate unnecessary classes from our designs.

In this example we have a citation, something that could be found in any academic paper, and 2 different ways of formatting it
  1. MLA (Modern Language Association)
  2. CMS (Chicago Manual of Style)
The obvious implementation ported from say Java to Ruby would look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class Citation

  class << self

    def mla
      new MLAFormat.new
    end

    def cms
      new CMSFormat.new
    end

  end

  attr_reader :format

  def initialize(format)
    @format = format
  end

  def to_s
    format.format self
  end

end

class Format

  def format(citation)
    raise 'must be implemented with specific citation formatting'
  end

end

class MLAFormat

  def format(citation)
    'MLA format'
  end

end

class CMSFormat

  def format(citation)
    'CMS format'
  end

end

citation = Citation.new MLAFormat.new
puts citation => 'MLA format'

citation = Citation.new CMSFormat.new
puts citation => 'CMS format'

citation = Citation.mla
puts citation => 'MLA format'

citation = Citation.cms
puts citation => 'CMS format'

Here our Citation class delegates the formatting specifics to a Format object. And I also created some class methods on Citation to make creating a Citation with a specific format cleaner and simpler.

Now do the various Format classes need to be classes? I’d say no, they don’t require initialization and they have no state; they’re just behavior.

In Ruby we can use modules instead.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class Citation

  class << self

    def mla
      citation = new
      class << citation
        include MLAFormat
      end
      citation
    end

    def cms
      citation = new
      class << citation
        include CMSFormat
      end
      citation
    end

  end

  def to_s
    format
  end

end

module MLAFormat

  def format
    'MLA format'
  end

end

module CMSFormat

  def format
    'CMS format'
  end

end

citation = Citation.new 
class << citation
  include MLAFormat
end
puts citation => 'MLA format'

citation = Citation.new 
class << citation
  include CMSFormat
end
puts citation => 'CMS format'

citation = Citation.mla
puts citation => 'MLA format'

citation = Citation.cms
puts citation => 'CMS format'

Here we mixin to a Citation object at runtime a specific format module. The Citation#to_s method is implemented in terms of #format, who’s implementation must be mixed in via a format module. So we’ve eliminated the abstract superclass, Format, changed 2 keywords from class to module and also eliminated the single citation argument from each Format module’s #format method.

Even though there’s not much difference between the 2 methods, I think using modules is more accurate because the classes just aren’t necessary.


About this entry

 

thoughtbot is hiring

We are hiring web developers and web designers in both Boston and New York, NY.

What are we up to?

We built Shoulda, an eclectic set of additions to Test::Unit; Paperclip to manage uploaded files without hassle; Jester, a REST/ActiveResource client library written in Javascript, and Squirrel, an enhancement for ActiveRecord's find syntax; — amongst some other projects.


Chad (President) and Jon (CTO) co-authored a technical book titled Pro Active Record: Databases with Ruby and Rails, which explores the ins and outs of the ActiveRecord ruby library. You can buy it today at Amazon.com.

About thoughtbot, inc.

We are a small web application development consulting business, with offices in Boston, MA and New York, NY. If you're looking to find a team for your next web development project or your new web application — get in touch.