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.
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
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.
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.
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.
if you derive a subclass, use only one ‘<’, i.e. class MyPrint2PDF <
MyPrint
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
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
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?
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.
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.
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
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
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
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.