Friday, 12 December 2008

Integration testing

I found this to be quite frustrating. Integration testing appears to be a relatively recent addition to Rails (since 1.1 I think), and pehaps for that reason there is not much on the web that explains the basics of how to do it. Coupled to that, my application is using an authentication system that I copied from a web site, and so I was just not that familiar with what is supposed to happen (at least, when I started; I know it a lot better after going through this). Also, the authentication system has some odd quirks, like defining session (rather than sessions) as a route, for a controller called sessions_controller.

Integration testing can be thought of functional testing for multiple controllers. This allows you to set up involved "stories" in which the user does a sequence of actions. Rails automatically generates unit and functional tests for you when you create a scaffold, but not an integration test (as it is not specific to one model/controller). However, there is a script to do the job for you.
ruby script/generate integration_test GeneralStories

Here is a test for the user authentication mentioned before:
  def test_authentication
user_bob = {:login => 'bob', :username => 'Robert',
:email => 'test@here.com', :password => '12345678',
:password_confirmation => '12345678', }
new_password = 'new_secret'

goes_to_home_page
goes_to_login
fails_to_log_in_with user_bob
goes_to_signup
signs_up_with user_bob
goes_to_login
logs_in_with user_bob
goes_to_change_password
changes_password user_bob, new_password
logs_out
goes_to_login
logs_in_with user_bob
goes_to_home_page
end

The code here is easy enough. A test method has to start test_ so Rails knows it is a test. First a hash is set up with details for a user, as well as a new password. Thereafter, it runs through a sequence of methods, each one corresponding to a single page download to the user (purely because that seems a convenient way to break it up). Reading down the sequence, we can read the story. The user goes to the home page, attempts to log in, but fails, then signs up, successfully logs in, changes his password, logs off, and logs on again, ending up back at the home page.

All the tricky stuff is, of course, hidden away in those methods. I am just going to look at the two involved in changing the password to illustrate the methodology.
def goes_to_change_password
get "/change_password"
assert_equal 200, status
assert_equal "/change_password", path
end

This corresponds to the user clicking on the "Change Password" link. The get command is equivalent to an HTTP GET. You then check the HTTP result is 200, and the returned page is correct. The next method is rather more complicated.
def changes_password(user_options, new_password)
post "/accounts/update",
:old_password => user_options[:password],
:password => new_password,
:password_confirmation => new_password
follow_redirect!
assert_equal 'Password successfully updated.', flash[:notice]
assert_equal "/", path
assert_equal 200, status
user_options[:password] = new_password
user_options[:password_confirmation] = new_password
end

Now the post command is invoked, for HTTP POST. The values on the form are sent in the form of a hash. Rails usually expects a hash of a hash here, which would look like this (and in the controller would be accessed with params[:user]):
  post "/accounts/update",
user => {:old_password => user_options[:password],
:password => new_password,
:password_confirmation => new_password}

I am not sure why my authentication system was set up differently; perhaps for older versions of Rails. If the action is successful, the user is redirected, and we want to see where that goes, so the next statement, follow_redirect!, does just that. Then we check everything: the flash notice should confirm the change, the path should be at the root and the HTTP status should be 200. Finally, a little bit of house-keeping; the user hash is updated with the new password ready to test logging on in a later step.

Multiple Sessions
In the background, Rails has a session object on the go, and this allows your test to proceed. If you want to you can invoke the session explicitly. One reason to do that would be to test what happens if multiple users are logged in at the same time (though I am suspicious that people use this when it is not necessary). It goes something like this (based in part on code here):
def test_signup_new_person
user_bob = {:login => 'bob', :username => 'Robert',
:email => 'test1@here.com', :password => '12345678',
:password_confirmation => '12345678'}
user_mary = {:login => 'mary', :username => 'Mary',
:email => 'test2@here.com', :password => '12345678',
:password_confirmation => '12345678'}
new_password = 'new_secret'

open_session do bob
open_session do mary
bob.extend(MyTestingDSL)
mary.extend(MyTestingDSL)

bob.goes_home
bob.goes_to_login
bob.fails_to_log_in_with user_bob
bob.goes_to_signup
bob.signs_up_with user_bob
mary.goes_to_signup
bob.goes_to_login
bob.logs_in_with user_bob
mary.signs_up_with user_mary
bob.goes_to_change_password
bob.changes_password user_bob, new_password
mary.goes_to_login
mary.logs_in_with user_mary
bob.logs_out
bob.goes_to_login
bob.logs_in_with user_bob
bob.goes_home
end
end
end

The test itself is more or less the same, except that the sequence is wrapped up in a block for open_session, and the session is addressed directly. This allows two sessions to be set up, bob and mary. The methods are set up the same, but again are wrapped up, this time in a module called MyTestingDSL. The module is mixed in to the session before the methods are invoked.

Finally...
I found it helpful to end my assertion with a call to this method:
def flash_me
"Error encountered. Flash[:error]=#{flash[:error]} Flash[:notice]=#{flash[:notice]}"
end

For example:
assert_equal 200, status, flash_me

If the assertion fails, you will get told the contents of flash. The method definition needs to be inside the module, if you are using sessions explicitly.

I found trouble-shooting a test failure to be something of a nightmare. The test informs you at which line in the test line it failed, but that gives you no clue about when in your actual code the problem is. It will report the same error (<200> expected but was <500>) for anything from an action that is not recognised, to an exception thrown in your model or a misnamed column. The best solution I found was to pepper the code with statements assigning values to the flash, which would then show up using the flash_me method above. Here is an example (with additional lines in red):
def qualify
flash[:notice] = 'here'
begin

@worksheet_meta_data = WorksheetMetaData.find(params[:id])
flash[:error] = '@worksheet_meta_data = nil' if @worksheet_meta_data.nil?
s = @worksheet_meta_data.qualify current_user.username
if s.nil?
flash[:notice] = "WorksheetMetaData was qualified (#{@worksheet_meta_data.qualified_by})."
else
flash[:error] = "Qualification failed: #{s}"
end
rescue Exception => ex
flash[:error] = "ex=#{ex}"
end

redirect_to(@worksheet_meta_data)
end

I also commented out the redirect! statement, so after the get or post, there is the assert, with a flash_me to see the result. The first extra line will confirm that the method is being invoked (are you using the right controller, action, and id; these might give a 404 error if not). Then everything up to the redirect is wrapped in a rescue block so any errors thrown get recorded in flash[:error]. Inside the block, I check

I would be interested to hear of any better ideas.

Struggling with Ruby: Contents Page

Wednesday, 10 December 2008

Ruby Classes

In Ruby, everything is an object. You can find the class of an object with my_object.class, which returns an object of the Class class.
10.class  # => Fixnum
10.class.class # => Class
"Hello World".class # => String

If you do not have an instance of a class, you can determine the name of the class using the inspect method (this might be useful if you are in a class method in a super class and want to know the subclass).

The superclass method will produce the superclass.
10.class.superclass # => Integer
10.class.superclass.superclass # => Numeric
10.class.superclass.superclass.superclass # => Object

As in Java, everything inherits from Object.

You can check if an object is of a certain class using the is_a? method. This will return true if the object is of the given class or of a subclass of it.

"Hello World".is_a? String # => true
"Hello World".is_a? Fixnum # => false
"Hello World".is_a? Object # => true


What methods are there?
The methods method returns an array of method names for an object, while a call on methods from the class will produce an array of class methods.

"Hello".methods
String.methods

There are also methods that list more particular methods, such as public_instance_methods or private_methods.

You can also check if an object has an associated method with the respond_to? or method_defined? methods. Unlike methods, these two methods will not find private methods. The respond_to? method really does what it say - it tells you if the object will respond to the method call, rather than if the method exists. Often an object can respond to method calls where there is no method present using the method_missing functionality (discussed here). Rails uses this a lot. For a model, Post, calling Post.methods will locate no find methods, but the respond_to? method confirms they are there. method_defined? will tell you if the method actually exists.

"Hello".respond_to? 'length' # => true
String.respond_to? 'constants' # => true
Post.respond_to? 'find_by_name' # => true
Post.method_defined? 'find_by_name' # => false


See also:
http://www.ruby-doc.org/docs/ProgrammingRuby/html/ospace.html

Dynamically adding a method
You can add a method to a class at runtime, using the define_method method. Note that this method is private (so the first attempt below results in an error), and so must be invoked with the send method.
a = "what".class.define_method('hello') {p 'Hello World'}
# => NoMethodError: private method `define_method' called for String:Class
a = "what".class.send(:define_method, 'hello') {p 'Hello World'}
# => #<Proc:0x08cb9294@(irb):17>
irb(main):018:0> "say hello".hello
# => Hello World

You can even replace existing methods:
"what".length
=> 4
a = "random".class.send(:define_method, 'length') { 10 }
=> #<Proc:0x08caaf28@(irb):21>
"what".length
=> 10

Alternatively (and more simply), you can define methods for existing classes just like you would for your own class. The first example could be done like this:
class String
def hello
p 'Hello World'
end
end

I can see security issues here if someone can change how your classes' behave at runtime, and how do you comprehensively unit test if you cannot be sure how the String class behaves? All part of the fun of a dynamic language...

ObjectSpace
ObjectSpace is the Ruby reflection mechanism. It is a module that tracks all the current objects. You can list them with this:

ObjectSpace.each_object{ i puts i}


I had over 8000 objects in one IRB session. You can specify the class of an object to list, for example, this will list all objects of the Class class (only 466 of them):

ObjectSpace.each_object(Class) { i puts i}


Creating new objects dynamically
You can use the ObjectSpace to create a new object from a string containing the class name (see here), but I prefer using the eval method. eval simply interprets a given string as Ruby (like the synonymous method in JavaScript). The eval method is none too fast, but I do not know how it compares to using ObjectSpace.

def create_object class_name
eval("#{class_name}.new")
end


Struggling with Ruby: Contents Page

Friday, 5 December 2008

Ruby Methods Part 2 - Not Overloading!

Method overloading is a common feature of several object-orientated languages. It allows the same name to be used for methods that perform the same, but accept different parameter types. Here is an example in Java:
  // Method overloading in Java
public int myMethod(String name, int age, int shoeSize) {
// do stuff
}

public int myMethod(String name, int shoeSize) {
myMethod(name, 0, shoeSize) {
}

public int myMethod(int age, int shoeSize) {
myMethod("", age, shoeSize) {
}

public int myMethod() {
myMethod("", 0, 0) {
}

// Invoking
public int main(String[] args) {
myMethod("F2Andy", 43, 8);
myMethod("F2Andy", 8);
myMethod(43, 8);
myMethod();
}

Ruby does not support method overloading, something I found very surprising at first. I suspect the reason is the way Ruby handles objects. Java is very strict that what gets sent to a method is exactly what the method expects. That gives (I would imagine) more robustness, but at the loss of flexibility.

Ruby has what could be described as "method over-writing" instead of overloading. In this example, the second test method over-writes the first:
def test s
puts "hello test"
end

def test
puts "goodbye test"
end

test # Causes "goodbye test" to be printed
#test "my string" # Causes an error:
# wrong # of arguments(1 for 0) (ArgumentError)
See here for how to use alias if you still need to access the old methods:
http://www.zweknu.org/blog/index.rhtml?start=77&

Default values and dynamic typing
Ruby has four tricks to mimic method overloading, and I am going to bundle the first two together. The first is default values in the method definition. Simply give a parameter a default value and it becomes optional. The second is the way Ruby handles objects; dynamic typing. Ruby does not care what sort of object you throw at a method, leaving the method writer the chance to handle different classes in different ways. In the next example the method has a default value - "default" - but the method is written in such a way that it will accept any class of object:
def test t = 'default'
puts "goodbye test t=#{t}"
end

test # => "goodbye test t=default"
test 34 # => "goodbye test t=34"

Something to consider, then, is how you handle unexpected objects in your class. One way is to ignore the issue, making the assumption that the method user knows what he is doing and any problems will be caught by the Ruby system.
def test t = 'default'
puts 'goodbye test t=' + t
end

test "one" # => "goodbye test t=one"
#test 2 # => can't convert Fixnum
# into String (TypeError)

Let us look at another example to see what options we have:
def test n
puts n.abs
end

test -4 # => 4
test 2 # => 2
#test "three" # => undefined method `abs'
# for "three": String (NoMethodError)

Rather than relying on your own method calls to generate errors, you might prefer to check that what you receive in your method is what you are expecting. You can then be quite clear about what exceptions your method is throwing, and more importantly, you will not have exceptions thrown half way through you method leaving the system in an unpredictable state.
def test n
raise StandardError.new("Oops, not a Fixnum") unless n.is_a? Fixnum
puts n.abs
end

test -4 # => 4
test 2 # => 2
test "three" # => Oops, not a Fixnum
# (StandardError)

Then again, perhaps you do not care what class the object is. The important question is whether it will behave as you expect. Instead of testing that it is of the right class, you could test that it responds to the appropriate methods calls. In this next example, a new method, abs, is added to the String class (how that is done was the subject of another post). Now the test method checks that the object it is given will respond to the necessary method call. This technique offers a lot more flexibility, but does rely on the API user sending sensible objects to the method. Does it really make sense that a string should return the ASCII code of the first character when asked for its absolute value? However, I would say that that is up to the API user; he has the responsibility now of ensuring the object he sends makes sense. You would need to check for each and every method call in your code.
class String
def abs
self[0]
end
end

def test n
raise StandardError.new('Oops, object does not respond to abs') unless n.respond_to? 'abs'
puts n.abs
end

puts "three".abs # => 116
test -4 # => 4
test 2 # => 2
test "three" # => 116

What would have happened if I had defined the abs method on String to return a string, rather than a Fixnum?
Using hashes for parameters
The third trick is to use a hash, and this is done throughout Rails. In the next example, the method expects a hash. Note that the default is set to be an empty hash, to avoid errors with nil.
def test options = {}
puts "hello #{options[:name]}"
end

test # => "hello "
test "here" # => "hello "
test :name => 'F2Andy' # => "hello F2Andy"

Compared to method overloading, there are advantages on both sides. If you have an existing method and you want to add overloading, it is no problem in Java. In Ruby, chances are your existng method is not expecting a hash and you either have to change your existing method calls or write the new method to expect either a hash or the original type. However, if your Ruby method already accepts a hash, adding more parameters becomes trivial - just add them to method calls if desired, and use in the method code where needed.

Variadic functions
Finally, Ruby supports variadic functions. These are methods that accept a vaiable number of parameters. In Ruby this is indicated by an asterix in the method parameter list, before the last parameter. This will then become an array holding all the "left over" values. Here is an example:
def vari_method name, *values
puts name
values.each { |x|
puts " -#{x}"
}
end

vari_method 'fruit', 'apple', 'banana', 'pear'

When vari_method is invoked, 'fruit' goes into the name variable, and the three other strings are collected into a single array, values. The method prints the name, then lists each member of the array. There is no reason for the members of the array to be of the same class or in any way connected to each other.

Struggling with Ruby: Contents Page

Thursday, 4 December 2008

Ruby Methods Part 1 - The Basics

A Ruby method definition starts with the keyword def followed by the method name and the parameter names, and ends with the keyword end.
def test
puts "Hello World"
end

test # Causes "Hello World" to be printed

Note that unlike Java and C#, Ruby does not require the return type or the parameter types to be stated. Every Ruby method returns a value. In the above example, puts returns nil, and as this is the last statement in the method test, test will also return nil. The return keyword is entirely optional (though necessary if you want to jump out of the middle of a method, of course).

Visibility
By default, all methods are public; they can be accessed from anywhere by anything.

Private methods can only be accessed from with the class and (unlike Java et al) its subclasses. That said, you can access private methods from anywhere using the send method, or modify their visibility on-the-fly, so it would seem that this is more advisory than anything else; the API writer is telling you that you probably should not use those methods.

The private keyword indicates that all subsequent methods (until public or protected is encounted) will be private.
class MyClass
def public_method
puts "In public_method"
end

# You can access private methods within the class
def private_via_public_method
puts "private_via_public_method"
private_method
end

private # Flags all subsequent methods
# as private

def private_method
puts "In private_methodstance_method"
end
end

class MySubClass < MyClass
# You can also access private methods
# within any subclass
def private_via_subclass_public_method
puts "private_via_subclass_public_method"
private_method
end
end

mc = MyClass.new
mc.private_via_public_method
mc.public_method
#mc.protected_method # Protected method
# not visible
#mc.private_method # Private method
# not visible

mc.send :private_method # Private method
# accessed with send

# Visibility can be changed dynamically
class MyClass
public :private_method
end

mc.private_method # Private method is
# now visible

The third visibility type is protected, which is a little more esoteric. The protected visibility is very similar to private, except that protected allows you call method from a different object, as long as that object is of the same class or subclass. To put it another way, private is restricted to the specific object, protected is restricted to the class.
class MyClass
# You can access private methods on the
# object within the class
def private_via_public_method
private_method
end

# You can access protected methods on another
# object within the class
def protected_via_other_public_method
mc = MyClass.new
mc.protected_method
end

# This will fail
# You cannot access private methods on another
# object, even within the class
def private_via_other_public_method
mc = MyClass.new
mc.private_method
end

protected
def protected_method
puts "In protected_method"
end

private
def private_method
puts "In private_methodstance_method"
end
end

The important point here is that both private and protected methods can only be used with the class (or subclass), but that private methods are further restricted to calls that do not require the dot operator.

As an aside, in Ruby all constants are public, and all variables are private

Class methods
Class methods are methods that are applied to the class rather than to a specific instance of the class. The most important example of a class method is new; in Rails the find method is also used very frequently.
s = String.new "My string"  # Equivalent to
# s = "My string"
post = Post.find :first # gets the first record
# from the posts table

As can be seen, class methods are invoked by using the class name, rather than the object, and as in the examples above they are often used to create or retrieve instances of the class.

Class methods are written much like instance methods, but with the keyword self before the method name. You cannot call a class method from an instance of the class, or call an instance method from the class itself. This does mean you can have a class method with the same name as an instance method.

See the next section for an example of a class method.

The initialize method
I have already mentioned the new class method. What this does is create a new instance of the object, and then it invokes the initialize method, if it exists. The initialize method is always private; there seems no way to change that. However, you can invoke it from within your class.

The following code illustrates the use of initialize and class methods.
class MyClass
def initialize
puts "In initialize"
end

def self.class_method
puts "In class_method"
end

def instance_method
puts "In instance_method"
end
end

MyClass.class_method # Prints "In class_method"
mc = MyClass.new # Would cause error; cannot call class methods on an object
#mc.class_method # Prints "In initialize"
mc.instance_method # Prints "In instance_method"


Class initialize method?
Sometimes you might want to do something to set up your class. You want it to run before any instances and created. I believe Rails does this for ActiveRecords, inspecting the database to see what fields are present, so it will know how to handle requests, rather than inspecting the database each time a request is made.

There is no method that supports this, you just put your code straight into the class definition.
class MyClass
p 'This will be printed when the class is loaded'

def initialize
p 'This will be printed when an object is instantiated'
end
end


Multiple return results
Pretty much every modern language allows you to send multiple arguments to a function/procedure/method. How many allow you to return multiple arguments? Certainly not the C derivatives, and really I cannot see why that should be so. At the machine code level, I would assume there is no difference. I would guess the reason is that C is based on a mathematical concept of functions, F = f(x,y,z), and Java and C# had followed suit.

Not so Ruby, which allows you to return as many values as you like. Here is a trivial example to illustrate that:
def test
return return 1, 'two', 3
end

a, b = test
p a # => 1
p b # => "two"

a, b, c, d = test
p a # => 1
p b # => "two"
p c # => 3
p d # => 4

Note that in this case the return keyword is required. What is actually returned is an array containing each each return value. This can lead to confusion...
def tester
return 3, 6
end

def output x, y = nil
p "x=#{x} y=#{y}"
end

x, y = tester
output x, y
# => "x=3 y=6"

output tester
# => "x=36 y="

The first call to tester correctly gets the two values assigned to the two variables. However, the second fails; the array is passed directly to the output method, which has no clue about the context, and just sees an array. What is really happening is a bit of trickery. Ruby does not pass back multiple values at all; it just passes back an array when multiple values are used with return. Meanwhile, at the other end, Ruby allows multiple varaibles to be set from an array.
x, y = [7, 6]
output x, y

Overall we have the illusion of muliple returns.

blog on dynamically creating methods:
http://ola-bini.blogspot.com/2008/05/dynamically-created-methods-in-ruby.html

Struggling with Ruby: Contents Page

Saturday, 22 November 2008

Ruby File Access

Reading Files
Note that while Ruby accepts backslashes, it seems to prefer forward slashes in file paths, even on Windows.

You can access the contents of a file like this:
file = File.open(filename, 'r')
do_stuff_with file
file.close

To test if the end of the file has been reached, use file.eof?. To test if a file has been closed, use file.closed?.

You can read the entire contents of file in a block:
contents = File.open(filename, 'r') { |file| file.read }

Or just:
contents = File.open('test.txt', 'r').read

If you prefer, you can put each line of the file into an array:
lines = []
File.open(filename, 'rb') { |file|
file.each_line { |line|
lines << line
}
}

Note that file here is exactly the same object as in the very first example. The big advantage of using it in a block is that Ruby will ensure the file is closed for you. You can use each_byte to iterate through the bytes (or, of course, your custom do_stuff_with file method). You can break lines at any character, for example, at the full stops:
lines = []
File.open(filename, 'rb') { |file|
file.each_line('.') { |line|
lines << line
}
}

Alternatively, use IO.foreach:
lines = []
IO.foreach(filename, '.') { |line|
lines << line
}

Or simply use IO.readlines (why was it not named read_lines?):
lines = IO.readlines(filename, '.')


Writing Files
To write data to a file, you can do something like this:
file = File.new("test.txt", "w")
file.syswrite("First line of text")
file.syswrite("Second line of text")
file.close

However, the prefered way, as with reading a file, is to do it in a block, and leave it to Ruby to close the file:
File.open("test.txt", "w") { |file|
file.syswrite("First line of text")
file.syswrite("Second line of text")
}

As well as syswrite, you can also use write, print, puts (adds a return at the end of the line), p (alias for puts) and printf (formated print, just like the C function of the same name). Or you can use the append function:
File.open("test.txt", "w") { |file|
file << "First line of text"
file << "Second line of text"
}


Binary Files
Binaries files are just the same; you just add a b to the second parameter of the open method. This example reads the contents of a binary file to contents (which is a string, by the way), and then writes that to a new binary file:
contents = File.open(in_filename, 'rb').read

File.open(out_filename, 'wb') { |file|
file << contents
}

Manipulating Files
These are all pretty self-explanatory:
File.rename(old_filename, new_filename)
File.exists?(filename)
File.file?(filename)
File.delete(filename)
File.directory?(filename)
File.executable?(filename) # Returns true for a text file!
File.readable?(filename)
File.writable?(filename)
File.zero?(filename)
File.size(filename) # Return 0 if the size is zero
File.size?(filename) # Returns nil if the size is zero
File.ftype(filename)

Using Directories
You can list the contents of a directory at least three way:
Dir.entries(dir)  # Surprisingly, no default for current directory
Dir["#{dir}/*.txt"] #
`dir`

The first and second will produce arrays of the files in the given folders. The first has just the filenames, and starts with entries for "." and "..". The second has the full path for each file, but does have the facility to filter (as in the example, only .txt files will be listed). The third uses a backquoted string to invoke a system command, and will produce a flat string with the same contents that you would see if you typed "dir" at the command prompt (presumably an error on some operating systems).

Other methods:
Dir.pwd                  # Gets the current directory
Dir.chdir('c:') # Changes the current directory (to c:)
Dir.mkdir('newfolder') # Create a new directory
Dir.delete('newfolder') # Or unlink or rmdir


Putting It Together
Here is an actual program. It uses the directory functionality to create an array of file names conforming to the filter (in this case, files end .nif; NetImmersion format), then iterates through the array. For each filename, the file is opened in binary format, the contents read to a string, and then each occurance of one set of strings replaced by another (this was subst, an array of hashes, giving replacement names for texture files). Finally, the file is saved, again in binary format, with a new filename.
files = Dir["#{mesh_dir}/in_r*.nif"]
print "Found #{files.length} files.\n"
Dir.chdir(mesh_dir)
files.each { |f|
contents = File.open(f, 'rb').read
subst.each { |h|
contents.gsub!(h[:old], h[:new])
}
File.open(f.gsub(/In_R/i, 'And'), 'wb') { |file|
file << contents
}
print '.'
}
print "\nDone.\n"


File data in with code?
If you put __END__ in your code, Ruby execution will terminate at that point. Everything beyond that will be considered data, and can be accessed as a file, DATA. Here is a complete program to illustrate that:
p DATA.read

__END__

Your data goes here


Reading a CSV file
This is trivial, as Ruby provides a library to do just this.
require "csv"
values = CSV.read "C:/my-data.csv"

This gives you your data in a two dimensional array.


Struggling with Ruby: Contents Page

Saturday, 15 November 2008

Contents Page

This is not a blog in the true sense, where I create a new post each time I learn something. Instead, I am collecting what I learn into a collection of larger posts, and updating those posts as I learn more. Below is a list (a contents page if you like) of posts to date (22/Dec/09) grouped by topic, rather than chronologically, for easy reference.

Ruby Basics
Constants
Variables
Conditional statements
Case statements and relationship operator
Loops
Regular Expressions
Exception Handling

Methods, classes, etc.
Ruby Classes
Duck-typing
Methods - Basics
Methods - Not Overloading
Methods - method_missing
Methods - calling
Modules
Singletons
Proc objects
Blocks
Accessors
Operator Overloading

The Ruby Library (excluding Rails)
Hashes
Arrays
Strings
File Access
Time, Date and DateTime
Unit Testing
YAML
REXML for XML
Sockets
GUI with Shoes
GUI with Monkeybars (and Swing)

Miscellaneous
JRuby and Java

Links to APIs
Because I am always refering to these three...
Array
String
Hash

Rails
Building a Project
Basic Project
Deploying on Tomcat

The Model
Creation
Validation and Association
Interactive Rails Environment
Testing
Find and other methods
Date and Time
Gotchas

The Controller
Routing
Basic Methods
Render and Filter
Functional Testing
Testing for valid HTML

The View
ERB and Links
Scope, Helpers and Partials
Using Forms
Using Select/Options
Partials as methods

Handling Images On Rails
Dynamic Images with RMagick
Dynamic Images with Java
Generating Line Graphs with Java
Uploadable Images with File Column

More On Rails
Integration Testing
Rails Mailer
Usage Statistics on a Bar Graph with HTML
Single Table Inheritance
Moving to Rails 2.2.2
Moving to Rails 2.3.8
Using JavaScript
Using Java applets
Organising in Subdirectories
named_scope
Iterating through records
Singularize and Pluralize
Capturing file uploads

Useful Links
General Ruby and Rails
Good Rails tutorial that go beyond the very basic:
http://www.akitaonrails.com/2008/5/25/rolling-with-rails-2-1-the-first-full-tutorial-part-1

Book on Ruby (not Rails):
http://ruby.activeventure.com/programmingruby/book/index.html

FAQ
http://www.ruby-doc.org/docs/ruby-doc-bundle/FAQ/FAQ.html

Cheatsheet:
http://www.ilovejackdaniels.com/cheat-sheets/ruby-on-rails-cheat-sheet/

Quick ref:
http://www.zenspider.com/Languages/Ruby/QuickRef.html

Others
PDF generation:
http://prawn.majesticseacreature.com/

XML handling with REXML
http://www.germane-software.com/software/rexml/docs/tutorial.html
http://www.developer.com/lang/article.php/3672621

The Controller Part 4 - Functional Testing

Functional testing targets individual methods in your controllers. It tests the user is redirected, gets the right HTTP response and so on, without actually generating any web pages (though it does generate the HTML so will find errors there).

Setting it up
Rails creates functional tests, but there are a few things to be done first... The test database must be set up (though this was probably done when you did your unit testing):
rake db:test:prepare

As with unit testing the require needs altering, if you are using NetBeans 6.1: require 'test_helper' becomes require 'test/test_helper'. You should now be able to run the default tests put there by Rails (depending on the changes you have made to the controller and model). Outside of an IDE, the easiest way is using rake.
rake test (run all tests)
rake test:functionals (run all functional tests)
rake test:recent (run all test files modified in last 10 minutes)

There are typically three parts to a test method. The first part is setting up, and might include, for instance, logging a user on. The second, and only required, part calls the method and checks changes to the database. The third part checks the HTTP result.
def test_should_get_new
login
get :new
assert_response :success
end

Method Calls
By default, controller method calls have all the parameters you would expect (eg :controller), but often you want to add more. In this example, an extra parameter is given. This is equivalent to ?run=199 at the end of the URL.
get :new, :run => '199'
In his example, parameters for a model are given. This is equivalent to data in a form, as usually accessed via params. I found that this was essential for create, otherwise there is no data in your model, and so nothing gets saved and the test fails.
post :create, :sample => { :sample_reference => 'P12345', :sample_type => 'PP1'}

Checking the results
While your view can, of course, access any instance variable in the controller, you do not have direct access to those variables from within your tests. Instead, they can be accessed though the assigns method. This allows you to test against values.
assert_not_nil assigns(:samples)
assert_equal 1, assigns(:samples).length
assert_equal 'glc', assigns(:sample).analysis
# Test value of a specific attribute for one record
To check the returned HTTP code, just use the standard method:
assert_response :redirect
Other options are :success, :missing and :error. Not sure why you would test for the last two.

You can also test the template used. This is for those occasions when you specified a template (like this; render :template => 'folder/template'), otherwise the template is nil.
assert_template 'samples/new'

The last check is where the page has been redirected to. You can use the same parameters for assert_redirected_to that you can for redirect_to.
assert_redirected_to :controller => 'samples', :action => 'home'
assert_redirected_to samples_path
assert_redirected_to sample_path(assigns(:sample))

See also:
http://api.rubyonrails.com/classes/ActionController/Assertions/ResponseAssertions.html

You can also check what has happened to your database table with the assert_difference method. Wrap this method around your method call, and it will check that the number of records has changed by the appropriate amount. This example checks the number of records for a model called Post has increased by one (the default value) when a new record is created:
def test_should_create_post
assert_difference('Post.count') do
post :create, :post => { }
end
assert_redirected_to post_path(assigns(:post))
end

The destroy method needs to check that the number of records has decreased by one, so in that case the method call looks like this:
    assert_difference('Post.count', -1) do

What assert_difference does is to determine the value of the first parameter (Post.count in the example), then run the block, and then determine the value of the first parameter again. It then compares the value before and after, and checks that difference against the second parameter (which defaults to +1).

Fixtures
Fixtures are a way to easily generate example data. You can point your tests to a fixtures file with:
fixture :my_data

By default, rails includes the statement fixture :all in tester_helper.rb, so all the fixtures get loaded for all your tests.

Rails will look for test/fixtures/my_data.yml (or test/fixtures/my_data.csv), and use the data in there to create a data structure, my_data (actually, I suspect my_data is a method that returns the data_structure, but the effect is the same), as well as in your database table. Here is an example of a fixtures file, employees.yml.
:one
name: Fred Smith

:two
name: Mary Jones

You can access specific records from your fixtures using their label. For the employees fixture, to access Fred Smith, use:
employees(:one)

Functional testing gets a little more complicated once you have a model that belongs_to another (see here for how to set up the model). Let us suppose the companies.xml fixture file look like this:
:one
name: The Excellent Software Company

:two
name: MegaSoft Ltd

In the Employees fixture file, employees.yml, you can set up the association for the company very easily. Just add a key named after your associated model, with the value equal to the label you used for your entry (without the colon):
:one
name: Fred Smith
company: one

:two
name: Mary Jones
company: one

To assign a company to an employee in a test, use something like this:
post :create, :employee => {:name => 'Tom Johnson', :company => companies(:one) }


Preventing something when testing
It may be desirable to prevent some actions happening while testing, for example, confirmation e-mails being sent whenever a new user account is set up. This is easily done by checking ENV['RAILS_ENV'].
def my_method
return if ENV['RAILS_ENV'] == 'test' # Do not during testing
do_stuff_only_if_not_in_test_environment
end

Obviously this could cause potential problems, as you are specifically not testing some of your code, so use with care!

Testing functions that require a logged in user
This is what works for me...

At the start of each relevant method, put in a line like this:
login_as_admin(@request)

In test_helper.rb, define the login method, something like this:
def login_as_admin(request)
user = User.new
user.login = "tester"
user.email = "tester@domain.com"
user.username = "Test Administrator"
#...
# Set up other details, permissions, etc.
#...
request.session[:user_id] = user.id
end

You should also test that a user will get redirected if not logged in (and that a user logged in but with permissions gets redirected too). Here is how:
def test_should_be_refused_without_login
get :new # Example method to test
assert_response :redirect
assert_redirected_to :controller => 'session', :action => 'new'
end

Similar methods can be set up for other user roles, to test how the system behaves.

Testing helper methods
You can test your helpers (in application_help.rb) in your functional tests.
class HelperTest < Test::Unit::TestCase
include ActionView::Helpers::TextHelper
include ActionView::Helpers::TagHelper
include ApplicationHelper
# include whatever helpers you want to test here, sometimes you'll need
# to include some of the Rails helpers, as I've done above.

def test_some_helper
end
end

However, you do not have access to the usual Rails helpers, and so any of your methods that rely on them are going to generate errors. There may well be a way around that that I have yet to discover, though as some Rails helpers use the context of the web user to determine their behavior (eg, the output of link_to depends on the current web page) there may not.

Credit: http://blog.lathi.net/articles/2006/03/31/testing-rails-helpers

Testing filter methods
You can test filter methods via the usual fuctional tests, for example having one test in which a user is signed in, and another in which the user is not. However, I feel it is better to also test the method itself. I would describe that as unit testing, but as your filter may well do a redirect, you will have to test it in your functional tests. This will be a protected, instance method, so can be invoked like this:
MyModelsController.new.send(:check)

However, as far as I can find, there is no way to set the params variable, so you cannot test a filter that, for example checks an id exists (see forum thread here for more).

What to test
My opinion is that you need one functional test for each outcome of each action in your controller. If an action can result in either of two pages being rendered, then that action needs two tests (that said, I rarely bother to test for when a save fails). This rule of thumb does rely on you having pretty much nothing in your controller besides setting up the instance variable, and deciding what page to display; all the complicated stuff is in the models, and that is adequately covered in your unit tests.

Things to watch out for
If you get a error complaining about a database table or column missing check that the table or column really is there via SQL - have you updated your test tables after modifying them in the development environment? Alternatively, if there is not supposed to be a table or column of that name (eg a subclass in STI or a model you later removed), check the test/fixtures directory; Rails may be trying to put data from here into your old table - just delete the .yml file for an erroneous table, or edit the data to remove references to the erroneous column.

If you are getting 300 responses when you expect successes, it might be because you need to set up a session with an appropriate user.

I write tests by copy-and-pasting existing tests. This leaves me liable to having two tests with the same name. Ruby will give no warning about this, but will only perform one of the tests, which can lead to a false sense of security.

You cannot rely on the values of the id field. In the development database they number sequentially from 1, but in the test database they start at some huge number.

I had a method that found the most recent entry of a certain type in the database and used that as a template for a new one. This is difficult to test, because all the entries in your fixtures file get created at the same time, so you have no idea which one will be selected as the most recent. Fortunately, in my case I had a reference number for each record and I could use that instead.

The data in fixture files gets loaded without validation, which is fair enough. However, validation will apply to the method calls in your tests. So if your update test uses update_attributes, and is working with a record that will not validate (perhaps the record has a required field missing, or the same supposedly unique field as another), the result will be that update_attributes will return false, with no clue as to why. It took me about a long time to realise this.

Similarly, if your create method is failing to increase the count in your database table, it may be because the validation fails.

Be aware that functional testing will not check any of your links actually go anywhere, or that your web pages make sense.

I have to say that in setting up functional tests for my project, most of the errors it turned up were down to the testing, rather than real bugs...

Struggling with Ruby: Contents Page

Sunday, 2 November 2008

Loops

Ruby has several options for loops; I will start with for and each loops. In practical terms, there is not a lot between for and each besides style, though each is actually a method call that takes a block, while for seems to be a language feature. They both iterate over a collection, which could be a hash, a string, an array or a range. Let us look at ranges first.

For and each with ranges
Use of a range takes the place of the normal for/next loop. In these examples, 1..10 and 1...11 are Range objects; the three dots indicates that the end stops before the terminator, two dots indicates it includes the terminator. These three examples will all print out the numbers 1 to 10, illustrating the difference between each and for, and the two and three dots.
for i in 1..10
do_stuff
end

for i in 1...11
do_stuff
end

(1..11).each do i
do_stuff
end

Ruby is perfectly happy with characters and variables in a Range object, and the next example shows how a range can be assigned to a variable.
r = 'a'..'z'
for i in r
puts i
end

n = 5
(1..n).each do i
puts i
end

These loops output the Range object, by the way, and you can test if something is within a range using the member? method (eg r.member? "c").

A quick note about arrays and ranges. I got really confused by putting square brackets around a range. What is [1..5]? This is an array with a single member, and that member is a Range object.

For and each with arrays, hashes and strings
The each method allows you to iterate over each member of a string, an array or a hash.
array.each { member do_stuff_with(member) }
array.each do member
do_stuff_with(member)
end

For a hash, you pass two values to the block
hash.each { key, value do_stuff_with(key, value) }
hash.each do key, value
do_stuff_with(key, value)
end

You can use the for feature with hashes and arrays too, for example:
h = {:a => 19, :b => 35, :c => 3456}
for k, v in h
puts "key=#{k} value=#{v}"
end


The times method
In Ruby everything is an object, and integer objects (of the class Fixnum) have a method called times, which, like each, accepts a block, and iterates from zero up to one less that the number. In these examples, the numbers zero to nine are printed.
10.times { i puts(i) }

10.times do i
puts i
end

n = 10
n.times { i puts i }

Invoking times on a negative number will create a loop with zero iterations!

The yield feature
You can easily define your own methods that work like each and times, using the keyword yield. Say this method is defined in a class, Car:
def cars
yield "Red car"
yield "Green car"
yield "Blue car"
end

The following will then produce a list of cars; red, green and blue.
m = MyClass.new
m.cars { c puts c }

The cars method runs until it hits the yield, and at that point passes the value back to the calling code, which then processes it as required. Then the loop goes again, and the cars method continues to the next yield stament. The loop iterates until it runs out of yields (the cars method will run to the end regardless of any further yields in the code).

The while and until loops
Ruby also supports while and until. The until loop is just a while not loop. There appears to be no option to check the conditional after the iteration that I have found.

Infinite loops
You can also create infinite loops with the loop method. Hmm, beter make sure there is some way to break out of the loop, which brings us to:

The break, redo, retry, next and return keywords
Ruby also has some interesting keywords to modify loop behaviour. As with C and derivatives, the break statement drops you out of the current loop. The next statement replaces continue; it stops the current iteration, moves to the next one, and passes control to the condition at the end of the loop. The retry statement is similar, but does not move to the next iteration; it repeats the current one. The redo statement starts the whole loop from the first iteration. There is also a return statement, which drops you straight out of the entire method (and optionally takes a parameter; a value returned by the method).

Struggling with Ruby: Contents Page

Saturday, 1 November 2008

Conditional statements

Before going any further, try this in the Ruby interactive console:

puts false or true        # prints false
puts true and false # prints true

Surprisingly, what gets printed is false and true respectively. What is going on? The problem is that and and or do not bind as strongly as the method call. In effect, you are doing this:

(puts false) or true
(puts true) and false

Contrast this with && and . These bind more tightly than the method call, and so behave as you would expect.

puts false  true        # prints true
puts true && false # prints false

More on operator precedence here:
http://www.techotopia.com/index.php/Ruby_Operator_Precedence

if and unless
While C and its derivatives have only if (condition) statement; Ruby supports two conditionals, if and unless, each in two formats:

if condition
statement
end

unless !condition
statement
end

statement if conditional

statement unless !conditional

You can optionally surround your conditional in brackets.

Ruby uses lazy conditionals. In the following code, if s is nil, Ruby knows that the expression will be false, and so does not test s.length > 10 - which is good, because that would throw an exception. This could, however, be a problem if you are changing the state of something in your condition; it might not change as often as you might imagine.

if !s.nil? and s.length > 10
puts 'Long string'
end


Conditional assignments can be done with the ||= operator.
s ||= "default"

The way this works is that nil counts as false, and in effect it say s = s || "default". If s is nil or false, then s becomes "default", otherwise it keeps its orignal value.

Ruby also supports the usual tertiary operator:
x = conditional ? value1 : value2

Struggling with Ruby: Contents Page

Monday, 27 October 2008

YAML

YAML is a format for structuring data in a file, like XML. Rails uses YAML to configure your databases, but it does so in such a way that you hardly notice. However, YAML is pretty neat, and you could be missing out.

YAML stands for "Yet Another Markup Language" (here). Or perhaps for "YAML Ain't Markup Language" (here). I guess it depends on whether you think it is a markup language or not (actually the latter seems the more common one).

There are basically two types of data; that which goes into a hash, and that which goes into an array.
name: value
- value

You can combine them to make complex data structures, such as this array of hashes:
-
name: Tom
age: 32
-
name: Dick
age: 19

And in fact you can also enter arrays and hashes more like Ruby:
- { name: Tom, age: 32}
- { name: Dick, age: 19}

Or as an array of arrays:
- [Tom, 32]
- [Dick, 19]

Comments start with a hash, #, just like Ruby. Ruby/YAML guesses the type from the format. Strings can be surrounded with single-quotes, double-quote (allowing escape sequences) or nothing. Repeated data can be replaced by an "anchor", by labelling the first occurance. Labels begin with an ampersand, and references to the label with an asterix. Use | or > for data that goes on to more than one line. The former preserves line breaks, the latter does not (see the example at the end of the YAML and Ruby section).

This example show all of these, and uses symbols as the keys, rather than string as above.
-
:name: Tom # A string
:age: 32 # An integer
:male: true # A boolean
:born: 1972-02-29 # A date
:address: &add
213 Main Street
Big City
England
:comment:
This is a potentially very
long comment that will be
just one line long.
-
:name: Dick
:age: 19
:address: *add
:comment: 'A very short comment'

See here for more details:
http://yaml.org/spec/current.html

By the way, NetBeans 6.1 flags up strings that go on to multiple lines as errors; this seems to be a bug in NetBeans.

YAML and Ruby
To use the data from your YAML file in a Ruby program, the first step is to load the YAML library:
require 'yaml'

Then load the data:
config = YAML.load(yaml_string)       # From string
config = YAML.load_file("config.yml") # From file

Then you can access to data just as you would any array of hashes (or whatever your data structure):
x = config[0]['age']    # x -> 32

Note that if you want to use symbols as the keys to your hashes the string needs to be prefixed with a colon (as in the examples above).

Going the other way is nearly as easy:
yaml_string = config.to_yaml            # To string
File.open('config.yml', 'w') do |out| # To file
YAML.dump(config, out)
end

Here is an example program that includes the YAML text (as a "here" document), and illustrates how YAML handles text that goes over multiple lines:
require 'YAML'

y = <<YAML_TEXT
:first: >
This is a folded block,
line breaks are discarded
for spaces. The line ends
with a return.
:second: |
This is a literal block,
and so the line-breaks
are preserved. Again the
line ends with a return
:third:
This is a folded block
too, as YAML defaults
to that. However, this time
there is no return at the end.
:fourth: And again a folded
block formated slightly
differently, with no return
at the end.
YAML_TEXT

h = YAML.load(y)
p h[:first]
# => "This is a folded block, line breaks are discarded for spaces. The line ends with a return.\n"
p h[:second]
# => "This is a literal block,\nand so the line-breaks\nare preserved. Again the\nline ends with a return\n"
p h[:third]
# => "This is a folded block too, as YAML defaults to that. However, this time there is no return at the end."
p h[:fourth]
# => "And again a folded block formated slightly differently, with no return at the end."

You can combine several data structures into a single file. Each should start with three dashes on a line on their own to indicate the start of a new document. Use the load_stream method to open the file. For example:
  data_doc = YAML::load_stream(File.open('data.yml'))
NAME_SCHEMAS = data_doc.documents[0]
DATA_TYPES = data_doc.documents[1]
CATEGORIES = data_doc.documents[2]


YAML and Rails
You can use a YAML file to kick start your database table in Rails. Rails already uses YAML, so no need for the require. The YAML file goes in the root directory for your web application.

One way that I have seen is to put the code inside your migration file, and when you migrate, the data goes straight into your table. However, this is a short term solution only. What happens in your production database or for testing? You could clone your database, but the prefered Rail way is to use Rake to generate the database tables, based on what is in db/schema.rb, and that will not have any initialisation data.

What I have done (and it may not be the best way) is to write setup methods in my models, which can be invoked from a console session. In this example, for a model called Role, defining the roles a user can have
  def self.setup_roles
return false unless Role.find(:all).length == 0
Role.create(:rolename => 'administrator')
Role.create(:rolename => 'manager')
true
end

Note that the method fitrst tests to see if there are any roles present already, and only adds the default roles if not. In your tests you can invoke the setup method before a test to load in the data, and be sure that it is the same default data as in your development and production databases.

Struggling with Ruby: Contents Page

Thursday, 23 October 2008

Single Table Inheritance

Rails supports Single Table Inheritance (STI). In this pattern, the superclass is the only model with an associated table in the database, and any object of that class and any subclass is stored in that same table. Rails use a special column named "type" which stores the class name (this means that you will have problems if you name a column "type" for any other reason).

The Models
To implement, create the top level object as a model as normal (with generate scaffold works best for me), but include a column called "type" that is a string. You also need to include every column that all your subclasses will use (though you can, of course, add columns later, as normal).

For each subclass, create a model (again, I recommend generate scaffold). Modify the model so that the class inherits from the desired superclass, and delete the database migration. Migrate the database, and you are pretty much done.

See also:
http://wiki.rubyonrails.org/rails/pages/SingleTableInheritance
http://www.juixe.com/techknow/index.php/2006/06/03/rails-single-table-inheritance/

You can access all the objects in the class hierarchy through the top level class, or a specific class through other classes.

A < B < C < ActiveRecord::Base
C.find :all # Get all objects in the table, whatever the class
B.find :all # Get objects of type B only (not A)


Your superclass will need all the fields of all the subclasses - otherwise there will be no table column for field. Each subclass needs its own controller and map.resources entry in routes.rb.

Controllers and Views

So how does STI affect the controllers and the views?

Possibly not at all. You can have one controller for each model, all completely independant, all with their own set of views. However, there is a good chance that there will be some overlap. It is quite likely that you will want to use the same "show" view for each model, for example. Let us suppose SubClass is a subclass of SuperClass; the default show method is this:

def show
@sub_class = SubClass.find(params[:id])
end


It is very easy to point the render method to the SuperClass view (remembering to rename the variable to what super_class/show.html.erb is expecting):

def show
@super_class = SubClass.find(params[:id])
render :template => 'super_class/show'
end


We can go a step further. If we set up SubClassController to inherit from SuperClassController, we do not need a show method in SubClassController at all; it can all be handled in SuperClassController. This will work without any changes to SuperClassController, but your object will be of the SuperClass type, and you will lose all the benefits of subclassing. It is better to instantiate your object as the subclass:

def show
@super_class = eval("#{params[:controller].classify}.find(params[:id])")
render :template => 'super_class/show'
end


Other methods for the SuperClass (destroy can be used as is):
def index
@super_class = eval("#{params[:controller].classify}.find")
render :template => 'super_class/index'
end

def edit
@super_class = eval("#{params[:controller].classify}.find(params[:id])")
render :template => 'super_class/update'
end

def create
@super_class = eval("#{params[:controller].classify}.new(params[:#{params[:controller].singularize}])")
if @super_class.save
flash[:notice] = 'Record was successfully created.'
redirect_to(:controller => 'super_class' , :action => 'show')
else
render :template => 'super_class/new'
end
end

def update
@super_class = eval("#{params[:controller].classify}.find(params[:id])")
if @super_class.update_attributes(params[params[:controller].singularize.to_sym])
flash[:notice] = 'Record was successfully updated.'
redirect_to(:action => 'show')
else
render :action => 'edit'
end
end


One last point. In your helps, instead of naming a model for the links, use polymorphic, eg, edit_polymorphic_path(super_class) rather than edit_super_class_path(super_class). This will make Rails point to the right controller for your subclass, whatever it might be.

Struggling with Ruby: Contents Page

Friday, 17 October 2008

The View Part 2 - Scope and Helpers

Scope
There are significant limits to what you can access from a view.

For local variables, you can only access variables defined in that view (anywhere in the file, not just that section of Ruby). You cannot access local variables in the controller, or in another view (not even in the layout that gets processed with your view).

You can access instance and class variables in your controller, as long as they have already been defined in the respective method (I guess a new instance is created with each web request). You cannot access instance and class variables that you have defined by calling a method from the view (they will always be null).

Helpers
This brings us to helpers. Helpers are methods in modules that you keep mostly hidden out of the way. The idea is to keep as much Ruby code out of the views, so helpers are mainly for use in that context. Helpers are the only methods you can access, other than instance methods for an instance you have access to.

In the helper folder
You can put your helpers in the helper folder. Rails puts in a helper :all directive, which will automatically load any helpers it finds there, as well as creating an ApplicationHelper file, plus a helper file for each controller or scaffold you generate. In this configuration all helpers are available to all views, regardless of what file you put it in.

Helpers are set up just like any other Ruby method, the only difference being that the file is a module, not a class.

module ApplicationHelper
def project_name
'My Great Web App'
end
end


The file must be named in the format "_helper.rb", and the module named "Helper".

In the lib folder
Helpers can go into the lib directory, with a filename of the form _help.rb. Again, these are modules, not classes, and look just as before. You need to point Rails to the file, with this in the controller:
helper :myfile

Rails will then look for myfile_helper.rb, which should contain a module MyfileHelper.

In the controller
If you want to be able to access your helper methods in your controller too, your only option is to put them into the controller, and flag them as helpers.

To be able to access the methods, you need to put them in your controller (or application controller to allow all views and controllers to use them). the methods are set up as normal, but you need a "helper_method" macro at the top to register the specified methods as helpers.
helper_method :myfirstmethod, :mysecondmethod

Accessing Rails' helpers
You can use the helpers in Rails in your own helpers (at least in your helpers folder). For example, the following will set up a quick link to page, using the standard link_to helper.
def link_to_log
link_to 'Sample Log', { :action => 'home', :controller => 'samples' }, :method => :get
end
As an aside, I once had a method, select_options, in a controller that was used by several views, and worked fine. I decided it would be better in a helper, and so moved it across. It stopped working, the interpreter complaining there was no such method. Other methods in the helper file worked fine, even in the same view. After some head scratching, I decided that the problem was due to a name conflict; changing the name of the method got it working again. Why would it fail to find the method (as opposed to invoking the other method or complaining about the wrong number of arguments)? I do not know. However, should your view fail to find a helper method, this could be the reason.

Testing Helpers

Naturally you will want to test your helpers. You may be able to do this in your unit tests, but you may require the infrastruction that comes with a functional test - and anyway, helpers are for views/controllers, not models, so it makes more sense here.

Here are the bare bones of a test file:
require 'test_helper'

class AppHelperTest < ActionController::TestCase
tests SampleLog::GlcsController

include ApplicationHelper
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper

def test_link_code
# code here
end
end
Note that it is necessary to specify which controller you are testing. It may not matter; just pick any. To get access to your new methods, include the helper class. To get access to other methods, include them too. In the above, I wanted access to link_to in ActionView::Helpers::UrlHelper, which in turn accessed escape_once in ActionView::Helpers::TagHelper.


For more on helpers see also:
http://api.rubyonrails.com/classes/ActionController/Helpers/ClassMethods.html


Edit: This page originally hada section on partials, however, partials have changed a lot in Rails 2.2.2, and most of it was no longer true. See this page:
http://strugglingwithruby.blogspot.com/2009/02/moving-to-rails-222.html


Struggling with Ruby: Contents Page

Thursday, 9 October 2008

Deploying a Ruby on Rails Application

I am in the process of developing a laboratory sample logging application, and before going to far down the road wanted to check it would work in the production environment. For development I am using WEBrick, MySQL and Ruby (1.8.6) with NetBeans (6.1), but the server has PostgreSQL, with Tomcat (which requires JRuby), so there are plenty of potential problems moving from one to the other. If you do not have Ruby installed, you still need to use "jruby " before rake.

Moving to JRuby and PostgreSQL
NOTE: I had a problem moving to JRuby, as I had Ruby installed as well, and I had not realised that "gem install " will install a gem to Ruby, rather than JRuby, and so I had a rather confusing time with gems installed in the wrong place. To install a gem specifcally on JRuby use "jruby -S gem install ", and to use rake and warble, likewise prefix the command with "jruby -S " The -S swich tells jruby to use its own version of the binary. See more here:
http://wiki.jruby.org/wiki/Getting_Started#How_do_I_run_rake.2C_gem.2C_etc

My first step was to install JRuby 1.1.4 on the server (JRuby already includes Gems), and then Rails (with: "jruby -S gem install rails -y --no-ri"). All very easy. Then I had to create a database server in PostgreSQL, which I did through the command line:
createdb mydb

I created a new project as normal in Rails, and modified the database.yml file to this:

development:
adapter: jdbcpostgresql
encoding: unicode
database: mydb_development
username: postgres
password:
host: localhost
port: 5432

test:
adapter: jdbcpostgresql
encoding: unicode
database: mydb_test
host: localhost
username: postgres
password:

production:
adapter: jdbcpostgresql
encoding: unicode
database: mydb_production
host: localhost
username: postgres
password:


I chose to use JDBC, if you chose not to, just delete "jdbc" from the above.

Then I could use rake to create the databases ("jruby -S rake db:create:all"). Next I copied across the contains of the app and the db/migrate folders from my PC to the server, along with routes.rb (you might want other files from config too) and the stylesheets (I did not copy the tests; there seemed no point). I migrated my databases ("jruby -S rake db:migrate").

Then I installed the gem to handle database connections, either one, depending on whether you want to use JDBC or not:
jruby -S gem install postgres-pr
jruby -S gem install activerecord-jdbcpostgresql-adapter


At this point I could fire up the server, and check everything was okay (with "jruby script/server"), or use the interactive environment (with "jruby script/console").

So I had transferred to a different computer (though it could as well been the same computer, if I had been willing to install PostgreSQL on it) with a different database and a different Ruby; there were several issues that I have glossed over, but once you know what to do, pretty straightforward. No actual deployment yet though.

Moving to the Production Environment
To ensure the production database is used, in config/environment.rb uncomment this line:
ENV['RAILS_ENV'] = 'production'

To create the database tables in the production database:
jruby -S rake environment RAILS_ENV=production db:migrate

Warbler
I used Warbler to create a .war file (Warbler replaces GoldSpike). More information from here:
http://caldersphere.rubyforge.org/warbler/http://wiki.jruby.org/wiki/Warbler

Warbler is installed with:
jruby -S gem install warbler

By default, Warbler will package Rails, but no other gems. To change that (so the database connections are including), first create a config file using warbler ("warble config"). This creates a new file in your application config/warbler.rb. Edit the file to include the gems you require, or insert the following code to include all gems:

# From http://wiki.jruby.org/wiki/Warbler
# Include all gems which are used by the web application
require "#{RAILS_ROOT}/config/boot"
BUILD_GEMS = %w(warbler rake rcov)
for gem in Gem.loaded_specs.values
next if BUILD_GEMS.include?(gem.name)
config.gems[gem.name] = gem.version.version
end

Some other configuration setting can be found in tmp/war/WEB-INF/web.xml

Now create the .war file with:
warble
This should give the following output (if it has an mk command, then it is using Ruby rather than JRuby, I think):
jar cf mydb.war -C tmp/war .

Note that warbler requires access to a Java JDK, so you need that in your path (PATH=C:\Program Files\Java\jdk1.6.0_07\bin;C:\jruby-1.1.4\bin;%path%).

Moving to Tomcat
The .war file can then be dragged to the Tomcat webapps directory. You may need to restart Tomcat, but not necessarily. Tomcat will decompress the .war file and the web application can now be accessed with:
http://<servername>:8080/<app>

8080 is the default port for Tomcat. is the project name, which is the folder name for your application.

To replace an old version in Tomcat, you need to stop Tomcat, delete the directory with your project name (which Tomcat created by decompressing your old .war file), copy across your new .war, and restart Tomcat.

Overall it probably took me two to three times longer to deploy the application than it did to build the first draft of it, however, now I know what to do, it will be much, much quicker next time!

Struggling with Ruby: Contents Page

Friday, 3 October 2008

file_column

file_column is a simple plugin that lets users upload images, which can then be displayed. There are alternatives out there; this was the first I found, but does the job so well I looked no further.

Download from here:
http://www.kanthak.net/opensource/file_column/

Install by dumping the files in vendor\plugins inside your project.

file_column does not work properly with more recent Rails. Modify file_column_help.rb
url = ""
url << request.relative_url_root.to_s << "/"
url << object.send("#{method}_options")[:base_url] << "/"


To
url = "/"
url << object.send("#{method}_options")[:base_url] << "/"


I think it allows for sub-domains and stuff like that, so is unnecessary in most cases. It fails as request (or @request) is nil. Why should that be?

Example

For a table called empire, with a column called image:

In the model, use something like this:
file_column :image, :magick => { :geometry => "200x100>" }


The magic bit uses RMagick to limit the size of the image.

In the new view, use something like this:
<% form_for(@empire, :html=> {:multipart=>true}) do f %>
<%= file_column_field "empire", "image" %>


In the show view, use something like this:
<%= unless @empire.image.nil?
image_tag url_for_file_column("empire", "image"), :align=>"right"
end %>


This will only display an image if one is entered in the database (though the file might still be missing). Note the align right option; this is not how it is documented; it should be done through a hash called option, but that did not work for me.

No changes in the controller.

Update: For Rails 2.2.2
Using this in Rails 2.2.2, I also had to make a change in file_column.rb, around line 619:
my_options = FileColumn::init_options(options,
Inflector.underscore(self.name).to_s,
attr.to_s)

Becomes:
my_options = FileColumn::init_options(options,
ActiveSupport::Inflector.underscore(self.name).to_s,
attr.to_s)


Update: Functional Testing
I found this quite a problem, with little guidance anywhere on the web as to what I should be doing. However, this is what I got working. The application is for a record of cylinder batches, each batch requiring a scanned certificate in PDF format.
test "should update cylinder_batch with file" do
login_as 'manager', @request
cb = CylinderBatch.find(:first)
filename = 'LittleBookOfRuby.pdf'
test_file = File.new("#{RAILS_ROOT}/test/fixtures/#{filename}")

put :update, :id => cb.id, :cylinder_batch => { :certificate => test_file}
assert File.exists? "#{RAILS_ROOT}/test/tmp/file_column/cylinder_batch/certificate/#{cb.id}/#{filename}"
assert_redirected_to cylinder_batch_path(assigns(:cylinder_batch))
cb2 = CylinderBatch.find cb.id
assert_equal "#{RAILS_ROOT}/test/tmp/file_column/cylinder_batch/certificate/#{cb.id}/#{filename}", cb2.certificate

get :delete_certificate, :id => cb.id
assert !File.exists?("#{RAILS_ROOT}/test/tmp/file_column/cylinder_batch/certificate/#{cb.id}/#{filename}")
assert_redirected_to cylinder_batch_path(assigns(:cylinder_batch))
cb2 = CylinderBatch.find cb.id
assert_nil cb2.certificate
end

The method is broken into three parts, the first setting up a few things, the second testing the update method with a new file, and the third part testing the delete_certificate method for removing a file. Usually I would only do one controller method per test method, but in this case one method sets up the other, while the other cleans up after the first, so this was more convenient.

I put a test file in the fixtures folders. This should be of the same type as you expect in your application. I tried using a YAML file, and while file_column would save the file, it did not update the table; I think file_column rejects file types it does not know. Also as a general point, be aware that file_column does not like anything besides letters, digits, underscores and hyphens in filenames.

In the parameters for the update method, I simply map the file to the appropriate column name. The file object contains the filename as well as the contents, and file_column can sort it all out. I then check that the file exists in the appropriate directory, the database has been updated correctly and the user redirected.

In the last section, after calling the delete_certificate method in the controller, I just check the file has gone, the database entry is nil and the redirect again. Simple. When you know how.

Struggling with Ruby: Contents Page

Saturday, 13 September 2008

Ruby GUI

I have been looking at how easy it would be to put a GUI front-end on Ruby. Ruby has no native support for a GUI (one place Java and C# really win over Ruby), and while Rails has become the standard for web aplications, there are numerous options for a GUI plug-in.

I have had a look at Tk, Fox and Swing (for JRuby). Recently, however, I found Ruby Shoes. Shoes is a very simple GUI, but that is part of its appeal. I doubt it has the comprehensive range of widgets that Tk does, for instance, but it can cope with JPEGs, which Tk cannot (as far as I could find). Also Shoes is dead easy to use:. Download. Install. Start the Shoes application, point it at your ruby file (or one of the samples included). Your application is runnning.

Unlike most other GUI toolkits, Shoes is not just a Ruby front end to an existing kit, which seems to make it feel more Ruby-like.

However, the big problem with Shoes is that there is no menu support incorporated. See this forum thread for a discussion on that:
http://www.ruby-forum.com/topic/165398

Find Shoes here:
http://code.whytheluckystiff.net/shoes/

My Quick Guide to Shoes

Everything in Shoes goes inside a Shoes.app block (usually, anyway). The simplest Shoes application is therefore

Shoes.app do
end


You can put in some options at this point - in a hash of course.

Shoes.app :title => "A Great Application", :width => 400, :height => 600, :resizable => false do

As usual, your require statements go at the very top of the file. Inside the Shoes.app block you can place the code that determines the GUI format and your methods.


Widgets are laid out inside either of two layouts, stacks for vertical stacking, and flows for horizonal flowing widgets. They can be nested for complex interfaces.

flow do
end

stack :margin_left => 5, :margin_right => 10, :width => 1.0, :height => 200, :scroll => true do
end


Note that the width in the second example is 1.0, i.e., 100%. You can assign the layout to a class variable, and then manipulate it later in a method for dynamic interfaces.

@gui_completed = stack
@gui_completed.clear


Flow and stack are not analogous. In the flow layouts, components fill a line, then go on to the next line, while in a stack, it is strickly one above the other. Shoes does not like horizontally scrollbars (neither do I). Sometimes components will stretch to fill the available room, and if they do that for a flow, they end up on the next line, and it looks more like a stack. The solution is to specify widths.

You can also use absolute positioning with :left and :top.

The background method set the background. If this is inside a layout, the background for the layout is set. The border method works the same

background white
background "back.jpg", :height => 40 # Set the top 40 pixels tan
border blue, :strokewidth => 5
background "#000".."#FFF", :curve => 15 # Gradient filled, black at the top
background "#000".."#FFF", :curve => 15, :angle => 90 # Gradient rotated 90 degrees anti-clockwise


You can use :width, :height, :right, :bottom to put different backgrounds along a certain side or corner. The :curve option should give your panel rounded corners, but not if you specify a border (note that the KNS manual says :radius; this is out of date).

You can manipulate your panels.

@my_stack.clear
@my_stack.clear { add_new_stuff }
@my_stack.append { add_new_stuff }
@my_stack.prepend { add_new_stuff }
@my_stack.before existing_component { add_new_stuff }
@my_stack.after existing_component { add_new_stuff }
@my_stack.remove existing_component
@my_stack.hide
@my_stack.show


Text can be done with: banner, title, subtitle, tagline, caption, para, inscription (in descending order of size).

title 'Here is my Application'
para 'some simple text'
caption "A caption, in red", :margin => 8
para strong('Some text in bold (like caption)')
para "Some fancy text", :stroke => red, :fill => yellow, :font => "Monospace 12px"


As well as accepting a string, these methods will also take arrays. You can use that to put in formatting within a line. In the second example, a link is created (looking as on a web page); click on it and the do_stuff method is invoked.

para ['Some text with ', strong('this'), ' in bold']
capture 'Please click ', link('here') { do_stuff }, '.' # Square brackets are optional


Options include strong, em, code, del, ins, link, span, sub, sup. It does not seem to cope well with characters outside the standard ASCII set. You can dynamically change content with the replace method.

You can change the style of a link (for the whole application).
style(Link, :underline => false, :stroke => 'red')

Text boxes can be done with edit_line or edit_box. The second example has some default text. The block gets invoked each time the text box is used, so @note will always have the text currently in the text box. How easy is that?

@text1 = edit_line :margin_left => 10, :width => 180
@text2 = edit_box "Default text", :width => 1.0, :height => 200, :margin_bottom => 20 do
@note = @text2.text
end


Buttons are easy too. When the button is pressed, the code in the block is invoked.

button("Add", :margin_left => 5) { add_todo(@add.text); @add.text = '' }
button "Swap" do
swap
end


There are some built in functions for dialog boxes:

ask("What is your name?")
confirm("Would you like to proceed?")
ask_open_file
ask_save_file
ask_open_folder
ask_save_folder
ask_color("Pick a Color")


Most components can be moved and resized

@comp.move(x, y)
@comp.size(w, h)


You can capture mouse movement like this:

motion do x, y
@o.move width - x, height - y
end


click do button, x, y
# button is 1for left button, 2 for right
end


Images are very easy, just use image, with the path to your image (plus style optins as required). Images can be changed on the fly by setting the path attribute (but why not have a replace method like there is for text elements?).

@little_image = image 'picture.jpg', :width => 50, :height => 50
@little_image.path = 'alternative.jpg'


You can even access the clipboard:

self.clipboard = ""

Shoes supports dropdown lists (list_box), checkboxes (check) and radio buttons (radio). see the manual included with the download for details. The big omission is menus, as mentioned earlier, but hopefully this will be rectified by the end of the year.

See also:
http://hackety.org/2008/06/12/martinDemellosGooeyChallenge.html
http://www.infoq.com/news/2007/09/ruby-shoes

Struggling with Ruby: Contents Page