Nexus Weblogging
ChinaonRails
You are here ChinaonRails > Ruby on Rails > Newbie > /bin/recykl
  /bin/recykl

Rails and Grails comparison 
Published on 2008-3-19 0:05:00 by Roman Mackovcak

I have spent few years developing in Ruby on Rails. For the last half of year I have been learning Grails, too. The things described here are a summarization of the differences that I had to come over.

The comparison will be written in a simple "table based" structure. I just got used to it when preparing comparisons or product studies for tenders.

Ruby on Rails

A lightweight web framework written in Ruby scripting programming language. It contains its own application server. See the Rails home website.

Grails

A web framework written in Groovy. The source code is compiled to byte code and can be run on Java application servers. Grails home website.

Maturity

Before I start comparison, it is fair to say, that Rails has been around from 2004 while Grails final version dates from 2008. So, some of the differences are caused by this time shift.

Both of the frameworks are more evolutionary than revolutionary. They just implement the right patterns and they do it right.

Ruby on Rails Grails
First release (version 1.0) July 2004 February 2008
Life cycle Mature framework with solid base of developers Young framework with a growing base of developers and a huge base of potential developers (from Java)

Documentation

Ruby on Rails Grails
Framework Excellent. Uses the RDoc that contains not only list of methods, classes and files, but also source code of a method, with syntax highlighting. Very good. Contains list of classes, methods, files... but I am really missing the source code. If the code is not documented, the documentation is useless
Application Excellent. Only the application files are documented using the RDoc (including syntax highlighting) On one hand it is exhaustive, because it generates documentation for all classes in the project (including plugins). On the other hand it does not contain the source code. So, once the code is not documented, the documentation is useless. So, on average I would say it is good.

Development

Both frameworks are based on flexible languages that allow meta programming - changing classes on the fly.
Ruby on Rails Grails
Developer audience From beginners Some experience is required
Language constructs Readable, sometimes like natural language Readable, sometimes too many brackets (but as I said, I spent some time in Ruby :o)
Mapping objects to database Excellent, all declarative Excellent, all declarative
Libraries Wide variety of libraries and plugins. Almost everything I needed was available (except for Kerberos support...) Huge amount of java libraries could be used together with Grails. This is one of Grails killer features.
Scaffold The default scaffold looks terrible, custom plugins needs to be installed. Looks nice out of the box, implements handy nice features like table sorting.
Tools rake (~make), rjs (ruby java script - library that allows to write java script functionality in ruby), migrations (tool that uses ruby syntax to change database schema - very useful) ant (~make)
Log file Very descriptive, it provides exactly the information needed: controller/action/parameters, time spent on DB, VIEW, CONTROLLER, SQL statements (including timing) Verbose... very. Exception generated 1000 lines of code in log, missing the information about SQL statements and the things that are in the Rails log. This was a disappointment.
Console Simple terminal window working in command/result mode Window based - command answer is displayed in different frame at the end of it (unfortunately it is not scrolling properly, so it is quite annoying). I take it as a temporary problem.
Folder structure Simple, follows the MVC Following MVC, slightly more complex than RoR
Thread support Poor Native

Production

Ruby on Rails Grails
Resource usage Medium resource usage Higher resource usage

Potential

Ruby on Rails Grails
Internet High potential. The framework allows fast development of an application with a very good performance. High potential. It alows fast development and it can utilize all of the Java frameworks.
Enterprises Just for prototyping or small applications. The support of enterprise technologies is not at the focus of the community. Could be used for prototyping and also for real applications
Last, but not least, the popularity of both frameworks:


There are many more differences that were not mentioned here. If you find a major one not mentioned here, please leave me a comment. I will appreciate it.


Ignore files in subversion 
Published on 2008-3-11 17:39:00 by Roman Mackovcak

This is just a simple procedure how to tell subversion to ignore files or directories.

cd parent_directory

# Check the current setup
svn proplist -v .

# set the editor to edit the properties
export EDITOR=vi

# open up editor with the properties
svn propedit svn:ignore .
Text editor opens (vi in my case) Here you have to specify files and directories to be ignored. E.g.
docs
*.log
and last, but not least, commit.
svn commit

Converting family to Linux 
Published on 2008-3-10 22:15:00 by Roman Mackovcak

I managed to convert my family to Linux. Not intentionally, it just happened. The final confirmation came on Saturday. My older son(5) was "coaching" the younger one(2). "Do not boot to Windows, there are no games there!"

And how did I do it?

  • Do not install games for kids to Windows
  • Do not install MS Office on Windows
  • Set Linux as the default operating system
As I said, it was no intention. It was just my laziness. :o)


Improve performance of MySQL driver for RoR 
Published on 2008-3-4 22:13:00 by Roman Mackovcak

Last week I was working on the performance tuning of a rails application. I ran a profiler and found something very interesting.

I found that there is a procedure that is called very often and takes a lot of time.

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 14.99    18.13     18.13    43430     0.42     0.85  Mysql#get_length
  8.84    28.82     10.69   172751     0.06     0.09  Kernel.===
  8.40    38.98     10.16   306964     0.03     0.04  Fixnum#==
  7.59    48.16      9.18     5566     1.65    30.60  Integer#times
  6.42    55.92      7.76     7582     1.02     1.63  Mysql::Net#read
...
  0.00   120.92      0.00        1     0.00 120920.00  #toplevel
The procedure was Mysql#get_length:
# File src/rails-1.2.3/activerecord/lib/active_record/vendor/mysql.rb
  def get_length(data, longlong=nil)
    return if data.length == 0
    c = data.slice!(0)
    case c
    when 251
      return nil
    when 252
      a = data.slice!(0,2)
      return a[0]+a[1]*256
    when 253
      a = data.slice!(0,3)
      return a[0]+a[1]*256+a[2]*256**2
    when 254
      a = data.slice!(0,8)
      if longlong then
        return a[0]+a[1]*256+a[2]*256**2+a[3]*256**3+
          a[4]*256**4+a[5]*256**5+a[6]*256**6+a[7]*256**7
      else
        return a[0]+a[1]*256+a[2]*256**2+a[3]*256**3
      end
    else
      c
    end
  end
There is obviously space for improvement! Replace the multiplications by shifts
class Mysql
  def get_length(data, longlong=nil)
    return if data.length == 0
    c = data.slice!(0)

    case c
    when 251
      return nil
    when 252
      a = data.slice!(0,2)
      return a[0]+(a[1]<<8)
    when 253
      a = data.slice!(0,3)
      return a[0]+(a[1]<<8)+(a[2]<<16)
    when 254
      a = data.slice!(0,8)
      if longlong then
        return a[0]+(a[1]<<8)+(a[2]<<16) +(a[3]<<24)+(a[4]<<32)+(a[5]<<40)+(a[6]<<48)+(a[7]<<56)
      else
        return a[0]+(a[1]<<8)+(a[2]<<16)+(a[3]<<24)
      end
    else
      c
    end
  end
end

I ran the profiler again and, well... a wisdom of my university times popped on my mind: “There is an elegant, simple, nice and obvious solution for each problem. Unfortunately, it is wrong!”

Performance remained the same. So, deeper investigation is needed! It was not difficult to find out that most of the times the “else” branch is executed. I tried something like this:

  def get_length(data, longlong=nil)
    return if data.length == 0
    c = data.slice!(0)

    return c if c<251

    case c
    when 251
      return nil
    when 252
      a = data.slice!(0,2)
      return a[0]+(a[1]<<8)
    when 253
      a = data.slice!(0,3)
      return a[0]+(a[1]<<8)+(a[2]<<16)
    when 254
      a = data.slice!(0,8)
      if longlong then
        return a[0]+(a[1]<<8)+(a[2]<<16) +(a[3]<<24)+(a[4]<<32)+(a[5]<<40)+(a[6]<<48)+(a[7]<<56)
      else
        return a[0]+(a[1]<<8)+(a[2]<<16)+(a[3]<<24)
      end
    else
      c
    end
  end
And the performance?
  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 11.26    11.25     11.25    43430     0.26     0.38  Mysql#get_length
  8.73    19.97      8.72     5566     1.57    23.25  Integer#times
  7.77    27.73      7.76     7582     1.02     1.63  Mysql::Net#read
  7.27    34.99      7.26     5140     1.41     3.69  Array#each
  5.52    40.50      5.51   139526     0.04     0.05  Fixnum#==
...
  0.00    99.88      0.00        1     0.00 99880.00  #toplevel
Improved! The toplevel cumulative time is down by over 20 seconds. Now, here's how you can embed this hack into your application:
  • Create a file called e.g. mysql_fix.rb
  • Add there
require 'active_record/vendor/mysql'

class Mysql
  def get_length(data, longlong=nil)
    return if data.length == 0
    c = data.slice!(0)

    return c if c < 251

    case c
    when 251
      return nil
    when 252
      a = data.slice!(0,2)
      return a[0]+(a[1]<<8)
    when 253
      a = data.slice!(0,3)
      return a[0]+(a[1]<<8)+(a[2]<<16)
    when 254
      a = data.slice!(0,8)
      if longlong then
        return a[0]+(a[1]<<8)+(a[2]<<16) +(a[3]<<24)+(a[4]<<32)+(a[5]<<40)+(a[6]<<48)+(a[7]<<56)
      else
        return a[0]+(a[1]<<8)+(a[2]<<16)+(a[3]<<24)
      end
    else
      c
    end
  end
end
  • Put it into e.g. lib/zmok directory
  • Add into your environment.rb following line
require File.join(File.dirname(__FILE__), '../lib/zmok/mysql_fix')
It is funny how my performance tuning session ended. Instead of changing my Rails application, I ended up improving the MySQL driver.

Bayes classification in Ruby made easy 
Published on 2008-2-21 2:58:00 by Roman Mackovcak

Recently I was experimenting with ruby bayes classification. At first sight it looks like a difficult topic, but with the right libraries it is interesting and funny.

Before you start experimenting, you have to install 3 gems.

gem install classifier
gem install madeleine
Confirm the required stemmer gem.

For the beginning, lets experiment with the plain bayes classifier.

require 'classifier'

bayes = Classifier::Bayes.new 'funny', 'sad', 'neutral'

# Train it slightly...
bayes.train 'funny', 'Finally all of them were smiling'
bayes.train :sad, 'Little ill puppy'
bayes.train :neutral, 'Tax declaration'

The classifier is "trained", so lets ask it something interesting...

bayes.classify 'Everybody have to pay taxes'
=> "Neutral"
Hmmm... this does not look like the expected answer :o). We have probably trained it incorrectly. So, let's undo it:
# Remove the incorrect statement
bayes.untrain :neutral, 'Tax declaration'

# Train it right
bayes.train :sad, 'Tax declaration'

# And provide something neutral (if there is no statement for a category, the classifier does not work as expected.
bayes.train :neutral, 'Rainbow is full of colors'

So, how does the classifier sees it now?

bayes.classify 'Everybody have to pay taxes'
=>'Sad'
Yes, this is how people feel it :o). For those who does not agree (and also for debugging purposes) it is possible to see score for each category.
bayes.classifications 'Everybody have to pay taxes'
=> {"Sad"=>-9.43348392329039, "Neutral"=>-10.2035921449865, "Funny"=>-10.2035921449865}

The classifier that was created and trained is nice, but disappears as soon as you stop your ruby console. To make it more persistent, you have to use Madeleine class.
"Madeleine is a Ruby implementation of Object Prevalence, that is, transparent persistence of business objects using command logging and complete system snapshots."

require 'madeleine'

# Store the data into bayes-dir directory
madeleine = SnapshotMadeleine.new("bayes-dir") { bayes } 
madeleine.take_snapshot

Next time load the classifier with command

madeleine = SnapshotMadeleine.new("bayes-dir")

# Perform more training
madeleine.system.train "sad", "Many people were injured by the earthquake"

# And test it once more
madeleine.system.classify 'smiling face'
=>'funny'
madeleine.system.classify 'strong earthquake'
=>'sad'

The classifier is a nice piece of code. I did enjoy it, and hope you will enjoy it too.


Ad-hoc fulltext search in RoR ActiveRecord 
Published on 2008-2-7 22:31:00 by Roman Mackovcak

I came to a situation where I needed to search my Active record, but I did not know which field contains the information. The solution with Ferret was just three steps away...

Let's say, you want to search Stories for 'Giant' keyword. You have to create a Ferret index in memory (ferret gem needs to be installed), index all active records and gather all IDs matching the keyword.

index=Ferret::I.new

Story.find(:all).each { |s| index << {:id=>s.id, :content=>s.inspect} }

index.search_each('Giant', :limit=>100) do |id, score| 
  puts "Active record ID: #{index[id][:id]} with score #{score}"
end
... now you have the full power of the Ferret engine in your hands.

Utilizing Caches.rb with Ferret 
Published on 2008-1-29 17:00:00 by Miroslav Škultéty

We needed to cache a Ruby class method calling the Ferret indexing engine. Yurii Rashkovskii developed a great library called Caches.rb.

When I googled it out, it seemed very simple to use and promised to do EXACTLY what I need (even the default timeout was JUST IT). I especially liked the very Rails-like tutorial Don’t tell, show me!. However, it required quite some effort to make it work, mainly because of the rather sparse documentation. Still, in the end the usage is very elegant, the solution is simple and it does what it promises. Thank you, Yurii!

To help our esteemed readers get faster over that less agreeable middle phase, here are a few tips:

  • Downloading it: I tried gems but the gem list server seemed to be overloaded, and when it worked at last, I just got an older version (0.2.0). When checking out (or exporting) version 0.4.0 from SVN trunk directly, the trick was in finding out the latest working SVN URL:
ruby script/plugin install http://svn.verbdev.com/rb/caches.rb/trunk
  • With all the typical Rails mixin stuff petrified in my mind, it took me a while to notice that caching should be declared AFTER the definition of the method to be cached, and not at the beginning of the class definition. The example in the documentation shows it, but it’s easily overseen.
  • We do use Rails, so I included class_cache_storage Caches::Storage::Global as suggested here
  • For some reason (I suspect my shallow knowledge of Ruby ;-), I did not manage to successfully require ‘caches.rb’ from the plugin installation dir #{RAILS_ROOT}/vendor/plugins/caches.rb/lib, so I copied it to #{RAILS_ROOT}/lib, which helped.
  • For similar reasons, I had to use a workaround to extend the class definition with caching, instead of the recommended way of extending conf/environment.rb by ActiveRecord::Base.extend Caches::ClassMethods So our class looks as follows:
require 'ferret'
include Ferret
require 'caches.rb'

class FIndex
  extend Caches
  ...
  def self.search(user=nil, results_per_page=10)
    ...
  end
  class_cache_storage Caches::Storage::Global
  class_caches :search
end

N.B.: The class to be cached works with Ferret and not the DB, so it did not inherit from ActiveRecord.

  • For a pure RoR developer, the terminology may be a little confusing (which indicates that Yurii can deal with more programming languages than just Ruby):
    • class methods mentioned in the library description are obviously just a shorthand for “the methods of a class”
    • in Yurii’s documentation, Ruby class methods are called static methods (as known in C/C++ or Java), and their caching is supported by class_caches (feature not available in caches.rb 0.2.0 from gems or RubyForge, but included in the more recent versions from SVN, like 0.4.0 we are using)

Calling Oracle stored procedure from rails 
Published on 2007-11-13 22:41:00 by Roman Mackovcak








Sometimes it is necessary to reuse existing logic from stored procedures. This line of code shows how to call a stored procedure in Oracle.
ActiveRecord::Base.connection.execute('BEGIN do_the_calculation; END;')

RoR in enterprise – lessons learned 
Published on 2007-10-24 5:27:00 by Roman Mackovcak

After a while my first enterprise prototype is finished and I have to summarize what was right and what was wrong during the period of prototyping.

Really nice surprise for me was the way of communication. The requirements were formulated more precisely then any requirement before, but not from the beginning. When I did start, the requirements were very vague, but after first screens and first few features the communication was excellent and clear. This was the phase of fundamental principles and relations creation.

After some time, the flow of requirements was stronger and stronger. It was necessary to start requirements management – yes, it is true. You cannot get rid of it.

In the middle of development users started to use it and a new set of “handy little” features was requested.

Later the system became very important and the users started to solve real production issues using the system.

Now, the system is almost finished. I mean – the necessary features are there, but sometimes it is not consistent. Especially the stuff that is used rarely. The enterprise is pushing me to pass it into production regime. And what are my lessons learned?

  • The start will be painful. Be prepared to completely redesign the code and model.
  • Scaffolding is for some time more than enough.
  • It is not necessary to focus on good graphical desing from the beginning. Focusing on features is more important. In my case I did implement a nice design after 80-90% of features was implemented. And in fact, I did it because the environment was really ugly, not because customer did ask me to do it.
  • Make sure you find the right group of people to prototype with. It is impossible to create a prototype without business experts.
  • It is not important how much you boost your performance using good tools. You will always have more requirements than could be implemented. Requirements management is a must.
  • Communicate clearly that you are working on a prototype. Otherwise you will be forced to make it a real application. Here I started to think about using grails for prototyping. Nevertheless, I have no experience to decide if it was a good idea or not.
  • Be prepared for success! The application that will be created is in line with the requirements and heals the biggest pains of the business users. It is highly probable that the users will love it.
  • Do not stick with one technology. Anything that makes the process faster is valuable. Eg. I did use Sybase PowerDesigner to generate “history keeping” triggers automatically. Adding a new table to my model was just few clicks and assigning the right trigger template.
  • And last, but not least. Listen! Listen more! And make sure you understand that you are not the one who knows the business. You came there to help them to communicate their needs, not to show them how to do their business.
Now I must say, that Ruby on Rails is a great tool for project communication. It is able to communicate user requirements very efficiently and precisely.

From pictures to picturables 
Published on 2007-10-3 3:43:00 by Roman Mackovcak

This article tries to explain how to convert existing class in Ruby on Rails to a class that can be used in polymorphic association class. As the beginning, I do recommend to read article about polymorphic associations. It explains what they are and how they work.

The whole process will be described on classes Picture (represents picture) and Item (an item that needs a picture).

1. First of all, it is necessary to create columns for the pictures table. The best way is to use migrations. So, create file e.g. 013_convert_pictures_to_polymorph.rb in your db/migrate directory. It should contain something like:

class ConvertPicturesToPolymorph < ActiveRecord::Migration

  def self.up
    # Change old item_id identifier to new picturable_id
    rename_column :pictures, :item_id, :picturable_id
    # Add type of association
    add_column :pictures, :picturable_type, :string, :length => 50
    # Fill in the type of association
    execute "update pictures set picturable_type = \"Item\""
  end


  def self.down
    # Bring it back
    rename_column :pictures, :picturable_id, :item_id
    remove_column :pictures, :picturable_type
  end
end
now run:
rake migrate 
to create the columns.

2. Once the appropriate columns were created, it is necessary to change the Item class. Change the

  has_many  :pictures
to
  has_many  :pictures, :as => :picturable

3. Now change the Picture class. Change

  belongs_to :item
to
  belongs_to :picturable, :polymorphic => true
Make sure, there is no "item" left in the Picture class. If so, change it to picturable and make sure all picturables have the method you are using.

4. Creating a new class enriched by pictures is simple. Just add

  has_many  :pictures, :as => :picturable
to your class.

5. Last but not least, change your fixtures. The easiest way is to export the current data from your database. For this purpose I do use manage_fixtures plugin.

Good luck!

10 items

欢迎使用 RSS 阅读器订阅本页种子 http://blog.zmok.net/xml/rss
© 2007 A Jesse Cai Production   -   About   -   京ICP备07020911号
a site powered by Project Babel