I was hoping to get some code review/suggestions on how to do things better for my SVG drawing library, I’ve been working on it for months now, and I’ve gotten it to a fairly complete point. That being said, I’m sure there are things I could do better, and since I’m at such a good point I thought I would work on some refactoring.
Everything in Celestine is done via a
Celestine.draw, this returns a string of SVG code created. During the
Celestine.draw method, a
Celestine::Meta::Context object is created to handle all the DSL calls, and hold all the objects created by the programmer.
puts \ Celestine.draw do |ctx| ctx.rectangle do |rect| # Modify the newly created rectangle rect.fill = "red" # Give it back since it's a struct. rect end end
All of the objects created by Celestine via context methods such as
ellipse need to be returned at the end of the method because they are structs. From my understanding this is supposed to give better performance over class. Is this true for this case?
Celestine::Meta::Context stores all created objects in an array of
Celestine::Circle and etc all inherit from. At the end of the
Celestine::Meta::Context#render method is called and SVG code is returned through
Ok, so this is where things start to get a little muddy.
Naturally the SVG spec is a monster and there is a lot to implement, some of which can be very difficult to test because it has to be done by a human AND with multiple browsers. This has lead to each of my drawable objects containing a similar but fundamentally different
draw method which renders the SVG code for the object. When looking at the spec for something like SVG’s
<rect> tag versus something like
<circle> there are similarities and differences, one notable example is that
<rect> has an
x attribute, but
<circle> has a
cx attribute instead, they both do the same thing, change the
To solve this I created modules which hold constants related to the attribute names. This gets included into the
Celestine::Drawable::Attrs module for each subclass of
Celestine::Drawable as long as it uses the
include_options macro. (Otherwise you would just have to include all the modules, attribute names yourself). This is done so programmer have access to all the attribute names in a
Drawable when using the
<animate> tag. For example, this makes using
Celestine::Rectangle::Attrs::X instead of needing to use the module that was included, like
This created a new problem in that each
Celestine::Drawable must implement their own
draw method, each of which have slight variations in them. For example,
Celestine::Modules::CPosition, this makes a difference in the draw method.
Is there a good way to simplify this or at the very least not need to reuse sooo much code? I tried concocting some way with macros, but nothing I came up with seemed like a sure winner.
I also can’t help but think there has to be a better way of programmatically putting together this HTML-like code, any suggestions on how to better handle this? An example of something I’d like to fix,
<animate> tags are allowed to have an inner
<animate> tag, allowing incredibly complicated animations to be made. Currently my system cannot handle this, as Celestine’s
animate cannot have inner tags yet, as well as
animate being immediately turned into a string when being handled through a context method.
structreally that much faster that
- Is there a better way to handle
- Better way to construct HTML like code?
- Advice on how to handle potentially recursive inner tags?
- Any other advice on DSLs?