benhoskings-ambitious-activerecord 0.1.3
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 +22 -0
- data/ambitious-activerecord.gemspec +72 -0
- data/lib/ambition/adapters/active_record.rb +12 -0
- data/lib/ambition/adapters/active_record/base.rb +53 -0
- data/lib/ambition/adapters/active_record/query.rb +75 -0
- data/lib/ambition/adapters/active_record/select.rb +99 -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 +69 -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 +47 -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 +69 -0
- metadata +113 -0
data/Manifest
ADDED
@@ -0,0 +1,22 @@
|
|
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/context.rb
|
8
|
+
lib/ambition/adapters/active_record.rb
|
9
|
+
test/benchmark.rb
|
10
|
+
test/chaining_test.rb
|
11
|
+
test/count_test.rb
|
12
|
+
test/enumerable_test.rb
|
13
|
+
test/helper.rb
|
14
|
+
test/join_test.rb
|
15
|
+
test/profiler.rb
|
16
|
+
test/ruby_test.rb
|
17
|
+
test/select_test.rb
|
18
|
+
test/slice_test.rb
|
19
|
+
test/sort_test.rb
|
20
|
+
test/source_test.rb
|
21
|
+
test/types_test.rb
|
22
|
+
Manifest
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
# Gem::Specification for Ambitious-activerecord-0.1.3
|
3
|
+
# Originally generated by Echoe
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{ambitious-activerecord}
|
7
|
+
s.version = "0.1.3"
|
8
|
+
|
9
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.authors = ["Chris Wanstrath"]
|
13
|
+
s.date = %q{2008-04-26}
|
14
|
+
s.description = %q{An ambitious adapter for ActiveRecord}
|
15
|
+
s.email = %q{chris@ozmm.org}
|
16
|
+
s.extra_rdoc_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"]
|
17
|
+
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"]
|
18
|
+
s.has_rdoc = true
|
19
|
+
s.homepage = %q{http://ambition.rubyforge.org/}
|
20
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Ambitious-activerecord", "--main", "README"]
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
s.rubyforge_project = %q{ambition}
|
23
|
+
s.rubygems_version = %q{1.3.1}
|
24
|
+
s.summary = %q{An ambitious adapter for ActiveRecord}
|
25
|
+
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"]
|
26
|
+
|
27
|
+
s.add_dependency(%q<activerecord>, [">= 1.15.6"])
|
28
|
+
s.add_dependency(%q<ambition>, [">= 0.5.3"])
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# # Original Rakefile source (requires the Echoe gem):
|
33
|
+
#
|
34
|
+
# require 'rake'
|
35
|
+
#
|
36
|
+
# Version = '0.1.3'
|
37
|
+
#
|
38
|
+
# begin
|
39
|
+
# require 'rubygems'
|
40
|
+
# gem 'echoe', '>=2.7'
|
41
|
+
# ENV['RUBY_FLAGS'] = ""
|
42
|
+
# require 'echoe'
|
43
|
+
#
|
44
|
+
# Echoe.new('ambitious-activerecord') do |p|
|
45
|
+
# p.dependencies << 'activerecord >=1.15.6'
|
46
|
+
# p.summary = "An ambitious adapter for ActiveRecord"
|
47
|
+
# p.author = 'Chris Wanstrath'
|
48
|
+
# p.email = "chris@ozmm.org"
|
49
|
+
#
|
50
|
+
# p.project = 'ambition'
|
51
|
+
# p.url = "http://ambition.rubyforge.org/"
|
52
|
+
# p.test_pattern = 'test/*_test.rb'
|
53
|
+
# p.version = Version
|
54
|
+
# p.dependencies << 'ambition >=0.5.3'
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# rescue LoadError
|
58
|
+
# puts "Not doing any of the Echoe gemmy stuff, because you don't have the specified gem versions"
|
59
|
+
#
|
60
|
+
# require 'rake/testtask'
|
61
|
+
# Rake::TestTask.new do |t|
|
62
|
+
# t.pattern = "test/*_test.rb"
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# desc 'Install as a gem'
|
67
|
+
# task :install_gem do
|
68
|
+
# puts `rake manifest package && gem install pkg/ambitious-activerecord-#{Version}.gem`
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# task :default => :test
|
72
|
+
#
|
@@ -0,0 +1,12 @@
|
|
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
|
+
require 'ambition/adapters/active_record/context'
|
10
|
+
|
11
|
+
ActiveRecord::Base.extend Ambition::API
|
12
|
+
ActiveRecord::Base.ambition_adapter = Ambition::Adapters::ActiveRecord
|
@@ -0,0 +1,53 @@
|
|
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 owner.connected?
|
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 owner.connected?
|
34
|
+
::ActiveRecord::Base.connection.quote_column_name(value)
|
35
|
+
else
|
36
|
+
value.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def dbadapter_name
|
41
|
+
::ActiveRecord::Base.connection.adapter_name
|
42
|
+
rescue ::ActiveRecord::ConnectionNotEstablished
|
43
|
+
'Abstract'
|
44
|
+
end
|
45
|
+
|
46
|
+
def statement(*args)
|
47
|
+
@statement_instance ||= DatabaseStatements.const_get(dbadapter_name).new
|
48
|
+
@statement_instance.send(*args)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,75 @@
|
|
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
|
+
alias_method :length, :size
|
15
|
+
|
16
|
+
def to_hash
|
17
|
+
hash = {}
|
18
|
+
|
19
|
+
unless (joins = stash[:joins]).blank?
|
20
|
+
hash[:joins] = joins
|
21
|
+
stash[:include] -= stash[:joins] unless stash[:include].blank?
|
22
|
+
end
|
23
|
+
|
24
|
+
unless (where = clauses[:select]).blank?
|
25
|
+
hash[:conditions] = Array(where)
|
26
|
+
hash[:conditions] *= ' AND '
|
27
|
+
end
|
28
|
+
|
29
|
+
unless (order = clauses[:sort]).blank?
|
30
|
+
hash[:order] = order.join(', ')
|
31
|
+
end
|
32
|
+
|
33
|
+
if Array(clauses[:slice]).last =~ /LIMIT (\d+)/
|
34
|
+
hash[:limit] = $1.to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
if Array(clauses[:slice]).last =~ /OFFSET (\d+)/
|
38
|
+
hash[:offset] = $1.to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
hash[:include] = stash[:include] if stash[:include]
|
42
|
+
|
43
|
+
hash
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
hash = to_hash
|
48
|
+
|
49
|
+
raise "Sorry, I can't construct SQL with complex joins (yet)" unless hash[:include].blank?
|
50
|
+
|
51
|
+
sql = []
|
52
|
+
sql << joins_sql(hash[:joins]) unless hash[:joins].blank?
|
53
|
+
sql << "WHERE #{hash[:conditions]}" unless hash[:conditions].blank?
|
54
|
+
sql << "ORDER BY #{hash[:order]}" unless hash[:order].blank?
|
55
|
+
sql << clauses[:slice].last unless hash[:slice].blank?
|
56
|
+
|
57
|
+
@@select % [ owner.table_name, sql.join(' ') ]
|
58
|
+
end
|
59
|
+
alias_method :to_sql, :to_s
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def joins_sql(joins)
|
64
|
+
joins.map {|join|
|
65
|
+
::ActiveRecord::Associations::ClassMethods::InnerJoinDependency.
|
66
|
+
new(owner, join, nil).
|
67
|
+
join_associations.
|
68
|
+
map(&:association_join).
|
69
|
+
join(' ')
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,99 @@
|
|
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 nil?(column)
|
85
|
+
left = "#{owner.table_name}.#{quote_column_name column}"
|
86
|
+
negated? ? not_equal(left, nil) : self.==(left, nil)
|
87
|
+
end
|
88
|
+
|
89
|
+
def downcase(column)
|
90
|
+
"LOWER(#{owner.table_name}.#{quote_column_name column})"
|
91
|
+
end
|
92
|
+
|
93
|
+
def upcase(column)
|
94
|
+
"UPPER(#{owner.table_name}.#{quote_column_name column})"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
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
|