Module ActiveRecord::Extensions
In: ar-extensions/lib/ar-extensions/create_and_update.rb
ar-extensions/lib/ar-extensions/extensions.rb
ar-extensions/lib/ar-extensions/util/sql_generation.rb
ar-extensions/lib/ar-extensions/util/support_methods.rb
ar-extensions/lib/ar-extensions/version.rb

ActiveRecord::Extensions provides additional functionality to the ActiveRecord ORM library created by DHH for Rails.

It‘s main features include:

  • better finder support using a :conditions Hash for ActiveRecord::Base#find
  • better finder support using any object that responds to the to_sql method
  • mass data import functionality
  • a more modular design to extending ActiveRecord

Using Better Finder Hash Support

Here are a few examples, please refer to the class documentation for each extensions:

 class Post < ActiveRecord::Base ; end

 Post.find( :all, :conditions=>{
   :title => "Title",                           # title='Title'
   :author_contains => "Zach",                  # author like '%Zach%'
   :author_starts_with => "Zach",               # author like 'Zach%'
   :author_ends_with => "Dennis",               # author like '%Zach'
   :published_at => (Date.now-30 .. Date.now),  # published_at BETWEEN xxx AND xxx
   :rating => [ 4, 5, 6 ],                      # rating IN ( 4, 5, 6 )
   :rating_not_in => [ 7, 8, 9 ]                # rating NOT IN( 4, 5, 6 )
   :rating_ne => 4,                             # rating != 4
   :rating_gt => 4,                             # rating > 4
   :rating_lt => 4,                             # rating < 4
   :content => /(a|b|c)/                        # REGEXP '(a|b|c)'
 )

Create Your Own Finder Extension Example

The following example shows you how-to create a robust and reliable finder extension which allows you to use Ranges in your :conditions Hash. This is the actual implementation in ActiveRecord::Extensions.

 class RangeExt
   NOT_IN_RGX = /^(.+)_(ne|not|not_in|not_between)$/

   def self.process( key, val, caller )
     return nil unless val.is_a?( Range )
     match_data = key.to_s.match( NOT_IN_RGX )
     key = match_data.captures[0] if match_data
     fieldname = caller.connection.quote_column_name( key )
     min = caller.connection.quote( val.first, caller.columns_hash[ key ] )
     max = caller.connection.quote( val.last, caller.columns_hash[ key ] )
     str = "#{caller.quoted_table_name}.#{fieldname} #{match_data ? 'NOT ' : '' } BETWEEN #{min} AND #{max}"
     Result.new( str, nil )
  end

Using to_sql Ducks In Your Find Methods!

The below example shows you how-to utilize objects that respond_to the method to_sql in your finds:

 class InsuranceClaim < ActiveRecord::Base ; end

 class InsuranceClaimAgeAndTypeQuery
   def to_sql
      "age_in_days BETWEEN 1 AND 60 AND claim_type IN( 'typea', 'typeb' )"
   end
 end

 claims = InsuranceClaim.find( :all, InsuranceClaimAgeAndTypeQuery.new )

 claims = InsuranceClaim.find( :all, :conditions=>{
   :claim_amount_gt => 30000,
   :age_and_type => InsuranceClaimAgeAndTypeQuery.new }
 )

Importing Lots of Data

ActiveRecord executes a single INSERT statement for every call to ‘create’ and for every call to ‘save’ on a new model object. When you have only a handful of records to create or save this is not a big deal, but when you have hundreds, thousands or hundreds of thousands of records you need to have better performance.

Below is an example of how to import the least amount of INSERT statements using mechanisms provided by your database vendor:

 class Student < ActiveRecord::Base ; end

 column_names = Student.columns.map{ |column| column.name }
 value_sets = some_method_to_load_data_from_csv_file( 'students.csv' )
 options = { :valudate => true }

 Student.import( column_names, value_sets, options )

The import functionality can be used even if there is not specific support for you vendor. This happens when a particular database vendor specific enhancement hasn‘t been added to ActiveRecord::Extensions. You can still use import though because the import functionality has been created with backwards compatibility. You may still get better performance using import, but you will definitely get no worse then ActiveRecord‘s create or save methods.

See ActiveRecord::Base.import for more information and other ways to use this functionality.

Developers

  • Zach Dennis
  • Mark Van Holsytn

Homepage

Classes and Modules

Module ActiveRecord::Extensions::ConnectionAdapters
Module ActiveRecord::Extensions::CreateAndUpdate
Module ActiveRecord::Extensions::FindToCSV
Module ActiveRecord::Extensions::FinderOptions
Module ActiveRecord::Extensions::ForeignKeys
Module ActiveRecord::Extensions::FullTextSearching
Module ActiveRecord::Extensions::VERSION
Class ActiveRecord::Extensions::ArrayExt
Class ActiveRecord::Extensions::Comparison
Class ActiveRecord::Extensions::DatetimeSupport
Class ActiveRecord::Extensions::Like
Class ActiveRecord::Extensions::MySQLRegexp
Class ActiveRecord::Extensions::OracleRegexp
Class ActiveRecord::Extensions::PostgreSQLRegexp
Class ActiveRecord::Extensions::RangeExt
Class ActiveRecord::Extensions::RegexpBase
Class ActiveRecord::Extensions::SqliteRegexp

Constants

Result = Struct.new( :sql, :value )

[Validate]