ambitious-activerecord 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Manifest +21 -0
- data/ambitious-activerecord.gemspec +55 -0
- data/lib/ambition/adapters/active_record.rb +11 -0
- data/lib/ambition/adapters/active_record/base.rb +57 -0
- data/lib/ambition/adapters/active_record/query.rb +56 -0
- data/lib/ambition/adapters/active_record/select.rb +94 -0
- data/lib/ambition/adapters/active_record/slice.rb +19 -0
- data/lib/ambition/adapters/active_record/sort.rb +43 -0
- data/lib/ambition/adapters/active_record/statements.rb +39 -0
- data/test/benchmark.rb +67 -0
- data/test/chaining_test.rb +36 -0
- data/test/count_test.rb +19 -0
- data/test/enumerable_test.rb +88 -0
- data/test/helper.rb +43 -0
- data/test/join_test.rb +62 -0
- data/test/profiler.rb +36 -0
- data/test/ruby_test.rb +11 -0
- data/test/select_test.rb +259 -0
- data/test/slice_test.rb +43 -0
- data/test/sort_test.rb +54 -0
- data/test/source_test.rb +43 -0
- data/test/types_test.rb +59 -0
- metadata +93 -0
data/Manifest
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
lib/ambition/adapters/active_record/base.rb
|
2
|
+
lib/ambition/adapters/active_record/query.rb
|
3
|
+
lib/ambition/adapters/active_record/select.rb
|
4
|
+
lib/ambition/adapters/active_record/slice.rb
|
5
|
+
lib/ambition/adapters/active_record/sort.rb
|
6
|
+
lib/ambition/adapters/active_record/statements.rb
|
7
|
+
lib/ambition/adapters/active_record.rb
|
8
|
+
test/benchmark.rb
|
9
|
+
test/chaining_test.rb
|
10
|
+
test/count_test.rb
|
11
|
+
test/enumerable_test.rb
|
12
|
+
test/helper.rb
|
13
|
+
test/join_test.rb
|
14
|
+
test/profiler.rb
|
15
|
+
test/ruby_test.rb
|
16
|
+
test/select_test.rb
|
17
|
+
test/slice_test.rb
|
18
|
+
test/sort_test.rb
|
19
|
+
test/source_test.rb
|
20
|
+
test/types_test.rb
|
21
|
+
Manifest
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
# Gem::Specification for Ambitious-activerecord-0.1.0
|
3
|
+
# Originally generated by Echoe
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{ambitious-activerecord}
|
7
|
+
s.version = "0.1.0"
|
8
|
+
s.date = %q{2008-01-29}
|
9
|
+
s.summary = %q{An ambitious adapter for ActiveRecord}
|
10
|
+
s.email = %q{chris@ozmm.org}
|
11
|
+
s.homepage = %q{http://ambition.rubyforge.org/}
|
12
|
+
s.rubyforge_project = %q{ambition}
|
13
|
+
s.description = %q{An ambitious adapter for ActiveRecord}
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.authors = ["Chris Wanstrath"]
|
16
|
+
s.files = ["lib/ambition/adapters/active_record/base.rb", "lib/ambition/adapters/active_record/query.rb", "lib/ambition/adapters/active_record/select.rb", "lib/ambition/adapters/active_record/slice.rb", "lib/ambition/adapters/active_record/sort.rb", "lib/ambition/adapters/active_record/statements.rb", "lib/ambition/adapters/active_record.rb", "test/benchmark.rb", "test/chaining_test.rb", "test/count_test.rb", "test/enumerable_test.rb", "test/helper.rb", "test/join_test.rb", "test/profiler.rb", "test/ruby_test.rb", "test/select_test.rb", "test/slice_test.rb", "test/sort_test.rb", "test/source_test.rb", "test/types_test.rb", "Manifest", "ambitious-activerecord.gemspec"]
|
17
|
+
s.test_files = ["test/chaining_test.rb", "test/count_test.rb", "test/enumerable_test.rb", "test/join_test.rb", "test/ruby_test.rb", "test/select_test.rb", "test/slice_test.rb", "test/sort_test.rb", "test/source_test.rb", "test/types_test.rb"]
|
18
|
+
s.add_dependency(%q<activerecord>, [">= 1.15.0"])
|
19
|
+
s.add_dependency(%q<ambition>, [">= 0.5.0"])
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# # Original Rakefile source (requires the Echoe gem):
|
24
|
+
#
|
25
|
+
# require 'rake'
|
26
|
+
#
|
27
|
+
# Version = '0.1.0'
|
28
|
+
#
|
29
|
+
# begin
|
30
|
+
# require 'rubygems'
|
31
|
+
# gem 'echoe', '>=2.7'
|
32
|
+
# ENV['RUBY_FLAGS'] = ""
|
33
|
+
# require 'echoe'
|
34
|
+
#
|
35
|
+
# Echoe.new('ambitious-activerecord') do |p|
|
36
|
+
# p.dependencies << 'activerecord >=1.15.0'
|
37
|
+
# p.summary = "An ambitious adapter for ActiveRecord"
|
38
|
+
# p.author = 'Chris Wanstrath'
|
39
|
+
# p.email = "chris@ozmm.org"
|
40
|
+
#
|
41
|
+
# p.project = 'ambition'
|
42
|
+
# p.url = "http://ambition.rubyforge.org/"
|
43
|
+
# p.test_pattern = 'test/*_test.rb'
|
44
|
+
# p.version = Version
|
45
|
+
# p.dependencies << 'ambition >=0.5.0'
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# rescue LoadError
|
49
|
+
# puts "Not doing any of the Echoe gemmy stuff, because you don't have the specified gem versions"
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# desc 'Install as a gem'
|
53
|
+
# task :install_gem do
|
54
|
+
# puts `rake manifest package && gem install pkg/ambitious-activerecord-#{Version}.gem`
|
55
|
+
# end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'ambition'
|
2
|
+
require 'active_record'
|
3
|
+
require 'ambition/adapters/active_record/query'
|
4
|
+
require 'ambition/adapters/active_record/base'
|
5
|
+
require 'ambition/adapters/active_record/select'
|
6
|
+
require 'ambition/adapters/active_record/sort'
|
7
|
+
require 'ambition/adapters/active_record/slice'
|
8
|
+
require 'ambition/adapters/active_record/statements'
|
9
|
+
|
10
|
+
ActiveRecord::Base.extend Ambition::API
|
11
|
+
ActiveRecord::Base.ambition_adapter = Ambition::Adapters::ActiveRecord
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract/quoting'
|
2
|
+
|
3
|
+
module Ambition
|
4
|
+
module Adapters
|
5
|
+
module ActiveRecord
|
6
|
+
class Base
|
7
|
+
include ::ActiveRecord::ConnectionAdapters::Quoting
|
8
|
+
|
9
|
+
def sanitize(value)
|
10
|
+
if value.is_a? Array
|
11
|
+
return value.map { |v| sanitize(v) }.join(', ')
|
12
|
+
end
|
13
|
+
|
14
|
+
case value
|
15
|
+
when true, 'true'
|
16
|
+
'1'
|
17
|
+
when false, 'false'
|
18
|
+
'0'
|
19
|
+
when Regexp
|
20
|
+
"'#{value.source}'"
|
21
|
+
else
|
22
|
+
if active_connection?
|
23
|
+
::ActiveRecord::Base.connection.quote(value)
|
24
|
+
else
|
25
|
+
quote(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
rescue
|
29
|
+
"'#{value}'"
|
30
|
+
end
|
31
|
+
|
32
|
+
def quote_column_name(value)
|
33
|
+
if active_connection?
|
34
|
+
::ActiveRecord::Base.connection.quote_column_name(value)
|
35
|
+
else
|
36
|
+
value.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def active_connection?
|
41
|
+
::ActiveRecord::Base.active_connection_name
|
42
|
+
end
|
43
|
+
|
44
|
+
def dbadapter_name
|
45
|
+
::ActiveRecord::Base.connection.adapter_name
|
46
|
+
rescue ::ActiveRecord::ConnectionNotEstablished
|
47
|
+
'Abstract'
|
48
|
+
end
|
49
|
+
|
50
|
+
def statement(*args)
|
51
|
+
@statement_instance ||= DatabaseStatements.const_get(dbadapter_name).new
|
52
|
+
@statement_instance.send(*args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
class Query
|
5
|
+
@@select = 'SELECT * FROM %s %s'
|
6
|
+
|
7
|
+
def kick
|
8
|
+
owner.find(:all, to_hash)
|
9
|
+
end
|
10
|
+
|
11
|
+
def size
|
12
|
+
owner.count(to_hash)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hash
|
16
|
+
hash = {}
|
17
|
+
|
18
|
+
unless (where = clauses[:select]).blank?
|
19
|
+
hash[:conditions] = Array(where)
|
20
|
+
hash[:conditions] *= ' AND '
|
21
|
+
end
|
22
|
+
|
23
|
+
if order = clauses[:sort]
|
24
|
+
hash[:order] = order.join(', ')
|
25
|
+
end
|
26
|
+
|
27
|
+
if clauses[:slice].last =~ /LIMIT (\d+)/
|
28
|
+
hash[:limit] = $1.to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
if clauses[:slice].last =~ /OFFSET (\d+)/
|
32
|
+
hash[:offset] = $1.to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
hash[:include] = stash[:include] if stash[:include]
|
36
|
+
|
37
|
+
hash
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
hash = to_hash
|
42
|
+
|
43
|
+
raise "Sorry, I can't construct SQL with complex joins (yet)" unless hash[:include].blank?
|
44
|
+
|
45
|
+
sql = []
|
46
|
+
sql << "WHERE #{hash[:conditions]}" unless hash[:conditions].blank?
|
47
|
+
sql << "ORDER BY #{hash[:order]}" unless hash[:order].blank?
|
48
|
+
sql << clauses[:slice].last unless hash[:slice].blank?
|
49
|
+
|
50
|
+
@@select % [ owner.table_name, sql.join(' ') ]
|
51
|
+
end
|
52
|
+
alias_method :to_sql, :to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
class Select < Base
|
5
|
+
def call(method)
|
6
|
+
"#{owner.table_name}.#{quote_column_name method}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def chained_call(*methods)
|
10
|
+
if reflection = owner.reflections[methods.first]
|
11
|
+
stash[:include] ||= []
|
12
|
+
stash[:include] << methods.first
|
13
|
+
"#{reflection.table_name}.#{quote_column_name methods.last}"
|
14
|
+
elsif respond_to? methods[1]
|
15
|
+
send(methods[1], methods.first)
|
16
|
+
else
|
17
|
+
raise "I don't understand: #{methods.inspect}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def both(left, right)
|
22
|
+
"(#{left} AND #{right})"
|
23
|
+
end
|
24
|
+
|
25
|
+
def either(left, right)
|
26
|
+
"(#{left} OR #{right})"
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==(left, right)
|
30
|
+
if right.nil?
|
31
|
+
"#{left} IS NULL"
|
32
|
+
else
|
33
|
+
"#{left} = #{sanitize right}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# !=
|
38
|
+
def not_equal(left, right)
|
39
|
+
if right.nil?
|
40
|
+
"#{left} IS NOT NULL"
|
41
|
+
else
|
42
|
+
"#{left} <> #{sanitize right}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def =~(left, right)
|
47
|
+
if right.is_a? Regexp
|
48
|
+
"#{left} #{statement(:regexp, right)} #{sanitize right}"
|
49
|
+
else
|
50
|
+
"#{left} LIKE #{sanitize right}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# !~
|
55
|
+
def not_regexp(left, right)
|
56
|
+
if right.is_a? Regexp
|
57
|
+
"#{left} #{statement(:not_regexp, right)} #{sanitize right}"
|
58
|
+
else
|
59
|
+
"#{left} NOT LIKE #{sanitize right}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def <(left, right)
|
64
|
+
"#{left} < #{sanitize right}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def >(left, right)
|
68
|
+
"#{left} > #{sanitize right}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def >=(left, right)
|
72
|
+
"#{left} >= #{sanitize right}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def <=(left, right)
|
76
|
+
"#{left} <= #{sanitize right}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def include?(left, right)
|
80
|
+
left = left.map { |element| sanitize element }.join(', ')
|
81
|
+
"#{right} IN (#{left})"
|
82
|
+
end
|
83
|
+
|
84
|
+
def downcase(column)
|
85
|
+
"LOWER(#{owner.table_name}.#{quote_column_name column})"
|
86
|
+
end
|
87
|
+
|
88
|
+
def upcase(column)
|
89
|
+
"UPPER(#{owner.table_name}.#{quote_column_name column})"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
class Slice < Base
|
5
|
+
def slice(start, length=nil)
|
6
|
+
if start.is_a? Range
|
7
|
+
length = start.end
|
8
|
+
length -= 1 if start.exclude_end?
|
9
|
+
start = start.first - 1
|
10
|
+
length -= start
|
11
|
+
end
|
12
|
+
out = "LIMIT #{length} "
|
13
|
+
out << "OFFSET #{start}" if start.to_i.nonzero?
|
14
|
+
out
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
class Sort < Base
|
5
|
+
def sort_by(method)
|
6
|
+
"#{owner.table_name}.#{quote_column_name method}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def reverse_sort_by(method)
|
10
|
+
"#{owner.table_name}.#{quote_column_name method} DESC"
|
11
|
+
end
|
12
|
+
|
13
|
+
def chained_sort_by(receiver, method)
|
14
|
+
if reflection = owner.reflections[receiver]
|
15
|
+
stash[:include] ||= []
|
16
|
+
stash[:include] << receiver
|
17
|
+
"#{reflection.table_name}.#{quote_column_name method}"
|
18
|
+
else
|
19
|
+
raise [ receiver, method ].inspect
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def chained_reverse_sort_by(receiver, method)
|
24
|
+
if reflection = owner.reflections[receiver]
|
25
|
+
stash[:include] ||= []
|
26
|
+
stash[:include] << receiver
|
27
|
+
"#{reflection.table_name}.#{quote_column_name method} DESC"
|
28
|
+
else
|
29
|
+
raise [ receiver, method ].inspect
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_proc(symbol)
|
34
|
+
"#{owner.table_name}.#{symbol}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def rand
|
38
|
+
'RAND()'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
module DatabaseStatements
|
5
|
+
def self.const_missing(*args)
|
6
|
+
Abstract
|
7
|
+
end
|
8
|
+
|
9
|
+
class Abstract
|
10
|
+
def regexp(regexp)
|
11
|
+
'REGEXP'
|
12
|
+
end
|
13
|
+
|
14
|
+
def not_regexp(regexp)
|
15
|
+
'NOT REGEXP'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class PostgreSQL < Abstract
|
20
|
+
def regexp(regexp)
|
21
|
+
if regexp.options == 1
|
22
|
+
'~*'
|
23
|
+
else
|
24
|
+
'~'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def not_regexp(regexp)
|
29
|
+
if regexp.options == 1
|
30
|
+
'!~*'
|
31
|
+
else
|
32
|
+
'!~'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/test/benchmark.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + '/../../../lib'
|
2
|
+
%w( rubygems ambition ambition/adapters/active_record benchmark ).each { |f| require f }
|
3
|
+
|
4
|
+
class User < ActiveRecord::Base
|
5
|
+
def self.reflections
|
6
|
+
return @reflections if @reflections
|
7
|
+
@reflections = {}
|
8
|
+
@reflections[:ideas] = Reflection.new(:has_many, 'user_id', :ideas, 'ideas')
|
9
|
+
@reflections[:invites] = Reflection.new(:has_many, 'referrer_id', :invites, 'invites')
|
10
|
+
@reflections[:profile] = Reflection.new(:has_one, 'user_id', :profile, 'profiles')
|
11
|
+
@reflections[:account] = Reflection.new(:belongs_to, 'account_id', :account, 'accounts')
|
12
|
+
@reflections
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.table_name
|
16
|
+
'users'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Reflection < Struct.new(:macro, :primary_key_name, :name, :table_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
Times = 10000
|
24
|
+
|
25
|
+
Benchmark.bm(30) do |x|
|
26
|
+
x.report 'simple select' do
|
27
|
+
Times.times do
|
28
|
+
User.select { |u| u.id == 20 }.to_hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
x.report 'simple select w/ eval' do
|
33
|
+
Times.times do
|
34
|
+
User.select { |u| u.created_at == Time.now }.to_hash
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
x.report 'dual select' do
|
39
|
+
Times.times do
|
40
|
+
User.select { |u| u.id == 20 && u.age > 20 }.to_hash
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
x.report 'join select' do
|
45
|
+
Times.times do
|
46
|
+
User.select { |u| u.id == 20 && u.ideas.name =~ /stuff/ }.to_hash
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
x.report 'dual select w/ sort' do
|
51
|
+
Times.times do
|
52
|
+
User.select { |u| u.id == 20 && u.age > 20 }.sort_by { |u| u.id }.to_hash
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
x.report 'dual select w/ sort & first' do
|
57
|
+
Times.times do
|
58
|
+
User.select { |u| u.id == 20 && u.age > 20 }.sort_by { |u| u.id }.first(20).to_hash
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
x.report "it's complicated" do
|
63
|
+
Times.times do
|
64
|
+
User.select { |u| (u.id == 20 && u.age > 20) || u.profile.name == 'Jon' }.sort_by { |u| [u.id, -u.name] }.first(20).to_hash
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "ActiveRecord Adapter" do
|
4
|
+
context "Chaining" do
|
5
|
+
specify "should join selects with AND" do
|
6
|
+
sql = User.select { |m| m.name == 'jon' }
|
7
|
+
sql = sql.select { |m| m.age == 22 }
|
8
|
+
sql.to_s.should == "SELECT * FROM users WHERE users.name = 'jon' AND users.age = 22"
|
9
|
+
end
|
10
|
+
|
11
|
+
specify "should join sort_bys with a comma" do
|
12
|
+
sql = User.select { |m| m.name == 'jon' }
|
13
|
+
sql = sql.sort_by { |m| m.name }
|
14
|
+
sql = sql.sort_by { |m| m.age }
|
15
|
+
sql.to_s.should == "SELECT * FROM users WHERE users.name = 'jon' ORDER BY users.name, users.age"
|
16
|
+
end
|
17
|
+
|
18
|
+
specify "should join selects and sorts intelligently" do
|
19
|
+
sql = User.select { |m| m.name == 'jon' }
|
20
|
+
sql = sql.select { |m| m.age == 22 }
|
21
|
+
sql = sql.sort_by { |m| -m.name }
|
22
|
+
sql = sql.sort_by { |m| m.age }
|
23
|
+
sql.to_s.should == "SELECT * FROM users WHERE users.name = 'jon' AND users.age = 22 ORDER BY users.name DESC, users.age"
|
24
|
+
end
|
25
|
+
|
26
|
+
specify "should join lots of selects and sorts intelligently" do
|
27
|
+
sql = User.select { |m| m.name == 'jon' }
|
28
|
+
sql = sql.select { |m| m.age == 22 }
|
29
|
+
sql = sql.sort_by { |m| m.name }
|
30
|
+
sql = sql.select { |m| m.power == true }
|
31
|
+
sql = sql.sort_by { |m| m.email }
|
32
|
+
sql = sql.select { |m| m.admin == true && m.email == 'chris@ozmm.org' }
|
33
|
+
sql.to_s.should == "SELECT * FROM users WHERE users.name = 'jon' AND users.age = 22 AND users.power = 1 AND (users.admin = 1 AND users.email = 'chris@ozmm.org') ORDER BY users.name, users.email"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/test/count_test.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "ActiveRecord Adapter" do
|
4
|
+
context "Count" do
|
5
|
+
setup do
|
6
|
+
hash = { :conditions => "users.name = 'jon'" }
|
7
|
+
User.expects(:count).with(hash)
|
8
|
+
@sql = User.select { |m| m.name == 'jon' }
|
9
|
+
end
|
10
|
+
|
11
|
+
specify "size" do
|
12
|
+
@sql.size
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "length" do
|
16
|
+
@sql.length
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "ActiveRecord Adapter" do
|
4
|
+
context "Each" do
|
5
|
+
specify "simple ==" do
|
6
|
+
hash = { :conditions => "users.age = 21" }
|
7
|
+
User.expects(:find).with(:all, hash).returns([])
|
8
|
+
User.select { |m| m.age == 21 }.each do |user|
|
9
|
+
puts user.name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
specify "limit and conditions" do
|
14
|
+
hash = { :limit => 5, :conditions => "users.age = 21" }
|
15
|
+
User.expects(:find).with(:all, hash).returns([])
|
16
|
+
User.select { |m| m.age == 21 }.first(5).each do |user|
|
17
|
+
puts user.name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
specify "limit and conditions and order" do
|
22
|
+
hash = { :limit => 5, :conditions => "users.age = 21", :order => 'users.name' }
|
23
|
+
User.expects(:find).with(:all, hash).returns([])
|
24
|
+
User.select { |m| m.age == 21 }.sort_by { |m| m.name }.first(5).each do |user|
|
25
|
+
puts user.name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "limit and order" do
|
30
|
+
hash = { :limit => 5, :order => 'users.name' }
|
31
|
+
User.expects(:find).with(:all, hash).returns([])
|
32
|
+
User.sort_by { |m| m.name }.first(5).each do |user|
|
33
|
+
puts user.name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "Enumerable Methods" do
|
39
|
+
specify "map" do
|
40
|
+
hash = { :conditions => "users.age = 21" }
|
41
|
+
User.expects(:find).with(:all, hash).returns([])
|
42
|
+
User.select { |m| m.age == 21 }.map { |u| u.name }
|
43
|
+
end
|
44
|
+
|
45
|
+
specify "each_with_index" do
|
46
|
+
hash = { :conditions => "users.age = 21" }
|
47
|
+
User.expects(:find).with(:all, hash).returns([])
|
48
|
+
User.select { |m| m.age == 21 }.each_with_index do |user, i|
|
49
|
+
puts "#{i}: #{user.name}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "any?" do
|
54
|
+
User.expects(:count).with(:conditions => "users.age > 21").returns(1)
|
55
|
+
User.any? { |u| u.age > 21 }.should == true
|
56
|
+
end
|
57
|
+
|
58
|
+
specify "all?" do
|
59
|
+
User.expects(:count).at_least_once.returns(10, 20)
|
60
|
+
User.all? { |u| u.age > 21 }.should == false
|
61
|
+
|
62
|
+
User.expects(:count).at_least_once.returns(10, 10)
|
63
|
+
User.all? { |u| u.age > 21 }.should == true
|
64
|
+
end
|
65
|
+
|
66
|
+
specify "empty?" do
|
67
|
+
User.expects(:count).with(:conditions => "users.age > 21").returns(1)
|
68
|
+
User.select { |u| u.age > 21 }.empty?.should.equal false
|
69
|
+
|
70
|
+
User.expects(:count).with(:conditions => "users.age > 21").returns(0)
|
71
|
+
User.select { |u| u.age > 21 }.empty?.should.equal true
|
72
|
+
end
|
73
|
+
|
74
|
+
specify "entries" do
|
75
|
+
User.expects(:find).with(:all, {})
|
76
|
+
User.entries
|
77
|
+
|
78
|
+
hash = { :conditions => "users.age = 21" }
|
79
|
+
User.expects(:find).with(:all, hash).returns([])
|
80
|
+
User.select { |m| m.age == 21 }.entries
|
81
|
+
end
|
82
|
+
|
83
|
+
specify "to_a" do
|
84
|
+
User.expects(:find).with(:all, {})
|
85
|
+
User.to_a
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
%w( rubygems test/spec mocha redgreen English ).each { |f| require f }
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift *[ File.dirname(__FILE__) + '/../lib', File.dirname(__FILE__) + '/../../../lib' ]
|
4
|
+
require 'ambition/adapters/active_record'
|
5
|
+
|
6
|
+
class User < ActiveRecord::Base
|
7
|
+
def self.reflections
|
8
|
+
return @reflections if @reflections
|
9
|
+
@reflections = {}
|
10
|
+
@reflections[:ideas] = Reflection.new(:has_many, 'user_id', :ideas, 'ideas')
|
11
|
+
@reflections[:invites] = Reflection.new(:has_many, 'referrer_id', :invites, 'invites')
|
12
|
+
@reflections[:profile] = Reflection.new(:has_one, 'user_id', :profile, 'profiles')
|
13
|
+
@reflections[:account] = Reflection.new(:belongs_to, 'account_id', :account, 'accounts')
|
14
|
+
@reflections
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.table_name
|
18
|
+
'users'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Reflection < Struct.new(:macro, :primary_key_name, :name, :table_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
module ActiveRecord
|
26
|
+
module ConnectionAdapters
|
27
|
+
class MysqlAdapter
|
28
|
+
def connect(*args)
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class PostgreSQLAdapter
|
34
|
+
def connect(*args)
|
35
|
+
true
|
36
|
+
end
|
37
|
+
class PGError; end
|
38
|
+
end
|
39
|
+
|
40
|
+
class FakeAdapter < AbstractAdapter
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/test/join_test.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "ActiveRecord Adapter" do
|
4
|
+
context "Joins" do
|
5
|
+
specify "simple == on an association" do
|
6
|
+
sql = User.select { |m| m.account.email == 'chris@ozmm.org' }
|
7
|
+
sql.to_hash.should == {
|
8
|
+
:conditions => "accounts.email = 'chris@ozmm.org'",
|
9
|
+
:include => [:account]
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
specify "simple mixed == on an association" do
|
14
|
+
sql = User.select { |m| m.name == 'chris' && m.account.email == 'chris@ozmm.org' }
|
15
|
+
sql.to_hash.should == {
|
16
|
+
:conditions => "(users.name = 'chris' AND accounts.email = 'chris@ozmm.org')",
|
17
|
+
:include => [:account]
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
specify "multiple associations" do
|
22
|
+
sql = User.select { |m| m.ideas.title == 'New Freezer' || m.invites.email == 'pj@hyett.com' }
|
23
|
+
sql.to_hash.should == {
|
24
|
+
:conditions => "(ideas.title = 'New Freezer' OR invites.email = 'pj@hyett.com')",
|
25
|
+
:include => [:ideas, :invites]
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "belongs_to" do
|
30
|
+
sql = User.select { |m| m.account.id > 20 }
|
31
|
+
sql.to_hash.should == {
|
32
|
+
:conditions => "accounts.id > 20",
|
33
|
+
:include => [:account]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "complex joins have no to_s" do
|
38
|
+
sql = User.select { |m| m.account.id > 20 }
|
39
|
+
should.raise { sql.to_s }
|
40
|
+
end
|
41
|
+
|
42
|
+
specify "non-existant associations" do
|
43
|
+
should.raise { User.select { |m| m.liquor.brand == 'Jack' } }
|
44
|
+
end
|
45
|
+
|
46
|
+
specify "in order" do
|
47
|
+
sql = User.sort_by { |m| m.ideas.title }
|
48
|
+
sql.to_hash.should == {
|
49
|
+
:order => "ideas.title",
|
50
|
+
:include => [:ideas]
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
specify "in a more complex order" do
|
55
|
+
sql = User.sort_by { |m| [ m.ideas.title, -m.invites.email ] }
|
56
|
+
sql.to_hash.should == {
|
57
|
+
:order => "ideas.title, invites.email DESC",
|
58
|
+
:include => [:ideas, :invites]
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/test/profiler.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + '/../../../lib'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'ambition'
|
4
|
+
require 'ambition/adapters/active_record'
|
5
|
+
require 'ruby-prof'
|
6
|
+
|
7
|
+
class User < ActiveRecord::Base
|
8
|
+
def self.reflections
|
9
|
+
return @reflections if @reflections
|
10
|
+
@reflections = {}
|
11
|
+
@reflections[:ideas] = Reflection.new(:has_many, 'user_id', :ideas, 'ideas')
|
12
|
+
@reflections[:invites] = Reflection.new(:has_many, 'referrer_id', :invites, 'invites')
|
13
|
+
@reflections[:profile] = Reflection.new(:has_one, 'user_id', :profile, 'profiles')
|
14
|
+
@reflections[:account] = Reflection.new(:belongs_to, 'account_id', :account, 'accounts')
|
15
|
+
@reflections
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.table_name
|
19
|
+
'users'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Reflection < Struct.new(:macro, :primary_key_name, :name, :table_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
result = RubyProf.profile do
|
27
|
+
1000.times do
|
28
|
+
User.select { |u| (u.id == 20 && u.age > 20) || u.profile.name == 'Jon' }.sort_by { |u| [u.id, -u.name] }.first(20).to_hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
printer = RubyProf::FlatPrinter.new(result)
|
33
|
+
#printer = RubyProf::GraphPrinter.new(result)
|
34
|
+
printer.print(STDOUT, 0)
|
35
|
+
|
36
|
+
puts User.select { |u| (u.id == 20 && u.age > 20) || u.profile.name == 'Jon' }.sort_by { |u| [u.id, -u.name] }.first(20).to_hash.inspect
|
data/test/ruby_test.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "ActiveRecord Adapter" do
|
4
|
+
context "Inline Ruby" do
|
5
|
+
xspecify "should know what to return" do
|
6
|
+
name = 'David'
|
7
|
+
sql = User.select { |u| name.nil? || u.name == name }.to_s
|
8
|
+
sql.should == "SELECT * FROM users WHERE (users.name = 'David')"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/test/select_test.rb
ADDED
@@ -0,0 +1,259 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "ActiveRecord Adapter" do
|
4
|
+
context "Select" do
|
5
|
+
specify "simple ==" do
|
6
|
+
sql = User.select { |m| m.name == 'jon' }.to_s
|
7
|
+
sql.should == "SELECT * FROM users WHERE users.name = 'jon'"
|
8
|
+
end
|
9
|
+
|
10
|
+
specify "simple !=" do
|
11
|
+
sql = User.select { |m| m.name != 'jon' }.to_s
|
12
|
+
sql.should == "SELECT * FROM users WHERE users.name <> 'jon'"
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "simple == && ==" do
|
16
|
+
sql = User.select { |m| m.name == 'jon' && m.age == 21 }.to_s
|
17
|
+
sql.should == "SELECT * FROM users WHERE (users.name = 'jon' AND users.age = 21)"
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "simple == || ==" do
|
21
|
+
sql = User.select { |m| m.name == 'jon' || m.age == 21 }.to_s
|
22
|
+
sql.should == "SELECT * FROM users WHERE (users.name = 'jon' OR users.age = 21)"
|
23
|
+
end
|
24
|
+
|
25
|
+
specify "mixed && and ||" do
|
26
|
+
sql = User.select { |m| m.name == 'jon' || m.age == 21 && m.password == 'pass' }.to_s
|
27
|
+
sql.should == "SELECT * FROM users WHERE (users.name = 'jon' OR (users.age = 21 AND users.password = 'pass'))"
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "grouped && and ||" do
|
31
|
+
sql = User.select { |m| (m.name == 'jon' || m.name == 'rick') && m.age == 21 }.to_s
|
32
|
+
sql.should == "SELECT * FROM users WHERE ((users.name = 'jon' OR users.name = 'rick') AND users.age = 21)"
|
33
|
+
end
|
34
|
+
|
35
|
+
specify "simple >/<" do
|
36
|
+
sql = User.select { |m| m.age > 21 }.to_s
|
37
|
+
sql.should == "SELECT * FROM users WHERE users.age > 21"
|
38
|
+
|
39
|
+
sql = User.select { |m| m.age >= 21 }.to_s
|
40
|
+
sql.should == "SELECT * FROM users WHERE users.age >= 21"
|
41
|
+
|
42
|
+
sql = User.select { |m| m.age < 21 }.to_s
|
43
|
+
sql.should == "SELECT * FROM users WHERE users.age < 21"
|
44
|
+
end
|
45
|
+
|
46
|
+
specify "array.include? item" do
|
47
|
+
sql = User.select { |m| [1, 2, 3, 4].include? m.id }.to_s
|
48
|
+
sql.should == "SELECT * FROM users WHERE users.id IN (1, 2, 3, 4)"
|
49
|
+
end
|
50
|
+
|
51
|
+
specify "variable'd array.include? item" do
|
52
|
+
array = [1, 2, 3, 4]
|
53
|
+
sql = User.select { |m| array.include? m.id }.to_s
|
54
|
+
sql.should == "SELECT * FROM users WHERE users.id IN (1, 2, 3, 4)"
|
55
|
+
end
|
56
|
+
|
57
|
+
specify "simple == with variables" do
|
58
|
+
me = 'chris'
|
59
|
+
sql = User.select { |m| m.name == me }.to_s
|
60
|
+
sql.should == "SELECT * FROM users WHERE users.name = '#{me}'"
|
61
|
+
end
|
62
|
+
|
63
|
+
specify "simple == with method arguments" do
|
64
|
+
def test_it(name)
|
65
|
+
sql = User.select { |m| m.name == name }.to_s
|
66
|
+
sql.should == "SELECT * FROM users WHERE users.name = '#{name}'"
|
67
|
+
end
|
68
|
+
|
69
|
+
test_it('chris')
|
70
|
+
end
|
71
|
+
|
72
|
+
specify "simple == with instance variables" do
|
73
|
+
@me = 'chris'
|
74
|
+
sql = User.select { |m| m.name == @me }.to_s
|
75
|
+
sql.should == "SELECT * FROM users WHERE users.name = '#{@me}'"
|
76
|
+
end
|
77
|
+
|
78
|
+
specify "simple == with instance variable method call" do
|
79
|
+
require 'ostruct'
|
80
|
+
@person = OpenStruct.new(:name => 'chris')
|
81
|
+
|
82
|
+
sql = User.select { |m| m.name == @person.name }.to_s
|
83
|
+
sql.should == "SELECT * FROM users WHERE users.name = '#{@person.name}'"
|
84
|
+
end
|
85
|
+
|
86
|
+
specify "simple == with global variables" do
|
87
|
+
$my_name = 'boston'
|
88
|
+
sql = User.select { |m| m.name == $my_name }.to_s
|
89
|
+
sql.should == "SELECT * FROM users WHERE users.name = '#{$my_name}'"
|
90
|
+
end
|
91
|
+
|
92
|
+
specify "simple == with method call" do
|
93
|
+
def band
|
94
|
+
'megadeth'
|
95
|
+
end
|
96
|
+
|
97
|
+
sql = User.select { |m| m.name == band }.to_s
|
98
|
+
sql.should == "SELECT * FROM users WHERE users.name = '#{band}'"
|
99
|
+
end
|
100
|
+
|
101
|
+
specify "simple =~ with string" do
|
102
|
+
sql = User.select { |m| m.name =~ 'chris' }.to_s
|
103
|
+
sql.should == "SELECT * FROM users WHERE users.name LIKE 'chris'"
|
104
|
+
|
105
|
+
sql = User.select { |m| m.name =~ 'chri%' }.to_s
|
106
|
+
sql.should == "SELECT * FROM users WHERE users.name LIKE 'chri%'"
|
107
|
+
end
|
108
|
+
|
109
|
+
specify "simple !~ with string" do
|
110
|
+
sql = User.select { |m| m.name !~ 'chris' }.to_s
|
111
|
+
sql.should == "SELECT * FROM users WHERE users.name NOT LIKE 'chris'"
|
112
|
+
|
113
|
+
sql = User.select { |m| !(m.name =~ 'chris') }.to_s
|
114
|
+
sql.should == "SELECT * FROM users WHERE users.name NOT LIKE 'chris'"
|
115
|
+
end
|
116
|
+
|
117
|
+
specify "simple =~ with regexp" do
|
118
|
+
sql = User.select { |m| m.name =~ /chris/ }.to_s
|
119
|
+
sql.should == "SELECT * FROM users WHERE users.name REGEXP 'chris'"
|
120
|
+
end
|
121
|
+
|
122
|
+
specify "simple =~ with regexp flags" do
|
123
|
+
sql = User.select { |m| m.name =~ /chris/i }.to_s
|
124
|
+
sql.should == "SELECT * FROM users WHERE users.name REGEXP 'chris'"
|
125
|
+
end
|
126
|
+
|
127
|
+
specify "simple LOWER()" do
|
128
|
+
sql = User.select { |m| m.name.downcase =~ 'chris%' }.to_s
|
129
|
+
sql.should == "SELECT * FROM users WHERE LOWER(users.name) LIKE 'chris%'"
|
130
|
+
end
|
131
|
+
|
132
|
+
specify "simple UPPER()" do
|
133
|
+
sql = User.select { |m| m.name.upcase =~ 'chris%' }.to_s
|
134
|
+
sql.should == "SELECT * FROM users WHERE UPPER(users.name) LIKE 'chris%'"
|
135
|
+
end
|
136
|
+
|
137
|
+
specify "undefined equality symbol" do
|
138
|
+
should.raise { User.select { |m| m.name =* /chris/ }.to_s }
|
139
|
+
end
|
140
|
+
|
141
|
+
specify "block variable / assigning variable conflict" do
|
142
|
+
m = User.select { |m| m.name == 'chris' }.to_s
|
143
|
+
m.should == "SELECT * FROM users WHERE users.name = 'chris'"
|
144
|
+
end
|
145
|
+
|
146
|
+
specify "simple == with inline ruby" do
|
147
|
+
sql = User.select { |m| m.created_at == 2.days.ago.to_s(:db) }.to_s
|
148
|
+
sql.should == "SELECT * FROM users WHERE users.created_at = '#{2.days.ago.to_s(:db)}'"
|
149
|
+
end
|
150
|
+
|
151
|
+
specify "deep chains" do
|
152
|
+
should.raise do
|
153
|
+
User.select { |m| m.created_at.something.else.perhaps }.to_s
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
specify "inspect" do
|
158
|
+
User.select { |u| u.name }.inspect.should.match %r(call #to_s or #to_hash)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "Detect" do
|
163
|
+
specify "simple ==" do
|
164
|
+
User.expects(:select).returns(mock(:first => true))
|
165
|
+
User.detect { |m| m.name == 'chris' }
|
166
|
+
end
|
167
|
+
|
168
|
+
specify "nothing found" do
|
169
|
+
User.expects(:select).returns(mock(:first => nil))
|
170
|
+
User.detect { |m| m.name == 'chris' }.should.be.nil
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
xcontext "[]" do
|
175
|
+
specify "finds a single row" do
|
176
|
+
User.expects(:find).with(1)
|
177
|
+
User[1]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context "PostgreSQL specific" do
|
182
|
+
setup do
|
183
|
+
ActiveRecord::Base.connection = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.new 'fake_connection', 'fake_logger'
|
184
|
+
end
|
185
|
+
|
186
|
+
teardown do
|
187
|
+
ActiveRecord::Base.remove_connection
|
188
|
+
end
|
189
|
+
|
190
|
+
specify "quoting of column name" do
|
191
|
+
me = 'chris'
|
192
|
+
sql = User.select { |m| m.name == me }.to_s
|
193
|
+
sql.should == %(SELECT * FROM users WHERE users."name" = '#{me}')
|
194
|
+
end
|
195
|
+
|
196
|
+
specify "simple =~ with regexp" do
|
197
|
+
sql = User.select { |m| m.name =~ /chris/ }.to_s
|
198
|
+
sql.should == %(SELECT * FROM users WHERE users."name" ~ 'chris')
|
199
|
+
end
|
200
|
+
|
201
|
+
specify "insensitive =~" do
|
202
|
+
sql = User.select { |m| m.name =~ /chris/i }.to_s
|
203
|
+
sql.should == %(SELECT * FROM users WHERE users."name" ~* 'chris')
|
204
|
+
end
|
205
|
+
|
206
|
+
specify "negated =~" do
|
207
|
+
sql = User.select { |m| m.name !~ /chris/ }.to_s
|
208
|
+
sql.should == %(SELECT * FROM users WHERE users."name" !~ 'chris')
|
209
|
+
end
|
210
|
+
|
211
|
+
specify "negated insensitive =~" do
|
212
|
+
sql = User.select { |m| m.name !~ /chris/i }.to_s
|
213
|
+
sql.should == %(SELECT * FROM users WHERE users."name" !~* 'chris')
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context "MySQL specific" do
|
218
|
+
setup do
|
219
|
+
ActiveRecord::Base.connection = ActiveRecord::ConnectionAdapters::MysqlAdapter.new('connection', 'logger', 'options', 'config')
|
220
|
+
end
|
221
|
+
|
222
|
+
teardown do
|
223
|
+
ActiveRecord::Base.connection = ActiveRecord::ConnectionAdapters::FakeAdapter.new('connection', 'logger')
|
224
|
+
end
|
225
|
+
|
226
|
+
specify "quoting of column name" do
|
227
|
+
me = 'chris'
|
228
|
+
sql = User.select { |m| m.name == me }.to_s
|
229
|
+
sql.should == "SELECT * FROM users WHERE users.`name` = '#{me}'"
|
230
|
+
end
|
231
|
+
|
232
|
+
specify "simple =~ with regexp" do
|
233
|
+
sql = User.select { |m| m.name =~ /chris/ }.to_s
|
234
|
+
sql.should == "SELECT * FROM users WHERE users.`name` REGEXP 'chris'"
|
235
|
+
end
|
236
|
+
|
237
|
+
specify "negated =~" do
|
238
|
+
sql = User.select { |m| m.name !~ 'chris' }.to_s
|
239
|
+
sql.should == "SELECT * FROM users WHERE users.`name` NOT LIKE 'chris'"
|
240
|
+
end
|
241
|
+
|
242
|
+
specify "negated =~ with regexp" do
|
243
|
+
sql = User.select { |m| m.name !~ /chris/ }.to_s
|
244
|
+
sql.should == "SELECT * FROM users WHERE users.`name` NOT REGEXP 'chris'"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
context "Adapter without overrides" do
|
249
|
+
setup do
|
250
|
+
ActiveRecord::Base.connection = ActiveRecord::ConnectionAdapters::FakeAdapter.new('connection', 'logger')
|
251
|
+
end
|
252
|
+
|
253
|
+
specify "quoting of column name" do
|
254
|
+
me = 'chris'
|
255
|
+
sql = User.select { |m| m.name == me }.to_s
|
256
|
+
sql.should == "SELECT * FROM users WHERE users.name = '#{me}'"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
data/test/slice_test.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "ActiveRecord Adapter" do
|
4
|
+
context "Slice" do
|
5
|
+
setup do
|
6
|
+
@sql = User.select { |m| m.name == 'jon' }
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "first" do
|
10
|
+
conditions = { :conditions => "users.name = 'jon'", :limit => 1 }
|
11
|
+
User.expects(:find).with(:all, conditions)
|
12
|
+
@sql.first
|
13
|
+
end
|
14
|
+
|
15
|
+
specify "first with argument" do
|
16
|
+
conditions = { :conditions => "users.name = 'jon'", :limit => 5 }
|
17
|
+
User.expects(:find).with(:all, conditions)
|
18
|
+
@sql.first(5).entries
|
19
|
+
end
|
20
|
+
|
21
|
+
specify "[] with two elements" do
|
22
|
+
conditions = { :conditions => "users.name = 'jon'", :limit => 20, :offset => 10 }
|
23
|
+
User.expects(:find).with(:all, conditions)
|
24
|
+
@sql[10, 20].entries
|
25
|
+
|
26
|
+
conditions = { :conditions => "users.name = 'jon'", :limit => 20, :offset => 20 }
|
27
|
+
User.expects(:find).with(:all, conditions)
|
28
|
+
@sql[20, 20].entries
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "slice is an alias of []" do
|
32
|
+
conditions = { :conditions => "users.name = 'jon'", :limit => 20, :offset => 10 }
|
33
|
+
User.expects(:find).with(:all, conditions)
|
34
|
+
@sql.slice(10, 20).entries
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "[] with range" do
|
38
|
+
conditions = { :conditions => "users.name = 'jon'", :limit => 10, :offset => 10 }
|
39
|
+
User.expects(:find).with(:all, conditions)
|
40
|
+
@sql[11..20].entries
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/test/sort_test.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "ActiveRecord Adapter" do
|
4
|
+
context "Sort" do
|
5
|
+
setup do
|
6
|
+
@sql = User.select { |m| m.name == 'jon' }
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "simple order" do
|
10
|
+
string = @sql.sort_by { |m| m.name }.to_s
|
11
|
+
string.should == "SELECT * FROM users WHERE users.name = 'jon' ORDER BY users.name"
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "simple combined order" do
|
15
|
+
string = @sql.sort_by { |m| [ m.name, m.age ] }.to_s
|
16
|
+
string.should == "SELECT * FROM users WHERE users.name = 'jon' ORDER BY users.name, users.age"
|
17
|
+
end
|
18
|
+
|
19
|
+
specify "simple combined order with single reverse" do
|
20
|
+
string = @sql.sort_by { |m| [ m.name, -m.age ] }.to_s
|
21
|
+
string.should == "SELECT * FROM users WHERE users.name = 'jon' ORDER BY users.name, users.age DESC"
|
22
|
+
end
|
23
|
+
|
24
|
+
specify "simple combined order with two reverses" do
|
25
|
+
string = @sql.sort_by { |m| [ -m.name, -m.age ] }.to_s
|
26
|
+
string.should == "SELECT * FROM users WHERE users.name = 'jon' ORDER BY users.name DESC, users.age DESC"
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "reverse order with -" do
|
30
|
+
string = @sql.sort_by { |m| -m.age }.to_s
|
31
|
+
string.should == "SELECT * FROM users WHERE users.name = 'jon' ORDER BY users.age DESC"
|
32
|
+
end
|
33
|
+
|
34
|
+
xspecify "reverse order with #reverse" do
|
35
|
+
# TODO: not implemented
|
36
|
+
string = @sql.sort_by { |m| m.age }.reverse.to_s
|
37
|
+
string.should == "SELECT * FROM users WHERE users.name = 'jon' ORDER BY users.age DESC"
|
38
|
+
end
|
39
|
+
|
40
|
+
specify "random order" do
|
41
|
+
string = @sql.sort_by { rand }.to_s
|
42
|
+
string.should == "SELECT * FROM users WHERE users.name = 'jon' ORDER BY RAND()"
|
43
|
+
end
|
44
|
+
|
45
|
+
specify "non-existent method to sort by" do
|
46
|
+
should.raise(NoMethodError) { @sql.sort_by { foo }.to_s }
|
47
|
+
end
|
48
|
+
|
49
|
+
specify "Symbol#to_proc" do
|
50
|
+
string = User.sort_by(&:name).to_s
|
51
|
+
string.should == "SELECT * FROM users ORDER BY users.name"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/test/source_test.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
context "Setting the ambition_source" do
|
5
|
+
setup do
|
6
|
+
# @users = [
|
7
|
+
# OpenStruct.new(:name => 'Chris', :age => 22),
|
8
|
+
# OpenStruct.new(:name => 'PJ', :age => 24),
|
9
|
+
# OpenStruct.new(:name => 'Kevin', :age => 23),
|
10
|
+
# OpenStruct.new(:name => '_why', :age => 65)
|
11
|
+
# ]
|
12
|
+
# User.ambition_source = @users
|
13
|
+
end
|
14
|
+
|
15
|
+
teardown do
|
16
|
+
# User.ambition_source = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
xspecify "should run all selects / detects against that collection" do
|
20
|
+
User.detect { |u| u.name == 'Chris' }.should == @users.first
|
21
|
+
end
|
22
|
+
|
23
|
+
xspecify "should run all sorts against that collection" do
|
24
|
+
User.sort_by { |u| -u.age }.entries.should == @users.sort_by { |u| -u.age }
|
25
|
+
end
|
26
|
+
|
27
|
+
xspecify "should chain successfully" do
|
28
|
+
User.select { |u| u.age > 22 }.sort_by { |u| -u.age }.entries.should == [ @users[3], @users[1], @users[2] ]
|
29
|
+
end
|
30
|
+
|
31
|
+
xspecify "should be able to revert to normal" do
|
32
|
+
block = proc { User.select { |m| m.name == 'PJ' }.first }
|
33
|
+
|
34
|
+
User.expects(:find).never
|
35
|
+
block.call.should == @users[1]
|
36
|
+
|
37
|
+
conditions = { :conditions => "users.name = 'PJ'", :limit => 1 }
|
38
|
+
User.expects(:find).with(:first, conditions)
|
39
|
+
|
40
|
+
User.ambition_source = nil
|
41
|
+
block.call
|
42
|
+
end
|
43
|
+
end
|
data/test/types_test.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Once dynamically, once hardcoded
|
5
|
+
context "Different types" do
|
6
|
+
types_hash = {
|
7
|
+
'string' => "'string'",
|
8
|
+
:symbol => "'--- :symbol\n'",
|
9
|
+
1 => '1',
|
10
|
+
1.2 => '1.2',
|
11
|
+
true => '1',
|
12
|
+
false => '0',
|
13
|
+
Time.now => "'#{Time.now.to_s(:db)}'",
|
14
|
+
DateTime.now => "'#{DateTime.now.to_s(:db)}'",
|
15
|
+
Date.today => "'#{Date.today.to_s(:db)}'"
|
16
|
+
}
|
17
|
+
|
18
|
+
types_hash.each do |type, translation|
|
19
|
+
specify "simple using #{type}" do
|
20
|
+
sql = User.select { |m| m.name == type }.to_s
|
21
|
+
sql.should == "SELECT * FROM users WHERE users.name = #{translation}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
specify "float" do
|
26
|
+
sql = User.select { |m| m.name == 1.2 }.to_s
|
27
|
+
sql.should == "SELECT * FROM users WHERE users.name = 1.2"
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "integer" do
|
31
|
+
sql = User.select { |m| m.name == 1 }.to_s
|
32
|
+
sql.should == "SELECT * FROM users WHERE users.name = 1"
|
33
|
+
end
|
34
|
+
|
35
|
+
specify "true" do
|
36
|
+
sql = User.select { |m| m.name == true }.to_s
|
37
|
+
sql.should == "SELECT * FROM users WHERE users.name = 1"
|
38
|
+
end
|
39
|
+
|
40
|
+
specify "false" do
|
41
|
+
sql = User.select { |m| m.name == false }.to_s
|
42
|
+
sql.should == "SELECT * FROM users WHERE users.name = 0"
|
43
|
+
end
|
44
|
+
|
45
|
+
specify "nil" do
|
46
|
+
sql = User.select { |m| m.name == nil }.to_s
|
47
|
+
sql.should == "SELECT * FROM users WHERE users.name IS NULL"
|
48
|
+
end
|
49
|
+
|
50
|
+
specify "not nil" do
|
51
|
+
sql = User.select { |m| m.name != nil }.to_s
|
52
|
+
sql.should == "SELECT * FROM users WHERE users.name IS NOT NULL"
|
53
|
+
end
|
54
|
+
|
55
|
+
specify "Time" do
|
56
|
+
sql = User.select { |m| m.name == Time.now }.to_s
|
57
|
+
sql.should == "SELECT * FROM users WHERE users.name = '#{Time.now.to_s(:db)}'"
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: ambitious-activerecord
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2008-01-29 00:00:00 -08:00
|
8
|
+
summary: An ambitious adapter for ActiveRecord
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: chris@ozmm.org
|
12
|
+
homepage: http://ambition.rubyforge.org/
|
13
|
+
rubyforge_project: ambition
|
14
|
+
description: An ambitious adapter for ActiveRecord
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Chris Wanstrath
|
31
|
+
files:
|
32
|
+
- lib/ambition/adapters/active_record/base.rb
|
33
|
+
- lib/ambition/adapters/active_record/query.rb
|
34
|
+
- lib/ambition/adapters/active_record/select.rb
|
35
|
+
- lib/ambition/adapters/active_record/slice.rb
|
36
|
+
- lib/ambition/adapters/active_record/sort.rb
|
37
|
+
- lib/ambition/adapters/active_record/statements.rb
|
38
|
+
- lib/ambition/adapters/active_record.rb
|
39
|
+
- test/benchmark.rb
|
40
|
+
- test/chaining_test.rb
|
41
|
+
- test/count_test.rb
|
42
|
+
- test/enumerable_test.rb
|
43
|
+
- test/helper.rb
|
44
|
+
- test/join_test.rb
|
45
|
+
- test/profiler.rb
|
46
|
+
- test/ruby_test.rb
|
47
|
+
- test/select_test.rb
|
48
|
+
- test/slice_test.rb
|
49
|
+
- test/sort_test.rb
|
50
|
+
- test/source_test.rb
|
51
|
+
- test/types_test.rb
|
52
|
+
- Manifest
|
53
|
+
- ambitious-activerecord.gemspec
|
54
|
+
test_files:
|
55
|
+
- test/chaining_test.rb
|
56
|
+
- test/count_test.rb
|
57
|
+
- test/enumerable_test.rb
|
58
|
+
- test/join_test.rb
|
59
|
+
- test/ruby_test.rb
|
60
|
+
- test/select_test.rb
|
61
|
+
- test/slice_test.rb
|
62
|
+
- test/sort_test.rb
|
63
|
+
- test/source_test.rb
|
64
|
+
- test/types_test.rb
|
65
|
+
rdoc_options: []
|
66
|
+
|
67
|
+
extra_rdoc_files: []
|
68
|
+
|
69
|
+
executables: []
|
70
|
+
|
71
|
+
extensions: []
|
72
|
+
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
dependencies:
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: activerecord
|
78
|
+
version_requirement:
|
79
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 1.15.0
|
84
|
+
version:
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: ambition
|
87
|
+
version_requirement:
|
88
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 0.5.0
|
93
|
+
version:
|