Understanding ruby classes

This question is all about me not understanding how ruby classes work.

I have three print classes. myPrint which is abstract class and
implements common methods for other two classes which are myPrint2PDF
and myPrint2HTML. They implement all the troubles of output to PDF or
HTML format. Without too much code, implementation looks like this:

class myPrint
def printHDR
end

end

class myPrint2PDF << myPrint

end

class myPrint2HTML << myPrint

end

Then I have fourth class which implements the job which has to be done.

class myPrint001 << myPrint2PDF
def printHDR
image ‘image.jpg’ 20, 1
print ‘some header text’ 1,5

end
def doJob
print ‘some text’ 1,5
end
end

Here is my catch 22. I would REALLY like to call my implementation
class like this:
pr = myPrint001 :format => ‘HTML’ or
pr = myPrint001 :format => ‘PDF’
pr.doJob

I have made statement ‘class myPrint001 << myPrint2PDF’ intentionaly
wrong and I know it does not provide the real solution, but this is how
it works for now.

What would be the best (ruby) solution to this problem.

by
TheR

You can use Object.const_get(klass) to get the Class class of every
defined Ruby class(in your scope).
Try this:

class MyPrint
def printHDR
raise NotImplementedError
end
end

class MyPrint2HTML
def printHDR
puts “I am HTML”
end
end

class MyPrint2PDF
def printHDR
puts “I am PDF”
end
end

class MyPrintFactory
def self.getPrinter(options = {})
printer = nil
if options.empty? || !options.key?(:format)
raise ArgumentError.new(“no Printer Format given”)
else
begin
printer = Object.const_get(“MyPrint2” +
options[:format].upcase).new
rescue NameError => ex
raise ArgumentError.new(“Unknown Format”)
end
end

   return printer
end

end
printer = MyPrintFactory.getPrinter :format => ‘html’
printer.printHDR
printer = MyPrintFactory.getPrinter :format => ‘pdf’
printer.printHDR
printer = MyPrintFactory.getPrinter :format => ‘doc’
printer.printHDR

Damjan R. wrote:

Here is my catch 22. I would REALLY like to call my implementation
class like this:
pr = myPrint001 :format => ‘HTML’ or
pr = myPrint001 :format => ‘PDF’
pr.doJob

If I understand you correctly:
class MyPrint
def printHDR
end

end

class MyPrint2PDF < MyPrint

end

class MyPrint2HTML < MyPrint

end

def myPrint001(opts)
raise ArgumentError unless opts[:format]
Class.new(opts[format]) do
def printHDR
image ‘image.jpg’ 20, 1
print ‘some header text’ 1,5

end
def doJob
print ‘some text’ 1,5
end
end.new
end

You should really consider using delegation instead of inheritance here,
though.

HTH,
Sebastian

Jano S. wrote:

  1. Convention is to start class names with a capital letter

That’s not convention, it’s syntax. class myPrint gives a SyntaxError.

On Mon, Mar 3, 2008 at 9:07 AM, Damjan R. [email protected] wrote:

end

print ‘some text’ 1,5
wrong and I know it does not provide the real solution, but this is how
it works for now.

What would be the best (ruby) solution to this problem.

  1. Convention is to start class names with a capital letter - it’s
    related to the fact that class is a constant.
    for method, lowercase_letters_with_underscores are used. It’s nice to
    get used to it, because most of the ruby
    stuff is written so.

  2. if you derive a subclass, use only one ‘<’, i.e. class MyPrint2PDF <
    MyPrint

  3. Now the real problem:

You have several possibilities:

  • one is to create a ‘register’ somewhere, and register the classes
    with it (I’m writing out of my head, without running
    the code!)

require ‘singleton’

class PrintFormatRegister
include Singleton
def initialize
@formats = {}
end
def register_format(format, klass)
@formats[format] = klass
end
def get_format(format)
return @formats[format]
end
end

now, in your

class MyPrint2PDF < MyPrint

call the register:

PrintFormatRegister.instance.register_format ‘PDF’, self

here comes your code


end

You don’t need to derive myPrint001 from any of the previous classes,
just call their methods.

i.e.
class MyPrint001
def print_hdr(format)
formatter = PrintFormatRegister.instance.get_format(format) ||
raise ‘unknown format’ # for the case, when format is unknown
… # do what you need
end
end

  1. Design note:

Try to keep classes to one responsibility. In ruby, you don’t have to
use inheritance as much as you might be used to from other languages.
Try to choose names that describe the functionality (MyPrint001 is not
such a name, unless there’s something missing).Think of looking at
your code
after several months - would you be able to find out what MyPrint001
does without looking at the actual code?

Thanks to all. So far I was able to reproduce this scenario which works.

def myPrint001(opts)
formater = ‘MyPrint2’ + opts[:format]
o = Class.new(Object.const_get(formater)) do
def printHDR
image ‘image.jpg’ 20, 1
print ‘some header text’ 1,5

end
def doJob
print ‘some text’ 1,5
end
end.new
o.doJob
end
But it’s kinda ugly.

I do prefare Thomas aproach with MyPrintFactory if there was a way to
avoid to preceide every keyword with ‘printer.’ statement.

printer = MyPrintFactory.getPrinter :format => ‘html’
printer.doJob

In my last OO language I was using (Alaska XBase++ if known to anybody
:wink: I actualy had precompile directive which replaced print with
printer.print before compilation.

More ideas anybody.

by
TheR

On Mon, Mar 3, 2008 at 9:07 AM, Damjan R. [email protected] wrote:

This question is all about me not understanding how ruby classes work.

I have three print classes. myPrint which is abstract class and
implements common methods for other two classes which are myPrint2PDF
and myPrint2HTML.
Which means it just factorizes work, I suggest you make it a module then
module MyPrint…

They implement all the troubles of output to PDF or

HTML format. Without too much code, implementation looks like this:

class myPrint
def printHDR
end

end

class myPrint2PDF
include MyPrint
include Workerbee ## see below

end

class myPrint2HTML
include MyPrint
include Workerbee

end

Then I have fourth class which implements the job which has to be done.
Should probably still be a module, let us name it Workerbee
end

Here is my catch 22. I would REALLY like to call my implementation
class like this:
pr = myPrint001 :format => ‘HTML’ or
pr = myPrint001 :format => ‘PDF’
pr = MyPrint2PDF or
pr = MyPrint2HTML
pr.doJob

I have made statement ‘class myPrint001 << myPrint2PDF’ intentionaly
wrong and I know it does not provide the real solution, but this is how
it works for now.

What would be the best (ruby) solution to this problem.
Actually that has not much to do with Ruby, it seems your design is a
little bit flawed.

HTH
Robert


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

One brainstorm later.

Suppose I have a function (myReport) which would initialize the proper
formater object and act as MyPrint001 is actualy defined as ex. ‘class
MyPrint001 < MyPrint2PDF’.

class MyPrint001
def printHDR
image ‘image.jpg’ 20, 1
print ‘some header text’ 1,5

end
def doJob
print ‘some text’ 1,5
end
end

def myReport(myCustomClass, opts={})
… do whatever must be done and I don’t know how to do it
end

myReport(‘MyPrint001’, :format => ‘PDF’)

by
TheR

Robert D. wrote:

What would be the best (ruby) solution to this problem.
Actually that has not much to do with Ruby, it seems your design is a
little bit flawed.

Well yes and no.

Maybe it is just beyond mine and yours imagination. And since Ruby is a
language with many faces I try too find one that I like the best.

by
TheR

Another brainstorm later.

This goes to myPrint001.rb
class MyPrint001 < MyXXXXClass
def printHDR
image ‘image.jpg’ 20, 1
print ‘some header text’ 1,5

end
def doJob
print ‘some text’ 1,5
end
end

def myReport(myCustomClass, opts={})
txt = File.open(myCustomClass + .rb’) {|f| f.read}
txt.sub(‘MyXXXXXClass’,‘myPrint2’ + opts[:format])

txt.find_out_how_to_evaluate_each_line
end

myReport(‘MyPrint001’, :format => ‘PDF’)

Althow not tested yet it should work. I have to find the
find_out_how_to_evaluate_each_line solution now.

by
TheR

A possible implementation would be the Template Method pattern, which
seems to fit your requirements perfectly.

class PrintQueue
def initialize
@title = “My Wonderful Document”
@text = “This is a report on the Ruby P.ming Language”
end

def print_output
print_start
print_head
@text.each do |line|
print_line(line)
end
print_end
end

def print_start
end

def print_head
print_line(@title)
end

def print_body_start
end

def print_line(line)
raise “Called abstract method: print_line”
end

def print_body_end
end

def print_end
end
end

class PrintHTML < PrintQueue
def print_start
puts ‘’
end

def print_head
puts ’ ’
puts " #{@title}"
puts ’ ’
end

def print_body_start
puts ‘’
end

def print_line(line)
puts "

#{line}

"
end

def print_body_end
puts(’’)
end

def print_end
puts(’’)
end
end

class PrintPDF < PrintQueue
def print_start
end

def print_head
puts “**** #{@title} ****”
puts
end

def print_body_start
end

def print_line(line)
puts line
end

def print_body_end
end

def print_end
end
end

printjob = PrintHTML.new
printjob.print_output
printjob = PrintPDF.new
printjob.print_output

Markus A.

At the end of a day I have this solution.

I have myreport001.rb which contains:

class Myreport001 < MyPrint
def printHDR
image ‘image.jpg’ 20, 1
print ‘some header text’ 1,5

end
def doJob
print ‘some text’ 1,5
end
end

def myReport(myCustomClass, opts={})

source filename

cFileName = myCustomClass + ‘.rb’

read the source file

src = File.open(cFileName) {|f| f.read}

Replaces ‘MyPrint’ with MyPrint2PDF

src.sub!(‘MyPrint’,‘MyPrint2’ + opts[:format])

evaluates sorce

eval src

creates new class

eval myCustomClass.capitalize + ‘.new’
end

r = myReport(‘myreport001’,:format => ‘PDF’)
r.doReport

It works. It probably has some kind of memory leak, but it works. And it
is wery Rail-ish with template source file doing actualy job. And I
belive it will be also very custumizeable.


I am kind of puzzeled by the fact that this doesn’t work.
src.split("\n").each do |line|
x+=1
eval(line)
end

myreport001.rb:2:in myReport': compile error (SyntaxError) myreport001.rb:2: syntax error, unexpected $end, expecting '\n' or ';' class Myreport001 < MyPrint2PDF ^ from testsrc.rb:167:ineval’
from testsrc.rb:170:in myReport' from testsrc.rb:167:ineach’
from testsrc.rb:167:in `myReport’
from testsrc.rb:174

Line 170 is: eval(line)

Any ideas. And I am still open to any other suggestions.

by
TheR