Saturday, 7 November 2009

Ruby Arrays

An array is a group of values in a certain order. You can mix-and-match what you put in the array (it is all objects with Ruby), including hashes and other arrays.
a = ['one', 2, 3.0]

A quick way to create an array of strings (if each string is a single word) is like this (you can use any matching brackets, or indeed more punctuation):
a = %w(one two three)
# => ["One", "Two", "Three"]

To add an element to an existing array do this:
array << "new element"

You can join two arrays using the addition operator.
b = [4, 16]
c = a + b
# => ["One", "Two", "Three", 4, 16]

Use include? to determine if the given object is in the array. To access an array member use [], or at or fetch. The [] and at methods return nil if the index is out of range, while fetch throws an exception, or a default value of given. A negative index counts back from the end.

delete_if
Ruby has some very neat tricks with arrays. Want to delete all the elements of an array of hashes that have a body that is nil?
array.delete_if { |x| x.body.nil? }

The delete_if removes from the array any elements that evaulate to true in the block. Note that the delete_if method seems to ignore the convention of naming methods that affect the object itself with an exclamation mark.

join
The join method concatenates each member of an array into a long string. The supplied parameter is used to separate each item. The * operator does the same.
a = %w(one two three four)
# => ["one", "two", "three", "four"]
a * ', '
# => "one, two, three, four"


map, select and reject
The map method (aka collect) constructs a new array by processing each element in the array as per the block, while select returns a new array containing only those elements where the block evaluates to true. The reject method gives an array for the elements where the block is not true.
people = [
{:name => 'Fred', :age => 19},
{:name => 'Boris', :age => 23},
{:name => 'Mary', :age => 27},
]

p people.map {|e| e[:name]}
# => ["Fred", "Boris", "Mary"]

p people.select {|e| e[:age] <> [{:name=>"Fred", :age=>19}]

p people.reject {|e| e[:age] <> [{:name=>"Boris", :age=>23},
{:name=>"Mary", :age=>27}]


sort
The sort method will, as the name suggests, sort the array, using the <=> relationship. Alternatively you can supply block to have it sorted by a custom comparison.
people.sort { |a, b| a[:age] <=> b[:age] }
# youngest to oldest

Note that your comparison must return -1, 0, or +1. In this example, I therefore could not use <.

If you use the Enumerable mixin discussed later you also have a sort_by method. This is not as fast to run, but can be quicker to code. In this method, the block determines a value for the element, for ranking purposes. Here is the previous example re-written.
people.sort_by { |a| a[:age] }


pop and push, shift and unshift
You can use push and pop to add or remove the last element, and unshift and shift to add and remove from the start.
a = %w(one two three)
# => ["one", "two", "three"]
a.push 'four'
# => ["one", "two", "three", "four"]
a.pop
# => "four"
a
# => ["one", "two", "three"]
a.unshift 'zero'
# => ["zero", "one", "two", "three"]
a.shift
# => "zero"
a
# => ["one", "two", "three"]


Extending Array, part 1
You can, of course, add your own methods to Array. Here are some examples.
class Array

# Shuffle an array
# from http://snippets.dzone.com/posts/show/2994
def shuffle
sort_by { rand }
end
def shuffle!
self.replace shuffle
end


# Randomly pick one element of the array.
def pick
fetch rand(length)
end


# Returns a total over each element in the array
# where the value for an element is determined
# by the given block. This example will look
# through an array of hashes
# and return the total of the square of values
# with the key :value
# ary.total { |e| e[:value] * e[:value] }
def total &prc
val = 0
each do |e|
val += prc.call(e)
end
val
end


# Returns an element that best fits the criteria
# given by the block. This example will look
# through an array of hashes and return the element
# with the highest value with the key :value
# ary.find_best { |x, y| x[:value] < y[:value] }
def find_best &prc
best = fetch(0)
each { |e| best = e if yield(best, e) }
best
end


# Returns the elements of the given array as
# string with each element listed
# in the form "one, two and three".
def list
join(', ').reverse.sub(' ,', ' dna ').reverse
end
end


Extending Array, part 2
Another way to extend Array is to "mixin" the Enumerable module.
class Array
include Enumerable
end

This has several interesting methods (and also some that are already in Array). Use any? and all? to determine if at least one element evalauates to true on the block, or all of them do.
a = %w(one two three)
a.any? {|e| e.length == 5}
# => true
p a.all? {|e| e.length == 5}
# => false

You can find an element in an array using find (aka detect). This method returns the first element for which the block evaulates to true. The method takes an optional parameter, this is returned if no element is found that fits.
people.find { |e| e[:age] == 23 }

The inject method combines each element of the array.
total_age = people.inject(0) { |memo, e| memo += e[:age] }
# => 69

The supplied parameter is the initial value (in this example, zero). This is technically optional, but going to be necessary in most cases (including this example) for the calculation to work in the first iteration. The memo is the ongoing value, so the increment is added to this.



Ref:
http://www.ruby-doc.org/core/classes/Array.html
http://ruby-doc.org/core/classes/Enumerable.html

Struggling with Ruby: Contents Page

Thursday, 22 October 2009

Duck-typing

Let us say I want to be able to output a variety of objects (say strings, floats and dates) on a web page. With Java I would create a new class with over-loaded methods, like this (warning: my Java is getting rusty, and this is untested):
class Formatter {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yy");

public static String show(String s) { return s; }
public static String show(Date d) { return sdf.format(d); }
public static String show(double x) { return x < 0.05 ? '<0.1' : "" + (Math.round(x * 10) / 10.0); }
public static String show(Object o) { return o.toString(); }
}

To invoke, I would use this:
Formatter.show(myObject);

Java will select the method based on the class I send. Note that there is a method for object to catch anything unexpected.

In Ruby, I would approach this quite differently. There is no need for a new class, just modify the existing classes. This is not possible in Java; I could extend Date, but I would have to ensure that every date I sent was of my date class. String and float cannot be extended at all.
class String
def show
to_s
end
end

# Remember the require 'date.rb'
class DateTime
DATE_FORMAT = '%d/%b/%y'

def show
strftime(DATE_FORMAT)
end
end

class Float
def show
self < 0.05 ? '<0.1' : (self * 10).round / 10.0
end
end

class Object
def show
to_s
end
end

That is more verbose, but the result is much neater, much more object-orientated, as now the method can be invoked like this:
my_object.show

I have a library of useful Java methods. It is a collection of static methods that do various operations on arrays and strings. In Ruby, I am building up a library that changes how arrays and string behave. Just occasionally, that is not the best way - again because of duck-typing, as it happens. Say I have a method to format dates consistently. All it does is invoke strftime with a certain format string. I could define my method as part of the DateTime class, however I would then be unable to use it with Time objects. In this case, I am better adding the method to the Object class, then DateTime, Date and Time objects would all be able to use it, as they all have strftime methods (and duck-typing allows this to work).

Wednesday, 21 October 2009

The Case Statement and Relationship Operator

Ruby supports a case statement, in which the value of something is matched against a set of options
case value
when 1, 2, 5
do_this
when 3
do_that
else
do_the_other
end

In this example the first when will catch three different values. Note that unlike the C family of languages, there is no break statement used. Options cannot fall though to the next one.

You do not need to give a parameter to the case statement, as seen here.
case
when @t == 7
p 't is 7'
when @s == :this
p 's is :this'
else
p 'none of the above'
end

In this form, the case is like an if/elsif chain.
if @t == 7
p 't is 7'
elsif @s == :this
p 's is :this'
else
p 'none of the above'
end

So why use case? Well, case returns a value, so instead we could do this:
s = case
when @t == 7
't is 7'
when @s == :this
's is :this'
else
'none of the above'
end


The Relationship Operator

The case statement uses the relationship operator, === (aka triple equals or trequals operator) when comparing the value to each when. The relationship operator is really a method, and in Object the relationship operator is defined to do the same as the equals operator. However, the important point here is that it can be overridden as required. Patterns override it to check for a match, and the Range class overrides it to check if the value is within the range. That allows you to do things like this:
mark = 56

grade = case mark
when 1..25
'Fail'
when 26..50
'C'
when 51..75
'B'
when 76..100
'A'
else
'Out of range!'
end

Here grade is set to 'B' because (51..75) === 56 evaulates to true. Note that this is calling the === method on 51..75. Write it the other way around, 56 === (51..75), and the === method of 56 is invoked, and the expression evaluates to false.

See more here:
http://www.pmamediagroup.com/2009/07/dont-call-it-case-equality/


Struggling with Ruby: Contents Page

Tuesday, 22 September 2009

Gotchas for Models

I have hit a few issues using Rails, some very frustrating. Here are a selection that relate to the models.

Constructors with Arguments
The first is how to handle a constructor that will take a parameter. In Ruby, when you call new for a class, the object is created, then the initialize method is invoked. If you want to do that for our model, then you need to invoke the initialize method in the superclass to ensure everything is set up, and that is done through the super keyword.

The problem is that super takes with it all the parameters that were sent to the method. If your initialize takes one parameter, that parameter gets sent to the initialize in ActiveRecord::Base, which throws an exception, because it does not expect any. The solution is to use brackets with super; super().

Column Values
The second gotcha is accessing column values within the model. When you are not in the model, it is very simple:
@post.title = 'My new title'
s = @post.body

Ruby and Rails work together to make this seem as though you are accessing a variable in a class. However, the reality is that you are invoking methods, and this runs into problems when you do it from inside the class. You might imagine that this would work:
class Post < ActiveRecord::Base
def initialize params = {}
body = params[:body]
s = body
title = "Post: #{s[0..100]}"
end
end

It does not. The s = body line is fine; I guess Ruby notes that no variable called body exists, so invokes the method_missing method, and everything is good. Not so with title = 'My new title'. Ruby assumes that this is an assignment to a new variable, rather than a method invocation (why does the interpretor not check in the method exists first? I can only assume this is a performance issue). This has lead to some obscure bugs where values are mysteriously failing to get assigned. The solution is to prefix with self., which gives Ruby the hint that this is a method call.
class Post < ActiveRecord::Base
def test
self.title = 'My new title'
s = body
end
end


Migrations
One of the most common reason my unit tests do not work is that I have done a migration on the development database, and forgotten the testing database (rake db:test:prepare).

Fixtures and Migrations
When you create a model, some default fixtures are also created for testing. If you subsequently modify the migration file, those fixtures might not work, and you will get an error like this when you run your test:
ActiveRecord::StatementInvalid: Mysql::Error: #42S22Unknown column


Reverting to Rails 2.2.2
Rails 2.3.2 adds a new helper file for new models, in units/test/helpers. If you then revert to 2.2.2, this will crash rake when you run your unit tests (but other tests will be okay).

Saving records
When you save a record (and it is not a new record), Rails will work out what has changed, and only update those fields that have actually changed. However, it is not that reliable at spotting a change. In this example, no change is made.
r = MyRecord.find 19
r.description.sub! 'this', 'that'
r.save

The problem is that Rails uses a flag on each attribute, and if you do not set the flag, the attribute is not updated. Assignment automatically sets the flag, so it is easy to over-look. The following will work fine:
r = MyRecord.find 19
r.description = r.description.sub 'this', 'that'
r.save

The alternative is to tell Rails explicitly that the attribute is being changed:
r = MyRecord.find 19
r.description_will_change
r.description.sub! 'this', 'that'
r.save
Refs:
http://api.rubyonrails.org/classes/ActiveRecord/Dirty.html
http://ryandaigle.com/articles/2008/3/31/what-s-new-in-edge-rails-dirty-objects

First may not be first
I have database that has been in use for some ten months ago. I wanted to retrieve the very first sample. Should be easy:
Sample.find(:first)

Apparently not. This was retrieving a record from only a couple of weeks ago, with an ID of 1269. And in my development database, when Itested this, it worked fine. To get the right sample, I had to specify what to order by (I have no idea what ordering brings record 1269 to the front).
Sample.find(:first, :order => "created_at ASC")



Erroneous commas in hash assignments
This one is not limited to models. If you are assigning values to a hash, an extra comma can wreak havoc! Usually extraneous punction is ignored (extra semi-colons, for example), or throws an error. Not in this case.
h = {}
h[:name] = 'Fred'
h[:desc] = 'Big'

p h.inspect
# => "{:name=>\"Fred\", :desc=>\"Big\"}"

h[:name] = 'Fred',
h[:desc] = 'Big'

p h.inspect
# => "{:name=>[\"Fred\", \"Big\"], :desc=>\"Big\"}"


Changes to a file not always noted
While you are developoing your system, chances are you will have your local web server running, and will be looking at how the web site behaves and looks as you make changes. Genberally, this is fine. However, some changes that you make are not going to change the web site, for example, changes to files in config. If you have the console open, and type reload! you might find that even changes to your model (I think this may be when connected to the production database, so it is going to be very rare, admittedly). You may need to shut down the console or web server, and restart it to get the changes to have an effect.


Struggling with Ruby: Contents Page

Tuesday, 16 June 2009

Displaying Usage Statistics on a Bar Graph

My last post was about creating images of line graphs in Rails. Now I am going to have a go at histograms (bar graphs), using HTML to draw the graph, rather than creating an image.

The Data
I recently wanted to see how frequently entries were added to a table in my database. Each entry has a created_at column, so it should be easy to break this down by week or by month. Or so I thought. It was not as easy as I had hoped. There is no simple Rails way to group records within a set time interval. Instead, I had to use some actual SQL. And as my development PC is running MySQL and my server has PostgreSQL, I had to use two different dialects of SQL. It is not pretty, but this is what worked for me. First, I set up a SQL string (I did this in the controller while trying it out, but moved it to config/initializers/constants.rb later; you need to restart the web server after changing constants.rb):
module Sql
# SQL to get usage stats by week

# Different SQL for different dialects
# Both will generate the number of days since the year dot
# when the records was created
# MySQL has year and dayofyear functions
MYSQL_YEAR = 'year(created_at)'
MYSQL_DAY = 'dayofyear(created_at)'
# PostgreSQL uses the extract function for both
POSTGRESQL_YEAR = 'extract(year from created_at)'
POSTGRESQL_DAY = 'extract(doy from created_at)'
# Select the correct SQL based on the name of the database adapter
SQL_YEAR = ActiveRecord::Base.configurations[RAILS_ENV]['adapter'].match(/mysql/) ?
MYSQL_YEAR : POSTGRESQL_YEAR
SQL_DAY = ActiveRecord::Base.configurations[RAILS_ENV]['adapter'].match(/mysql/) ?
MYSQL_DAY : POSTGRESQL_DAY

# The rest of the SQL is the same
# The second line calculates the week number for the record
SQL =
<<END
select count(sample_reference) as count,
floor((#{SQL_YEAR} * 365 + #{SQL_DAY}) / 7) as week,
#{SQL_YEAR} as year,
floor(#{SQL_DAY} / 7) as week_no
from samples
group by week
order by week desc
;
END

end

You do have to careful when invoking SQL directly that you guard against SQL injection attacks. Not a problem in this case as the SQL command is in no way altered by the end-user.

SQL does have functions for getting the week number, however, funny things happen just before the new year, with records created in week zero, but in the previous year, so I abandoned using these functions. Months would have easier, but I wanted weeks.

The Controller
After all that, the method in my controller is fairly simple. Some sites I looked at used establish_connection before connection. As I understand it, establish_connection tells Rails that you want to potentially make a connection to a database, allowing you to create actual connections as oftyen as you like in the future. Rails does that automatically for ActiveRecord, so there is no need in this situation for me to. The map method adds a new key-value pair to each hash in the array; this will be used for the labels (eg "2009 week 3").
def graph
ary = ActiveRecord::Base.connection.select_all(Sql::SQL)
@result = ary.map do |e|
e['label'] = "#{e['year']} week #{e['week_no']}"
e
end
end


A Generic Partial
I created a partial to display the results in the bar graph. This is a general thing that could be used in any number of web pages to display this sort of data. It uses HTML to extend a small image across a table, according to the data. Note that this uses the find_best method I described on the blocks page; this pulls out the entry with the highest count, against which all the others are scaled.
<table>
<%
max = data.find_best { |x, y| x['count'].to_i < y['count'].to_i }['count'].to_i
data.each do |element|
count = element['count'].to_i
size = "#{count * 800 / max}x20"
%>
<tr>
<td><%=h element['label'] %></td>
<td><%=h count %></td>
<td><%= image_tag 'bar.gif', :size => size %></td>
</tr>
<% end %>
</table>


The View
The partial is invoked like this:
<%= render :partial => 'bargraph',
:locals => { :data => @result } %>

In the locals hash, :data is mapped to the array that holds the data. Each entry in the array must have a 'count' value, used for the size of the bar, and another value, the key for which is 'label'.

Struggling with Ruby: Contents Page

Monday, 15 June 2009

Displaying Trends on a Graph Image

This builds on my last two posts on drawing images for Rails with Java.

My objective here was to display values from a series of records on a graph to show possible trends (specifically analytical results for samples submitted to a lab). There are two quite separate issue here involving firstly extracting data from the database table, and secondly creating an image of a line graph on-the-fly.

Getting the Data
Step one, then, is to grab the data. The easy way is to just use find all:
ary = Worksheet.find :all

However, that could involve a lot of data coming across from your database that you do not need. We are only interested in the contents of one column. Better to just extract out the data from that column.
ary = Worksheet.find :all, :select => 'result'

This gives us an array of Worksheet objects, but each object has only the specific column set to a value. We need to grab those values, and the map method gives an easy way to convert to an array of numbers. This can all go inside a class method in Worksheet (in this case the name of the column is in a variable, column_name, to keep it general).
def self.collect_data column_name
Worksheet.find(:all, :select => column_name).map do |e|
e.send(column_name)
end
end

Draw the Graph
Step two is drawing the graph (see also here for drawing with Java on Rails). I created a new Ruby class in its own file in the models directory, using class methods. I wanted the graph to be able to scale itself, but for the lower and upper bounds to be round numbers; the graph_limits methods handles that.

Here is the basis of the class. Java needs to be included, and for convenience I have imported all the Java classes I will be using. Then I set up some constants:
include Java

class Graph

java_import java.io.ByteArrayOutputStream
java_import java.awt.image.BufferedImage
java_import java.awt.Color
java_import java.awt.RenderingHints
java_import java.awt.geom.GeneralPath
java_import java.awt.geom.Line2D
java_import java.awt.Font
java_import java.awt.BasicStroke
java_import javax.imageio.ImageIO

WIDTH = 800
HEIGHT = 600
FOREGROUND = Color.new 0.2, 0.2, 0.2
BACKGROUND = Color.new 0.95, 0.95, 0.95
PLOT = Color.blue
FONT = Font.new("SansSerif", Font::PLAIN, 22)

end

I wrote three helper methods (all protected). Note that the second uses the find_best method I described on the blocks page;
  # Converts the data point to a vertical
# position on the image,
# scaled between min and max.
def self.calc y, min, max
19.0 * HEIGHT / 20 - (y - min) *
18 * HEIGHT / (max - min) / 20
end

# Determines the minimum and maximum
# values from the data, and rounds them
# down and up respectively to give lower
# and upper display limits.
def self.graph_limits data
# See my blocks page for find_best method
min = data.find_best { |x, y| x > y }
max = data.find_best { |x, y| x < y }
modifier = 10 **
-(Math.log10(max - min).floor)
min = (min * modifier).floor *
1.0 / modifier
max = (max * modifier).ceil *
1.0 / modifier
# Ruby allows a method to return
# multiple values
return min, max
end

# Draws a line and label for the y-axis
def self.graph_line g2, text, h
g2.drawString(text, 0, h)
# Note that Line2D uses inner
# classes, accessed via ::
g2.draw(Line2D::Double.new(WIDTH / 10,
h, 9 * WIDTH / 10, h));
end

Now that we have the foundations, we can draw the graph.
  def self.line_graph data
min, max = graph_limits data

# Set up image object
off_image = BufferedImage.new(WIDTH, HEIGHT,
BufferedImage::TYPE_INT_ARGB)
g2 = off_image.createGraphics()
g2.setRenderingHint(RenderingHints::KEY_ANTIALIASING,
RenderingHints::VALUE_ANTIALIAS_ON)

# Set up background
g2.setPaint(BACKGROUND)
g2.fillRect(0, 0, WIDTH - 1, HEIGHT - 1)
# Note: You get wierd results if you do not
# fill your background
g2.setPaint(FOREGROUND)
g2.drawRect(0, 0, WIDTH - 1, HEIGHT - 1)

# Set up the y-axis (no x-axis drawn)
g2.setFont(FONT)
graph_line g2, min.to_s[0..5], HEIGHT * 9 / 10
graph_line g2, max.to_s[0..5], HEIGHT / 10
graph_line g2, ((min + max) / 2).to_s[0..5],
HEIGHT / 2

# Convert data to image coordinates
x_points = Array.new(data.length) do |i|
(i * 8.0 * WIDTH / (data.length - 1) / 10) +
WIDTH / 10
end
y_points = Array.new(data.length) do |i|
calc(data[i], min, max)
end

# Generate a GeneralPath object from the
# coordinates
polygon = GeneralPath.new(
GeneralPath::WIND_EVEN_ODD,
data.length)
polygon.moveTo(x_points[0], y_points[0])
(1...x_points.length).each do |index|
polygon.lineTo(x_points[index],
y_points[index])
end

# Draw the GeneralPath object
g2.setPaint(PLOT)
g2.setStroke(BasicStroke.new(1.5))
g2.draw(polygon)

# Convert for web image
os = java.io.ByteArrayOutputStream.new
ImageIO.write(off_image, "gif", os)
String.from_java_bytes(os.toByteArray)
end
Ptting it Together
Step 3 is to put it all together. In my controller I put in a new method (this needs to be mentioned as a collection in your routes, as it has no specific record associated with it):
  def graph
respond_to do |format|
format.html
format.gif do
send_data Graph.line_graph(
Worksheet.collect_data('result')),
:type => "image/gif",
:disposition => "inline"
end
end
end

The important part is the bit starting with send_data. The collect_data method is invoked on Worksheet, returning an array of values from the "result" column of the database table. This is sent to the line_graph method of Graph, which generates the image.

Finally, we need a view. This must be called graph.html.erb, of course, and should include the following:
<%= image_tag "/Worksheet/graph/x.gif", :size => "800x600" %>

If this was an image for a specific record in your table, you would need the id of the record. As this image is for a collection of records, no id is applicable. Nevertheless, you need something there, so I have called in "x".

Struggling with Ruby: Contents Page

Tuesday, 9 June 2009

Creating Images For Rails With Java

Some time ago I had a look at RMagick to create images on-the-fly in a Rails application. The problem with RMagick is that it is not compatible with JRuby, and if you are going to be using TomCat as your webserver, you need to be using JRuby. However, JRuby does present a ready alternative through Java.

To look at this I created a new controller, images_controller, in an existing Rails project. In hindsight, images might have been a bad name, as there is a folder called public/images already. However, Rails was able to work out whether the image should come from public/images or via my images controller, so the issue is really only whether it is confusing to the developer.

The first thing you need to do is set up the new MIME types. There are various places you can do this, but the correct one, I believe, is in config/initializers/mime_types.rb. This is what I added:
Mime::Type.register "image/jpg", :jpg
Mime::Type.register "image/png", :png
Mime::Type.register "image/gif", :gif

Now let us create the controller. The first thing you need to do is include Java. Then you should import all the classes you will need. The second step is optional, but the alternative is writing out the full name of he Java classes every time you use them, which leads to longer and harder to read code. Note that unlike in Java you cannot use the * wildcard to import a whole bunch of classes in one line. Here is the start of my controller:
class ImagesController < ApplicationController
include Java

java_import java.io.ByteArrayOutputStream
java_import java.io.File

java_import java.awt.image.BufferedImage
java_import java.awt.Color
java_import java.awt.RenderingHints
java_import java.awt.geom.GeneralPath
java_import java.awt.Font

java_import javax.imageio.ImageIO
end

Now I need an action defined by a method, image. A clever feature of Rails is that you can respond differently depending on the file extention, which is what I do here. The respond_to block directs the action to the correct format (as long as the format is a known MIME type).

The HTML response is blank. Rails will do the default action, which is to return a page derived from the view images/image.html.erb. The other three responses follow the same format. Each invokes the send_data method, with the data (the image) as the first parameter, followed by a hash of options. The :type is simply the MIME type. The :disposition determines if the data is to be displayed ("inline"), or downloaded ("attachment"). For attachments, you can set a :filename parameter too, and there is also a :status parameter, which conveniently defaults to 200, success.

API for send_data:
http://api.rubyonrails.org/classes/ActionController/Streaming.html
def image
@name = params[:id]

respond_to do |format|
format.html
format.jpg do
send_data get_jpeg(@name), :type => "image/jpeg",
:disposition => "inline"
end
format.png do
send_data get_png(@name), :type => "image/png",
:disposition => "inline"
end
format.gif do
send_data get_gif(@name), :type => "image/gif",
:disposition => "inline"
end
end
end

So that just leaves the methods that generate the image data. These should all be protected, by the way.

This first method simply grabs a file from public/images and displays it. Once it has the file, it creates an output stream, and writes the image file to that stream in the specified format. It then converts from the stream to a Ruby string, via a Java byte array.
def get_jpeg filename
imagefile = File.new("#{RAILS_ROOT}/public/images/#{filename}.jpg")
os = ByteArrayOutputStream.new
ImageIO.write(ImageIO.read(imagefile), "jpeg", os)
String.from_java_bytes(os.toByteArray)
end

Converting to another format is trivial (though noticeably slower).
def get_png filename
imagefile = File.new("#{RAILS_ROOT}/public/images/#{filename}.jpg")
os = ByteArrayOutputStream.new
ImageIO.write(ImageIO.read(imagefile), "png", os)
String.from_java_bytes(os.toByteArray)
end

This version does an operation, converting the image to its negative.
def get_jpeg filename
imagefile = File.new("#{RAILS_ROOT}/public/images/#{filename}.jpg")
bi = ImageIO.read(imagefile)
big = bi.getGraphics

# Create a look-up table as an array
lut = Array.new(256) { |j| 256 - j }
# Convert to Java byte array
jlut = lut.to_java :byte
# Convert to Java byte look-up table
blut = java.awt.image.ByteLookupTable.new(0, jlut)
# Convert to Java operation look-up table
op = java.awt.image.LookupOp.new(blut, nil)
# Apply the operation
dest = op.filter(bi, nil)
# Draw the new image
big.drawImage(dest, 0, 0, nil);
# Create an output stream, and write the image to it
os = ByteArrayOutputStream.new
ImageIO.write(dest, "jpeg", os)
String.from_java_bytes(os.toByteArray)
end

The next method actually generates a GIF image on-the-fly, putting the filename in the middle of a simple image. The basic methodology is to create a blank BufferedImage object (rather than from a file), use that to create a Graphics2d object (just as before). The image is built by calling methods on the Graphics2d object, and finally the last three lines create the output stream and write the image to it (just as before).
WIDTH = 400
HEIGHT = 300

def get_gif filename
off_image = BufferedImage.new(WIDTH, HEIGHT,
BufferedImage::TYPE_INT_ARGB)

g2 = off_image.createGraphics()
g2.setRenderingHint(RenderingHints::KEY_ANTIALIASING,
RenderingHints::VALUE_ANTIALIAS_ON)

# Set up background
g2.setPaint(Color.lightGray)
g2.draw3DRect(0, 0, WIDTH - 1, HEIGHT - 1, true);
g2.draw3DRect(3, 3, WIDTH - 7, HEIGHT - 7, false);

x = 7;
y = 7;
x3_points = [x, WIDTH - 2 * x, x, WIDTH - 2 * x]
y3_points = [y, HEIGHT - 2 * y, HEIGHT - 2 * y, y]
filled_polygon = GeneralPath.new(GeneralPath::WIND_EVEN_ODD,
x3_points.length);
filled_polygon.moveTo(x3_points[0], y3_points[0])
(1...x3_points.length).each do |index|
filled_polygon.lineTo(x3_points[index], y3_points[index])
end
filled_polygon.closePath()
g2.setPaint(Color.red)
g2.fill(filled_polygon)
g2.setPaint(Color.black)
g2.draw(filled_polygon)
g2.setPaint(Color.yellow)
g2.setFont(Font.new("Helvetica", Font::PLAIN, 22))
g2.drawString(filename, WIDTH / 2, HEIGHT / 2)

os = java.io.ByteArrayOutputStream.new
ImageIO.write(off_image, "gif", os)
String.from_java_bytes(os.toByteArray)
end

Okay, so we can generate images; how do we get them on to a web browser? The easy way (to test the above work) is to invoke it though the address bar. Say I have an image saved in public/images called sheep.jpg, I can use the following:
http://localhost:3000/images/image/sheep.jpg
# => Gives a negative of the image
http://localhost:3000/images/image/sheep.png
# => Gives the PNG converted image
http://localhost:3000/images/image/sheep.gif
# => Gives the generated GIF with "sheep" written on it

What we really want to to have those images embedded in a web page. So here is a view, views/images/image.html.erb, that will do just that:
<h1>My <%= @name %> image</h1>

<%# This image has been generated on the fly and served via my controller %>
<%= image_tag "/images/image/#{@name}.gif", :size => '400x300' %>

<%# This image has been processed and served via my controller %>
<%= image_tag "/images/image/#{@name}.jpg", :size => '400x300' %>

<%# This image has been processed and served via my controller; uses HTML tags directly %>
<img alt="Sheep" height="300" src="/images/image/<%= @name %>.jpg" width="400" />

<%# This image has been processed and served via my controller as a PNG %>
<%= image_tag "/images/image/#{@name}.png", :size => '400x300' %>

<%# This image came directly from public/images %>
<%= image_tag "/images/#{@name}.jpg", :size => '400x300' %>

<%# This image has been processed and served via my controller, va a helper method %>
<%= images_image_tag @name, :jpg, :size => '400x300' %>

One of those methods uses a helper method, and that would be my prefered way. Here is that method:
def images_image_tag name, type, options = {}
image_tag "/images/image/#{@name}.#{type.to_s}", options
end

Struggling with Ruby: Contents Page