Wednesday 31 March 2010

Variables

In Ruby (as in most OO languages) there are four sort of variables; global, class, instance and local. In languages like Java and C++, you must declare a variable before use, and where you do that determines what sort it is. In Ruby, variables are not declared, so we need another way to indicate this; a prefix.

Variables can contain any object in Ruby; you can readily assign a string to a variable that previously held an integer. Variable names must begin with a lower-case letter or underscore (after the prefix). By convention, they are all lower-case, with words separated by underscores (eg my_variable).

Local Variables

Local variables have no prefix. They exist only within a block of code, such as a method. Once the block completes, the variable disappears.
def test2
local1 = 2
local1.times do
local2 = 3
p "l1=#{local1} l2=#{local2}"
end
p "l1=#{local1}"
#p "l2=#{local2}" # Out of scope
end


Instance Variables

An instance variable belongs to that one instance of the class. It is denoted by a single at symbol (eg @var), and can be accessed from any instance of a class from within a method. They have private access... however, you can access them anyway through these methods:
instance_variables    # Array of strings, listing instance variables
instance_variable_defined?
instance_variable_set
instance_variable_get
remove_instance_variable # Private method


You can also use attr_accessor, attr_reader and attr_writer to allow others to access your instance variables. Note that while the above methods do require the at sign, these three methods do not.

You can also access instance variables using self. These two statements are equivalent.
self.var = 19
@var = 19


Class Variables

A class variable belongs to the whole class (and indeed subclasses), and is accessible from anywhere inside the class, or from any instance of the class. If you change the value in one instance, it will be changed in every instance. It must start with two at symbols (eg @@var).

In Rails, you can access a class variable like a class method (eg MyClass.var) for a descendant of ActiveRecord::Base (analogous to how you access column names). Outside Rails class variables have private access.

Class variables have some perhaps surprising behavior when you look at inheritance, as a value set in a subclass can affect other classes. Let us see that in action. SubClass1 has a parent, SuperClass, a sibling, SubClass2, and a child SubSubClass1.
class SuperClass
@@var1 = 11
@@var2 = 21
def self.out1; @@var1; end
def self.out2; @@var2; end
end

class SubClass1 < var1 =" 12" var3 =" 31" var1 =" 13" var3 =" 32" var2 =" 22"> 13
p SubClass1.out1
# => 13
p SubClass2.out1
# => 13

# @var2 is in the top class and its grandchild class. Despite "skipping
# a generation", the value set in the sub-sub-class still affects the
# super-class
p SuperClass.out2
# => 22
p SubSubClass1.out2
# => 22

# @var3 is in the sibling classes only, not in a common super-class. In this
# case setting the value in one has no effect on the other.
p SubClass1.out3
# => 31
p SubClass2.out3
# => 32

There are a set of methods available to use class variables, analogous to those for instance variables.
class_variables    # Array of strings, listing instance variables
class_variable_defined?
class_variable_set
class_variable_get
remove_class_variable # Private method

In Rails, you can define setters and getters just as you can for instance variables; cattr_accessor, cattr_reader and cattr_writer.

Global Variables

A global variable must start with a dollar (eg $var). It accessible from any class at any time. They are generally regarded as something to avoid; better to keep sections of code isolated from each other as far as possible.

Ruby has a number of predefined global variables. I do not like to use them myself; they make code that much harder to understand, and where there is a more explicit alternative, I would always use that. However, there is a list here.

There is an interesting trace feature for global variables. The trace_var method takes either a string or a symbol representing the global variable (with the dollar sign), together with a proc object (the documentation claims it will take a string or block as well; I do not believe that that is true).
trace_var "$global_var", proc do |x|
puts "$global_var is now #{x}"
end

You can set more than one trace on a global variable, and you can remove them (all) with untrace_var.

The global_variables method will return an array of strings, the names of all the global variables (complete with dollar sign).

Special Variables

Ruby includes a handful of special variables:
self     # "this" in Java, etc.
true
false
nil # "null" in Java, etc., also counts as false
__FILE__ # the current file
__LINE__ # the current line number.



All Together Now

Let us them all in action. Here is some sample code.
$global_var = 32

class TestClass
@@class_var = 14

def initialize
@instance_var = 20
end

def test
local_var = 7
$global_var += 1
@@class_var += 1
@instance_var += 1
local_var += 1
puts "$global_var=#{$global_var}"
puts "@@class_var=#{@@class_var}"
puts "@instance_var=#{@instance_var}"
puts "local_var=#{local_var}"
end
end

Both $global_var and @@class_var are set when the file is loaded. While $global_var is available to everything, @@class_var can only be accessed from within TestClass. Looking at @instance_var, this will be set to 20 for a specific instance of TestClass when the instance is created. It will be incremented whenever test is invoked on that instance, in contrast to $global_var and @@class_var, both of which will get incremented every time test is invoked on any instance. Finally, local_var is created when it is set in the test method, and destroyed when the method terminates.


Struggling with Ruby: Contents Page

Thursday 18 March 2010

Capturing File Uploads

Here is the requirement: The web page asks the user to upload a file. The data in the file is then used to populate a new record in the database.

The first thing I want, then, is for the view to have a file upload button. Here is what worked for me:
<% form_tag 'create', :multipart => true do %>
<%= hidden_field_tag :sample_id, @sample.id %>
<p>
<%= file_field_tag 'datafile' %>
</p>
<p>
File names can only contain numbers, letters, hyphens and underscores, and must end ".csv".
Files must be on your C: drive to be successfully uploaded.
</p>
<p>
<%= submit_tag "Okay" %>
</p>

You could also use the form_for method instead, with file_field, rather than file_field_tag.

Some points to note:

You need to flag the form as "multipart", so that the file gets submitted as one part, and other data on the form as a second part. My experience is that uploaded files are a little restricted, and will not upload from a networked drive, or if there are strange characters in the name, and I warn the user of this. This may be dependant on your system. In this particular case, I am looking for a .csv file. The file type may need to be defined as a MIME type (CSV is registed by default).

The file received is of the UploadedTempfile type, and seems to be useable like any other file. As far as I can tell, Rails closes and deletes it for you. It is accessible though params in the normal way (so as params[:datafile] from the above code).
params[:datafile].each_line do |s|
# do stuff
end

I could not find away to process a CSV file using the CSV library; it seems to demand a filename, rather than the file itself. I was obliged to do it by hand (in my case I knew every field would be surrounded by quotes, so it is pretty easy).

In the functional test, just send the file as a parameter (with a test file in the fixtures folder).
post :create, :sample_id => Sample.find(:first).id,
:datafile => File.new("#{RAILS_ROOT}/test/fixtures/results.csv")



Struggling with Ruby: Contents Page

Thursday 11 March 2010

Rails: Singularize and Pluralize

Rails has functions buit-in that will turn a word into its plural or singular. It uses this when generating models, etc. to create names that conform to the standard (so user.rb but users_controller.rb). The rules are set up in a file called:
...\lib\ruby\gems\1.8\gems\activesupport-2.3.2\lib\active_support\inflections.rb

The action all happened inside a block like this:
module ActiveSupport
Inflector.inflections do |inflect|
# definitions
end
end

There are four types of rules. The first sets a general rule for making a plural, like this:
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')

If the regular expression in the first parameter matches, then it is replaced by the the second parameter (but note that a capture group is used, so a part of the discarded ending is still used). This rule will match a word ending in y, but not preceded by a vowel, replacing the "y" with "ies"

The second sets a rule for making a singluar, like this (which is the reverse of the previous):
inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')

Then there are the irregulars, defined like this:
inflect.irregular('person', 'people')

And those that do not change, like this:
inflect.uncountable(%w(equipment information rice money species series fish sheep))

Unfortunately, it is not perfect (not in 2.3.3 anyway), which is why I was obliged to learn about it. For example:
inflect.plural(/(octop|vir)us$/i, '\1i')

The plural of virus is viruses, not viri; octopus can use either form, though octopuses is prefered (see here).

The particular one I ran up against was this, for "metal analyses":
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
inflect.singular(/(^analy)ses$/i, '\1sis')

The system runs through the rules starting with the last defined, until it finds a match. For "analyses", it hits the second of the lines shown above, and returns "analysis" (why this was not put in as an irregular I cannot imagine). There is no match there for "metal analysis" as the pattern specifies the start of the string. So it looks at the previous rule. Now a match is found. However, this match defines several capture groups, the first is "analy", the second is just the "a", and both these are used in the replacement, so the resulting plural goes like this:
<whatever was before the pattern> <"analy"> <"a"> <"sis">

"metal analyses -> metal analyasis

Because the replacement uses only the first and second capture groups, the other words in that rule work fine. For "theses", for example, the "t" is in capture group 8, which is not used. And as analysis on its own gets caught by the previous rule, the error can easily be missed.

This issue was bought up as a bug, but dismissed (see here). The simple workaround is to define your own rule. I have done this in a file inside config/initializers (I have called mine called initial.rb).
ActiveSupport::Inflector.inflections do |inflect|
inflect.singular(/(analy|ba|diagno|parenthe|progno|synop|the)ses$/i, '\1sis')
end

You can add as many of your own rules as you like. As they get added later, they will take precedence over the existing rules.


Struggling with Ruby: Contents Page

Thursday 4 March 2010

Adventures in programming...

Back in the day I used to love text-based adventure games (the one that stands out is Leather Goddesses of Phobos). I have made several attempts to write my own, in various languages from BASIC, to C, to Java. The use of an object-orientated language like Java was a big help, but I was recently wondering how Ruby might work (whether it will even come to anything is dubious).

Ruby has a number of apparent advantages, and a consideration of how the data file might be illustrates that.
def setup
locations = [
Location.new(:id => :start, :name => 'The Start', :south => :hall,
:north => :lobby),
Location.new(:id => :hall, :name => 'The Great Hall', :north => :lobby),
Location.new(:id => :lobby, :name => 'The Swanky Lobby', :south => :hall),
]
items = [
Item.new(:id => :sword, :name => "Widowmaker Sword", :loc => :hall),
Item.new(:id => :robes, :name => "Magical Robes", :loc => :lobby,
:wearable => true),
Item.new(:id => :health, :name => "Healing Potion", :loc => :hall,
:consume => "He gained 5 hit points.<% @char[:health] += 5 %>"),
]
characters = [
Character.new(:id => :boris, :name => 'Boris', :location => 'Home',
:inv_limit => 2, :health => 5),
]
commands = [
{:verb => :take_from, :alias => [/^take ([\w ]+) from (\w+)$/,
/^take ([\w ]+) out of (\w+)$/,
/^remove ([\w ]+) from (\w+)$/],
:item_not_here => "It's not there."
},
{:verb => :get, :alias => [/^get ([\w ]+)$/,
/^take ([\w ]+)$/,
/^pick up ([\w ]+)$/],
:okay => "+char+ picked up +item+.",
:item_not_here => "It's not there.",
:inv_limit => "You're holding enough already.",
:cannot_get_static => "You can't get that!"
},
{:verb => :examine, :alias => [/^examine ([\w ]+)$/,
/^look at ([\w ]+)$/,
/^describe ([\w ]+)$/],
:item_not_present => "It's not here.",
},
{:verb => :simple_com, :alias => [/^look$/],
:script => "<%= @location.describe %>",
},
]
World.new locations, items, characters, commands
end

Data files are code

In all my previous attempts, my data files have been simple text files (perhaps in XML). This time I took the example of Rails; migrations, the routes files and rake files are all Ruby code, so there is far more you can put in there when you configure. Previously I have had to write code just to load my data; not so this time.

It should be easy to extend the system, even in run time. Create a new data file, perhaps with code to modify existing locations, and run it as a script.

Hashes

Ruby just asks for you to use hashes for pretty much everything (again, Rails does this a lot). They are so easy to create, to use and to pass around. Sure, Java has hashes too, but it never encouraged me to use them (so really this is only an apparent advantage).

At its simplest, you just make everything, whether the player, an item or the location, a hash, and give it the values appropriate. Want to add a new property? Just add a new value to the relevant hashes and away you go.

Want to save the data? In one line you can save an array of hashes to YAML, and load it back in in another line.

Regular Expressions

For understanding what the user is saying, regular expressions are so useful. An input like "put the cat in the bag" can readily be matched against /^put ([\s ])+ in ([\s ])$/, and straight away you can pull out what went in where (regular expressions have been in Java since 1.4; too late for when I was trying to use it).

Dynamic Method Calls

Once the command has been recognised, the appropriate method can be invoked simply by calling send, with the :verb value from the hash. Could be done using reflection in Java, but not as easy. Earlier languages... no way.

Scripts

With Ruby a scripting language, it is easy to just put scripts into the hashes. In the healing potion above, an ERB script is used to restore health.

Wednesday 3 March 2010

Ruby Sockets

I was messing around with Ruby sockets, and came up with a simple chat-server. Testing proved to be rather more complicated... If you run this program, you can connect to it using "telnet 6606".
require 'socket'
require 'thread'

# Boardcaster maintains a list of users.
class Broadcaster
def initialize; @users = []; end
def add user; @users << socket =" TCPServer.open(6606)" broadcaster =" Broadcaster.new" lock =" Mutex.new">")
user = {:name => s.gets.strip, :socket => s }
b.add user
print("#{user[:name]} is accepted\n")
s.write("Hello #{user[:name]}\n\rUsers on-line: #{b.list}\n\r>")
while true
st = s.gets.strip
#p st
break if st == 'bye'
lock.synchronize do
b.broadcast "#{user[:name]} says \"#{st}\"\n\r>"
end
end
lock.synchronize do
b.broadcast "#{user[:name]} has left\n\r>"
end
s.close
b.remove user
print("#{user[:name]} is gone\n")
end

end

This was my first experience of both threads and sockets on Ruby, and with regards to threads, I have to admit to being pretty clueless! However, it does seem worthwhile locking the shared resource, b, when used on a thread.

Sockets seems straightforward enough. A new socket is opened using the TCPServer class. Data is collected with gets, and sent with write. At the end it is closed. I suspect there should be some exception handling in there, but it certainly proves the concept.


Testing Stream-Handling Methods

Okay, so now I want to test my methods that handle streams. Let us suppose that you have a method that accepts data from some stream and outputs to another, like the broadcast method above, and you want to test it. How do you do it?

First, let me simplify, and instead consider this method:
def get_data source, sink
print "\n>"
name = source.gets.strip
print "\n>"
age = source.gets.strip
sink.print "Name: #{name}, age: #{age}"
end

This could be invoked for use with the keyboard like this
get_data $stdin, $stdout

Or across a network, like this:
require 'socket'
socket = TCPServer.open(6606)
get_data socket, socket

If I want to test that method the trick is to use StringIO objects.
def test_get_data1
StringIO.open { |sink|
get_data(StringIO.new("Boris\n32\n"), sink)
assert_equal "Name: Boris, age: 32", sink.string
}
end

Actually, Ruby would happily let you use the same StringIO object for both input and output, but the output would be appended to the input string, so your assertion would need to check for both the input and the output.
def test_get_data2
StringIO.open("Boris\n32\n") { |io|
get_data(io, io)
assert_equal "Boris\n32\nName: Boris, age: 32", io.string
}
end

That is bad; if we change the get_data method to accept different input, we would need to change the test in two places, and that is clearly a bad thing. Well, okay, we change it so the input gets inserted into what we expect. The problem now is that Ruby is modifying that string during the test, so we need instead to give Ruby a duplicate of the input string for it to play with, so we still have the orignal for comparison at the end.
def test_get_data3
input = "Boris\n32\n"
StringIO.open(input.clone) { |io|
get_data(io, io)
assert_equal "#{input}Name: Boris, age: 32", io.string
}
end

Then again, perhaps we need to rethink. The whole thing can be generalised into a new method, which can test any method against any input. The test itself can then be reduced to a single line.
def test_get_data4
stream_test("Boris\n32\n", "Name: Boris, age: 32") do |io|
get_data(io, io)
end
end

def stream_test input, output
StringIO.open(input.clone) { |io|
yield io
assert_equal "#{input}#{output}", io.string
}
end

A serious problem with all of these is that errors do not get caught by the test regime. A message is sent to the output, but the error is not counted in the totals (failures, on the other hand, are). I guess this is because the redirect is capturing the exception. A way around this is to capture the exception inside the block, and then flag this as a failure:
def stream_test input, output
StringIO.open(input.clone) { |io|
begin
yield io
rescue Exception => ex
assert false, "ERROR: #{ex.inspect}\n#{$!.backtrace[0..12] * "\n"}"
end
assert_equal "#{input}#{output}", io.string
}
end



Testing Multiple Threads

Those tests are all very well, but my Broadcast object sends messages to multiple users. How do I test that? Now I need threads in my tests!

Here is a test method that worked for me:
def test_broadcast
# SETTING UP

# The number of threads to spawn
number = 100
test_string = 'teststring'
b = Broadcaster.new
# Define strings outside the blocks so we can access them
# later on
string_ary = Array.new(number, '')
thread_ary = Array.new(number)
main_s = nil

# LISTENING THREADS
# A number of threads are spawned, they listen for
# messages for 0.2 seconds, then write their StringIO
# string to string_ary, before terminating.

number.times do |i|
# Spawn a new thread
thread_ary[i] = Thread.start(b, i) do
# Create a StringIO object to collect the string
StringIO.open do |sink|
# Create a new user, and add it to the Broadcaster
user = {:name => 'test1', :socket => sink }
b.add user
# Wait a short time for the message to be broadcast
# Choose wisely, 0.2 on my system led to failures
sleep(0.3)
# Set s1 to a copy of the sink string, so it
# is still around outside the block
string_ary[i] = sink.string.clone
end
end
end

# SENDING THREAD
# On the main thread, this sends the message, then waits
# for all the other threads to terminate.

# Create a StringIO object to collect the string
StringIO.open do |sink|
begin
# Create a new user, and add it to the Broadcaster
user = {:name => 'test2', :socket => sink }
b.add user
# Broadcast the test string
b.broadcast(test_string)
# Wait for the other threads to finish
# by which time the broadcast should have been received.
number.times { |i| thread_ary[i].join }
main_s = sink.string.clone
rescue Exception => ex
# Flag any exceptions as a failure
assert false, ex.inspect
end
end

# TESTING
# Test all the threads received the test_string
assert_equal test_string, main_s
number.times { |i| assert_equal test_string, string_ary[i] }
end



Struggling with Ruby: Contents Page