ambition 0.3.1 → 0.5.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 +41 -48
- data/README +6 -203
- data/ambition.gemspec +111 -0
- data/app_generators/ambition_adapter/USAGE +1 -0
- data/app_generators/ambition_adapter/ambition_adapter_generator.rb +66 -0
- data/app_generators/ambition_adapter/templates/LICENSE +18 -0
- data/app_generators/ambition_adapter/templates/README +6 -0
- data/app_generators/ambition_adapter/templates/Rakefile +31 -0
- data/app_generators/ambition_adapter/templates/lib/base.rb.erb +12 -0
- data/app_generators/ambition_adapter/templates/lib/init.rb.erb +22 -0
- data/app_generators/ambition_adapter/templates/lib/query.rb.erb +52 -0
- data/app_generators/ambition_adapter/templates/lib/select.rb.erb +100 -0
- data/app_generators/ambition_adapter/templates/lib/slice.rb.erb +19 -0
- data/app_generators/ambition_adapter/templates/lib/sort.rb.erb +43 -0
- data/app_generators/ambition_adapter/templates/test/helper.rb.erb +9 -0
- data/app_generators/ambition_adapter/templates/test/select_test.rb.erb +157 -0
- data/app_generators/ambition_adapter/templates/test/slice_test.rb.erb +36 -0
- data/app_generators/ambition_adapter/templates/test/sort_test.rb.erb +53 -0
- data/bin/ambition_adapter +13 -0
- data/lib/ambition.rb +8 -13
- data/lib/ambition/api.rb +42 -35
- data/lib/ambition/context.rb +62 -0
- data/lib/ambition/{proc_to_ruby.rb → core_ext.rb} +13 -0
- data/lib/ambition/enumerable.rb +6 -0
- data/lib/ambition/processors/base.rb +126 -0
- data/lib/ambition/processors/ruby.rb +24 -0
- data/lib/ambition/processors/select.rb +105 -0
- data/lib/ambition/processors/slice.rb +15 -0
- data/lib/ambition/processors/sort.rb +51 -0
- data/test/adapters/exemplar/association_test.rb +34 -0
- data/test/adapters/exemplar/count_test.rb +0 -0
- data/test/adapters/exemplar/detect_test.rb +9 -0
- data/test/adapters/exemplar/enumerable_test.rb +0 -0
- data/test/adapters/exemplar/helper.rb +3 -0
- data/test/adapters/exemplar/index_operator.rb +6 -0
- data/test/adapters/exemplar/reject_test.rb +0 -0
- data/test/adapters/exemplar/select_test.rb +151 -0
- data/test/adapters/exemplar/slice_test.rb +0 -0
- data/test/adapters/exemplar/sort_test.rb +0 -0
- data/test/debug +9 -0
- data/test/helper.rb +2 -52
- metadata +56 -71
- data/Rakefile +0 -64
- data/init.rb +0 -1
- data/lib/ambition/database_statements.rb +0 -31
- data/lib/ambition/processor.rb +0 -123
- data/lib/ambition/query.rb +0 -91
- data/lib/ambition/ruby_processor.rb +0 -22
- data/lib/ambition/select_processor.rb +0 -149
- data/lib/ambition/simple_processor.rb +0 -10
- data/lib/ambition/sort_processor.rb +0 -47
- data/lib/ambition/source.rb +0 -53
- data/test/benchmark.rb +0 -68
- data/test/chaining_test.rb +0 -34
- data/test/console +0 -9
- data/test/count_test.rb +0 -17
- data/test/databases/boot.rb +0 -3
- data/test/databases/database.yml +0 -17
- data/test/databases/fixtures/admin.rb +0 -3
- data/test/databases/fixtures/companies.yml +0 -24
- data/test/databases/fixtures/company.rb +0 -23
- data/test/databases/fixtures/developer.rb +0 -11
- data/test/databases/fixtures/developers_projects.yml +0 -13
- data/test/databases/fixtures/project.rb +0 -4
- data/test/databases/fixtures/projects.yml +0 -7
- data/test/databases/fixtures/replies.yml +0 -20
- data/test/databases/fixtures/reply.rb +0 -5
- data/test/databases/fixtures/topic.rb +0 -19
- data/test/databases/fixtures/topics.yml +0 -32
- data/test/databases/fixtures/user.rb +0 -2
- data/test/databases/fixtures/users.yml +0 -35
- data/test/databases/lib/activerecord_test_connector.rb +0 -65
- data/test/databases/lib/load_fixtures.rb +0 -13
- data/test/databases/lib/schema.rb +0 -41
- data/test/enumerable_test.rb +0 -95
- data/test/join_test.rb +0 -61
- data/test/limit_test.rb +0 -41
- data/test/order_test.rb +0 -52
- data/test/profiler.rb +0 -34
- data/test/ruby_test.rb +0 -9
- data/test/source_test.rb +0 -43
- data/test/types_test.rb +0 -59
- data/test/where_test.rb +0 -245
@@ -1,22 +0,0 @@
|
|
1
|
-
module Ambition
|
2
|
-
class RubyProcessor < RubyToRuby
|
3
|
-
def self.process(node)
|
4
|
-
@processor ||= new
|
5
|
-
@processor.process node
|
6
|
-
end
|
7
|
-
|
8
|
-
##
|
9
|
-
# This is not DRY, and I don't care.
|
10
|
-
def process(node)
|
11
|
-
node ||= []
|
12
|
-
|
13
|
-
if respond_to?(method = "process_#{node.first}")
|
14
|
-
send(method, node[1..-1])
|
15
|
-
elsif node.blank?
|
16
|
-
''
|
17
|
-
else
|
18
|
-
raise "Missing process method for sexp: #{node.inspect}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,149 +0,0 @@
|
|
1
|
-
module Ambition
|
2
|
-
class SelectProcessor < Processor
|
3
|
-
def initialize(owner, block)
|
4
|
-
super()
|
5
|
-
@receiver = nil
|
6
|
-
@owner = owner
|
7
|
-
@table_name = owner.table_name
|
8
|
-
@block = block
|
9
|
-
@key = :conditions
|
10
|
-
end
|
11
|
-
|
12
|
-
##
|
13
|
-
# Sexp Processing Methods
|
14
|
-
def process_and(exp)
|
15
|
-
joined_expressions 'AND', exp
|
16
|
-
end
|
17
|
-
|
18
|
-
def process_or(exp)
|
19
|
-
joined_expressions 'OR', exp
|
20
|
-
end
|
21
|
-
|
22
|
-
def process_not(exp)
|
23
|
-
type, receiver, method, other = *exp.first
|
24
|
-
|
25
|
-
case type
|
26
|
-
when :call
|
27
|
-
translation(receiver, negate(method, other), other)
|
28
|
-
when :match3
|
29
|
-
regexp = receiver.last
|
30
|
-
"#{process(method)} #{statement(:negated_regexp, regexp)} #{sanitize(regexp)}"
|
31
|
-
else
|
32
|
-
process_error(exp)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def process_call(exp)
|
37
|
-
receiver, method, other = *exp
|
38
|
-
|
39
|
-
translation(receiver, method, other)
|
40
|
-
end
|
41
|
-
|
42
|
-
def process_lit(exp)
|
43
|
-
exp.shift.to_s
|
44
|
-
end
|
45
|
-
|
46
|
-
def process_str(exp)
|
47
|
-
sanitize exp.shift
|
48
|
-
end
|
49
|
-
|
50
|
-
def process_nil(exp)
|
51
|
-
'NULL'
|
52
|
-
end
|
53
|
-
|
54
|
-
def process_false(exp)
|
55
|
-
sanitize 'false'
|
56
|
-
end
|
57
|
-
|
58
|
-
def process_true(exp)
|
59
|
-
sanitize 'true'
|
60
|
-
end
|
61
|
-
|
62
|
-
def process_match3(exp)
|
63
|
-
regexp, target = exp.shift.last, process(exp.shift)
|
64
|
-
"#{target} #{statement(:regexp, regexp)} #{sanitize(regexp)}"
|
65
|
-
end
|
66
|
-
|
67
|
-
def process_dvar(exp)
|
68
|
-
target = exp.shift
|
69
|
-
if target == @receiver
|
70
|
-
@table_name
|
71
|
-
else
|
72
|
-
value(target.to_s[0..-1])
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def process_ivar(exp)
|
77
|
-
value(exp.shift.to_s[0..-1])
|
78
|
-
end
|
79
|
-
|
80
|
-
def process_lvar(exp)
|
81
|
-
value(exp.shift.to_s)
|
82
|
-
end
|
83
|
-
|
84
|
-
def process_vcall(exp)
|
85
|
-
value(exp.shift.to_s)
|
86
|
-
end
|
87
|
-
|
88
|
-
def process_gvar(exp)
|
89
|
-
value(exp.shift.to_s)
|
90
|
-
end
|
91
|
-
|
92
|
-
def process_attrasgn(exp)
|
93
|
-
raise "Assignment not supported. Maybe you meant ==?"
|
94
|
-
end
|
95
|
-
|
96
|
-
##
|
97
|
-
# Processor helper methods
|
98
|
-
def joined_expressions(with, exp)
|
99
|
-
clauses = []
|
100
|
-
while clause = exp.shift
|
101
|
-
clauses << clause
|
102
|
-
end
|
103
|
-
"(" + clauses.map { |c| process(c) }.join(" #{with} ") + ")"
|
104
|
-
end
|
105
|
-
|
106
|
-
def negate(method, target = nil)
|
107
|
-
if Array(target).last == [:nil]
|
108
|
-
return 'IS NOT'
|
109
|
-
end
|
110
|
-
|
111
|
-
case method
|
112
|
-
when :==
|
113
|
-
'<>'
|
114
|
-
when :=~
|
115
|
-
'!~'
|
116
|
-
else
|
117
|
-
raise "Not implemented: #{method.inspect}"
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def translation(receiver, method, other = nil)
|
122
|
-
case method.to_s
|
123
|
-
when 'IS NOT'
|
124
|
-
"#{process(receiver)} IS NOT #{process(other)}"
|
125
|
-
when '=='
|
126
|
-
case other_value = process(other)
|
127
|
-
when "NULL"
|
128
|
-
"#{process(receiver)} IS #{other_value}"
|
129
|
-
else
|
130
|
-
"#{process(receiver)} = #{other_value}"
|
131
|
-
end
|
132
|
-
when '<>', '>', '<', '>=', '<='
|
133
|
-
"#{process(receiver)} #{method} #{process(other)}"
|
134
|
-
when 'include?'
|
135
|
-
"#{process(other)} IN (#{process(receiver)})"
|
136
|
-
when '=~'
|
137
|
-
"#{process(receiver)} LIKE #{process(other)}"
|
138
|
-
when '!~'
|
139
|
-
"#{process(receiver)} NOT LIKE #{process(other)}"
|
140
|
-
when 'upcase'
|
141
|
-
"UPPER(#{process(receiver)})"
|
142
|
-
when 'downcase'
|
143
|
-
"LOWER(#{process(receiver)})"
|
144
|
-
else
|
145
|
-
extract_includes(receiver, method) || "#{process(receiver)}.#{quote_column_name(method)}"
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module Ambition
|
2
|
-
class SortProcessor < Processor
|
3
|
-
def initialize(owner, block)
|
4
|
-
super()
|
5
|
-
@receiver = nil
|
6
|
-
@owner = owner
|
7
|
-
@table_name = owner.table_name
|
8
|
-
@block = block
|
9
|
-
@key = :order
|
10
|
-
end
|
11
|
-
|
12
|
-
##
|
13
|
-
# Sexp Processing Methods
|
14
|
-
def process_call(exp)
|
15
|
-
receiver, method, other = *exp
|
16
|
-
exp.clear
|
17
|
-
|
18
|
-
translation(receiver, method, other)
|
19
|
-
end
|
20
|
-
|
21
|
-
def process_vcall(exp)
|
22
|
-
if (method = exp.shift) == :rand
|
23
|
-
'RAND()'
|
24
|
-
else
|
25
|
-
raise "Not implemented: :vcall for #{method}"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def process_masgn(exp)
|
30
|
-
exp.clear
|
31
|
-
''
|
32
|
-
end
|
33
|
-
|
34
|
-
##
|
35
|
-
# Helpers!
|
36
|
-
def translation(receiver, method, other)
|
37
|
-
case method
|
38
|
-
when :-@
|
39
|
-
"#{process(receiver)} DESC"
|
40
|
-
when :__send__
|
41
|
-
"#{@table_name}.#{eval('to_s', @block)}"
|
42
|
-
else
|
43
|
-
extract_includes(receiver, method) || "#{@table_name}.#{method}"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
data/lib/ambition/source.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
module Ambition
|
2
|
-
module Source
|
3
|
-
def self.api_methods
|
4
|
-
Ambition::API.instance_methods(false).reject do |method|
|
5
|
-
method =~ /ambition|context/
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.included(base)
|
10
|
-
singleton = (class << base; self end)
|
11
|
-
api_methods.each do |method|
|
12
|
-
##
|
13
|
-
# This is less than cool, but we do it to get what we want.
|
14
|
-
# When define_method can create a method which is block-aware,
|
15
|
-
# then we can move to pure Ruby.
|
16
|
-
singleton.class_eval <<-end_eval
|
17
|
-
def #{method}(*args, &block)
|
18
|
-
return super unless @ambition_source
|
19
|
-
@ambition_source.#{method}(*args, &block)
|
20
|
-
end
|
21
|
-
end_eval
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
module API
|
27
|
-
attr_reader :ambition_source
|
28
|
-
|
29
|
-
##
|
30
|
-
# This is a destructive method. When given an array,
|
31
|
-
# all Enumerable operations will be performed on the array
|
32
|
-
# rather than on the database.
|
33
|
-
#
|
34
|
-
# User.ambition_source = users(:chris, :pj)
|
35
|
-
# User.select { |u| u.age > 30 }
|
36
|
-
#
|
37
|
-
# You don't have to use fixtures, of course. Make stuff up on the fly.
|
38
|
-
#
|
39
|
-
# users = [ User.new(:name => 'chris', :age => 11), User.new(:name => 'pj', :age => 100) ]
|
40
|
-
# User.ambition_source = users
|
41
|
-
# User.select { |u| u.age > 30 }
|
42
|
-
#
|
43
|
-
# Revert back to normal by setting ambition_source to nil.
|
44
|
-
#
|
45
|
-
# User.ambition_source = nil
|
46
|
-
# User.select { |u| u.age > 30 }
|
47
|
-
#
|
48
|
-
def ambition_source=(records)
|
49
|
-
@ambition_source = records
|
50
|
-
include Source unless ancestors.include? Source
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
data/test/benchmark.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__) + '/../lib'
|
2
|
-
$NO_TEST = true
|
3
|
-
%w( ambition rubygems active_record benchmark ).each { |f| require f }
|
4
|
-
|
5
|
-
class User < ActiveRecord::Base
|
6
|
-
def self.reflections
|
7
|
-
return @reflections if @reflections
|
8
|
-
@reflections = {}
|
9
|
-
@reflections[:ideas] = Reflection.new(:has_many, 'user_id', :ideas, 'ideas')
|
10
|
-
@reflections[:invites] = Reflection.new(:has_many, 'referrer_id', :invites, 'invites')
|
11
|
-
@reflections[:profile] = Reflection.new(:has_one, 'user_id', :profile, 'profiles')
|
12
|
-
@reflections[:account] = Reflection.new(:belongs_to, 'account_id', :account, 'accounts')
|
13
|
-
@reflections
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.table_name
|
17
|
-
'users'
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
class Reflection < Struct.new(:macro, :primary_key_name, :name, :table_name)
|
22
|
-
end
|
23
|
-
|
24
|
-
Times = 1000
|
25
|
-
|
26
|
-
Benchmark.bm(30) do |x|
|
27
|
-
x.report 'simple select' do
|
28
|
-
Times.times do
|
29
|
-
User.select { |u| u.id == 20 }.to_hash
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
x.report 'simple select w/ eval' do
|
34
|
-
Times.times do
|
35
|
-
User.select { |u| u.created_at == Time.now }.to_hash
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
x.report 'dual select' do
|
40
|
-
Times.times do
|
41
|
-
User.select { |u| u.id == 20 && u.age > 20 }.to_hash
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
x.report 'join select' do
|
46
|
-
Times.times do
|
47
|
-
User.select { |u| u.id == 20 && u.ideas.name =~ /stuff/ }.to_hash
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
x.report 'dual select w/ sort' do
|
52
|
-
Times.times do
|
53
|
-
User.select { |u| u.id == 20 && u.age > 20 }.sort_by { |u| u.id }.to_hash
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
x.report 'dual select w/ sort & first' do
|
58
|
-
Times.times do
|
59
|
-
User.select { |u| u.id == 20 && u.age > 20 }.sort_by { |u| u.id }.first(20).to_hash
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
x.report "it's complicated" do
|
64
|
-
Times.times do
|
65
|
-
User.select { |u| (u.id == 20 && u.age > 20) || u.profile.name == 'Jon' }.sort_by { |u| [u.id, -u.name] }.first(20).to_hash
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
data/test/chaining_test.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/helper'
|
2
|
-
|
3
|
-
context "Chaining" do
|
4
|
-
specify "should join selects with AND" do
|
5
|
-
sql = User.select { |m| m.name == 'jon' }
|
6
|
-
sql = sql.select { |m| m.age == 22 }
|
7
|
-
sql.to_sql.should == "SELECT * FROM users WHERE users.name = 'jon' AND users.age = 22"
|
8
|
-
end
|
9
|
-
|
10
|
-
specify "should join sort_bys with a comma" do
|
11
|
-
sql = User.select { |m| m.name == 'jon' }
|
12
|
-
sql = sql.sort_by { |m| m.name }
|
13
|
-
sql = sql.sort_by { |m| m.age }
|
14
|
-
sql.to_sql.should == "SELECT * FROM users WHERE users.name = 'jon' ORDER BY users.name, users.age"
|
15
|
-
end
|
16
|
-
|
17
|
-
specify "should join selects and sorts intelligently" do
|
18
|
-
sql = User.select { |m| m.name == 'jon' }
|
19
|
-
sql = sql.select { |m| m.age == 22 }
|
20
|
-
sql = sql.sort_by { |m| -m.name }
|
21
|
-
sql = sql.sort_by { |m| m.age }
|
22
|
-
sql.to_sql.should == "SELECT * FROM users WHERE users.name = 'jon' AND users.age = 22 ORDER BY users.name DESC, users.age"
|
23
|
-
end
|
24
|
-
|
25
|
-
specify "should join lots of selects and sorts intelligently" do
|
26
|
-
sql = User.select { |m| m.name == 'jon' }
|
27
|
-
sql = sql.select { |m| m.age == 22 }
|
28
|
-
sql = sql.sort_by { |m| m.name }
|
29
|
-
sql = sql.select { |m| m.power == true }
|
30
|
-
sql = sql.sort_by { |m| m.email }
|
31
|
-
sql = sql.select { |m| m.admin == true && m.email == 'chris@ozmm.org' }
|
32
|
-
sql.to_sql.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"
|
33
|
-
end
|
34
|
-
end
|
data/test/console
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
3
|
-
libs = []
|
4
|
-
dirname = File.dirname(__FILE__)
|
5
|
-
|
6
|
-
libs << 'irb/completion'
|
7
|
-
libs << File.join(dirname, 'databases', 'boot')
|
8
|
-
|
9
|
-
exec "#{irb} #{libs.map{|l| " -r #{l}" }.join} --simple-prompt"
|
data/test/count_test.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/helper'
|
2
|
-
|
3
|
-
context "Count" do
|
4
|
-
setup do
|
5
|
-
hash = { :conditions => "users.name = 'jon'" }
|
6
|
-
User.expects(:count).with(hash)
|
7
|
-
@sql = User.select { |m| m.name == 'jon' }
|
8
|
-
end
|
9
|
-
|
10
|
-
specify "size" do
|
11
|
-
@sql.size
|
12
|
-
end
|
13
|
-
|
14
|
-
specify "length" do
|
15
|
-
@sql.length
|
16
|
-
end
|
17
|
-
end
|
data/test/databases/boot.rb
DELETED
data/test/databases/database.yml
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
sqlite3:
|
2
|
-
adapter: sqlite3
|
3
|
-
database: db/development.sqlite3
|
4
|
-
|
5
|
-
mysql:
|
6
|
-
adapter: mysql
|
7
|
-
database: ambition_development
|
8
|
-
username: root
|
9
|
-
password:
|
10
|
-
host: localhost
|
11
|
-
|
12
|
-
postgres:
|
13
|
-
adapter: postgresql
|
14
|
-
database: ambition_development
|
15
|
-
username: root
|
16
|
-
password:
|
17
|
-
host: localhost
|