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.
- data/README +5 -0
- data/bin/cheat +8 -2
- data/lib/ambition/LICENSE +18 -0
- data/lib/ambition/README +143 -0
- data/lib/ambition/Rakefile +29 -0
- data/lib/ambition/init.rb +1 -0
- data/lib/ambition/lib/ambition.rb +21 -0
- data/lib/ambition/lib/ambition/count.rb +8 -0
- data/lib/ambition/lib/ambition/enumerable.rb +9 -0
- data/lib/ambition/lib/ambition/limit.rb +34 -0
- data/lib/ambition/lib/ambition/order.rb +52 -0
- data/lib/ambition/lib/ambition/processor.rb +54 -0
- data/lib/ambition/lib/ambition/query.rb +85 -0
- data/lib/ambition/lib/ambition/where.rb +164 -0
- data/lib/ambition/lib/proc_to_ruby.rb +36 -0
- data/lib/ambition/test/chaining_test.rb +34 -0
- data/lib/ambition/test/count_test.rb +17 -0
- data/lib/ambition/test/enumerable_test.rb +51 -0
- data/lib/ambition/test/helper.rb +30 -0
- data/lib/ambition/test/join_test.rb +32 -0
- data/lib/ambition/test/limit_test.rb +37 -0
- data/lib/ambition/test/order_test.rb +48 -0
- data/lib/ambition/test/types_test.rb +56 -0
- data/lib/ambition/test/where_test.rb +131 -0
- data/lib/cheat.rb +81 -11
- data/lib/{diffr.rb → cheat/diffr.rb} +0 -1
- data/lib/{responder.rb → cheat/responder.rb} +3 -3
- data/lib/cheat/rv_harness.rb +41 -0
- data/lib/{site.rb → cheat/site.rb} +114 -93
- data/lib/cheat/version.rb +3 -0
- data/lib/{wrap.rb → cheat/wrap.rb} +0 -0
- metadata +77 -42
- data/lib/cheat/commands.rb +0 -41
- data/lib/new_cheat.rb +0 -163
- data/test/test_cheat.rb +0 -50
@@ -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
|