Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Support for ActiveRecord 3.1

  • Loading branch information...
commit 519faaf20428d32d557a1812ab53c4daf1b70153 1 parent ebae05a
@staugaard staugaard authored
View
9 Appraisals
@@ -0,0 +1,9 @@
+appraise "activerecord-2.3" do
+ gem 'activerecord', '~> 2.3.4'
+ gem 'temping', '~> 1.3.0'
+end
+
+appraise "activerecord-3.1" do
+ gem 'activerecord', '~> 3.1.1'
+ gem 'temping', '~> 2.0.4'
+end
View
24 Gemfile
@@ -1,15 +1,15 @@
source "http://rubygems.org"
-gem 'activerecord', '~> 2.3.4'
+gem 'appraisal'
+gem 'rake'
+gem 'bundler'
+gem 'shoulda'
+gem 'mocha'
+gem 'sqlite3', :platforms => :ruby
+gem 'activerecord-jdbcsqlite3-adapter', :platforms => :jruby
+gem 'ruby-debug', :platforms => :mri_18
+gem 'ruby-debug19', :platforms => :mri_19
-group :development do
- gem 'rake'
- gem 'bundler'
- gem 'shoulda'
- gem 'mocha'
- gem 'temping', '~> 1.3.0'
- gem 'sqlite3', :platforms => :ruby
- gem 'activerecord-jdbcsqlite3-adapter', :platforms => :jruby
- gem 'ruby-debug', :platforms => :mri_18
- gem 'ruby-debug19', :platforms => :mri_19
-end
+#these are overridden in the Appraisals file
+gem 'activerecord', '~> 3.1.1'
+gem 'temping', '~> 2.0.4'
View
4 Rakefile
@@ -1,6 +1,8 @@
-require 'bundler'
+require 'bundler/setup'
Bundler::GemHelper.install_tasks
+require 'appraisal'
+
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
View
16 gemfiles/activerecord-2.3.gemfile
@@ -0,0 +1,16 @@
+# This file was generated by Appraisal
+
+source "http://rubygems.org"
+
+gem "appraisal"
+gem "rake"
+gem "bundler"
+gem "shoulda"
+gem "mocha"
+gem "sqlite3", :platforms=>:ruby
+gem "activerecord-jdbcsqlite3-adapter", :platforms=>:jruby
+gem "ruby-debug", :platforms=>:mri_18
+gem "ruby-debug19", :platforms=>:mri_19
+gem "activerecord", "~> 2.3.4"
+gem "temping", "~> 1.3.0"
+
View
76 gemfiles/activerecord-2.3.gemfile.lock
@@ -0,0 +1,76 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ actionmailer (2.3.14)
+ actionpack (= 2.3.14)
+ actionpack (2.3.14)
+ activesupport (= 2.3.14)
+ rack (~> 1.1.0)
+ activerecord (2.3.14)
+ activesupport (= 2.3.14)
+ activerecord-jdbc-adapter (1.2.1)
+ activerecord-jdbcsqlite3-adapter (1.2.1)
+ activerecord-jdbc-adapter (~> 1.2.1)
+ jdbc-sqlite3 (~> 3.7.2)
+ activeresource (2.3.14)
+ activesupport (= 2.3.14)
+ activesupport (2.3.14)
+ appraisal (0.4.0)
+ bundler
+ rake
+ archive-tar-minitar (0.5.2)
+ columnize (0.3.5)
+ jdbc-sqlite3 (3.7.2)
+ linecache (0.46)
+ rbx-require-relative (> 0.0.4)
+ linecache19 (0.5.12)
+ ruby_core_source (>= 0.1.4)
+ metaclass (0.0.1)
+ mocha (0.10.0)
+ metaclass (~> 0.0.1)
+ rack (1.1.2)
+ rails (2.3.14)
+ actionmailer (= 2.3.14)
+ actionpack (= 2.3.14)
+ activerecord (= 2.3.14)
+ activeresource (= 2.3.14)
+ activesupport (= 2.3.14)
+ rake (>= 0.8.3)
+ rake (0.9.2.2)
+ rbx-require-relative (0.0.5)
+ ruby-debug (0.10.4)
+ columnize (>= 0.1)
+ ruby-debug-base (~> 0.10.4.0)
+ ruby-debug-base (0.10.4)
+ linecache (>= 0.3)
+ ruby-debug-base19 (0.11.25)
+ columnize (>= 0.3.1)
+ linecache19 (>= 0.5.11)
+ ruby_core_source (>= 0.1.4)
+ ruby-debug19 (0.11.6)
+ columnize (>= 0.3.1)
+ linecache19 (>= 0.5.11)
+ ruby-debug-base19 (>= 0.11.19)
+ ruby_core_source (0.1.5)
+ archive-tar-minitar (>= 0.5.2)
+ shoulda (2.11.3)
+ sqlite3 (1.3.5)
+ temping (1.3.0)
+ rails (>= 2.3.3)
+
+PLATFORMS
+ java
+ ruby
+
+DEPENDENCIES
+ activerecord (~> 2.3.4)
+ activerecord-jdbcsqlite3-adapter
+ appraisal
+ bundler
+ mocha
+ rake
+ ruby-debug
+ ruby-debug19
+ shoulda
+ sqlite3
+ temping (~> 1.3.0)
View
16 gemfiles/activerecord-3.1.gemfile
@@ -0,0 +1,16 @@
+# This file was generated by Appraisal
+
+source "http://rubygems.org"
+
+gem "appraisal"
+gem "rake"
+gem "bundler"
+gem "shoulda"
+gem "mocha"
+gem "sqlite3", :platforms=>:ruby
+gem "activerecord-jdbcsqlite3-adapter", :platforms=>:jruby
+gem "ruby-debug", :platforms=>:mri_18
+gem "ruby-debug19", :platforms=>:mri_19
+gem "activerecord", "~> 3.1.1"
+gem "temping", "~> 2.0.4"
+
View
76 gemfiles/activerecord-3.1.gemfile.lock
@@ -0,0 +1,76 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activemodel (3.1.3)
+ activesupport (= 3.1.3)
+ builder (~> 3.0.0)
+ i18n (~> 0.6)
+ activerecord (3.1.3)
+ activemodel (= 3.1.3)
+ activesupport (= 3.1.3)
+ arel (~> 2.2.1)
+ tzinfo (~> 0.3.29)
+ activerecord-jdbc-adapter (1.2.1)
+ activerecord-jdbcsqlite3-adapter (1.2.1)
+ activerecord-jdbc-adapter (~> 1.2.1)
+ jdbc-sqlite3 (~> 3.7.2)
+ activesupport (3.1.3)
+ multi_json (~> 1.0)
+ appraisal (0.4.0)
+ bundler
+ rake
+ archive-tar-minitar (0.5.2)
+ arel (2.2.1)
+ builder (3.0.0)
+ columnize (0.3.5)
+ i18n (0.6.0)
+ jdbc-sqlite3 (3.7.2)
+ linecache (0.46)
+ rbx-require-relative (> 0.0.4)
+ linecache19 (0.5.12)
+ ruby_core_source (>= 0.1.4)
+ metaclass (0.0.1)
+ mocha (0.10.0)
+ metaclass (~> 0.0.1)
+ multi_json (1.0.4)
+ rake (0.9.2.2)
+ rbx-require-relative (0.0.5)
+ ruby-debug (0.10.4)
+ columnize (>= 0.1)
+ ruby-debug-base (~> 0.10.4.0)
+ ruby-debug-base (0.10.4)
+ linecache (>= 0.3)
+ ruby-debug-base19 (0.11.25)
+ columnize (>= 0.3.1)
+ linecache19 (>= 0.5.11)
+ ruby_core_source (>= 0.1.4)
+ ruby-debug19 (0.11.6)
+ columnize (>= 0.3.1)
+ linecache19 (>= 0.5.11)
+ ruby-debug-base19 (>= 0.11.19)
+ ruby_core_source (0.1.5)
+ archive-tar-minitar (>= 0.5.2)
+ shoulda (2.11.3)
+ sqlite3 (1.3.5)
+ temping (2.0.4)
+ activerecord (~> 3.1.1)
+ activesupport (~> 3.1.1)
+ sqlite3 (~> 1.3.4)
+ tzinfo (0.3.31)
+
+PLATFORMS
+ java
+ ruby
+
+DEPENDENCIES
+ activerecord (~> 3.1.1)
+ activerecord-jdbcsqlite3-adapter
+ appraisal
+ bundler
+ mocha
+ rake
+ ruby-debug
+ ruby-debug19
+ shoulda
+ sqlite3
+ temping (~> 2.0.4)
View
4 kasket.gemspec
@@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
-require 'kasket'
+require 'kasket/version'
Gem::Specification.new do |s|
s.name = "kasket"
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
s.summary = "A write back caching layer on active record"
s.description = "puts a cap on your queries"
- s.add_runtime_dependency("activerecord", "~> 2.3.4")
+ s.add_runtime_dependency("activerecord", ">= 2.3.4", "< 3.2")
s.add_development_dependency("rake")
s.add_development_dependency("bundler")
View
27 lib/kasket.rb
@@ -2,33 +2,36 @@
require 'active_record'
require 'active_support'
-require 'kasket/active_record_patches'
+require 'kasket/version'
+#require 'kasket/active_record_patches'
module Kasket
autoload :ConfigurationMixin, 'kasket/configuration_mixin'
autoload :ReloadAssociationMixin, 'kasket/reload_association_mixin'
autoload :Query, 'kasket/query'
+ autoload :Visitor, 'kasket/visitor'
+ autoload :SelectManagerMixin, 'kasket/select_manager_mixin'
+ autoload :RelationMixin, 'kasket/relation_mixin'
CONFIGURATION = {:max_collection_size => 100}
- class Version
- MAJOR = 1
- MINOR = 0
- PATCH = 2
- STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
- end
-
module_function
def setup(options = {})
- return if ActiveRecord::Base.extended_by.member?(Kasket::ConfigurationMixin)
+ return if ActiveRecord::Base.respond_to?(:has_kasket)
CONFIGURATION[:max_collection_size] = options[:max_collection_size] if options[:max_collection_size]
ActiveRecord::Base.extend(Kasket::ConfigurationMixin)
- ActiveRecord::Associations::BelongsToAssociation.send(:include, Kasket::ReloadAssociationMixin)
- ActiveRecord::Associations::BelongsToPolymorphicAssociation.send(:include, Kasket::ReloadAssociationMixin)
- ActiveRecord::Associations::HasOneThroughAssociation.send(:include, Kasket::ReloadAssociationMixin)
+
+ if defined?(ActiveRecord::Relation)
+ ActiveRecord::Relation.send(:include, Kasket::RelationMixin)
+ Arel::SelectManager.send(:include, Kasket::SelectManagerMixin)
+ end
+
+ # ActiveRecord::Associations::BelongsToAssociation.send(:include, Kasket::ReloadAssociationMixin)
+ # ActiveRecord::Associations::BelongsToPolymorphicAssociation.send(:include, Kasket::ReloadAssociationMixin)
+ # ActiveRecord::Associations::HasOneThroughAssociation.send(:include, Kasket::ReloadAssociationMixin)
end
def self.cache_store=(options)
View
17 lib/kasket/read_mixin.rb
@@ -8,8 +8,17 @@ class << base
end
end
- def find_by_sql_with_kasket(sql)
- query = kasket_parser.parse(sanitize_sql(sql)) if use_kasket?
+ def find_by_sql_with_kasket(*args)
+ sql = args[0]
+
+ if use_kasket?
+ if sql.is_a?(String)
+ query = kasket_parser.parse(sanitize_sql(sql))
+ else
+ query = sql.to_kasket_query(self, args[1])
+ end
+ end
+
if query && has_kasket_index_on?(query[:index])
if query[:key].is_a?(Array)
find_by_sql_with_kasket_on_id_array(query[:key])
@@ -21,11 +30,11 @@ def find_by_sql_with_kasket(sql)
Array.wrap(value).collect { |record| instantiate(record.dup) }
end
else
- store_in_kasket(query[:key], find_by_sql_without_kasket(sql))
+ store_in_kasket(query[:key], find_by_sql_without_kasket(*args))
end
end
else
- find_by_sql_without_kasket(sql)
+ find_by_sql_without_kasket(*args)
end
end
View
9 lib/kasket/relation_mixin.rb
@@ -0,0 +1,9 @@
+module Kasket
+ module RelationMixin
+ def to_kasket_query(binds = [])
+ if arel.is_a?(Arel::SelectManager)
+ arel.to_kasket_query(klass, binds)
+ end
+ end
+ end
+end
View
26 lib/kasket/select_manager_mixin.rb
@@ -0,0 +1,26 @@
+module Kasket
+ module SelectManagerMixin
+ def to_kasket_query(klass, binds = [])
+ query = Kasket::Visitor.new(klass, binds).accept(ast)
+
+ return nil if query.nil? || query == :unsupported
+ return nil if query[:attributes].blank?
+
+ query[:index] = query[:attributes].map(&:first)
+
+ if query[:limit]
+ return nil if query[:limit] > 1
+ # return nil if !query[:index].include?(:id)
+ end
+
+ if query[:index].size > 1 && query[:attributes].any? { |attribute, value| value.is_a?(Array) }
+ return nil
+ end
+
+ query[:key] = klass.kasket_key_for(query[:attributes])
+ query[:key] << '/first' if query[:limit] == 1 && query[:index] != [:id]
+
+ query
+ end
+ end
+end
View
9 lib/kasket/version.rb
@@ -0,0 +1,9 @@
+# -*- encoding: utf-8 -*-
+module Kasket
+ class Version
+ MAJOR = 2
+ MINOR = 0
+ PATCH = 0
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
+ end
+end
View
135 lib/kasket/visitor.rb
@@ -0,0 +1,135 @@
+require 'arel'
+
+module Kasket
+ class Visitor < Arel::Visitors::Visitor
+ def initialize(model_class, binds)
+ @model_class = model_class
+ @binds = binds.dup
+ end
+
+ def accept(object)
+ self.last_column = nil
+ super
+ end
+
+ def last_column=(col)
+ Thread.current[:arel_visitors_to_sql_last_column] = col
+ end
+
+ def last_column
+ Thread.current[:arel_visitors_to_sql_last_column]
+ end
+
+ def column_for(name)
+ @model_class.columns_hash[name.to_s]
+ end
+
+ def visit_Arel_Nodes_SelectStatement(o)
+ return :unsupported if o.with
+ return :unsupported if o.offset
+ return :unsupported if o.lock
+ return :unsupported if o.orders.any?
+ return :unsupported if o.cores.size != 1
+
+ query = visit_Arel_Nodes_SelectCore(o.cores[0])
+ return query if query == :unsupported
+
+ query = query.inject({}) do |memo, item|
+ memo.merge(item)
+ end
+
+ query.merge!(visit(o.limit)) if o.limit
+ query
+ end
+
+ def visit_Arel_Nodes_SelectCore(o)
+ return :unsupported if o.groups.any?
+ return :unsupported if o.having
+ return :unsupported if o.set_quantifier
+ return :unsupported if !o.source || o.source.empty?
+ return :unsupported if o.projections.size != 1
+
+ select = o.projections[0]
+ select = select.name if select.respond_to?(:name)
+ return :unsupported if select != '*'
+
+ parts = [visit(o.source)]
+
+ parts += o.wheres.map {|where| visit(where) }
+
+ parts.include?(:unsupported) ? :unsupported : parts
+ end
+
+ def visit_Arel_Nodes_Limit(o)
+ {:limit => o.value.to_i}
+ end
+
+ def visit_Arel_Nodes_JoinSource(o)
+ return :unsupported if !o.left || o.right.any?
+ return :unsupported if !o.left.is_a?(Arel::Table)
+ visit(o.left)
+ end
+
+ def visit_Arel_Table(o)
+ {:from => o.name}
+ end
+
+ def visit_Arel_Nodes_And(o)
+ attributes = o.children.map { |child| visit(child) }
+ return :unsupported if attributes.include?(:unsupported)
+ attributes.sort! { |pair1, pair2| pair1[0].to_s <=> pair2[0].to_s }
+ { :attributes => attributes }
+ end
+
+ def visit_Arel_Nodes_In(o)
+ left = visit(o.left)
+ return :unsupported if left != :id
+
+ [left, visit(o.right)]
+ end
+
+ def visit_Arel_Nodes_Equality(o)
+ right = o.right
+ [visit(o.left), right ? visit(right) : nil]
+ end
+
+ def visit_Arel_Attributes_Attribute(o)
+ self.last_column = column_for(o.name)
+ o.name.to_sym
+ end
+
+ def literal(o)
+ if o == '?'
+ column, value = @binds.shift
+ value.to_s
+ else
+ o.to_s
+ end
+ end
+
+ def visit_Array(o)
+ o.map {|value| quoted(value) }
+ end
+
+ #TODO: We are actually not using this?
+ def quoted(o)
+ @model_class.connection.quote(o, self.last_column)
+ end
+
+ alias :visit_String :literal
+ alias :visit_Fixnum :literal
+ alias :visit_TrueClass :literal
+ alias :visit_FalseClass :literal
+ alias :visit_Arel_Nodes_SqlLiteral :literal
+
+ def method_missing(name, *args, &block)
+ return :unsupported if name.to_s.start_with?('visit_')
+ super
+ end
+
+ def respond_to?(name, include_private = false)
+ return super || name.to_s.start_with?('visit_')
+ end
+
+ end
+end
View
2  test/find_some_test.rb
@@ -25,7 +25,7 @@ class FindSomeTest < ActiveSupport::TestCase
assert(Kasket.cache.read(post1.kasket_key))
assert_nil(Kasket.cache.read(post2.kasket_key))
- Post.expects(:find_by_sql_without_kasket).with("SELECT * FROM \"posts\" WHERE (\"posts\".\"id\" = #{post2.id}) ").returns([post2])
+ Post.expects(:find_by_sql_without_kasket).returns([post2])
found_posts = Post.find(post1.id, post2.id)
assert_equal([post1, post2].map(&:id).sort, found_posts.map(&:id).sort)
View
10 test/helper.rb
@@ -21,8 +21,6 @@
class ActiveSupport::TestCase
include ActiveRecord::TestFixtures
- fixtures :all
-
def create_fixtures(*table_names)
if block_given?
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
@@ -39,11 +37,19 @@ def create_fixtures(*table_names)
def clear_cache
Kasket.cache.clear
end
+
+ def ar3?
+ ActiveRecord::VERSION::MAJOR == 3
+ end
end
ActiveSupport::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
$LOAD_PATH.unshift(ActiveSupport::TestCase.fixture_path)
+class ActiveSupport::TestCase
+ fixtures :all
+end
+
module Rails
module_function
CACHE = ActiveSupport::Cache::MemoryStore.new
View
176 test/parser_test.rb
@@ -9,35 +9,72 @@ class ParserTest < ActiveSupport::TestCase
end
should 'not support IN queries in combination with other conditions' do
- parsed_query = @parser.parse('SELECT * FROM `posts` WHERE (`posts`.`id` IN (1,2,3) AND `posts`.`is_active` = 1)')
- assert(!parsed_query)
+ if ar3?
+ kasket_query = Post.where(:id => [1,2,3], :is_active => true).to_kasket_query
+ else
+ kasket_query = @parser.parse('SELECT * FROM `posts` WHERE (`posts`.`id` IN (1,2,3) AND `posts`.`is_active` = 1)')
+ end
+ assert(!kasket_query)
end
should "extract conditions" do
- assert_equal [[:blog_id, "big"], [:title, "red"]], @parser.parse('SELECT * FROM `posts` WHERE (`posts`.`title` = red AND `posts`.`blog_id` = big)')[:attributes]
+ if ar3?
+ kasket_query = Post.where(:title => 'red', :blog_id => 1).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (`posts`.`title` = 'red' AND `posts`.`blog_id` = 1)")
+ end
+ assert_equal [[:blog_id, "1"], [:title, "red"]], kasket_query[:attributes]
end
should "extract required index" do
- assert_equal [:blog_id, :title], @parser.parse('SELECT * FROM `posts` WHERE (`posts`.`title` = red AND `posts`.`blog_id` = big)')[:index]
+ if ar3?
+ kasket_query = Post.where(:title => 'red', :blog_id => 1).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (`posts`.`title` = 'red' AND `posts`.`blog_id` = 1)")
+ end
+ assert_equal [:blog_id, :title], kasket_query[:index]
end
should "only support queries against its model's table" do
- assert !@parser.parse('SELECT * FROM `apples` WHERE (`users`.`id` = 2) ')
+ if ar3?
+ kasket_query = Post.where('users.id' => 2).from('apples').to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `apples` WHERE (`users`.`id` = 2)")
+ end
+ assert(!kasket_query)
end
should "support cachable queries" do
- assert @parser.parse('SELECT * FROM `posts` WHERE (`posts`.`id` = 2) ')
- assert @parser.parse('SELECT * FROM `posts` WHERE (`posts`.`id` = 2) LIMIT 1')
+ if ar3?
+ assert Post.where(:id => 1).to_kasket_query
+ else
+ assert @parser.parse("SELECT * FROM `posts` WHERE (`posts`.`id` = 2)")
+ end
+
+ if ar3?
+ assert Post.where(:id => 1).limit(1).to_kasket_query
+ else
+ assert @parser.parse("SELECT * FROM `posts` WHERE (`posts`.`id` = 2) LIMIT 1")
+ end
end
should "support IN queries on id" do
- parsed_query = @parser.parse('SELECT * FROM `posts` WHERE (`posts`.`id` IN (1,2,3))')
- assert(parsed_query)
- assert_equal([[:id, ['1', '2', '3']]], parsed_query[:attributes])
+ if ar3?
+ kasket_query = Post.where(:id => [1,2,3]).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (`posts`.`id` IN (1,2,3))")
+ end
+ assert(kasket_query)
+ assert_equal([[:id, ['1', '2', '3']]], kasket_query[:attributes])
end
should "not support IN queries on other attributes" do
- assert(!@parser.parse('SELECT * FROM `posts` WHERE (`posts`.`hest` IN (1,2,3))'))
+ if ar3?
+ kasket_query = Post.where(:hest => [1,2,3]).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (`posts`.`hest` IN (1,2,3))")
+ end
+ assert(!kasket_query)
end
should "support vaguely formatted queries" do
@@ -47,11 +84,19 @@ class ParserTest < ActiveSupport::TestCase
context "extract options" do
should "provide the limit" do
- sql = 'SELECT * FROM `posts` WHERE (`posts`.`id` = 2)'
- assert_equal nil, @parser.parse(sql)[:limit]
+ if ar3?
+ kasket_query = Post.where(:id => 2).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (`posts`.`id` = 2)")
+ end
+ assert_equal nil, kasket_query[:limit]
- sql << ' LIMIT 1'
- assert_equal 1, @parser.parse(sql)[:limit]
+ if ar3?
+ kasket_query = Post.where(:id => 2).limit(1).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (`posts`.`id` = 2) LIMIT 1")
+ end
+ assert_equal 1, kasket_query[:limit]
end
end
@@ -59,43 +104,108 @@ class ParserTest < ActiveSupport::TestCase
context "unsupported queries" do
should "include advanced limits" do
- assert !@parser.parse('SELECT * FROM `posts` WHERE (title = red AND blog_id = big) LIMIT 2')
+ if ar3?
+ kasket_query = Post.where(:title => 'red', :blog_id => 1).limit(2).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (title = 'red' AND blog_id = 1) LIMIT 2")
+ end
+ assert !kasket_query
end
should "include joins" do
- assert !@parser.parse('SELECT * FROM `posts`, `trees` JOIN ON apple.tree_id = tree.id WHERE (title = red)')
+ if ar3?
+ kasket_query = Post.where(:title => 'test', 'apple.tree_id' => 'posts.id').from(['posts', 'apple']).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts`, `trees` JOIN ON apple.tree_id = tree.id")
+ end
+ assert !kasket_query
+
+ if ar3?
+ kasket_query = Post.where(:title => 'test').joins(:comments).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` JOIN `trees` ON apple.tree_id = tree.id")
+ end
+ assert !kasket_query
end
should "include specific selects" do
- assert !@parser.parse('SELECT id FROM `posts` WHERE (title = red)')
+ if ar3?
+ kasket_query = Post.where(:title => 'red').select(:id).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT id FROM `posts` WHERE (title = 'red')")
+ end
+ assert !kasket_query
end
should "include offset" do
- assert !@parser.parse('SELECT * FROM `posts` WHERE (title = red) LIMIT 1 OFFSET 2')
+ if ar3?
+ kasket_query = Post.where(:title => 'red').limit(1).offset(2).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (title = 'red') LIMIT 1 OFFSET 2")
+ end
+ assert !kasket_query
end
should "include order" do
- assert !@parser.parse('SELECT * FROM `posts` WHERE (title = red) ORDER DESC')
+ if ar3?
+ kasket_query = Post.where(:title => 'red').order(:title).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (title = 'red') ORDER DESC")
+ end
+ assert !kasket_query
end
should "include the OR operator" do
- assert !@parser.parse('SELECT * FROM `posts` WHERE (title = red OR blog_id = big) LIMIT 2')
+ if ar3?
+ kasket_query = Post.where("title = 'red' OR blog_id = 1").to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (title = 'red' OR blog_id = 1) LIMIT 2")
+ end
+ assert !kasket_query
end
end
context "key generation" do
should "include the table name and version" do
- assert_match(/^kasket-#{Kasket::Version::STRING}\/posts\/version=3558\//, @parser.parse('SELECT * FROM `posts` WHERE (id = 1)')[:key])
+ if ar3?
+ kasket_query = Post.where(:id => 1).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (id = 1)")
+ end
+ assert_match(/^kasket-#{Kasket::Version::STRING}\/posts\/version=3558\//, kasket_query[:key])
end
should "include all indexed attributes" do
- assert_match(/id=1$/, @parser.parse('SELECT * FROM `posts` WHERE (id = 1)')[:key])
- assert_match(/blog_id=2\/id=1$/, @parser.parse('SELECT * FROM `posts` WHERE (id = 1 AND blog_id = 2)')[:key])
- assert_match(/id=1\/title='title'$/, @parser.parse("SELECT * FROM `posts` WHERE (id = 1 AND title = 'title')")[:key])
+ if ar3?
+ kasket_query = Post.where(:id => 1).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (id = 1)")
+ end
+ assert_match(/id=1$/, kasket_query[:key])
+
+ if ar3?
+ kasket_query = Post.where(:id => 1, :blog_id => 2).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (id = 1 AND blog_id = 2)")
+ end
+ assert_match(/blog_id=2\/id=1$/, kasket_query[:key])
+
+ if ar3?
+ kasket_query = Post.where(:id => 1, :title => 'title').to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (id = 1 AND title = 'title')")
+ end
+ assert_match(/id=1\/title='title'$/, kasket_query[:key])
end
should "generate multiple keys on IN queries" do
- keys = @parser.parse('SELECT * FROM `posts` WHERE (id IN (1,2))')[:key]
+ if ar3?
+ kasket_query = Post.where(:id => [1,2]).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (id IN (1,2))")
+ end
+
+ keys = kasket_query[:key]
assert_instance_of(Array, keys)
assert_match(/id=1$/, keys[0])
assert_match(/id=2$/, keys[1])
@@ -103,10 +213,20 @@ class ParserTest < ActiveSupport::TestCase
context "when limit 1" do
should "add /first to the key if the index does not include id" do
- assert_match(/title='a'\/first$/, @parser.parse("SELECT * FROM `posts` WHERE (title = 'a') LIMIT 1")[:key])
+ if ar3?
+ kasket_query = Post.where(:title => 'a').limit(1).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (title = 'a') LIMIT 1")
+ end
+ assert_match(/title='a'\/first$/, kasket_query[:key])
end
should "not add /first to the key when the index includes id" do
- assert_match(/id=1$/, @parser.parse("SELECT * FROM `posts` WHERE (id = 1) LIMIT 1")[:key])
+ if ar3?
+ kasket_query = Post.where(:id => 1).limit(1).to_kasket_query
+ else
+ kasket_query = @parser.parse("SELECT * FROM `posts` WHERE (id = 1) LIMIT 1")
+ end
+ assert_match(/id=1$/, kasket_query[:key])
end
end
end
View
10 test/test_models.rb
@@ -1,5 +1,12 @@
require 'temping'
-include Temping
+
+if Temping.respond_to?(:create)
+ def create_model(name, &block)
+ Temping.create(name, &block)
+ end
+else
+ include Temping
+end
create_model :comment do
with_columns do |t|
@@ -33,6 +40,7 @@ def make_dirty!
self.updated_at = Time.now
self.connection.execute("UPDATE posts SET updated_at = '#{updated_at.utc.to_s(:db)}' WHERE id = #{id}")
end
+
kasket_dirty_methods :make_dirty!
end
Please sign in to comment.
Something went wrong with that request. Please try again.