Ruby WEB Site Support ProgramsIntroductionI maintain the 912 site using a set of scripts written in an object oriented scripting language called Ruby. There are now several English books on Ruby, one of which is on-line.
I use Ruby with Linux, but it also run on Windows. There even is a version that uses a standard Window installer that you can find here. Of course it also runs on Macs. This tutorial describes the basic logic of a program similar to the one used in maintaining the 912 WEB site. It serves as a brief introduction to the Ruby language using WEB page creation as the sample application. At the bottom of this page is a link to download all the source and scripts discussed in this tutorial and an enhanced version that will process this tutorial. RubyRuby is similar to Perl and Python. All three of these are computationally complete programming languages that are compiled on the fly and executed immediately. Here is a Ruby program to print "Hello World" on the console. puts "Hello World" The program can be created as a one line text file named 'hello1.rb' .
In a console window this program can be executed like this: $ ruby hello1.rb Hello World $ Ruby MethodsHere is a Ruby method: def function_name(parameters) The parameters are optional. Here is 'hello2.rb' :
def hello puts "Hello World" end Ruby ClassesRuby is extended by making classes. Our hello program can be extended by putting the interesting statements into a class.
Comments start with '#' and continue to the end of the line. We have defined a class named 'Hello' We have created an instance of the class by calling 'new' as in 'Hello.new' which creates a Hello object. The initialization of the object always calls the 'initialize' method of a class. This method is normally used to initialize the class by creating default values for variables. The 'initialize' method is not required if there is nothing for it to do. Ruby ObjectsWe can create an object from a class definition and execute methods against that object.
And parameters can be passed to methods
TemplatesNow lets get back to using Ruby to help us make and maintain WEB pages. The philosophy used is to separate content from presentation. In this context, presentation is HTML and content is words and pictures. Templates are used to define the HTML. Let's do a quick example. Here is "Hello World" in HTML. <html> <head> <title>Hello World</title> </head> <body> <h1>Hello World</h1> Almost lost in the middle of this bit HTML is our actual content. <h1>Hello World</h1> And by the way, most WEB pages have a lot more standard boiler plate surrounding the content than this little snippet. If our site has many pages (the 912 site has 502 pages on Jan 2, 2002), then how can we possibly maintain it in a consistent way? By separating presentation and content. By using templates. Here is a simple division. Imagine dividing up our files into two pieces:
Here is hello.src. We have stripped all the presentation layer from what we want to display and are left with a command and some text. [title Hello World] I am telling the world "Hello"! presentation1.tpl: <html> <head> <title>_TITLE_</title> </head> <body> <h1>_TITLE_</h1> _TEXT_ </body> </html> The template has two special variables:
We want to write a Ruby program that will produce HTML from the source file by analyzing our content file to find the TITLE and the TEXT and substituting that into the template. $ruby mkhtml.rb hello.src > hello.html Our next step is write some Ruby code that will combine the content and template files. A little RubyBefore giving the answer, lets look at strings and regular expressions a moment by using some examples. StringsA string is a built-in class in Ruby. Here is string1.rb: a = 'I am telling the world "Hello"!' puts a puts a.length puts "There are #{a.length} characters in the string \'#{a}\'" There are many methods available for strings. Regular ExpressionsA regular expression uses arcane rules for dividing strings into pieces based on patterns. Patterns in Ruby have a special notation such as this (regex1.rb): p_title = /\[title (.*)\]/ a = "[ltitle Hello World] I am telling the world \"Hello\"!" if a =~ p_title puts "pre =" + $` puts "match=" + $1 puts "post =" + $' end The brackets have to be escaped using backslashes because brackets have special meaning in regular expressions. Which produces: $ ruby regex1.rb pre = match=Hello World post = I am telling the world "Hello"! $ The statement a =~ p_title produces 'true' if the pattern
is matched.
'pre =' is empty because there are no characters before
the pattern.
'match=' shows the information inside of the parenthesis
on the pattern.
'post =' shows the <cr> following the match, then the
rest of the string.
You can also see how to write a simple if statement. First Template Processormkpage1.rbtpl = File.open('presentation1.tpl').read dat = File.open(ARGV[0]).read title = '' dat.gsub!(/\[title (.*)\]/) {title = $1; ''} tpl.gsub!('_TITLE_',title) tpl.sub!('_TEXT_',dat) puts tpl presentation1.tpl<html> <head> <title>_TITLE_</title> </head> <body> <h1>_TITLE_</h1> _TEXT_ </body> </html> hello.src[title Hello World] I am telling the world "Hello"! ExecutionHere is the command to do the work: $ ruby mkpage1.rb hello.src > hello.html Output: hello.html<html> <head> <title>Hello World</title> </head> <body> <h1>Hello World</h1> SummaryCongratulations, you have made a template process that splits content and presentation.Of course it is not very pretty, abstract, flexible, extensible or robust. It usually produces incorrect output and we have not explained it. But we have done a lot conceptually in only 7 lines of Ruby code! Second Template ProcessorLet's start our second version by solving some of problems of the first by making the program a little more abstract and extensible. mkpage2.rbclass Mkpage def initialize(fn) @dat = load(fn) @tpl = load('presentation1.tpl') @title = '' end ExplanationWell, we have added 20 lines of code with only a bit of functionality change. Specifically:
The meat of the program is in the show_page()
method. Let's look at one of the string functions:
String#gsub! . There are several forms.
s.gsub(x,y) s.gsub(x) {...} s.gsub!(x,y) s.gsub!(x) {...} All strings matching x are replaced with y. If a block is specified, matched strings are replaced with the result of the block. A block is the statements within the braces following the call. The form with the ! change the string in place, otherwise a new string is returned with the substitutions made in it. @dat.gsub!(/\[title (.*)\]/) { @title = $1; '' } @dat is our content file. The function looks for all instances of the [title (.*)] . (.*) means any character
following the space and before ]. The regular expression
processor assigns these characters to a numbered variable based on the
number of expressions within parenthesis. In this case we have one
such set and the variable assigned is $1. The block is
executed each time the pattern is matched (presumably once). The
object instance variable @title is assigned the value. The last
statement in the block is '', an empty string. This replaces
the [title ...] tag.
We use the String#gsub often in our processing of the
content and template files for HTML creation.
Third Template ProcessorWe next make two new modifications that extend our program in several ways:
mkpage3.rbclass Mkpage def initialize(fn) @dat = load(fn) @tpl = includes(load('presentation2.tpl')) @title = '' end Explanation of mkpage3.rbThanks to the includes(content) method, we can have
nested template files. I use these extensively on my web sites.
presentation2.tpl<html> <head> <title>_TITLE_</title> <include "style.tpl"> </head> <body> <h1>_TITLE_</h1> _TEXT_ </body> </html> style.tpl<style type=text/css> h1 { color : red; text-align : center } h2,h3,h4,h5,h6 { color : red; font-family:sans-serif } </style> Output$ ruby mkpage3.rb hello.src <html> <head> <title>Hello World</title> <style type=text/css> h1 { color : red; text-align : center } h2,h3,h4,h5,h6 { color : red; font-family:sans-serif } </style> An EnhancementThe other change is to add the following code to our program:def tplparam { '_TITLE_' => @title, '_TEXT_' => @dat, } end This routine uses a hash data structure which has the form { kkk
=> val } , or key value pairs. The name of the hash is
tplparam. Hashes are another Ruby class that is rich
with functionality. The expand_template routine iterates
through the hash using Hash#each_key , passing the
key to a block. The substitution uses the
Hash#fetch method to retrieve the value associated with
the key.
Modification DateThe hash can be easily expanded by adding another key value pair to tplparam. For example, I look up the modification date on the content file and create an HTML string that I can append to the end of the page by adding the following code:In the initialize() method add:
@date = File.mtime(fn).strftime("%a, %d-%b-%y %H:%M:%S %Z") In the tplparam definition add:
'_DATE_' => @date, And finally add _DATE_ following _TEXT_ in the template file. Thus, by adding three lines to two files, we can cause all our pages to display the page modification date and time. Fourth Sample Template EngineNow we need to solve a problem with the content processing; browsers require a <p> tag to start a new paragraph. I think an
empty line should start a new paragraph.
It is time to split the content processing out of the show_page() routine.
At the same time we can add support for adding pictures to the WEB site. I like pictures and have created a number of mechanisms to support their display. Here is the new and modified code that makes up mkpage4.rb $p_title = /\[title (.*)\]/ $p_blank = /^\s*$/ $p_pix = /\[pix (.*)\]/ def expand_content @dat.gsub!($p_title) { @title = $1; '' } @dat.gsub!($p_pix) { "<img src='/images/#{$1}'>" } @dat.gsub!($p_blank, "<p>") end New TagsWe have added support for two new tags.
pix.src[title Pix Example] This is a multi-paragraph example: [pix L1-11.jpg] This picture is an example of joy. command execution$ ruby mkpage4.rb pix.src <html> <head> <title>Pix Example</title> <style type=text/css> h1 { color : red; text-align : center } h2,h3,h4,h5,h6 { color : red; font-family:sans-serif } </style> DownloadThe examples in this tutorial are here including this tutorial in source and html form using an even fancier version of mkpage.rb. It includes expansions for block quotes, lists, HTTP tags, headers and table of contents. |