cheat 1.2.1 → 1.3.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.
@@ -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