Tech Stuff - Ruby

This a collection of notes and an embryonic glossary about the ruby language. Our chosen language for scripting, application and web development since we saw the light in 2003. Mostly it's about the stupid things that you get badly wrong, were confusing (to us) at the beginning, are obscure or just not IOHO adequately covered in the documentation.

Note: Languages should be like hammers. Once you learn to use them they should work the same way every time. We note with considerable regret that ruby has embarked on a fool's errand. The making of an incompatible change from 1.8.x to 1.9.x releases in pursuit of the holy grail of language purity. Good luck with that. While the volume of ruby code is still significantly less than that of Python we note that the Python project cheefullfully accepted a 2 to 3 year transition stratgey in 2007 from Version 2 to the incompatible Version 3. Eight years later (2015) they are still mainaining Version 2 and 3 with, frankly, no prospect of ever escaping. Some of Python's function libraries were written by folks who have long since moved on to new work or may even be dead. Give us a healthy dose of language pragmatism (and we'll learn the few exeptions, perhaps even hold our nose while using them) with consistent backward compatibility rather than purism which means every couple of years stuff which used to work - no longer does. Maybe Ruby can get back its mojo - were it not for Ruby on Rails I doubt it. No one will ever trust them again. Shame, it is (or is that, was) a good language.

We chose ruby because we needed a stand-alone language for both application development and to use as a web scripting language. That left us with Perl (parrot), Python and Ruby. We chose ruby because it appears simpler to add C functionality to speed things up.

We have decided to completely overhaul our development methodology with ruby as its key component.

Use ruby as a single very high level language (VHLL) to quickly implement 100% functionality - this is not prototyping this is a fully functional system - it may run like a dog but it lets us move the next stage

Using the profiler output as a priority list - re-implement ONLY THOSE PARTS of the working design (methods or complete objects) into C as required to meet performance objectives

The operational system will be mixed language implementation with all the top-level or control logic visible in ruby

Fixes and corrections are applied to the original implementation to keep a fully functional system in pure ruby as well as our hybrid final system.

Well that's the theory. We are currently using it to develop a complete web controlled mail system (SMTP, IMAP, POP3) as a full scale implementation exercise.

We do development using a windows based HTML Editor-IDE, upload the files using a web page written in ruby and then run them via the same interface. So you'll find these notes very web-centric.

ruby, eruby and erb

The following notes may help when you come to look at the Apache configuration files below:

modules directly executed by mod_ruby have .rbx or .rb suffixes (the config below allows both) and are assumed to be pure ruby scripts. i.e they are not contained in HTML. These files are located in /usr/local/www/cgi-ruby/ directory (via ScriptAlias directive) and invoked with urls of .../ruby/name.rb or .rbx.

Ruby scripts embedded in HTML files i.e using <%....%> constructs are contained in files with suffixes of .rhtml and use eruby. These files are located in /usr/local/www/eruby/ directory (via ScriptAlias directive) and are invoked with URLs of ../eruby/myruby.rhtml. A scipt may additionally be placed in the normal directory with a suffix of *.rhtml e.g. index.rhtml - this is less secure since the module content may be viewed.

mod_ruby display Real Error Script

mod_ruby does not fail with warnings AND warning messages are NOT written to the log file (e.g. /var/log/apache/error.log or whatever), only error messages are written to this file. So forget about warnings completely under mod_ruby. Maybe there is a diagnostic level you can set but we have not found it.

When Ruby has a syntax or other error it returns an 'Internal Server Error (500)' and the error message is written to the apache error log. Not very convenient. To return the real error in the response page use the ErrorDocument feature of Apache and the following script (which would be placed in the error_500.rb file referenced in the http.conf file fragment above). Note: If you are using MSIE it intercepts error pages and gives you one of its own - so use a decent browser e.g. any of the Mozilla family):

When a error occurs (but not a warning) the actual failure message is displayed on the returned page.

Ruby Objects, Constants and Variables

In Ruby everything is an object? Actually in Ruby we have Objects and references to objects (variables). There are 2 types of Variables. Constant varibles which we'll refer to as constants and regular variables which we'll just call variables.

arr = ["a", "b", "c"] # arr is a variable that has been assigned
# the address of a dynamically created array Object
B = "Hello" # B is a constant that has been assigned
# the address of a dynamically created String Object

Freezing Objects

The idea of freezing an object is to make the object unmodifiableExample:

Files Reading 'n writing

Some notes about reading and writing from/to files. Like a lot of folks we like to close files. But ruby can get a little confusing and if its autoclosed then a reference to your file object will throw an exception 'cos is been garbage collected and ..like...its gone man. IO methods don't even open a file so they always close it! Simple rule of thumb. If you handle a file as series of lines there is no autoclose, if you touch it as a file wham its autoclosed see below:

Stuff about Objects

So you are stuck, the documentation is either non-existent or you can't find it. You have no idea what to do next. The following code provides some information about the object:

# display a list of object methods
thing.methods.each {|x| print x+'<br />'}
# check for a single method
if thing.respond_to?("to_s") then thing.to_s end
# test for a particular class
if thing.kind_of?("String") then ...
# displays objects class
print thing.class.to_s
# you still see thing.type but we understand
# the 'type' method is being deprecated

Using the Stub Resolver:

# use only the base domain name
# Resolv::DNS::Resource::IN::ANY gets all RR types
dns = Resolv::DNS.new
dns.getresources("mydomain.com", Resolv::DNS::Resource::IN::ANY).collect do |r|
# each record type has different properties - need to test
# see property list below
if r.kind_of?(Resolver::DNS::Resource::IN::MX) then
# Note: every property nned a .to_s method
print r.preference.to_s
print r.exchange.to_s
elsif etc.
...
end
end

Resource Record Properties

Following is an incomplete list of RRs and their properties - excludes boring and not very useful records such as TEXT, HINFO, WKS:

Each RR record type has differing properties
Properties of each record type
must use .to_s on ALL records to convert from type
e.g. obj.address.to_s
MX
preference = preference number
exchange = domain name of mail exchanger
NS
name = name server host name
A
address = ip address
SOA
mname = primary name server for domain
rname = email address (in dot format)
serial = serial number of record
refresh = refresh period
retry = retry
expire = expiration of record
minimum = timeout
PTR
name = host name

Notes on using FileUtils.rb

We needed to change permissions and ownership on a number of files and directories from ruby. FileUtils (up to 1.8.2) does not hack it - but 1.9 FileUtils provides a number of new methods - particularly chown_R, chmod and chmod_R which we wanted. So... We downloaded a CVS copy of FileUtils.rb and tried it. It works perfectly. We now have chown_R etc available in our otherwise stock 1.8.2 ruby lib.

However we ran into a problem. The particular application we were writing takes a command line argument which defines the mode (permission mask) to be applied to a series of directories created by the application e.g. 0770 type values. So we saved the command line argument in a variable and tried to use the variable in the resulting chmod and ruby choked - badly. All the examples shown explicit values being used. Here is out little journey to a solution in code fragments:

# all the class examples use explicit mask values like this - which works
FileUtils.chmod_R(0774,directory)
# when a variable is used ruby chokes - it wants a fixnum
mask = "0774"
directory = "/var/new/some-directory"
FileUtils.chmod_R(mask,directory)
# OK so this really stupid but we wanted to see the result
mask = 0774
directory = "/var/new/some-directory"
FileUtils.chmod_R(mask,directory)
# worked but with wild mask results as expected
# when in doubt use eval so we used this - and it works
mask = "0774"
directory = "/var/new/some-directory"
# messy escaping required
ts = "FileUtils.chmod_R("+mask+',"'+directory+'")'
eval(ts)

Multi-Level Hashes

We use a lot of multi-level hashes to reflect complex record structures. Here are some notes that may help you:

Returning Multiple Values

There is no end to the magic of ruby - or more probably we've had too many years with single return procedural languages - OK so you can return structures. Well we're talking ruby now - so forget all that stuff. You can return as many comma separated values as you want with ruby, see examples below:

# simple in-line function with multiple return values
def multi_return(one)
return one+1, one+2, one+3
end
# appears that the above actually returns an array
# e.g. [2,3,4] which can then be assigned or ignored
# assigns all returns
two, three, four = multi_return(one)
# assigns first two returns - ignore third
two,three = multi_return(one)
# by-the-way you can omit return if
# the thing you want to return is the last result
# this works as expected and returns true or false
class Test
def Test.test_it(num)
num > 20
end
end
if Test.test_it(25) then
# do something
end

Errors and Exceptions

We found this very confusing for a long time - still do actually! Here is our explanation - if we are wrong let us know...

The main error object is class Exception, a number of subclasses seem to have been defined - some of which have additional methods or attributes but many don't. The reason they seem to have been created is to classify errors and to allow you to handle, via rescue, specific errors.

Problems, comments, suggestions, corrections (including broken links) or something to add? Please take the time from a busy life to 'mail us' (at top of screen), the webmaster (below) or info-support at zytrax. You will have a warm inner glow for the rest of the day.