Friday, 22 August 2008

Dynamic Images on Rails

You can serve images through a dedicated method (or action) in your controller, but to use the format facility, you need to register the MIME type. A lot of places say to do that in environment.rb, but leave you stranded after that. What is missing is the require you need in there too:
require 'action_controller/mime_type'

However, I now believe they are wrong (or perhaps out-of-date), and the correct place to put them is in (to ensure they work in the interactive environment):
config/initializers/mime_types.rb
For example:
Mime::Type.register "image/png", :png
Mime::Type.register "image/gif", :gif

In your controller, you can now do this (for the usual post controller example):
# GET /posts/1
# GET /posts/1.xml
# GET /posts/1.gif
# GET /posts/1.png
def show
@post = Post.find(params[:id])
respond_to do format
format.html # show.html.erb
format.xml { render :xml => @post }
format.gif { render :text => @post.get_image_as_gif.to_blob,
:status => 200, :content_type => 'image/gif' }
format.png { render :text => @post.get_image_as_png.to_blob,
:status => 200, :content_type => 'image/png' }
end
end

This will respond to a request in four ways depending on the extension, serving up an image if it ends .gif or .png. The methods for creating the images go in Post.

To embed the image in the web page (note that pages can display slightly faster if you give the size and it is good practice to provide some alt text):

<%= image_tag formatted_post_path(@post, :gif),
:size => "250x250", :alt => "Image title" %>


Using RMagic
RMagick is a Ruby interface for ImageMagick, and seems to be the biggest image drawing package for Ruby.
http://www.imagemagick.org/RMagick/doc/

Unfortunately, there is no documentation with it about using it with Rails, and precious little elsewhere. There is an example program, axes.rb, so by way of illustration, this is how to convert it for use in Rails.

1. Move the require 'RMagick' to the top of the file, as normal.
2. Wrap the program in a method, with def get_image_as_png (or whatever) at the top, and end at the bottom.
3. Edit the bottom from this:
labels.draw(canvas)
#canvas.display
canvas.write("axes.gif")
exit

To this:
labels.draw(canvas)
canvas.format = 'png'
canvas
end

If there is any problem with your code, Rails just refuses to send the image, with no error message anywhere I could find. If there is no image getting through, try using the interactive environment; it will not display images, but it will show the error messages.

Note that images have to be converted to blobs before sending. I have choosen to do that in the controller's show method above. Also, you do have to inform RMagick of your file format before invoking to_blob (get a "no decode delegate for this image format" error otherwise).

Some useful pages:
http://jeremyweiland.com/archives/49
http://nubyonrails.com/articles/dynamic-graphics-with-rails-1-2
http://macdiggs.com/2007/03/08/rmagickrvg-outputting-an-inline-image/

Colour names:
http://www.imagemagick.org/www/color.html

Using RMagick with FileColumn
FileColumn is a plug-in that makes it very easy to allow users to upload images to your site, and then display them. It uses RMagick, but this seems to lead to a name clash. I found it was necessary to use "include Magick" inside any model that I was using RMagick, rather than specifying Magick::Draw, etc., as Rails complained that the latter was un undefined constant in FileColumn otherwise. Strangely this was not the case in the controller.

Struggling with Ruby: Contents Page

Additional: If you use JRuby, you will need to use Jva to create images, see here. That page also explores in more depth how to access images in your views, relevant to RMagick images as well as Java.

Monday, 18 August 2008

The Model Part 4 - Testing

Rails sets up the skeleton for unit testing when it creates a model. The skeleton is all set to run, though it does not actually test anything, with one trivial test case. However, there are a few steps of preparation:

Set up the "test" database
You need to do this whenever you make changes to the database (any time you do a migration basically). There is a rake task that will do this for you:
rake db:test:prepare

Note: Rake also supports db:test:clone and db:test:clone_structure; what db:test:prepare does is make a good guess as to which of those is best and does that.

Set up the fixtures
Rails generates a fixtures file for each model. A fixture is a file containing entries to populate the database before running each test. If you used the references type for any attributes in your model, you will need to have a belongs_to tag in your model, so that Rails will understand these to be my_model_id rather than my_model. You will get a SQL error otherwise complaining about an unknown column name.

Correct the require
If you are using NetBeans 6.1, you need to modify the require so Rails can find the test_helper file. require 'test_helper' should be require 'test/test_helper'. This has been corrected in NetBeans 6.5.

The default test should now work. In NetBeans you can right click in the file contents (i.e., the right pane), and choose test from the menu to just unit test a single file, or right click the project in the left pane and select test to run all unit tests.

Tests
Rails creates one test file for each model. I write one test method to test one use-case of a method, but I usually put in several assertions to check that the state is what I think it is before and after the method call.

Note that tests are run alphabetically. Also, be warned that if two tests have the same name, only one will be run, and you will receive no warning that one was not.

I find unit tests often involve significantly more code that the methods they test, and so most of the bugs are in the tests. However, once the bugs are gone, and the tests are passed, you do have a lot more confidence in your code. If you decide to rewrite the code, the tests should still work fine and are an excellent way to test your rewrite.

There is an issue with how comprehensive testing should be. If you have a method that manipulates a string, should you test if with nil, objects of a variety of other classes, string objects where the class has been modified? In Java or C# most of these are irrelevant.

Testing Exceptions
Use this:
e = assert_raise(RuntimeError) { my_code_that_raises }
assert_match(/Error message here/i, e.message)

From Jason Roelofs here:
http://groups.google.com/group/ruby-talk-google/browse_thread/thread/1553af54ab8beaf7

Testing Helpers
Not really models, but they can and should be tested the same. Helpers are modules, so you just need to include them. Here is how:
require 'test/test_helper'

class AppHelperTest < ActiveSupport::TestCase

include ApplicationHelper

def test_my_helper_method
end
end


Testing private and protected methods
t is just as important to test these methods, but how do you access them from outside the class? In Ruby it is actually very easy to access private methods using the send method. This will accept your method name (as a symbol), followed by the appropriate arguments. Something like this:
assert_equal 'My expected result',
my_model.send(:my_private_method, arg1, arg2)


Tips
If you want to test your model with the parameters from a complex form, you can get Rails to build the hash for you. Throw an exception at the start of the controller method that will handle the submitted form, then, in your web browser, submit the form. Rails will hit the exception, and show an error page, which will include the POST data as a Ruby hash. Copy-and-paste into your test. Rails creates a special hash in which symbols and strings are equivalent for keys; chances are you will need to convert the strings to symbols (assuming you are accessing values with symbols).

There is an excellent series of blog pages on unit testing by Kevin Skoglund that starts here:
http://www.nullislove.com/2007/11/14/testing-in-rails-part-1-unit-testing-in-ruby/
For easy reference, part ten lists the assertions available here:
http://www.nullislove.com/2008/02/20/testing-in-rails-part-10-assertions/

Struggling with Ruby: Contents Page

Wednesday, 13 August 2008

The Model Part 3 - Interactive Rails Environment

Interactive Rails Environment (IRE)
You can test the methods in your model in an Interactive Rails Environment (IRE), just like IRb. Navigate to your project's root directory, and type:
ruby script/console

In this environment you can access all your models, and the development database (the other databases can be accessed by appending "test" or "production" as a parameter to the above).

When you are in the Rails interactive environment, you can invoke reload! after modifying your code to get Rails to notice the changes. However, if you have an object on the go, it will lose its methods (at least, all your methods; methods inherited from, say, ActiveRecord::Base will still be there).

Here is an example session:
>> p = Post.find :first
=> #
>> p.test
test
=> nil
>> reload!
Reloading...
=> true
>> p.test
NoMethodError: undefined method `test' for #<post:0xafcd578>
from c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.1.0/lib/active_record/attribute_methods.rb:256:in `method_missing'
from (irb):4
>> p = Post.find :first
=> #<post>
>> p.test
test
=> nil

As can be seen above, the solution is to retrieve the object again after reload. The problem seems to be down to versions; the old object is an instance of the old version, which no longer exists.

See also:
http://www.spacevatican.org/2008/5/28/reload-me-reload-me-not
http://rpheath.com/posts/163-rails-tips-and-tricks-part-3

Struggling with Ruby: Contents Page

Monday, 11 August 2008

The View Part 1 - ERB and Links

Rails uses the Model-View-Controller architecture. I have already discussed the model (here) and the controller (here); today it is the turn of the view.

The render method usually (depending on the parameters, as discussed with controllers) opens up a file, parses the file through ERB, and sends the result back to the requesting web browser. There really is not much more to it than that. The only complications are HTML and control structures.

As the web browser is expecting an HTML formatted page, your ERB template must be in HTML. Generally this is split between two files - the layout and the view itself - with the layout having the header and footer (both in the HTML sense, and the generic stuff at the top and bottom of all your pages), and the view having the HTML specific to that view. You really do need a good knowledge of HTML to do anything beyond the basics, but if you can cope with Ruby, HTML is dead easy.

ERB
ERB (embedded Ruby) is a template system built into the Ruby standard library, and used by Rails. It takes a template string, and where it finds certain codes will perform Ruby processing, substituting the output of that with the codes in the template. Here is an example IRb session using ERB:

require 'ERB'
template = ERB.new 'My template is called <%= @name %>'
@name = 'Fred'
puts template.result(binding)


The first line points the interpreter to the ERB.rb file (as this is in the standard library, not the core library). The second line creates a new ERB object called template. The last line calls the results method on template. The binding parameter gives the context so the template uses the value of @name from here, rather than some other method.

ERb supports a number of tags. Your Ruby code should go between the tags.

<% %> Perform the Ruby code; no output
<%= %> Perform the Ruby code; output the result
<%=h %> Perform the Ruby code; output the HTML escaped result
<%# %> Comment (note that unlike an HTML comment this is not visible to a visitor looking at the HTML source)

A % at the begining of the line can also be used to indicate that the line is ruby code (escape with %%). This appears to not be used conventionally. Also, <%% or %%> are replaced with <% or %> respectively, presumably to make it easier to write pages about ERb.

Actually, <%=h some_ruby_code %> is a bit of a trick. It is actually a method call, for the h method. It could be written <%= h(some_ruby_code) %>; it is just the same.

Reference
http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/

ERB and Rails
Rails already includes ERB, so the require 'ERB' is not required.

The layout file, pehaps application.html.erb, must include a call to the yield method, like this:
<%= yield %>

The yield uses the view file, perhaps edit.html.erb, parsing it with ERB, and inserting the result into layout result.

All this processing is done in your controller's superclass (ActionController::Base), so the environment used (the binding) is that of the method in your controller. ERB can access any variables and methods that you can inside that method.

Links
ActionController::Base includes some methods to help with links. The link_to method will generate the HTML code for a simple link, as in this example:
<%= link_to 'New post', new_post_path %>


"New post" is the text that will appear in the link. The method new_post_path will generate the URL on the fly (and will take into account the current URL; if the web browser is at http://mydomain.com/, your page will require a different URL to http://mydomain.com/posts/index).

As well as link_to there are also button_to, link_to_if and link_to_unless methods.

API for link_to (with links to alternatives)
http://www.railsapi.org:8100/actionview-helpers-urlhelper-link_to