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
@@ -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
|