RailsConf registration opens today. Be ready!

May 29-June 1, 2008 in Portland, Oregon,

UPDATE: registration is now open.

UPDATE2: I’ll be presenting with Tony a 3 hour tutorial on Powering AIR Applications with Rails. See you all there!

Posted by Daniel Wanja Tue, 29 Jan 2008 14:10:53 GMT


Rails 2.0

Rails 2.0 is out! Thanks guys for all the hard work, this release is just impressive.

Posted by Daniel Wanja Fri, 07 Dec 2007 16:07:46 GMT


Acts_as_nested_set ActiveRecord rendered with mx:Tree in Flex.

ActiveRecord: app/models/category.rb

app/models/category.rb
class Category < ActiveRecord::Base acts_as_nested_set end

Controller: app/controllers/categories_controller.rb

app/controllers/categories_controller.rb
class CategoriesController < ApplicationController def index Category.result_to_attributes_xml(Category.root.full_set) end end

Flex Application: ActsAsNestedSet.mxml

ActsAsNestedSet.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" applicationComplete="categories.send()"> <mx:HTTPService id="categories" url="http://localhost:3000/categories" resultFormat="e4x" /> <mx:Tree dataProvider="{categories.lastResult}" labelField="@name" width="100%" height="100%" /> </mx:Application>

Result:
20071123_categories.jpg

XML generated by Category.result_to_attributes_xml(Category.root.full_set):

XML generated by Category.result_to_attributes_xml(Category.root.full_set)
<node name="Main Category" id="15" description=""> <node name="Cameras &amp; Photo" id="16" description=""> <node name="Bags" id="17" description=""/> <node name="Accessories" id="18" description=""/> <node name="Analog Cameras" id="19" description=""/> <node name="Digital Cameras" id="20" description=""/> </node> <node name="Cell Phones" id="21" description=""> <node name="Accessories" id="22" description=""/> <node name="Phones" id="23" description=""/> <node name="Prepaid Cards" id="24" description=""/> </node> <node name="Dvds" id="25" description=""> <node name="Blueray" id="26" description=""/> <node name="HD DVD" id="27" description=""/> <node name="DVD" id="28" description=""/> </node> </node>

I used the http://wiki.rubyonrails.org/rails/pages/BetterNestedSet plugin.

Too cool!

UPDATE: The BetterNestedSet plugin doesn’t work out of the box with Rails 2.0 RC1. Thanks Joel for that info. Read more in the comment of this blog entry.

UPDATE2: Thanks Fabien, BetterNestedSet now works with Rails 2.0!

Posted by Daniel Wanja Sat, 24 Nov 2007 03:30:13 GMT


RailsLogVisualizer0.7 for AIR beta 2.

I recompiled the RailsLogVisualizer for AIR beta. I added drag&drop of log files to bypass an AIR bug on Leopard. File.browseForOpen doesn't trigger the Event.SELECT when the file is selected. I haven't yet tried this version of the application on older versions of OSX or on Windows. Let me know how it works. Also the feedback when loading large log files could be improved, as the application seems to freeze once the progress bar is complete. Just be a little patient as the AVM is working hard for you to compute all these number.

Install RailsLogVisualizer0.7.air

Install Manually

1) Instal Adobe AIR beta 2. (See release notes if previous version was installed)
Download AIR for OSX Download AIR for Windows
Learn more on AIR

2) Download and install http://myspyder.net/tools/railslogvisualizer/RailsLogVisualizer0.7.air

For time.onrails.org the log file is currently 98Mb and is loaded and process in less than a minute. Here are the loading details:

Loaded 98571986bytes in 28093 milliseconds.
Parsing file. Please Wait this may take some time....
Parsing. Split 1639453entries in 1447 milliseconds.
found:220767 in 1925 milliseconds.
Aggregating data.
	aggregated:220767 in 13426 milliseconds.
	Aggregated:89135
	aggregated String :4440464(bytes) in 2790 milliseconds.
Then you can navigation through time and see how many request where processed and drill down in specific action and specific methods. For example, here we can quickly see that for October 99 people signed up, 869 did login, 22 forgot their password.
20071106_railslogvisualizer.jpg
Enjoy, Daniel.

Posted by Daniel Wanja Thu, 08 Nov 2007 04:12:00 GMT


Sweet way to write Flex Unit tests for Rails

Using ActiveResources from Flex? Using FlexUnit? Here is a nice way to write your tests.

Example Test Case

package tests { import flexunit.framework.*; import mx.rpc.AsyncToken; import mx.rpc.events.ResultEvent; import resources.Rafflespackage tests
{
import flexunit.framework.*;
import mx.rpc.AsyncToken;
import mx.rpc.events.ResultEvent;
import resources.Raffles;

public class TestRaffles extends BaseTestCase { private var raffles:Raffles; public function TestRaffles(name : String = null) { super(name); fixtures(["raffles"]); raffles = new Raffles(); } public function testRemoteFindRaffle():void { assertRemote(raffles.show(1)); } public function assertRemote_testRemoteFindRaffle(data:Object):void { Assert.assertTrue("Raffle show successfully called", data is ResultEvent); assertEquals("MyString", data.result.name); } }

}

Note this code is not yet a plugin and is using code you can find here: http://code.google.com/p/flexonrails/source. I was starting to use it on multiple projects so I thought it was to time find a home for it. Also it is using the org.onrails.rails.ActiveResourceClient Flex class. I would recommend that you use Alex MacCaw’s ActvieResrouce for Actionscript. I still need to talk with Alex and integrate this fixture loading code with his code.

The BaseTestCase Flex class is an extended TestCase that provides support for fixtures. Now in your constructor you can define which fixtures you want to reload between each test. Only tests methods starting with “testRemote” will trigger refreshing the fixtures. As you know, when using AMF or HttpService remote invocations are asynchronous and you cannot test the result of a remote call in the same method than where the call is made from. That’s why I added the assertRemote method which takes an AsyncToken as parameters. This will automatically invoke a method whos name starts with assertRemote_ followed by the test method name. This simplifies greatly writing asynchronous tests. FlexUnit provides the addAsync method, we just add the convenience assertRemote function to setup all the callbacks.

To make this work for you Flex with Rails project. You need to fixtures_controller.rb to your controllers and setup the following routes:

  if RAILS_ENV == "test"
    map.resources :fixtures, :new => { :test_results => :post }
    map.crossdomain '/crossdomain.xml', :controller => 'fixtures', :action => 'crossdomain'
  end

You need to extend your Flex TestCase from tests.BaseTestCase.

Enjoy,
Daniel.

Posted by Daniel Wanja Mon, 05 Nov 2007 15:11:56 GMT


Installing RMagick on Leopard (without MacPorts or Fink)

I’ve recently upgraded to OS X 10.5 (Leopard), and all-in-all, I’m pleased with the experience. My biggest issue has been the default stacks behavior—the icon changes to the last thing added to the stack, making visual identification unnecessarily cumbersome. I worked around this annoyance (as outlined here) by changing the sort to name rather than date added, and adding a dummy folder named “_1” that will sort to the top. For extra bonus points, I customized the icon of the dummy folder. For some yet unknown reason, the most recently downloaded item still peeks through from time to time, but it’s much better than before.

Maybe it’s my Windows history showing through, but I went with the “clean-sweep” erase and install method. For a non-developer, I’d probably recommend the upgrade (and in fact I used that method for my Father-in-law’s MacBook), but I had lots of custom bits scattered about my machine, and didn’t want to be chasing any incompatibility gremlins.

So now, to get my development environment set up on the new machine… Leopard includes a fairly complete Rails stack out of the box, with a non-broken Ruby, readline support, and most of the commonly used gems. Read more here
.

MySQL was not included, but the latest installer (mysql-5.0.45-osx10.4-i686.dmg) for 10.4 from dev.mysql.com downloads worked (mostly) fine. The Server and the StartupItem install and operate correctly. The PrefPane installs, but does not appear to actually do … anything. I’ll have to work on that, but I can live without it for now. After a bit of manual hacking on my database dump file from Tiger (where I was running a 5.1.x beta of MySQL), all my databases are back in place.

One last piece that I needed for my Rails apps—RMagick. I know it’s possible to install RMagick and its dependencies, um, “autoRMagickally” via a package management system like MacPorts or Fink, but I prefer not to. For some background on why not, you can read this article at hivelogic. The last time I was rebuilding my laptop and desktop near the same time, I put together a shell script to automate the process of installing RMagick. I got it back out and dusted off the cobwebs, and voila! RMagick on Leopard. (note: replace wget with “curl -O”, if you don’t have wget installed on your machine) Here’s the code:

install_rmagick.sh

#!/bin/sh
wget http://download.savannah.gnu.org/releases/freetype/freetype-2.3.5.tar.gz
tar xzvf freetype-2.3.5.tar.gz
cd freetype-2.3.5
./configure —prefix=/usr/local
make
sudo make install
cd ..

wget http://superb-west.dl.sourceforge.net/sourceforge/libpng/libpng-1.2.22.tar.bz2
tar jxvf libpng-1.2.22.tar.bz2
cd libpng-1.2.22
./configure —prefix=/usr/local
make
sudo make install
cd ..

wget ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz
tar xzvf jpegsrc.v6b.tar.gz
cd jpeg-6b
ln -s `which glibtool` ./libtool
export MACOSX_DEPLOYMENT_TARGET=10.5
./configure —enable-shared —prefix=/usr/local
make
sudo make install
cd ..

wget ftp://ftp.remotesensing.org/libtiff/tiff-3.8.2.tar.gz
tar xzvf tiff-3.8.2.tar.gz
cd tiff-3.8.2
./configure —prefix=/usr/local
make
sudo make install
cd ..

wget http://jaist.dl.sourceforge.net/sourceforge/wvware/libwmf-0.2.8.4.tar.gz
tar xzvf libwmf-0.2.8.4.tar.gz
cd libwmf-0.2.8.4
make clean
./configure
make
sudo make install
cd ..

wget http://www.littlecms.com/lcms-1.17.tar.gz
tar xzvf lcms-1.17.tar.gz
cd lcms-1.17
make clean
./configure
make
sudo make install
cd ..

wget ftp://mirror.cs.wisc.edu/pub/mirrors/ghost/GPL/gs860/ghostscript-8.60.tar.gz
tar zxvf ghostscript-8.60.tar.gz
cd ghostscript-8.60/
./configure —prefix=/usr/local
make
sudo make install
cd ..

wget ftp://mirror.cs.wisc.edu/pub/mirrors/ghost/GPL/current/ghostscript-fonts-std-8.11.tar.gz
tar zxvf ghostscript-fonts-std-8.11.tar.gz
sudo mv fonts /usr/local/share/ghostscript

wget http://imagemagick.site2nd.org/imagemagick/ImageMagick-6.3.5-9.tar.gz
tar xzvf ImageMagick-6.3.5-9.tar.gz
cd ImageMagick-6.3.5
export CPPFLAGS=-I/usr/local/include
export LDFLAGS=-L/usr/local/lib
./configure —prefix=/usr/local —disable-static —with-modules —without-perl —without-magick-plus-plus —with-quantum-depth=8 —with-gs-font-dir=/usr/local/share/ghostscript/fonts
make
sudo make install
cd ..

sudo gem install RMagick

Posted by Solomon White Sat, 03 Nov 2007 00:07:00 GMT


Rails Rocks!

I am trying so see the result of a change in a Java program….5 minutes compilation…5 minutes deployment. Arggggggggggg!

Rails Rocks! Change + refresh = done!

Short term memory is good, hopefully I will forget that experience soon.

;-)

Posted by Daniel Wanja Tue, 02 Oct 2007 17:37:00 GMT


Exceptional Slicehost support

Our server that hosts onrails.org and time.onrails.org died on Sunday. Before we could react Slicehost migrated us to new hardware and everything was up and running. Thanks guys, awesome support!

The events where the following:

  • [Slicehost] Sep 1, 2007 3:04 PM, Emergency Server Reboot and HW Migration – onrails
  • [Montastic] Sep 1, 2007 3:20 PM, Website status: unreachable
  • [Slicehost] Sep 1, 2007 3:44 PM, Emergency HW Migration #2 – onrails.
  • [Montastic] Sep 1, 2007 4:12 PM, Website status: OK

[Slicehost] indicates emails we received from Slicehost, and [Montastic] emails from the monitoring system we use. Slicehost warned us of the situation and action they are taking before our monitoring system found out that the service was down. Well, apparently they migrated us to new hardware that had another issue (bad memory) and they moved us a second time to different hardware. Well no data was lost, all our service are up and running and I didn’t have to cut short my BBQ. Thanks!

Update1: Well something is wrong with the template of our blogs. I am not sure it’s related to the update of hardware as it was working last night. More to come.

Posted by Daniel Wanja Mon, 03 Sep 2007 16:51:00 GMT


Monitoring Rails Performance with Munin and a Mongrel

Rails makes things easy on developers—maybe too easy. It’s not uncommon to reference an association while iterating over a collection of objects, resulting in a performance-devouring N+1 queries being executed. Of course, this is easily fixed with eager loading of the association, but you get my point. Rails can be a big gun, and it’s easy to blow off your foot.

Once your application is moved into production, it becomes important to keep an eye on performance over time in order to get a feel for trends and plan capacity intelligently. Numerous Rails performance measuring tools exist, but I find that it helps to correlate performance of your application with simultaneous lower-level performance metrics of your system (CPU and Memory usage, Load Average, etc). Besides that, hey! Pretty Graphs! Enter munin, an open-source, extensible monitoring tool with a number of out-of-the-box plugins that are useful to SysAdmin type folks. There’s a pretty decent (though brief) writeup on howtoforge that explains a bit more about how to go about getting your own munin. Go ahead and check it out — I’ll chill here until you’re back.

That wasn’t too bad, huh? Okay, so at this point you should have a working munin instance. It will take a few minutes of data collection before your graphs look like anything, but going forward, you’ll have historical graphs of several key metrics for managing your server(s). It’s outside the scope of this article, but you can also set up munin to monitor multiple servers on your network, and/or alert you when critical threshold levels are passed.

Well, knowing CPU usage is great, but it would also be nice to have some idea what the average user experience is like on your site. Does your Rails app perform differently at different times of day? How long does it typically take for your app to render a page? Is the majority of the time spent in the database, or rendering? Is the response time about the same between production deployment versions? I wanted answers to these questions too, so I came up with a small ruby program that watches your Rails log in realtime, and when munin asks, provides summary information about the performance of your application. Below, I’ll go over the code section by section; for those who want it now, scroll to the bottom of the article to find the download link.

The basis of the solution is a Ruby array: we stuff values into it, then compute the average of all the values and clear the array each time munin pings us. Every time a value is added, we also check it against the maximum already seen, so we can report the maximum response time in addition to the average. We keep three of these objects around, one each for DB, Rendering, and Total response times. There is also a mode that lets you look at the current value without consuming it — useful for peeking inside without affecting the data that munin will ultimately see. Here’s the class that implements this functionality:

accumulator

class Accumulator def initialize @values = Array.new() @max = 0 endclass Accumulator
def initialize
values</span> = <span class="co">Array</span>.new() <span class="iv">max = 0
end

def add(value) @values << value max</span> = value <span class="r">if</span> value &gt; <span class="iv">max end def average(read_only=false) return_value = if @values.length == 0 nil else values</span>.inject(<span class="i">0</span>) {|sum,value| sum + value } / <span class="iv">values.length end @values = Array.new() unless read_only return_value end def max(read_only=false) return_value = @max @max = 0 unless read_only return_value end

end

In the next section of the code, we build our accumulators, and begin tailing the logfile to extract performance numbers. This requires the file-tail gem, available from rubyforge. Note that in my setup, this file resides in a subdirectory under lib in RAILS_ROOT. If you choose to place this file elsewhere, you’ll have to adjust the path to the logfile accordingly. Another thing to note: in our environment, the load balancer continually pings a “heartbeat” action on each node to make sure it is still responsive. As we will be hitting this action repeatedly, it is engineered to be as lightweight as possible. Therefore, any numbers from it are pretty meaningless to our overall statistics, so we don’t want to include them. To keep these numbers from skewing our results, we define an IGNORE_PATTERNS regexp (earlier in the code). If the request matches a pattern we want to ignore, its statistics are not collected.

tail

LOGFILE = File.join(File.dirname(__FILE__), '..', '..', 'log', "#{RAILS_ENV}.log") $response_data = { :total => Accumulator.new(), :rendering => Accumulator.new(), :db => AccumulatorLOGFILE = File.join(File.dirname(FILE), .., .., log, "#{RAILS_ENV}.log")
$response_data = { :total => Accumulator.new(),
:rendering => Accumulator.new(),
:db => Accumulator.new() }

Thread.abort_on_exception = true
logtail = Thread.new do
File::Tail::Logfile.tail(LOGFILE) do |line|
if line =~ /^Completed in /
parts = line.split(/\s\|\s/)
resp = parts.pop
requested_url = resp[/http:\/\/[^\]]*/]
next if requested_url =~ IGNORE_PATTERNS

parts.each do |part| part.gsub!(/Completed in/, "total") type, time, pct = part.split(/\s+/) type = type.gsub(/:/,).downcase.to_sym $response_data[type].add(time.to_f) end end end

end

So now we have a thread busy gathering our data—how can we expose the data to a munin plugin? There are multiple ways to do this, but I chose to use a small HTTP server listening to requests from the local machine only. We could build such a thing in Rails, but we really don’t need about 90% of the features Rails has to offer. Since we’re running Rails as a Mongrel cluster, we already have a perfect tool at our disposal for writing small HTTP request handlers in Ruby: Mongrel. Here are a couple of pages about how to get started writing Mongrel handlers —it’s pretty straightforward. Here’s our handler:

mongrel handler

class ResponseTimeHandler < Mongrel::HttpHandler def initialize(method) @method = method endclass ResponseTimeHandler < Mongrel::HttpHandler
def initialize(method)
@method = method
end

def process(request, response) response.start(200) do |head,out| debug = Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]).has_key? "debug" head["Content-Type"] = "text/plain" output = $response_data.map do |k,v| value = v.send(@method, debug) formatted = value.nil? ? U : sprintf(%.5f, value) "#{k}.value #{formatted}" end.join("\n") output << "\n" out.write output end end

end

h = Mongrel::HttpServer.new("127.0.0.1", PORT)
h.register("/avg_response_time", ResponseTimeHandler.new(:average))
h.register("/max_response_time", ResponseTimeHandler.new(:max))
h.run.join

The handler is generic so that it can call an arbitrary method on our collection of data arrays, so we can set up one URI for the average, and one for the maximum. With this in place, we can write a simple munin plugin script that uses Net::HTTP to query our Mongrel to get at the performance data.

Basically, a munin plugin has two usage scenarios. When called with the single argument “config”, it should output information about itself in a format that Munin understands. This includes how to label the chart, scaling information, how many series will be included on the chart, etc. When called with no arguments, the plugin should output the current values of each series. For more information on writing your own munin plugin, start with the HowToWritePlugins munin wiki page. And now, our plugin script:

rails_response_time

#!/usr/bin/env ruby#!/usr/bin/env ruby

# munin plugin to render rails response time graphs
# link to /etc/munin/plugins/avg_response_time and /etc/munin/plugins/max_response_time

require open-uri
PORT = ENV[PORT] || "8888"

def config
title = File.basename($0).split(_).map{|s| s.capitalize }.join( )
config=<<END_CONFIG
graph_title
#{title}
graph_vlabel response time
graph_category rails
total.label total
rendering.label rendering
db.label db

END_CONFIG

puts config
end

def get_data(read_only=false)
qs = read_only ? ?debug :
puts open("http://127.0.0.1:#{PORT}/#{File.basename($0)}#{qs}").read
end

case ARGV.first
when config
config
when debug
get_data(true)
else
get_data
end

The script will examine the name with which you linked it in to the munin plugins directory to determine which URI to query. I have also added a debug mode that will show you the current values, so you’re not consuming any data that munin needs to see for an accurate graph. The final piece is a small Daemons wrapper script to control the main log-tailing process, and you should be set. Make sure to restart munin-node so it will notice the new plugins, and after a while, you’ll see something like this:

It is worth noting that the numbers from the Rails log might not be 100% accurate, and this won’t replace the results that you can get from seriously profiling your application. Also, the information you are getting is a bit generic — all actions are lumped together, so there is not a lot of information about the cause of the performance problem. But, for insight into your production application performance, this setup should at least give you some indications about how well your baby is playing in the interwebs.

Download rails_log_monitor.rb

Download rails_response_time

Posted by Solomon White Fri, 31 Aug 2007 18:29:00 GMT


Unobtrusive Javascript with Lowpro and Ruby On Rails

Check out http://www.danwebb.net/lowpro for more info on LowPro, a very elegant approach to do Unobtrusive Javascript with Ruby On Rails. Find hereafter a small example of how to add a custom behavior to link.

The View

watch_results.erb
<%= javascript_include_tag 'prototype', 'lowpro', 'remote', 'application' %> <div id="result_list"> <ul> <% for watch_result in @watch_results %> <li> <%= link_to watch_result.created_at.to_s(:db), diff_watch_result_url(@watch, watch_result), {:id = <%= javascript_include_tag ‘prototype’, ‘lowpro’, ‘remote’, ‘application’ >
<div id
=
"result_list">
<ul>
< for
watch_result in watch_results</span> %&gt; &lt;li&gt; &lt;<span class="s"><span class="dl">%=</span><span class="k"> link_to watch_result.created_at.to_s(:db), diff_watch_result_url(watch, watch_result),
{:id
=
> dom_id(watch
result) }
%>
</li
>

<% end %>
</ul>
<
/
div>

The Javascript

application.js
LoadWatchResult = Remote.Link({ onLoading : function() { $('watch_result_difference').innerHTML=''; $('watch_result_difference').addClassName('pleaseWait'); }, onComplete : function(e) { var source = Event.element(e); $('watch_result_difference').removeClassName('pleaseWait'); $$('div#result_list ul a.active').each(function (e) {e.removeClassName('active')}); source.addClassName('active'
LoadWatchResult = Remote.Link({
onLoading : function() {
$(watch_result_difference).innerHTML=;
$(watch_result_difference).addClassName(pleaseWait);
},
onComplete : function(e) {
var source = Event.element(e);
$(watch_result_difference).removeClassName(pleaseWait);
$$(div#result_list ul a.active).each(function (e) {e.removeClassName(active)});
source.addClassName(active);
}
});

Event.addBehavior({
#result_list ul li a: LoadWatchResult
});

The ‘remote.js’ provides additional behaviors creatde by Dan Wedb as part of LowPro (http://svn.danwebb.net/external/lowpro/trunk/behaviours/). The LoadWatchResult behavior we created in this example transforms a ‘standard’ link_to to a link_to_remote with additional behavior on the onLoading and onComplete of the remote call. The view stays clean.

Enjoy!
Daniel

Posted by Daniel Wanja Tue, 28 Aug 2007 02:41:41 GMT