If you see this 6

Posted by pratik
on Saturday, April 12

Then you’re experiencing modrails !

So here’s your candy :

  • cp /opt/local/etc/bash_completion.d/git ~/.git_auto_complete.sh
  • mate ~/.profile
  • Add “source ~/.git_auto_complete.sh” at the very bottom
  • Open a new shell and go to your git directory
  • Enjoy tabbing

RSpec & COBOL 3

Posted by pratik
on Saturday, March 29

It’s a pretty saturday morning and I am in very jolly mood. Thought I’d share some joy ;-)

In 1959, there was COBOL

1
2
3
4
5
6
7
8
9
10
11
ADD YEARS TO AGE.
MULTIPLY PRICE BY QUANTITY GIVING COST.
SUBTRACT DISCOUNT FROM COST GIVING FINAL-COST.
MULTIPLY B BY B GIVING B-SQUARED.  
MULTIPLY 4 BY A GIVING FOUR-A.  
MULTIPLY FOUR-A BY C GIVING FOUR-A-C.  
SUBTRACT FOUR-A-C FROM B-SQUARED GIVING RESULT-1.  
COMPUTE RESULT-2 = RESULT-1 ** .5.
SUBTRACT B FROM RESULT-2 GIVING NUMERATOR.
MULTIPLY 2 BY A GIVING DENOMINATOR.
DIVIDE NUMERATOR BY DENOMINATOR GIVING X.

And almost 50 years later, it resurrected !

1
2
3
4
5
6
Scenario: savings account has insufficient funds
Given my savings account balance is $50
And my checking account balance is $10
When I transfer $60 from savings to checking
Then my savings account balance should be $50
And my checking account balance should be $10

I know history repeats itself. I just wish it wouldn’t have done it in English !

Improve Rails Documentation 8

Posted by pratik
on Wednesday, March 19

This is almost a cross post from my email to rails-core mailing list :

Lately there had been many tiny doc patches at trac, which could easily escape our normal verification cycle. On top of that, we’re entering the whole new git-era of social development which eliminates the notion of “core” in a way.

Our aim is to make it really easy to get your doc patches in main repository. So to move forward, I’ve setup a git repository at GitHub : http://github.com/lifo/doc-rails/tree

I plan to give commit access to everyone who in the past have written a decent doc patch. Or anyone who is interested in writing one ( and can prove the quality of it by submitting at least one patch via whatever method ). And I’ll be syncing it with main Rails repository every week or so, after verifying all the changes.

So if you’re interested, you could email me directly or even easier is to catch me on IRC ( nick : lifo ). And once I add you to the doc-rails github project, you can commit directly to the repository without forking it ( and not bother about chasing anyone to pull your changes ).

Due to nature of this project/experiment, we’re gonna have to follow a strict set of guidelines for committers :

  • You are only allowed to change only docs and absolutely nothing else.
  • You should spell check your changes.
  • You shouldn’t add an entry to CHANGELOG. ( I will be adding changelog entries when syncing with main repo )
  • If you’re introducing a whole new style of doc writing, you should verify it with me first before you commit it.

Violation of any rule will result in lose of commit rights.

This is just a start. Moving forward, we could even do hackfest for writing docs and probably try to convince caboose doc fund holders to support this move in any way possible. But it all really depends on how things work out with doc-rails.

So anyone who has ever complained about quality of Rails API docs, now is your chance.

The project received a very positive response on the first day. We got whopping 8 patches !!

Now that you’re at it, you should follow Evan Weaver’s advise and throw some love all around ;-)

Single file Rails Application 30

Posted by pratik
on Saturday, February 16

UPDATE 1 : The inspiration for this article came from here ( it predates yo mama ). It has abso-fucking-lutely nothing to do with merb whatsoever. And “fuck you to your face” is NOT directed towards anything/anyone :)

Just for fun and profit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
require 'rubygems'
require 'thin'
require "action_controller"
require 'dispatcher'

# Rails pwned
ActionController::Base.session = { :session_key => "_myapp_session", :secret => "some secret phrase of at least 30 characters" }
ActionController::Routing.use_controllers! ['home']
ActionController::Dispatcher.unprepared = false
Dependencies.mechanism = :require

class ActionController::Dispatcher
  def prepare_application
  end
end

# And your routes
ActionController::Routing::Routes.draw do |map|
  map.root :controller => 'home'
  map.connect ':controller/:action/:id'
end

# Application code
class ::HomeController < ActionController::Base
  def index
    render :text => "fuck you to your face"
  end
  
  def hello
    render :text => params.inspect
  end
end

# Make Thin Happy
class LifoAdapter
  def call(env)
    rack_response = Rack::Response.new
    rack_request = Rack::Request.new(env)    
    cgi = Rack::Adapter::Rails::CGIWrapper.new(rack_request, rack_response)
    ActionController::Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, rack_response)    
    rack_response.finish
  end
end

# Run LifoAdapter
Thin::Server.start('0.0.0.0', 3000) do
  use Rack::CommonLogger
  run LifoAdapter.new
end

UPDATE 2

I just made a few more changes for funkification, which you can find at my github repository

With tinyrails, the “hello world” application looks like :

1
2
3
4
5
6
7
8
9
10
11
12
13
# thin -p 3000 -r mini.rb start

require 'tinyrails'

routes { root :controller => 'home' }

controller "home" do
  def index
    render :text => "Hello World"
  end
end

start

You can also use models/views with tinyrails, as demonstrated here

UPDATE 3 : Fabiano França changed the code to run it with webrick/mongrel. He also has a funky microrest framework. His pastie can be found here

Tidbits from my crap 1

Posted by pratik
on Saturday, February 02

I’ve always been in the habit of maintaining a file called crap.rb under my home directory, which I mainly use for benchmarking and testing some tiny stuff. So here are some amusing/useful benchmarks from my crap( :?\.rb), the only file where I use __END__ !

The irregular Regular Expressions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
require 'benchmark'

n = 1000000
s = "hey hello world"

r1 = Regexp.new(/hello/)
r2 = /hello/

Benchmark.bm do |x|
  x.report("Regxp.new       ") { n.times { s =~ r1 } }
  x.report("Funky slash     ") { n.times { s =~ r2 } }
  x.report("No Object       ") { n.times { s =~ /hello/ } }
  
  x.report("Regxp.new match ") { n.times { r1.match(s) } }
  x.report("Funky match     ") { n.times { r2.match(s) } }
  x.report("No Object match ") { n.times { /hello/.match(s) } }
end

null:~ lifo$ ruby crap.rb 
      user     system      total        real
Regxp.new         0.570000   0.000000   0.570000 (  0.584298)
Funky slash       0.600000   0.000000   0.600000 (  0.599363)
No Object         0.450000   0.010000   0.460000 (  0.454105)
Regxp.new match   1.340000   0.000000   1.340000 (  1.353320)
Funky match       1.350000   0.010000   1.360000 (  1.352977)
No Object match   1.340000   0.000000   1.340000 (  1.357741)

Various http client libraries

This is one of my favorites.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
['rubygems', 'benchmark', 'eventmachine', 'net/http', 'open-uri', 'rfuzz/session'].each {|lib| require lib }

server      = 'localhost'
port        = 9292
request_uri = "http://#{server}:#{port}/"

def run(name, x)
  x.report(name) do
    100.times do
      yield
    end
  end
end

uri = URI.parse(request_uri)
puts Net::HTTP.get(uri)

rfuzz = RFuzz::HttpClient.new(server, port)
puts rfuzz.get('/').http_body

puts open(request_uri).read

EM.epoll
http = nil
EM.run do
  http = EM::Protocols::HttpClient2.connect(server, port).get("/")
  http.callback { EM.stop  }
end
puts http.content
EM.run { EM::Protocols::HttpClient2.connect(server, port).get("/").callback { EM.stop  } }

Benchmark.bm do |x|
  
  run("Ruby Net::HTTP ", x) do
    Net::HTTP.get(uri)
  end
  
  run("Open URI       ", x) do
    open(request_uri).read
  end
  
  run("RFuzz          ", x) do
    rfuzz.get('/').http_body
  end
  
  run("Event Machine  ", x) do
    EM.run { EM::Protocols::HttpClient2.connect(server, port).get("/").callback {  EM.stop } }
  end
  
end

      user     system      total        real
Ruby Net::HTTP   0.090000   0.070000   0.160000 (  7.380255)
Open URI         0.160000   0.100000   0.260000 (  7.816298)
RFuzz            0.050000   0.050000   0.100000 (  7.988522)
Event Machine    0.040000   0.020000   0.060000 (  0.186210)

Camelize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
require 'benchmark'
require 'strscan'

n = 100000

u = "hello_world/whatever"

class String
  # From rails
  def camelize
    self.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
  end
  
  # From merb
  def mamelize
    new_string = ""
    input = StringScanner.new(self.downcase)
    until input.eos?
      if input.scan(/([a-z][a-zA-Z\d]*)(_|$|\/)/)
        new_string << input[1].capitalize
        new_string << "::" if input[2] == '/'
      end
    end
    new_string
  end
  
  def lamelize
    self.split('/').map { |ss| ss.split('_').map { |sub| sub.capitalize }.join }.join('::')
  end
  
  def damelize
    self.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
  end
end

puts u.camelize
puts u.mamelize
puts u.lamelize
puts u.damelize

Benchmark.bm do |x|
  x.report("Camelize") do 
    n.times { u.camelize }
  end
  
  x.report("Mamelize") do
    n.times { u.mamelize } 
  end
  
  x.report("Lamelize") do
    n.times { u.mamelize } 
  end
  
  x.report("Damelize") do
    n.times { u.damelize } 
  end
end

      user     system      total        real
Camelize  1.600000   0.010000   1.610000 (  1.616453)
Mamelize  1.560000   0.000000   1.560000 (  1.635481)
Lamelize  1.560000   0.010000   1.570000 (  1.578037)
Damelize  1.480000   0.010000   1.490000 (  1.486758)

ActiveRecord partial updates 7

Posted by pratik
on Tuesday, December 18

OMFG! This is the moment ya all have been waiting for..ActiveRecord partial updates are now possible !! It’ll make your application run 100x faster!!

Background

ActiveRecord updates all the columns when you save the object, without bothering to see if the column was changed or not.

Notice the UPDATE statement in the following console session :

1
2
3
4
5
6
>> p = Person.find :first
  Person Load (0.002784)   SELECT * FROM people LIMIT 1
=> #<Person id: 1, name: "Pratik", address: "Shangri-la", history: "pff", created_at: "2007-12-18 05:07:53", updated_at: "2007-12-18 06:08:13">
>> p.save
  Person Update (0.001178)   UPDATE people SET "created_at" = '2007-12-18 05:07:53', "name" = 'Pratik', "history" = 'pff', "address" = 'Shangri-la', "updated_at" = '2007-12-18 06:20:11' WHERE "id" = 1
=> true

O MAN !

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
module ActiveRecord
  module Changed
    def self.included(base)
      base.alias_method_chain :write_attribute, :changed
      base.alias_method_chain :update_without_timestamps, :changed
      base.alias_method_chain :save, :changed
      base.alias_method_chain :save!, :changed
    end
    
    private
    
    def write_attribute_with_changed(attr_name, value)
      # If you're accessing attr= method, you should change the value ;-)
      changed_attributes << attr_name.to_s
      write_attribute_without_changed(attr_name, value)
    end
    
    def update_without_timestamps_with_changed      
      quoted_attributes = attributes_with_quotes(false, false)
      quoted_attributes.reject! { |key, value| !changed_attributes.include?(key.to_s)}
      return 0 if quoted_attributes.empty?
      connection.update(
        "UPDATE #{self.class.quoted_table_name} " +
        "SET #{quoted_comma_pair_list(connection, quoted_attributes)} " +
        "WHERE #{connection.quote_column_name(self.class.primary_key)} " +
        "= #{quote_value(id)}",
        "#{self.class.name} Update"
      )
    end
    
    def changed_attributes
      @changed_attributes ||= Set.new
    end
    
    def save_with_changed
      save_without_changed ensure changed_attributes.clear
    end

    def save_with_changed!
      save_without_changed! ensure changed_attributes.clear
    end
    
  end
end

ActiveRecord::Base.send :include, ActiveRecord::Changed

Ok, you won’t too much of performance boost with this. I lied. It was a joke. Get over it.

Using this code :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>> p = Person.find :first
  Person Load (0.000538)   SELECT * FROM people LIMIT 1
=> #<Person id: 1, name: "Hellll", address: "whatever", history: "pff", created_at: "2007-12-18 05:07:53", updated_at: "2007-12-18 05:40:45">
>> p.save
  Person Update (0.000536)   UPDATE people SET "updated_at" = '2007-12-18 06:07:55' WHERE "id" = 1
=> true
>> p.name = "Pratik"
=> "Pratik"
>> p.save
  Person Update (0.000908)   UPDATE people SET "name" = 'Pratik', "updated_at" = '2007-12-18 06:08:04' WHERE "id" = 1
=> true
>> p.address = "Shangri-la"
=> "Shangri-la"
>> p.save
  Person Update (0.000542)   UPDATE people SET "address" = 'Shangri-la', "updated_at" = '2007-12-18 06:08:13' WHERE "id" = 1
=> true

But..

Yes, there is a big fat but here

Do partial updates make sense ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Whatever < <Something>::Base
  def validate
    errors.add("Invalid") if self.foo == "Hello" and self.bar == "World"
  end
end

Current Record State : { :foo => "abc", :bar => "xyz" }

( Two processes fetch the same record concurrently )
t0 : Process 1 : Fetch the record | Process 2 : Fetch the record

t1 : Process 1 : set 'foo' to "Hello". keep 'bar' as "xyz"
 { :foo => "Hello", :bar => "xyz" }
Partial update will execute the query only to update :foo

t2 : Process 2 : set 'bar' to "World". keep 'bar' as "abc"
 { :foo => "abc", :bar => "World" }
Partial update will execute the query only to update :bar

t3 : Final state of the record :
 { :foo => "Hello", :bar => "World" }

Yes, it can leave your records in invalid state

Awww…Did I make you sad :-( ?

Well, don’t worry. The point is :

  • It is very simple to do partial updates with ActiveRecord. It’s no rocket science.
  • Know what you’re doing. Make sure you don’t screw up validations. Use optimistic locking. ( Thanks to Lawrence for pointing it out )
  • Probably go for more declarative style if you really have a situation where partial updates will make a difference. Something like :
1
2
3
class Whatever < <Something>::Base
  lazy_attributes :some_column_which_is_huge_and_rarely_changes
end

And apply the partial update logic only to the attributes supplied to lazy_attributes

A few tiny never/rarely seen before rails 2.0 features 2

Posted by pratik
on Saturday, December 15

Asset Caching

I had talked about this before in the past. Basically, rails allows you to merge your css and javascript files in a single file which is cachable by the browsers. Hence, there’ll only be just one request ( which would return 304 response from 2nd time onwards ) per page, which would result in faster loading time for your visitors.

I tend to put these lines in my layouts :

1
2
<%= javascript_include_tag :all, :cache => true %>
<%= stylesheet_link_tag :all, :cache => true %>

Migrations now accept command line attributes

So let’s say to add a string called “name” to my users table, now I’d just do :


script/generate migration AddNameToUser name:string

And to remove a column called “useless” from users table :


script/generate migration RemoveUselessFromUser useless:string

Please note that, when you generate a migration to remove columns, attribute type may seem trivial. But it is used to generate down migration, which would re-create those columns.

Better Exception Handling

In addition to what Ryan said, you can have inline exception handlers as well.

1
2
3
rescue_from ActiveRecord::RecordNotFound
  render :file => '/bad_record', :status => 404
end

Block can even take have an argument for exception object.

1
2
3
rescue_from  ActiveRecord::RecordInvalid do |exception|
  render :action => (exception.record.new_record? ? 'new' : 'edit')
end

The above snippet would greatly DRY up your controllers if you use save! and create! in your RESTful application.

Request Profiler

Rails now includes request profiler which uses ruby-prof and generates fancy pants graphs for you. You’ll need to supply an input file to use with request profiler, which would be in your usual integration test format. A sample input file for an application using restful_authentication plugin can look somewhat like :

1
2
lifo:freeonrails pratik$ cat perfscript 
post('/sessions', { :login => 'quentin', :password => 'test' })

And then roll it like :


lifo:freeonrails pratik$ script/performance/request perfscript

And the one liners

  • render partial collection now works with a collection of hash.

<%= render :partial => 'foo', :collection => [{ :name => 'world', :address => 'bar' }, { :name => 'food', :address => 'world' }] %>
  • ActiveRecord::Base#becomes to change record’s type

render :partial => @pet.becomes(Cat) # renders cats/cat instead of pets/pet
  • link_to(:back) for “Back” button using request.env[“HTTP_REFERER”] or ‘javascript:history.back()’ ( fallback )
  • error_messages_for accepts objects so that you can display error for local variable

<%= error_messages_for 'user', :object => @question.user  %>
  • Array#rand
1
2
3
[].rand       # => nil
['a'].rand    # => 'a'
[1,2,3].rand  # => 1 or 2 or 3
  • Hash#to_query

{ :user => {:name => "lifo"} }.to_query => "user%5Bname%5D=lifo"

This post is for covering smaller fixes/features which are not very easy to spot. For very exciting and rather major Rails 2.0 features, I’d recommend you check out :

Query objects and delayed execution 8

Posted by pratik
on Wednesday, December 12

So this morning I got up after sleeping 3-4 hours and all I can somehow think of is having Query objects for ActiveRecord finders and delayed query execution. If done well, this could open pandora’s box of neat ways to extend ActiveRecord. So talk stops here.

Here’s the very basic first draft done under influence ( coffee ;-) ) :

# activerecord/lib/active_record/abstract_query.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
module ActiveRecord
  class AbstractRecords
    attr_reader :records, :klass, :query
        
    delegate :connection, :instantiate, :name, :to => :klass
    delegate :sql, :options, :to => :query
    
    def initialize(query, klass)
      @query = query
      @klass = klass
      @loaded = false
    end
    
    def method_missing(method_id, *args, &block)
      load_records
      records.send(method_id, *args, &block)
    end
    
    def loaded?
      @loaded
    end
    
    private
    
    def load_records
      return if @loaded
      @records = connection.select_all(sql, "#{name} Load").collect! { |record| instantiate(record) }
      @records.each { |record| record.readonly! } if options[:readonly]
      @loaded = true
    end
  end
  
  class AbstractQuery < DelegateClass(AbstractRecords)
    attr_reader :sql, :options
    
    def initialize(klass, sql, options = {})
      @sql = sql
      @options = options
      super(AbstractRecords.new(self, klass))
    end
    
  end
end

So after this, a session from console would look something like :

1
2
3
4
5
>> i = Item.find :all, :limit => 10
=> #<ActiveRecord::AbstractRecords:0x19520e4 @klass=Item(id: integer, name: string, created_at: datetime, updated_at: datetime), @loaded=false, @query=#<ActiveRecord::AbstractRecords:0x19520e4 ...>>
>> i.first
  Item Load (0.000361)   SELECT * FROM `items` LIMIT 10
=> #<Item id: 1, name: "wtf", created_at: "2007-12-12 13:28:56", updated_at: "2007-12-12 13:28:56">

Notice when the query gets executed. Experimental patch can be found here

Namespaced models 19

Posted by pratik
on Sunday, December 09

I don’t really understand why people use namespaced models. I see ActiveRecord models as DSL for database. There is no concept of namespacing in Database, then why should you have them with models ? Apart from that, they are very buggy too !

“I am generally not a huge fan of namespaces for models. As I don’t think that’s a good fit for splitting up your domain.” - DHH

From what I’ve seen, the most common explanations given are :

  • To organize models
  • To reuse the code

Now let’s look at elegant solutions for both these problems.

For the purpose of this article, let’s assume you have models for different kind of pets. e.g. Dog, Cat & Rabbit.

How to organize models ?

Rails by default, wants you to put all your models in RAILS_ROOT/app/models directory. But that’s a convention. There is absolutely nothing that stops you from putting your model files anywhere you wish and organize them according to your liking and based on application specific logical groups.

1
2
3
4
5
Rails::Initializer.run do |config|
  # Your existing stuff

  config.load_paths << "#{RAILS_ROOT}/app/models/pets"
end

That’s it ! Now you can have dog.rb, cat.rb & rabbit.rb inside RAILS_ROOT/app/models/pets directory.

But what about reuse !?

Two ways to skin this cat :

  • Good ol’ mixins
  • Abstract models

Abstract models are the models which cannot have objects ( cannot be instantiated ) and hence they don’t have associated table as well. Every rails developer uses abstract model in their code without knowing it. ActiveRecord::Base. In our case, we can have an abstract model called Pet for keeping the common behavior of all the pets. And our models would look something like :

# RAILS_ROOT/app/models/pets/pet.rb
1
2
3
4
5
6
class Pet < ActiveRecord::Base
  self.abstract_class = true
  
  belongs_to :person
  validates_presence_of :name
end
# RAILS_ROOT/app/models/pets/dog.rb
1
2
3
4
5
class Dog < Pet
  def bark
    "baaw"
  end
end

That’s it. Dog will inherit all the methods/validations/associations from parent Pet model and so will all the other models who would inherit from Pet abstract model. Please note that this is not STI as we have set self.abstract_class = true in Pet.

Find users with at least 'n' items 5

Posted by pratik
on Thursday, November 01

This question is asked quite a few times in #rubyonrails

When your models look like :

1
2
3
4
5
6
7
class User < ActiveRecord::Base 
  has_many :items
end

class Item < ActiveRecord::Base
  belongs_to :user
end

How do you find all the users with at least ‘n’ number of items ?

Here’s how :


User.find :all, :joins => "INNER JOIN items ON items.user_id = users.id", :select => "users.*, count(items.id) items_count", :group => "items.user_id HAVING items_count > 5"

This will give you all the users with at least 5 items.

The statement is using INNER JOIN to eliminate users with no items. Also, in :select, there is count(items.id) aliased items_count and in :group is items.user_id. This will group items by user_id and also count number of items per user. Now, database requires HAVING clause when you want to supply conditions for group functions ( items_count in our case ). ActiveRecord, as of now, doesn’t provide :having key for find(). Hence, we need to use a very little hack ( more like workaround ) to overcome that and supply HAVING clause in :group key.

May be someone interested can submit a patch for :having key in AR finders.

Faster eager loading and funky joins 0

Posted by pratik
on Tuesday, October 30

I was able to spend some time on a flight and at home to work on a very annoying performance pit associated with eager loading association’s instantiation code. So, with changeset 8051, hopefully you should see some performance improvement with eagerloading associations with large data sets ( even 100 rows should be good enough to notice the difference – remember, rails does a big fat assed cartesian join when you eager load multiple associations ) – at the cost of a little extra bit of memory. You can catch the mailing list discussion here.

On a very related side note, please take some time to check changeset 8054 as well. As the changeset contains really well written documentation, I wouldn’t reinvent the wheel here. This would make life a lot easier for those of you who are shit scared of sql and use eager loading in some of the worst ways possible because of that.

In any cases, I’d really encourage you to benchmark your code before choosing any solution. If you have never done it before, and you got scared by looking at the generated output, it’s time to try it again. I’d suggest you start with ruby-prof HTML call graphs which are explained very well here

The sample performance script can look as simple as :

1
2
3
4
5
6
7
8
require 'ruby-prof'
puts "Sanity check..."
puts Person.find(:all, :include => :items).inspect
results = RubyProf.profile { Person.find(:all, :include => :items) }
File.open "#{RAILS_ROOT}/tmp/profile-graph.html", 'w' do |file|
  RubyProf::GraphHtmlPrinter.new(results).print(file)
  `open #{file.path}`
end

And just run the script with script/runner of your rails application. For changeset 8051, you can see my before and after graphs to get a basic idea.

Fixtures go foxy 3

Posted by pratik
on Friday, October 26

With Changeset 8036

Following

1
2
3
4
5
6
7
8
9
10
11
# clients.yml
monkeys: 
  id: 1 
  name: 3 Monkeys Ltd.

# employees.yml 

lifo: 
  id: 1 
  name: Pratik
  client_id: 1

Now can be re-written as

1
2
3
4
5
6
7
8
9
# clients.yml
monkeys: 
  name: 3 Monkeys Ltd.

# employees.yml 

lifo: 
  name: Pratik
  client: monkeys 

There are some more goodies there as well. Please check out documentation and code for more details.

P.S -> Now there is a built-in performance and benchmarking support as well. Just do rake rails:update:scripts to get them in your app running bleeding edge.

How to access session, cookies, params, request in Model !! 16

Posted by pratik
on Thursday, October 18

For fuck’s sake, STOP asking this question in IRC

You’re completely/totally/fucking wrong if you want to access sessions, params, cookies, etc. in your Models.

You can’t get more wrong than this in rails world probably. But some people just don’t get it. So if you must, this is how you can do it ( I’d suggest you go back to PHP or whatever ) :

Add this code at the bottom of your application.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ApplicationController < ActionController::Base
  # Your existing stuff
  around_filter :you_dont_have_bloody_clue
  
  protected
  
  def you_dont_have_bloody_clue
    klasses = [ActiveRecord::Base, ActiveRecord::Base.class]
    methods = ["session", "cookies", "params", "request"]
    
    methods.each do |shenanigan|
      oops = instance_variable_get(:"@_#{shenanigan}") 
      
      klasses.each do |klass|
        klass.send(:define_method, shenanigan, proc { oops })
      end
    end
    
    yield
    
    methods.each do |shenanigan|      
      klasses.each do |klass|
        klass.send :remove_method, shenanigan
      end
    end
    
  end
end

Again, do it if you wish, just don’t ask anyone how to do it ever again!

Dispatcher callbacks 1

Posted by pratik
on Tuesday, October 16

Changeset 7640 introduced 2 new members to rails family of callbacks : before_dispatch and after_dispatch. These callbacks are executed before/after every request, and provides you with hooks at much higher level than normal before_filter and after_filter of ActionController::Base.

Also, note that after_filter are not executed if your action raises an exception. So dispatcher callbacks might be a better fit for performance related logging, etc. where the final outcome of your controller doesn’t really matter, and you want your callbacks to be executed for each and every request.

For example, try the following in your environment.rb :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require 'dispatcher'
module ActionController
  class Dispatcher
    def start_timer
      @start_time = Time.now
    end
    
    def end_timer
      RAILS_DEFAULT_LOGGER.info "="*100
      RAILS_DEFAULT_LOGGER.info "      URL : #{@request.url}"
      RAILS_DEFAULT_LOGGER.info "     Real : #{Time.now-@start_time} seconds"
      RAILS_DEFAULT_LOGGER.info "Less Real : #{@response.headers["X-Runtime"]} seconds"
      RAILS_DEFAULT_LOGGER.info "="*100
    end
    
    before_dispatch :start_timer
    after_dispatch :end_timer
  end
end

has_many and habtm callbacks 5

Posted by pratik
on Tuesday, October 16

Very few people are aware of existence of has_many and habtm association callbacks : before/after_add & before/after_remove as they’re hidden somewhere deep inside documentation. But until now they were very much unusable and buggy. Thanks to bitsweat’s commit of my patch, now we can actually use them :-)

This can be a great step towards our famous Skinny Controller, Fat Model methodology, as these callbacks allow you to move a great amount of logic to models :

1
2
3
4
5
6
7
8
9
10
11
class Client < ActiveRecord::Base  
  has_many :employees, :after_add => :assign_project, :after_remove => :reassign_projects
  
  def assign_projects(employee)
    ...
  end
  
  def reassign_projects(employee)
    ...
  end
end

These callbacks still may have some room for improvement, please do use them and report back any issues you face with them at Rails Trac and you can add me ( trac username : lifofifo ) to the CC list of you ticket.