Peter Marklund

Peter Marklund's Home

Mon March 26, 2007
Programming

Rails Extension: ActiveRecord::Base.find(:last)

I'm at the Big Nerd Ranch Ruby on Rails bootcamp in a monastery outside Frankfurt right now having a great time. One of the students asked if you can say ActiveRecord::Base.find(:last), which you can't. The value of the first argument is typically :first, but you can't use :last. Just for educational purposes I decided to change that (it's probably not something I would use in production):

ActiveRecord::Base # Make sure class is loaded, which it probably is by now anyway
module ActiveRecord
  class Base
    def self.find_with_last(*args)
      if args.first == :last
        options = extract_options_from_args!(args)
        find_without_last(:first, options.merge(:order => "#{primary_key} DESC"))
      else
        find_without_last(*args)
      end
    end

    class << self # Needed because we are redefining a class method
      alias_method_chain :find, :last
    end    
  end
end

I guess most interestingly this code illustrates how to alias a static method.

Comments

Dr Nic said over 7 years ago:

I was thinking about this API just the other day, cool.

If you want part 2 of the challenge (that is, where I stopped thinking about it :) make :last work for where an :order option is passed in.

Again, very cool.

--------------------------------------------------------------------------------

Mathias Stjernstrom said over 7 years ago:

Thats cool!

But find(:first) is not a method for returning the row with lowest primary key is a method return the first row found. For example PostgreSQL moves a updated row to the end of the table (if not order is used).

If you for example have a Page model with 3 rows

# select * from pages;

ID, Title
-----------------
1 Page 1
2 Page 2
3 Page 3

Page.find(:first).id
=> 1

Will generate this query

"SELECT * FROM pages LIMIT 1"

And returning 1

But if you update page 1's title

p = Page.find(:first)
p.title = 'Page 1 upd'
p.save

Page.find(:first).id
=> 2

# select * from pages;

ID, Title
-----------------
2 Page 2
3 Page 3
1 Page 1 upd

I think :first should be renamed to :one

Anyhow cool post and it does illustrates how to alias a static method :-)

Cheers!

--------------------------------------------------------------------------------

F said over 7 years ago:

Ruby is not Java!!

There is no such thing as a "static method" in ruby. What you are referring to are class methods and they are very different from static methods in Java.

Static methods in java are statically resolved (hence the name) and don't have a receiver (i.e. the object to which the method belongs). Methods in ruby are _never_ statically resolved, and _always_ have a receiver. It just happens that the receiver can be class object.

Also, you cannot assume anything about the ordering of primary keys the way you do so your plugin basically doesn't work.

--------------------------------------------------------------------------------

Herb said over 6 years ago:

find(:first, :order => 'id DESC')

--------------------------------------------------------------------------------

phil said over 6 years ago:

Hi,

Nice, thanks!

since 2.0 you should use
options = args.extract_options!
instead of
options = extract_options_from_args!(args)

Cheers!

--------------------------------------------------------------------------------