We have some legacy procedural code (xml builders) which really needs to be refactored into proper classes. This is a perfect case where classes are preferred to procedural code because we have several of these modules which have identical functions with identical arguments. They share a lot of behavior but have to pass their arguments everywhere. There are plenty of ad hoc functions to add XML elements, but I find myself routinely needing a simple, easy to use 'single element builder'. For example, if I just have <foo version="2">bar</foo>, I'm in a lot of pain because of how difficult it can be to build XML with this system. So the first thing I did was create this function:
sub add_simple_node { my ( $doc, $node, $name, $value, $attribs ) = @_; $attribs ||= {}; my $element = $doc->createElement($name); $element->addChild($doc->createTextNode($value)); foreach my $attrname (keys %{$attribs}) { my $attrvalue = $attribs->{$attrname}; $element->setAttribute($attrname, $attrvalue); } $node->addChild($element); }
To use it, I have to do something like the following. This is really ugly, but it's easier than doing this manually every time (the examples below don't show the optional attribute creation).
add_simple_node( $document, $segment_event_node, 'offset', $segment_event->seconds_offset, ); add_simple_node( $document, $segment_event_node, 'position', $segment_event->position, ); add_simple_node( $document, $segment_event_node, 'title', $segment_event->title, );
That might build and attach three simple elements like this:
200 2 first title
However, I still don't like that duplication. What I want is a proper instance where I can store the document and node, but I don't have that. Closures to the rescue!
sub make_node_builder { my ( $document, $node ) = @_; return sub { add_simple_node( $document, $node, @_ ) }; }
With that, my above code simplifies to the following much more readable code:
my $add_simple_node = make_node_builder( $document, $segment_event_node ); $add_simple_node( 'offset', $segment_event->seconds_offset ); foreach my $property (qw/position title/) { $add_simple_node->($property, $segment_event->$property); }
If you find yourself wanting objects but you don't have classes, think about closures.