ambition 0.3.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Processors
|
3
|
+
class Ruby < RubyToRuby
|
4
|
+
def self.process(node)
|
5
|
+
@processor ||= new
|
6
|
+
@processor.process node
|
7
|
+
end
|
8
|
+
|
9
|
+
##
|
10
|
+
# This is not DRY, and I don't care.
|
11
|
+
def process(node)
|
12
|
+
node ||= []
|
13
|
+
|
14
|
+
if respond_to?(method = "process_#{node.first}")
|
15
|
+
send(method, node[1..-1])
|
16
|
+
elsif node.blank?
|
17
|
+
''
|
18
|
+
else
|
19
|
+
raise "Missing process method for sexp: #{node.inspect}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Processors
|
3
|
+
class Select < Base
|
4
|
+
def initialize(context, block)
|
5
|
+
@context = context
|
6
|
+
@block = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def process_call(args)
|
10
|
+
# Operation (m.name == 'chris')
|
11
|
+
# [[:call, [:dvar, :m], :name], :==, [:array, [:str, "chris"]]]
|
12
|
+
if args.size == 3
|
13
|
+
left, operator, right = args
|
14
|
+
|
15
|
+
# params are passed as an array, even when only one element:
|
16
|
+
# abc(1)
|
17
|
+
# => [:fcall, :abc, [:array, [:lit, 1]]
|
18
|
+
# abc([1])
|
19
|
+
# => [:fcall, :abc, [:array, [:array, [:lit, 1]]]]
|
20
|
+
if right.first == :array
|
21
|
+
right = process(right)
|
22
|
+
right = right.is_a?(Array) ? right.first : right
|
23
|
+
else
|
24
|
+
right = process(right)
|
25
|
+
end
|
26
|
+
|
27
|
+
translator.send(process_operator(operator), process(left), right)
|
28
|
+
|
29
|
+
# Property of passed arg:
|
30
|
+
# [[:dvar, :m], :name]
|
31
|
+
elsif args.first.last == @receiver
|
32
|
+
translator.call(*args[1..-1])
|
33
|
+
|
34
|
+
# Method call:
|
35
|
+
# [[:call, [:dvar, :m], :name], :upcase]
|
36
|
+
elsif args.first.first == :call && args.first[1].last == @receiver
|
37
|
+
receiver, method = args
|
38
|
+
translator.chained_call(receiver.last, method)
|
39
|
+
|
40
|
+
# Deep, chained call:
|
41
|
+
# [[:call, [:call, [:call, [:dvar, :m], :created_at], :something], :else], :perhaps]
|
42
|
+
elsif args.flatten.include? @receiver
|
43
|
+
calls = []
|
44
|
+
|
45
|
+
until args.empty?
|
46
|
+
args = args.last if args.last.is_a?(Array)
|
47
|
+
break if args.last == @receiver
|
48
|
+
calls << args.pop
|
49
|
+
end
|
50
|
+
|
51
|
+
translator.chained_call(*calls.reverse)
|
52
|
+
|
53
|
+
else
|
54
|
+
raise args.inspect
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def process_match3(exp)
|
59
|
+
right, left = exp
|
60
|
+
process_call [ left, :=~, right ]
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_and(exp)
|
64
|
+
joined_expressions exp, :both
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_or(exp)
|
68
|
+
joined_expressions exp, :either
|
69
|
+
end
|
70
|
+
|
71
|
+
def joined_expressions(exp, with = nil)
|
72
|
+
expressions = []
|
73
|
+
|
74
|
+
while expression = exp.shift
|
75
|
+
expressions << process(expression)
|
76
|
+
end
|
77
|
+
|
78
|
+
translator.send(with, *expressions)
|
79
|
+
end
|
80
|
+
|
81
|
+
def process_not(args)
|
82
|
+
negate { process(args.first) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def process_operator(operator)
|
86
|
+
@negated ? negate_operator(operator) : operator
|
87
|
+
end
|
88
|
+
|
89
|
+
def negate_operator(operator)
|
90
|
+
case operator
|
91
|
+
when :== then :not_equal
|
92
|
+
when :=~ then :not_regexp
|
93
|
+
else raise "Missing negated operator definition: #{operator}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def negate
|
98
|
+
@negated = true
|
99
|
+
yield
|
100
|
+
ensure
|
101
|
+
@negated = false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Processors
|
3
|
+
class Sort < Base
|
4
|
+
def initialize(context, block)
|
5
|
+
@context = context
|
6
|
+
@block = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def process_call(args)
|
10
|
+
if args.first.last == @receiver
|
11
|
+
translator.sort_by(*args[1..-1])
|
12
|
+
|
13
|
+
# sort_by { |m| -m.age }
|
14
|
+
# [[:call, [:dvar, :m], :age], :-@]
|
15
|
+
elsif args[0][1][-1] == @receiver && args.last == :-@
|
16
|
+
translator.reverse_sort_by(*args.first[2..-1])
|
17
|
+
|
18
|
+
# sort_by(&:name).to_s
|
19
|
+
# [[:call, [:dvar, :args], :shift], :__send__, [:argscat, [:array, [:self]], [:dvar, :args]]]
|
20
|
+
elsif args[1] == :__send__
|
21
|
+
translator.to_proc(value('to_s'))
|
22
|
+
|
23
|
+
# sort_by { |m| m.ideas.title }
|
24
|
+
# [[:call, [:dvar, :m], :ideas], :title]
|
25
|
+
elsif args[0][1][-1] == @receiver
|
26
|
+
first = args.first.last
|
27
|
+
last = args.last
|
28
|
+
translator.chained_sort_by(first, last)
|
29
|
+
|
30
|
+
# sort_by { |m| [ m.ideas.title, -m.invites.email ] }
|
31
|
+
# [[:call, [:call, [:dvar, :m], :invites], :email], :-@]
|
32
|
+
elsif args[0][1][1][-1] == @receiver && args.last == :-@
|
33
|
+
first = args.first[1].last
|
34
|
+
last = args.first.last
|
35
|
+
translator.chained_reverse_sort_by(first, last)
|
36
|
+
|
37
|
+
else
|
38
|
+
raise args.inspect
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def process_vcall(args)
|
43
|
+
translator.send(args.shift, *args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def process_masgn(exp)
|
47
|
+
''
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Analog of the join_test.rb in the ActiveRecord adapter.
|
2
|
+
# Not all adapters will need these behaviors. E.g. there are no joins in LDAP.
|
3
|
+
# ActiveLdap has associations, but doesn't try to fake joins.
|
4
|
+
# CouchDb doesn't so much have joins as it has the capacity for creative
|
5
|
+
# use of its views.
|
6
|
+
|
7
|
+
require File.dirname(__FILE__) + '/helper'
|
8
|
+
|
9
|
+
context "Object associations" do
|
10
|
+
xspecify "simple ==" do
|
11
|
+
result = User.select { |m| m.account.email == 'chris@ozmm.org' }
|
12
|
+
end
|
13
|
+
|
14
|
+
xspecify "simple mixed == on object and an association" do
|
15
|
+
result = User.select { |m| m.name == 'chris' && m.account.email == 'chris@ozmm.org' }
|
16
|
+
end
|
17
|
+
|
18
|
+
xspecify "multiple associations" do
|
19
|
+
result = User.select { |m| m.ideas.title == 'New Freezer' || m.invites.email == 'pj@hyett.com' }
|
20
|
+
end
|
21
|
+
|
22
|
+
xspecify "non-existant associations" do
|
23
|
+
result = User.select { |m| m.liquor.brand == 'Jack' }
|
24
|
+
should.raise { result.to_hash }
|
25
|
+
end
|
26
|
+
|
27
|
+
xspecify "simple order by association attribute" do
|
28
|
+
result = User.sort_by { |m| m.ideas.title }
|
29
|
+
end
|
30
|
+
|
31
|
+
xspecify "in a more complex order" do
|
32
|
+
result = User.sort_by { |m| [ m.ideas.title, -m.invites.email ] }
|
33
|
+
end
|
34
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,151 @@
|
|
1
|
+
context "Exemplar Adapter :: Select" do
|
2
|
+
xspecify "simple ==" do
|
3
|
+
translator = User.select { |m| m.name == 'jon' }
|
4
|
+
translator.to_s.should == %Q(foo)
|
5
|
+
end
|
6
|
+
|
7
|
+
xspecify "simple !=" do
|
8
|
+
translator = User.select { |m| m.name != 'jon' }
|
9
|
+
translator.to_s.should == %Q(foo)
|
10
|
+
end
|
11
|
+
|
12
|
+
xspecify "simple == && ==" do
|
13
|
+
translator = User.select { |m| m.name == 'jon' && m.age == 21 }
|
14
|
+
translator.to_s.should == %Q(foo)
|
15
|
+
end
|
16
|
+
|
17
|
+
xspecify "simple == || ==" do
|
18
|
+
translator = User.select { |m| m.name == 'jon' || m.age == 21 }
|
19
|
+
translator.to_s.should == %Q(foo)
|
20
|
+
end
|
21
|
+
|
22
|
+
xspecify "mixed && and ||" do
|
23
|
+
translator = User.select { |m| m.name == 'jon' || m.age == 21 && m.password == 'pass' }
|
24
|
+
translator.to_s.should == %Q(foo)
|
25
|
+
end
|
26
|
+
|
27
|
+
xspecify "grouped && and ||" do
|
28
|
+
translator = User.select { |m| (m.name == 'jon' || m.name == 'rick') && m.age == 21 }
|
29
|
+
translator.to_s.should == %Q(foo)
|
30
|
+
end
|
31
|
+
|
32
|
+
xspecify "simple >/<" do
|
33
|
+
translator = User.select { |m| m.age > 21 }
|
34
|
+
translator.to_s.should == %Q(foo)
|
35
|
+
|
36
|
+
translator = User.select { |m| m.age >= 21 }
|
37
|
+
translator.to_s.should == %Q(foo)
|
38
|
+
|
39
|
+
translator = User.select { |m| m.age < 21 }
|
40
|
+
translator.to_s.should == %Q(foo)
|
41
|
+
end
|
42
|
+
|
43
|
+
xspecify "array.include? item" do
|
44
|
+
translator = User.select { |m| [1, 2, 3, 4].include? m.id }
|
45
|
+
translator.to_s.should == %Q(foo)
|
46
|
+
end
|
47
|
+
|
48
|
+
xspecify "variabled array.include? item" do
|
49
|
+
array = [1, 2, 3, 4]
|
50
|
+
translator = User.select { |m| array.include? m.id }
|
51
|
+
translator.to_s.should == %Q(foo)
|
52
|
+
end
|
53
|
+
|
54
|
+
xspecify "simple == with variables" do
|
55
|
+
me = 'chris'
|
56
|
+
translator = User.select { |m| m.name == me }
|
57
|
+
translator.to_s.should == %Q(foo)
|
58
|
+
end
|
59
|
+
|
60
|
+
xspecify "simple == with method arguments" do
|
61
|
+
def test_it(name)
|
62
|
+
translator = User.select { |m| m.name == name }
|
63
|
+
translator.to_s.should == %Q(foo)
|
64
|
+
end
|
65
|
+
|
66
|
+
test_it('chris')
|
67
|
+
end
|
68
|
+
|
69
|
+
xspecify "simple == with instance variables" do
|
70
|
+
@me = 'chris'
|
71
|
+
translator = User.select { |m| m.name == @me }
|
72
|
+
translator.to_s.should == %Q(foo)
|
73
|
+
end
|
74
|
+
|
75
|
+
xspecify "simple == with instance variable method call" do
|
76
|
+
require 'ostruct'
|
77
|
+
@person = OpenStruct.new(:name => 'chris')
|
78
|
+
|
79
|
+
translator = User.select { |m| m.name == @person.name }
|
80
|
+
translator.to_s.should == %Q(foo)
|
81
|
+
end
|
82
|
+
|
83
|
+
xspecify "simple == with global variables" do
|
84
|
+
$my_name = 'boston'
|
85
|
+
translator = User.select { |m| m.name == $my_name }
|
86
|
+
translator.to_s.should == %Q(foo)
|
87
|
+
end
|
88
|
+
|
89
|
+
xspecify "simple == with method call" do
|
90
|
+
def band
|
91
|
+
'skinny puppy'
|
92
|
+
end
|
93
|
+
|
94
|
+
translator = User.select { |m| m.name == band }
|
95
|
+
translator.to_s.should == %Q(foo)
|
96
|
+
end
|
97
|
+
|
98
|
+
xspecify "simple =~ with string" do
|
99
|
+
translator = User.select { |m| m.name =~ 'chris' }
|
100
|
+
translator.to_s.should == %Q(foo)
|
101
|
+
|
102
|
+
translator = User.select { |m| m.name =~ 'chri%' }
|
103
|
+
translator.to_s.should == %Q(foo)
|
104
|
+
end
|
105
|
+
|
106
|
+
xspecify "simple !~ with string" do
|
107
|
+
translator = User.select { |m| m.name !~ 'chris' }
|
108
|
+
translator.to_s.should == %Q(foo)
|
109
|
+
|
110
|
+
translator = User.select { |m| !(m.name =~ 'chris') }
|
111
|
+
translator.to_s.should == %Q(foo)
|
112
|
+
end
|
113
|
+
|
114
|
+
xspecify "simple =~ with regexp" do
|
115
|
+
translator = User.select { |m| m.name =~ /chris/ }
|
116
|
+
translator.to_s.should == %Q(foo)
|
117
|
+
end
|
118
|
+
|
119
|
+
xspecify "simple =~ with regexp flags" do
|
120
|
+
translator = User.select { |m| m.name =~ /chris/i }
|
121
|
+
translator.to_s.should == %Q(foo)
|
122
|
+
end
|
123
|
+
|
124
|
+
xspecify "simple downcase" do
|
125
|
+
translator = User.select { |m| m.name.downcase =~ 'chris%' }
|
126
|
+
translator.to_s.should == %Q(foo)
|
127
|
+
end
|
128
|
+
|
129
|
+
xspecify "simple upcase" do
|
130
|
+
translator = User.select { |m| m.name.upcase =~ 'chris%' }
|
131
|
+
translator.to_s.should == %Q(foo)
|
132
|
+
end
|
133
|
+
|
134
|
+
xspecify "undefined equality symbol" do
|
135
|
+
should.raise { User.select { |m| m.name =* /chris/ } }
|
136
|
+
end
|
137
|
+
|
138
|
+
xspecify "block variable / assigning variable conflict" do
|
139
|
+
m = User.select { |m| m.name == 'chris' }
|
140
|
+
m.should == %Q(foo)
|
141
|
+
end
|
142
|
+
|
143
|
+
xspecify "simple == with inline ruby" do
|
144
|
+
translator = User.select { |m| m.created_at == 2.days.ago(:db) }
|
145
|
+
translator.to_s.should == %Q(foo)
|
146
|
+
end
|
147
|
+
|
148
|
+
xspecify "inspect" do
|
149
|
+
User.select { |u| u.name }.inspect.should.match %r(call #to_s or #to_hash)
|
150
|
+
end
|
151
|
+
end
|
File without changes
|
File without changes
|
data/test/debug
ADDED
@@ -0,0 +1,9 @@
|
|
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, '..', 'lib', 'ambition')
|
8
|
+
|
9
|
+
exec "#{irb} -Ilib #{libs.map{|l| " -r #{l}" }.join} --simple-prompt"
|
data/test/helper.rb
CHANGED
@@ -1,54 +1,4 @@
|
|
1
|
-
require
|
2
|
-
begin
|
3
|
-
require 'test/spec' unless $NO_TEST
|
4
|
-
require 'mocha'
|
5
|
-
rescue LoadError
|
6
|
-
puts "=> You need the test-spec and mocha gems to run these tests."
|
7
|
-
exit
|
8
|
-
end
|
9
|
-
require 'active_support'
|
10
|
-
require 'active_record'
|
1
|
+
%w( rubygems test/spec mocha redgreen English ).each { |f| require f }
|
11
2
|
|
12
|
-
|
13
|
-
|
14
|
-
$:.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
15
4
|
require 'ambition'
|
16
|
-
|
17
|
-
class User < ActiveRecord::Base
|
18
|
-
def self.reflections
|
19
|
-
return @reflections if @reflections
|
20
|
-
@reflections = {}
|
21
|
-
@reflections[:ideas] = Reflection.new(:has_many, 'user_id', :ideas, 'ideas')
|
22
|
-
@reflections[:invites] = Reflection.new(:has_many, 'referrer_id', :invites, 'invites')
|
23
|
-
@reflections[:profile] = Reflection.new(:has_one, 'user_id', :profile, 'profiles')
|
24
|
-
@reflections[:account] = Reflection.new(:belongs_to, 'account_id', :account, 'accounts')
|
25
|
-
@reflections
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.table_name
|
29
|
-
'users'
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class Reflection < Struct.new(:macro, :primary_key_name, :name, :table_name)
|
34
|
-
end
|
35
|
-
|
36
|
-
module ActiveRecord
|
37
|
-
module ConnectionAdapters
|
38
|
-
class MysqlAdapter
|
39
|
-
def connect(*args)
|
40
|
-
true
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class PostgreSQLAdapter
|
45
|
-
def connect(*args)
|
46
|
-
true
|
47
|
-
end
|
48
|
-
class PGError; end
|
49
|
-
end
|
50
|
-
|
51
|
-
class FakeAdapter < AbstractAdapter
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|