Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added statement cache

  • Loading branch information...
commit af1a4bdc564864e80b234755651303dac68ae82e 1 parent 45321a6
@olliwer olliwer authored
View
17 activerecord/CHANGELOG.md
@@ -1,5 +1,22 @@
## Rails 4.0.0 (unreleased) ##
+* Added Statement Cache to allow the caching of a single statement. The cache works by
+ duping the relation returned from yielding a statement which allows skipping the AST
+ building phase.
+
+ Example:
+
+ cache = ActiveRecord::StatementCache.new do
+ Book.where(:name => "my book").limit(100)
+ end
+
+ books = cache.execute
+
+ The solution attempts to get closer to the speed of `find_by_sql` but still maintaining
+ the expressiveness of the AR queries.
+
+ *Olli Rissanen*
+
* Fixing issue #8345. Now throwing an error when one attempts to touch a
new object that has not yet been persisted. For instance:
View
1  activerecord/lib/active_record.rb
@@ -56,6 +56,7 @@ module ActiveRecord
autoload :SchemaMigration
autoload :Scoping
autoload :Serialization
+ autoload :StatementCache
autoload :Store
autoload :Timestamp
autoload :Transactions
View
26 activerecord/lib/active_record/statement_cache.rb
@@ -0,0 +1,26 @@
+module ActiveRecord
+
+ # Statement cache is used to cache a single statement in order to avoid creating the AST again.
+ # Initializing the cache is done by passing the statement in the initialization block:
+ #
+ # cache = ActiveRecord::StatementCache.new do
+ # Book.where(:name => "my book").limit(100)
+ # end
+ #
+ # The cached statement is executed by using the +execute+ method:
+ #
+ # cache.execute
+ #
+ # The relation returned by yield is cached, and for each +execute+ call the cached relation gets duped.
+ # Database is queried when +to_a+ is called on the relation.
+ class StatementCache
+ def initialize
+ @relation = yield
+ raise ArgumentError.new("Statement cannot be nil") if @relation.nil?
+ end
+
+ def execute
+ @relation.dup.to_a
+ end
+ end
+end
View
64 activerecord/test/cases/statement_cache_test.rb
@@ -0,0 +1,64 @@
+require 'cases/helper'
+require 'models/book'
+require 'models/liquid'
+require 'models/molecule'
+require 'models/electron'
+
+module ActiveRecord
+ class StatementCacheTest < ActiveRecord::TestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_statement_cache_with_simple_statement
+ cache = ActiveRecord::StatementCache.new do
+ Book.where(name: "my book").where("author_id > 3")
+ end
+
+ Book.create(name: "my book", author_id: 4)
+
+ books = cache.execute
+ assert_equal "my book", books[0].name
+ end
+
+ def test_statement_cache_with_nil_statement_raises_error
+ assert_raise(ArgumentError) do
+ cache = ActiveRecord::StatementCache.new do
+ nil
+ end
+ end
+ end
+
+ def test_statement_cache_with_complex_statement
+ cache = ActiveRecord::StatementCache.new do
+ Liquid.joins(:molecules => :electrons).where('molecules.name' => 'dioxane', 'electrons.name' => 'lepton')
+ end
+
+ salty = Liquid.create(name: 'salty')
+ molecule = salty.molecules.create(name: 'dioxane')
+ electron = molecule.electrons.create(name: 'lepton')
+
+ liquids = cache.execute
+ assert_equal "salty", liquids[0].name
+ end
+
+ def test_statement_cache_values_differ
+ cache = ActiveRecord::StatementCache.new do
+ Book.where(name: "my book")
+ end
+
+ for i in 0..2 do
+ Book.create(name: "my book")
+ end
+
+ first_books = cache.execute
+
+ for i in 0..2 do
+ Book.create(name: "my book")
+ end
+
+ additional_books = cache.execute
+ assert first_books != additional_books
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.