Search code examples
ruby-on-railsrubyactiverecordopen-sourcecustomization

I would like to create 'second', 'third' (perhaps up to 'ninth') custom methods for ActiveRecord and maybe push (contribute) the changes to rails


I've never submitted anything to an open source project, and frankly, don't care what the rails people think about the methods. I love the 'last' and 'first' methods and would like to extend that concept into the methods 'second'-'ninth' for the purposes of sampling.

How might I create my own custom method that would graft these methods onto ActiveRecord?

**Currently watching Ryan Bates's screencast #50 at railscasts.com

** brainstorming on how to implement this, I'm thinking it could loop over the :id values (0, 1, 2, 3, 4...) until it finds the next record. And then the 'third' command could skip past the first match until it finds the second match (Model.third), and so on. Ha now I'm wondering if .first uses this same method. I suppose I'll check out the source code. :)

** As I venture down this road I'll be leaving a list of relevant links (I can't list more than 2 links unless I get 10 points. give me a one-up so I can add more)

http://railscasts.com/episodes/50-contributing-to-rails

http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html


Solution

  • It's actually a bit more complex than you might think, because first and last are not individual methods on individual classes, but they are implemented on many different classes in ActiveRecord.

    For example,

    • You can use Post.first, in which case you are calling ActiveRecord::Base#first.

    • You can use Post.where(:submitted => true).first, in which case you are calling ActiveRecord::Relation#first

    • You can use Post.comments.first, in which case you are calling ActiveRecord::Associations::CollectionAssociation#first

    See where this is going? In order to have similar behavior for second, third, and so on, you need to implement these methods in several places, not just one.

    But just to get you started, here's how you could implement ActiveRecord::Relation#second:

    class ActiveRecord::Relation
      def second
        if loaded?
          @records[1]
        else
          @second ||= limit(2).to_a[1]
        end
      end
    end
    

    Execute this piece of code in an initializer, for example, and all of a sudden you call second on all relations.