cheat 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,164 @@
1
+ module Ambition
2
+ module Where
3
+ def select(*args, &block)
4
+ ##
5
+ # XXX: AR::Base hack / workaround
6
+ if args.empty?
7
+ query_context.add WhereProcessor.new(self, block)
8
+ else
9
+ super
10
+ end
11
+ end
12
+
13
+ def detect(&block)
14
+ select(&block).first
15
+ end
16
+ end
17
+
18
+ class WhereProcessor < Processor
19
+ attr_reader :includes
20
+
21
+ def initialize(owner, block)
22
+ super()
23
+ @receiver = nil
24
+ @owner = owner
25
+ @table_name = owner.table_name
26
+ @block = block
27
+ @key = :conditions
28
+ @includes = []
29
+ end
30
+
31
+ ##
32
+ # Sexp Processing Methods
33
+ def process_and(exp)
34
+ joined_expressions 'AND', exp
35
+ end
36
+
37
+ def process_or(exp)
38
+ joined_expressions 'OR', exp
39
+ end
40
+
41
+ def process_not(exp)
42
+ _, receiver, method, other = *exp.first
43
+ exp.clear
44
+ return translation(receiver, negate(method), other)
45
+ end
46
+
47
+ def process_call(exp)
48
+ receiver, method, other = *exp
49
+ exp.clear
50
+
51
+ return translation(receiver, method, other)
52
+ end
53
+
54
+ def process_lit(exp)
55
+ exp.shift.to_s
56
+ end
57
+
58
+ def process_str(exp)
59
+ sanitize exp.shift
60
+ end
61
+
62
+ def process_nil(exp)
63
+ 'NULL'
64
+ end
65
+
66
+ def process_false(exp)
67
+ sanitize 'false'
68
+ end
69
+
70
+ def process_true(exp)
71
+ sanitize 'true'
72
+ end
73
+
74
+ def process_match3(exp)
75
+ regexp, target = exp.shift.last.inspect.gsub('/',''), process(exp.shift)
76
+ "#{target} REGEXP '#{regexp}'"
77
+ end
78
+
79
+ def process_dvar(exp)
80
+ target = exp.shift
81
+ if target == @receiver
82
+ return @table_name
83
+ else
84
+ return value(target.to_s[0..-1])
85
+ end
86
+ end
87
+
88
+ def process_ivar(exp)
89
+ value(exp.shift.to_s[0..-1])
90
+ end
91
+
92
+ def process_lvar(exp)
93
+ value(exp.shift.to_s)
94
+ end
95
+
96
+ def process_vcall(exp)
97
+ value(exp.shift.to_s)
98
+ end
99
+
100
+ def process_gvar(exp)
101
+ value(exp.shift.to_s)
102
+ end
103
+
104
+ def process_attrasgn(exp)
105
+ exp.clear
106
+ raise "Assignment not supported. Maybe you meant ==?"
107
+ end
108
+
109
+ ##
110
+ # Processor helper methods
111
+ def joined_expressions(with, exp)
112
+ clauses = []
113
+ while clause = exp.shift
114
+ clauses << clause
115
+ end
116
+ return "(" + clauses.map { |c| process(c) }.join(" #{with} ") + ")"
117
+ end
118
+
119
+ def value(variable)
120
+ sanitize eval(variable, @block)
121
+ end
122
+
123
+ def negate(method)
124
+ case method
125
+ when :==
126
+ '<>'
127
+ when :=~
128
+ '!~'
129
+ else
130
+ raise "Not implemented: #{method}"
131
+ end
132
+ end
133
+
134
+ def translation(receiver, method, other)
135
+ case method.to_s
136
+ when '=='
137
+ "#{process(receiver)} = #{process(other)}"
138
+ when '<>', '>', '<'
139
+ "#{process(receiver)} #{method} #{process(other)}"
140
+ when 'include?'
141
+ "#{process(other)} IN (#{process(receiver)})"
142
+ when '=~'
143
+ "#{process(receiver)} LIKE #{process(other)}"
144
+ when '!~'
145
+ "#{process(receiver)} NOT LIKE #{process(other)}"
146
+ else
147
+ build_condition(receiver, method, other)
148
+ end
149
+ end
150
+
151
+ def build_condition(receiver, method, other)
152
+ if receiver.first == :call && receiver[1].last == @receiver
153
+ if reflection = @owner.reflections[receiver.last]
154
+ @includes << reflection.name unless @includes.include? reflection.name
155
+ "#{reflection.table_name}.#{method}"
156
+ else
157
+ raise "No reflection `#{receiver.last}' found on #{@owner}"
158
+ end
159
+ else
160
+ "#{process(receiver)}.`#{method}` #{process(other)}"
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,36 @@
1
+ ##
2
+ # Taken from ruby2ruby, Copyright (c) 2006 Ryan Davis under the MIT License
3
+ require 'parse_tree'
4
+ require 'unique'
5
+ require 'sexp_processor'
6
+
7
+ class Method
8
+ def with_class_and_method_name
9
+ if self.inspect =~ /<Method: (.*)\#(.*)>/ then
10
+ klass = eval $1
11
+ method = $2.intern
12
+ raise "Couldn't determine class from #{self.inspect}" if klass.nil?
13
+ return yield(klass, method)
14
+ else
15
+ raise "Can't parse signature: #{self.inspect}"
16
+ end
17
+ end
18
+
19
+ def to_sexp
20
+ with_class_and_method_name do |klass, method|
21
+ ParseTree.new(false).parse_tree_for_method(klass, method)
22
+ end
23
+ end
24
+ end
25
+
26
+ class Proc
27
+ def to_method
28
+ Unique.send(:define_method, :proc_to_method, self)
29
+ Unique.new.method(:proc_to_method)
30
+ end
31
+
32
+ def to_sexp
33
+ body = self.to_method.to_sexp[2][1..-1]
34
+ [:proc, *body]
35
+ end
36
+ end
@@ -0,0 +1,34 @@
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
@@ -0,0 +1,17 @@
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
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ context "Each" do
4
+ specify "simple ==" do
5
+ hash = { :conditions => "users.`age` = 21" }
6
+ User.expects(:find).with(:all, hash).returns([])
7
+ User.select { |m| m.age == 21 }.each do |user|
8
+ puts user.name
9
+ end
10
+ end
11
+
12
+ specify "limit and conditions" do
13
+ hash = { :limit => '5', :conditions => "users.`age` = 21" }
14
+ User.expects(:find).with(:all, hash).returns([])
15
+ User.select { |m| m.age == 21 }.first(5).each do |user|
16
+ puts user.name
17
+ end
18
+ end
19
+
20
+ specify "limit and conditions and order" do
21
+ hash = { :limit => '5', :conditions => "users.`age` = 21", :order => 'users.name' }
22
+ User.expects(:find).with(:all, hash).returns([])
23
+ User.select { |m| m.age == 21 }.sort_by { |m| m.name }.first(5).each do |user|
24
+ puts user.name
25
+ end
26
+ end
27
+
28
+ specify "limit and order" do
29
+ hash = { :limit => '5', :order => 'users.name' }
30
+ User.expects(:find).with(:all, hash).returns([])
31
+ User.sort_by { |m| m.name }.first(5).each do |user|
32
+ puts user.name
33
+ end
34
+ end
35
+ end
36
+
37
+ context "Enumerable Methods" do
38
+ specify "map" do
39
+ hash = { :conditions => "users.`age` = 21" }
40
+ User.expects(:find).with(:all, hash).returns([])
41
+ User.select { |m| m.age == 21 }.map { |u| u.name }
42
+ end
43
+
44
+ specify "each_with_index" do
45
+ hash = { :conditions => "users.`age` = 21" }
46
+ User.expects(:find).with(:all, hash).returns([])
47
+ User.select { |m| m.age == 21 }.each_with_index do |user, i|
48
+ puts "#{i}: #{user.name}"
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'test/spec'
3
+ require 'mocha'
4
+ require 'redgreen'
5
+ require 'active_support'
6
+ require 'active_record'
7
+
8
+ $:.unshift File.dirname(__FILE__) + '/../lib'
9
+ require 'ambition'
10
+
11
+ class User
12
+ extend Ambition
13
+
14
+ def self.reflections
15
+ return @reflections if @reflections
16
+ @reflections = {}
17
+ @reflections[:ideas] = Reflection.new(:has_many, 'user_id', :ideas, 'ideas')
18
+ @reflections[:invites] = Reflection.new(:has_many, 'referrer_id', :invites, 'invites')
19
+ @reflections[:profile] = Reflection.new(:has_one, 'user_id', :profile, 'profiles')
20
+ @reflections[:account] = Reflection.new(:belongs_to, 'account_id', :account, 'accounts')
21
+ @reflections
22
+ end
23
+
24
+ def self.table_name
25
+ 'users'
26
+ end
27
+ end
28
+
29
+ class Reflection < Struct.new(:macro, :primary_key_name, :name, :table_name)
30
+ end
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ context "Joins" do
4
+ specify "simple == on an association" do
5
+ sql = User.select { |m| m.account.email == 'chris@ozmm.org' }
6
+ sql.to_hash.should == {
7
+ :conditions => "accounts.email = 'chris@ozmm.org'",
8
+ :includes => [:account]
9
+ }
10
+ end
11
+
12
+ specify "simple mixed == on an association" do
13
+ sql = User.select { |m| m.name == 'chris' && m.account.email == 'chris@ozmm.org' }
14
+ sql.to_hash.should == {
15
+ :conditions => "(users.`name` = 'chris' AND accounts.email = 'chris@ozmm.org')",
16
+ :includes => [:account]
17
+ }
18
+ end
19
+
20
+ specify "multiple associations" do
21
+ sql = User.select { |m| m.ideas.title == 'New Freezer' || m.invites.email == 'pj@hyett.com' }
22
+ sql.to_hash.should == {
23
+ :conditions => "(ideas.title = 'New Freezer' OR invites.email = 'pj@hyett.com')",
24
+ :includes => [:ideas, :invites]
25
+ }
26
+ end
27
+
28
+ specify "non-existant associations" do
29
+ sql = User.select { |m| m.liquor.brand == 'Jack' }
30
+ should.raise { sql.to_hash }
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ context "Limit" do
4
+ setup do
5
+ @sql = User.select { |m| m.name == 'jon' }
6
+ end
7
+
8
+ specify "first" do
9
+ conditions = { :conditions => "users.`name` = 'jon'", :limit => '1' }
10
+ User.expects(:find).with(:first, conditions)
11
+ @sql.first
12
+ end
13
+
14
+ specify "first with argument" do
15
+ conditions = { :conditions => "users.`name` = 'jon'", :limit => '5' }
16
+ User.expects(:find).with(:all, conditions)
17
+ @sql.first(5)
18
+ end
19
+
20
+ specify "[] with one element" do
21
+ conditions = { :conditions => "users.`name` = 'jon'", :limit => '10, 1' }
22
+ User.expects(:find).with(:all, conditions)
23
+ @sql[10]
24
+ end
25
+
26
+ specify "[] with two elements" do
27
+ conditions = { :conditions => "users.`name` = 'jon'", :limit => '10, 20' }
28
+ User.expects(:find).with(:all, conditions)
29
+ @sql[10, 20]
30
+ end
31
+
32
+ specify "[] with range" do
33
+ conditions = { :conditions => "users.`name` = 'jon'", :limit => '10, 10' }
34
+ User.expects(:find).with(:all, conditions)
35
+ @sql[10..20]
36
+ end
37
+ end
@@ -0,0 +1,48 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ context "Order" do
4
+ setup do
5
+ @sql = User.select { |m| m.name == 'jon' }
6
+ end
7
+
8
+ specify "simple order" do
9
+ string = @sql.sort_by { |m| m.name }.to_sql
10
+ string.should == "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY users.name"
11
+ end
12
+
13
+ specify "simple combined order" do
14
+ string = @sql.sort_by { |m| [ m.name, m.age ] }.to_sql
15
+ string.should == "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY users.name, users.age"
16
+ end
17
+
18
+ specify "simple combined order with single reverse" do
19
+ string = @sql.sort_by { |m| [ m.name, -m.age ] }.to_sql
20
+ string.should == "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY users.name, users.age DESC"
21
+ end
22
+
23
+ specify "simple combined order with two reverses" do
24
+ string = @sql.sort_by { |m| [ -m.name, -m.age ] }.to_sql
25
+ string.should == "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY users.name DESC, users.age DESC"
26
+ end
27
+
28
+ specify "reverse order with -" do
29
+ string = @sql.sort_by { |m| -m.age }.to_sql
30
+ string.should == "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY users.age DESC"
31
+ end
32
+
33
+ xspecify "reverse order with #reverse" do
34
+ # TODO: not implemented
35
+ string = @sql.sort_by { |m| m.age }.reverse.to_sql
36
+ string.should == "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY users.age DESC"
37
+ end
38
+
39
+ specify "random order" do
40
+ string = @sql.sort_by { rand }.to_sql
41
+ string.should == "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY RAND()"
42
+ end
43
+
44
+ specify "Symbol#to_proc" do
45
+ string = User.sort_by(&:name).to_sql
46
+ string.should == "SELECT * FROM users ORDER BY users.name"
47
+ end
48
+ end