This simple program copies decorates one xml file with another.
Two files are input.
One is the decoration for the other.
It looks for lines in the decoration
that contain a string like [decorated body goes here]
.
When it finds one,
that string is replaced
by the body of the other file,
the decorated file.
Such a sting is called a directive.
Below is the source.
# Decorate the input. # Usage: ruby -s decoratexml.rb -with=decore.xhtml [input] # The decore is copied to the output. # Strings in the decore like [decorated tag goes here] # are replaced with the tagged section of the input. # Strings in the decore like [date goes here] # are replaced with the current date in yyyy-mm-dd form. # Strings in the decore like [global var goes here] # are replaced with the value of global variable $var. # e.g. ruby -s decoratexml.rb -var=value ... # Strings in the decore like [env var goes here] # are replaced with the value of environment variable $var. # Strings in the decore like [system command goes here] # are replaced with the stdout of running "command". # Such strings are called "directives". # Directives cannot span lines. i.e. they must be fully in one line. # There can be multiple directives in one line. # Directives cannot nest or overlap. require 'rexml/document' decore = nil decore = $with if $with source = STDIN source = File.open( ARGV[0] ) if 0 < ARGV.length if ! decore puts "usage: ruby -s decoratexml.rb -with=decore.xhtml [input]" exit end if true == decore puts "usage: ruby -s decoratexml.rb -with=decore.xhtml [input]" exit end $xml = REXML::Document.new(source) #global_variables.each { |g| puts g } #wanted = "$with" #puts eval wanted def replacement(theLine) if ( m = /\[decorated ([^\] ]*) goes here\]/.match theLine ) selector = m[1] if ( m2 = /([^.#]+)\.(.+)/.match selector ) tag = m2[1] tagclass = m2[2] ea = $xml.elements.to_a( "//#{tag}[@class='#{tagclass}']" ) section = ea.first elsif ( m2 = /([^.#]+)#(.+)/.match selector ) tag = m2[1] tagname = m2[2] ea = $xml.elements.to_a( "//#{tag}[@name='#{tagname}']" ) if ea.size < 1 ea = $xml.elements.to_a( "//#{tag}[@id='#{tagname}']" ) end section = ea.first else section = $xml.elements["//#{selector}"] end if section return m.pre_match + section.text + section.elements.each { |theElement| theElement.to_s }.join( "\n" ) + m.post_match else return theLine end elsif ( m = /\[date goes here\]/.match theLine ) return m.pre_match + Time.now.strftime( "%Y-%m-%d" ) + m.post_match elsif ( m = /\[global ([^ ]*) goes here\]/.match theLine ) g = eval "$" + m[1] return theLine unless g return m.pre_match + g + m.post_match if g elsif ( m = /\[env ([^ ]+) goes here\]/.match theLine ) e = ENV[m[1]] return theLine unless e return m.pre_match + e + m.post_match if e elsif ( m = /\[system ([^\[\]]+) goes here\]/.match theLine ) # This bit is problematic. We cannot just return a string. # So in this case there is the side effect # of the print and the system function writing to stdout. # return m.pre_match + %x{#{m[1]}} + m.post_match # The prefix (before the match is printed directly (without a newline). print m.pre_match # Then the stdout from the system command goes directly to stdout. system m[1] # Only the suffix (after the match) is returned. return m.post_match else return theLine end return "ERROR" end File.open( decore ).each { |theLine| ourLine = theLine newLine = replacement(ourLine) while newLine != ourLine ourLine = newLine newLine = replacement(ourLine) end puts newLine }
Look at the Makefile in this directory to see one way it can be used. We export some shell variables that are used within decoratexml.html.uc. This way all the file names appear in the Makefile and none are hidden in decoratexml.html.uc. Ubercat.rb is then applied to create decoratexml.html. At last decore.xhtml is applied by decoratexml.rb to create decoratexml.xhtml.
Notice that all pages, those being decorated and those used as decoration as well as the result, should be readily readable and understandable when viewed in a browser.
Notice also that decoration can be nested.
c.f. SiteMesh
(2013-01-08: Not sure what I meant by this.
Maybe that one could generate a template from another one
since unreplaced directives should remain in the result.
Hence, one could replace some directives with a first run
and then others with a second run.
One run being more widely usable
and one more specialized.
Nesting directives
like [decorated [global ourTag goes here] goes here]
does work,
but not the system
directive.)
The tag in the syntax [decorated tag goes here]
is like a CSS selector.
So I'm going to call it a "selector".
There are only a few forms it can take:
[date goes here]
[global var goes here]
[env var goes here]
[system cmd goes here]
Helpful might be:
[filename goes here]
- abreviates [global filename goes here]
[url goes here]
- abreviates [global url goes here]
[file f goes here]
- abreviates [system cat f goes here]
Maybe the terms should be changed:
So the undecorated + the decore -> the decorated. This will allow us to refer in the decore to both the undecorated and the decorated. e.g. we might want the file name of the decorated in the footer. Hence there might be both "[undecorated body goes here]" and "[decorated filename goes here]".
Perhaps a syntax
like [undecorated attr of selector goes here]
would be useful?
Or other CSS like selector syntax like .class
, tag subtag
, etc.?
We could add some other stuff to make it a bit like a poor man's XSLT. Note that we want to continue the idea that every page stands on it's own, and can be reasonably viewed and understood in a web browser.
[xpath path goes here]
[for each [tag|xpath|global|env|] name ]
[it goes here]
[repeat]
[for each {one, two,
} ]
[it goes here]
[repeat]
Maybe strip any mock up division
(<div class="mockup">
).
This would allow web designers to put in mock text
to see how a decore might look.