Understanding ruby classes

D

Damjan Rems

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
 
T

Thomas Wieczorek

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
 
S

Sebastian Hungerecker

Damjan said:
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
 
J

Jano Svitok

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.

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

4. 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?
 
S

Sebastian Hungerecker

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

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

Damjan Rems

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
;-) I actualy had precompile directive which replaced print with
printer.print before compilation.


More ideas anybody.

by
TheR
 
R

Robert Dober

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
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 = 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
 
D

Damjan Rems

Robert said:
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
 
D

Damjan Rems

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
 
M

Markus Arike

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 Programming 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 '<html>'
end

def print_head
puts ' <head>'
puts " <title>#{@title}</title>"
puts ' </head>'
end

def print_body_start
puts '<body>'
end

def print_line(line)
puts " <p>#{line}</p>"
end

def print_body_end
puts('</body>')
end

def print_end
puts('</html>')
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 Arike
 
D

Damjan Rems

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
 
D

Damjan Rems

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:in `eval'
from testsrc.rb:170:in `myReport'
from testsrc.rb:167:in `each'
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,173
Latest member
GeraldReund
Top