Friday, 26 October 2007

How to upgrade Ruby on Rails Restful routes to 2.0

Ok, you were quite proud when on Rails 1.2.x you made your app restful.
Now in 2.0 REST has been improved cleaned reworked and... changed the naming of the nested Routes

Let's take a look at the differences in 1.2.x

the following code


map.resources :dinners do |dinner|
dinner.resources :dishes do |dish|
dish.resources :side_courses
end
dinner.resources :drinks
end


would generate (among many others)


dinners_url
dinner_url

dishes_url
dish_url

side_courses_url
side_course_url

drinks_url
drink_url


and so on.
With the same code in the routes rails 2.0 generates


dinners_url
dinner_url

dinner_dishes_url
dinner_dish_url

dinner_dish_side_courses_url
dinner_dish_side_course_url

dinner_drinks_url
dinner_drink_url


And your REST routes methods will change more if you use the new routes namespacing introduced with 2.0
This is good and much nicer, and is a great help in complicated applications that use the same resources at different nesting levels and for different purposes.

But it means that to migrate from 1.2.x you've got to change many of your original calls to the *_url or *_path methods.

Well, I wrote a small dodgy script to manage the search and replace of the old routes with the new ones.

And I thought I'd share it...

The script should run as it is on linux. It uses find/grep/sed system commands.

first two steps are quite simple


  1. upgrade a non critical copy of your rails app to version 2.0 of ruby on rails

  2. download the script to migrate your routes from 1.2 to 2.0 and save it in the root directory of your rails project

  3. execute script/console and type the following

    require 'route_migrator'

    RouteMigrator.dump_current_route_methods


  4. exit from the console and open with a text editor the file route_map_2_0.rb. This file should contain a ruby hash with keys and values containing the old routes

  5. now you have to update the values of the hash with the new routes, for the previous example a line would look like


    "side_courses_url" => "side_courses_url"


    and you need to update it to


    "side_courses_url" => "dinner_dish_side_courses_url"


    If you don't have a clear idea about what the new routes are you can use

    rake routes


    or open again script/console and type


    require 'route_migrator'

    RouteMigrator.dump_named_routes

    Be aware of that the hash might contain some rubbish as keys, just delete the lines that you think aren't proper routes actually used in your applications,

  6. When you think your hash is complete and ready to go you can check your work firing yet another time your script/console and typing

    require 'route_migrator'

    RouteMigrator.check_new_routes

    This will output a warning if you've typed in the hash some routes that are not in the list generated from 2.0

  7. If the previous check succeeded you can try the following that will substitute every occurrence odf your old routes with the new ones
    WARNING: this script is guaranteed to screw up >>BADLY<< your code if you use it without understanding what it does. Read the source code of the script before proceeding. As always svn diff and svn revert will be your friends

    require 'route_migrator'

    RouteMigrator.upgrade_route_methods!

    At this point your application should be updated with the new routes

Thursday, 9 August 2007

RMagick 2.0 beta released with memory control functions

Yesterday was announced RMagick2.0.0 beta1 release.

Among others new features I've found interesting the "new methods to help control and monitor memory usage" I'm looking forward to playing with them when I'll be back from my holiday in late august.

RMagick has been quite often considered prone to memory leaks, but it seems that memory leaks are not the actual cause of the high memory consumption of RMagick it will be interesting to see what is now available to keep under control the memory used by this amazing library.

Thursday, 26 July 2007

google analytics problems?



Since yesterday google analytics seems to be experiencing problems.

Some login problems were experienced and a delay in the processing was announced.

Now in the dashboard I can see funny things like the image above (47 visits and no pageviews, quite a mistery). In the specific case the correct data should be 0 visits since I know yesterday there was no activity on that specific regexp-filtered profile.

Statistics for july the 25th have stopped at 17 GMT for me.

sh*t happens, at google too.

-- update 30 july

Today for some accounts google analytics data are delayed again.
In my case data last update is 6.00 AM GMT july 29 so at the moment the delay is more than one day.

Wednesday, 6 June 2007

ruby on rails params and empty strings

A little annoyance I've found using ruby on rails is the following:

If in your model you have some strings that can be null, and the user doesn't fill the related fields in the web form, rails will end up storing an empty string "" instead of the good old NULL in the database.

Now, this is for sure not a big problem but it has some implications that might affect the model validations and it can be even worse if the rails app is not the only program writing in the database, in this last case you easily end up with a mix of empty string and NULL values in the column making trickier to write queries conditions that involve that column.

Now I was playing today to fix this problem and I came with a solution that uses SimpleDescriptor.

For any model that uses SimpleDescriptor as described in this post the following is a way to have NULL saved in the database instead of the empty string ''

first you need to define the clean_strings method


def clean_strings
self.class.described_as(:string).each do { |field| send("#{field}=", nil) if send(field) == '' }
end


and then add the clean_strings method in your before_validation callback


before_validation :clean_strings


Starting from this moment your model will just save NULL instead of an empty string and the :allow_nil => true in the model validations will start behaving as expected.

Monday, 4 June 2007

Simple Descriptor, code less validate more

After 6 month I've released a new version of Simple Descriptor.

This gem makes possible to attach some extra informations to a generic ruby class, but its mission is now includes specific methods to allow more control and flexibility on ActiveRecord validations, in addition to this, Simple Descriptor may help you in building a consistent approach in areas where rails doesn't.

Let's start getting our hands dirty, first of all install the gem


sudo gem install simple_descriptor


after that you need to require the gem i.e in your config/environments.rb file

require 'simple_descriptor'


now let's tweak our Employee ActiveRecord model

the migration to create the Employee model will be

class CreateEmployees < ActiveRecord::Migration
def self.up
create_table :employees do |t|
t.column :first_name, :string, :limit => 30, :null => false
t.column :middle_name, :string, :limit => 10, :null => true
t.column :last_name, :string, :limit => 30, :null => false
t.column :gross_wage, :integer, :null => false
t.column :personal_email, :string, :limit => 100, :null => false
end
end

def self.down
drop_table :employees
end

the basic model definition will be

class Employee < ActiveRecord::Base
end

to get some validations for free we need to add 2 lines of code

class Employee < ActiveRecord::Base
extend SimpleDescriptor::ClassDescriptor
validates_ar_descriptions
end

These 2 extra lines will

  • validates_presence_of every field defined as NOT NULL

  • validates_length_of every field defined with a limit

  • validates_numericality_of every field defined as anumber


but there some extra as well, fire the console of your rails app and type

Employee.described_as :string

this will return all the fields defined as string in the database.
Now always in the console try

Employee.maxlegth(:first_name)

this will return 30 which is the maximum length of the first_name field

Or again you can have the list of the required fields

Employee.described_as :required
=> [:first_name, :gross_wage, :last_name, :personal_email]


Or you can ask your model if specific field is required

Employee.describes? :first_name as => :required
=> true

If you want to inspect a bit more what's happening behind the scenes you can use

Employee.descriptions

Now, let's say you have some different models i.e. Employee, Manager, Consultant and in all these models you have some sensible fields you want to show only to authorized people.

If you use Simple Descriptor for all these models you can easily define which fields contains sensible informations, for the Employee it would be


class Employee < ActiveRecord::Base
extend SimpleDescriptor::ClassDescriptor
validates_ar_descriptions
describe :gross_wage as => :sensible
end

Starting from this moment you can ask the Employee class if a field is sensible or not

Employee.describes? :gross_wage, :as => :sensible
=> true

Employee.describes? :first_name, :as => :sensible
=> false

Or you can obtain the list of sensible fields

Employee.described_as :sensible
=> [:gross_wage]

This may help you to manage sensible data show rules in your views or in your controller.

To know more about Simple Descriptor you can just visit the online docs

Thursday, 17 May 2007

ActiveRecord :select with :include

ActiveRecord is an important slice of the joy included in Ruby on Rails, writing a SQL database driven application without actually writing any SQL is really pleasurable.

But sometime, even in the joy you get some pain.
The pain for me arrived when I was working on some reporting feature that was pulling various data across a quite long chain of has_many relationships.

I experienced a quite high load in the DB at the moment of the query and after that seconds of CPU full load eaten by my ruby process.
I checked what was going on in my log/development.log and I noticed the query was extracting all the columns of every table even if I specified a :select option, this turned a query originally quite light into a heavy one.

I googled a bit and I found out that with ActiveRecord, if you specify :select and :include option you don't get anymore 100% joy but just 50%, your :select statement will be ignored.

Today I'm trying to put back some percent of the joy.

select_with_include gem is meant to move the joy level up to 80% (no, not 100% yet)

select_with_include will allow you to specify a limited :select statement like


"table1.column1, table1.column3, table2.column2"


or like


"table1.*, table2.column3, table2.column4, talbe3.*"


At the moment you can't specify functions or calculated fields. There are reason for that and I'm going to discuss these reason in the next posts. I'll try as well to explain how this gem works around the original ActiveRecord code.

For the moment you're invited to get select_with_include gem using


sudo gem install select_with_include


and test it yourself specifying


require 'select_with_include'


in your config/environment.rb file.

If you're a script/console user you can just try, while you

tail -f log/development.log

to launch the same find with :include and :select with and without requiring the gem.

If you find any issue you're really welcome to use the issue tracker


And if you want to know more just stay tuned for the next posts

Wednesday, 16 May 2007

attachment_fu and Rmagick FAT thumbnails

I'm using attachment_fu on the project I'm currently working on, using it to process the images the users upload.

As backend I use Rmagick, since it was already used in the same app. At some point I felt curious about the size in kb of the thumbnails generated, to estimate the increase of bandwidth the new feature would add on the server.

I quickly spotted the size of the generated jpegs was unreasonable for the pixel size of them.

It turned out that attachment_fu + Rmagick on Ubuntu 6.0.6 and Ruby 1.4 aren't compressing jpegs.

To fix here's a one line patch on attachment_fu.

this patch sets the jpeg quality to 85%


[~]$ cat atch_fu/attachment_fu_rmagick_compression.patch
Index: lib/technoweenie/attachment_fu/processors/rmagick_processor.rb
===================================================================
--- lib/technoweenie/attachment_fu/processors/rmagick_processor.rb (revision 2864)
+++ lib/technoweenie/attachment_fu/processors/rmagick_processor.rb (working copy)
@@ -45,9 +45,9 @@
else
img.change_geometry(size.to_s) { |cols, rows, image| image.resize!(cols, rows) }
end
- self.temp_path = write_to_temp_file(img.to_blob)
+ self.temp_path = write_to_temp_file(img.to_blob {self.quality = 85})
end
end
end
end
-end
\ No newline at end of file
+end