Hi!
I have some questions re. the Template Method Pattern (GoF) in Ruby.
I need to update a simple management reporting system and have to add HTML to the so far text-based reports. Looks to me like the classical use case for the Template Method Pattern. I learned from Russ Olsen’s book »Design Patterns in Ruby« that we do not have / use / „make up“ abstract methods in Ruby. But a lot of infos concerning this topic on the web use these 'abstract‘ methods by raising some kind of error upon calling the methods (see [1, 2, 3]).
Does anybody out there in real ruby code bases actually use this approach with made up 'abstract‘ methods?
I’ve come up with three simple and obviously contrived example implementations without using abstract methods (and without meta programming):
a) Inheritance from a simple(r) use case [4]
b) Inheritance from an 'abstract‘ class [5]
c) Composition by mixin of an 'abstract' module with the concerning template method(s) [6]
Anything I might have missed here? And which way is most idiomatic Ruby (if at all;-)
Many thanks!
Cheers,
Michael
···
~~~
[1]: https://medium.com/@dljerome/design-patterns-in-ruby-template-method-753443f5a1c8
[2]: https://github.com/pruett/ruby-patterns/blob/master/patterns/template_method.md
[3]: https://stackoverflow.com/questions/12873873/ruby-base-class-call-child-class-like-in-abstract-classes/12873925#12873925
[4]: Example 1 - Inheritance from a simple(r) use case:
# Represents a simple (text) report
class Report
def initialize
@title = 'My Report'
@text = ['Line 1', 'Line 2.']
end
def output_report
output = head
output << body
output << footer
output
end
def head
"#{@title}\n"
end
def body
output = ''
@text.each do |line|
output << "#{line}\n"
end
output
end
def footer
''
end
end
# Represents an HTML report, inherits from the simple text report
class HTMLReport < Report
def head
<<~HTML
<html>
<head>
<title>#{@title}</title>
</head>
HTML
end
def body
output = "<body>\n"
@text.each do |line|
output << "<p>#{line}</p>\n"
end
output << "</body>\n"
output
end
def footer
"</html>\n"
end
end
[5]: Example 2 - Inheritance from an 'abstract‘ class:
# Represents an abstract report / template method(s)
class Report
def initialize
@title = 'My Report'
@text = ['Line 1', 'Line 2.']
end
def output_report
output = head
output << body
output << footer
output
end
end
# Represents a text report
class TextReport < Report
def head
"#{@title}\n"
end
def body
output = ''
@text.each do |line|
output << "#{line}\n"
end
output
end
def footer
''
end
end
# Represents an HTML report
class HTMLReport < Report
def head
<<~HTML
<html>
<head>
<title>#{@title}</title>
</head>
HTML
end
def body
output = "<body>\n"
@text.each do |line|
output << "<p>#{line}</p>\n"
end
output << "</body>\n"
output
end
def footer
"</html>\n"
end
end
[6]: Example 3 - Mixin of an 'abstract' module with the template method(s):
# Template method(s) for a report
module Report
def initialize
@title = 'My Report'
@text = ['Line 1', 'Line 2.']
end
def output_report
output = head
output << body
output << footer
output
end
end
# Represents a text report
class TextReport
include Report
def head
"#{@title}\n"
end
def body
output = ''
@text.each do |line|
output << "#{line}\n"
end
output
end
def footer
''
end
end
# Represents an HTML report
class HTMLReport
include Report
def head
<<~HTML
<html>
<head>
<title>#{@title}</title>
</head>
HTML
end
def body
output = "<body>\n"
@text.each do |line|
output << "<p>#{line}</p>\n"
end
output << "</body>\n"
output
end
def footer
"</html>\n"
end
end