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,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "<%= adapter_module %> Adapter :: Slice" do
|
4
|
+
setup do
|
5
|
+
@klass = User
|
6
|
+
@block = @klass.select { |m| m.name == 'jon' }
|
7
|
+
end
|
8
|
+
|
9
|
+
xspecify "first" do
|
10
|
+
@klass.expects(:find).with(:limit => 1, :name => 'jon')
|
11
|
+
@block.first
|
12
|
+
end
|
13
|
+
|
14
|
+
xspecify "first with argument" do
|
15
|
+
@klass.expects(:find).with(:limit => 5, :name => 'jon')
|
16
|
+
@block.first(5).entries
|
17
|
+
end
|
18
|
+
|
19
|
+
xspecify "[] with two elements" do
|
20
|
+
@klass.expects(:find).with(:limit => 20, :offset => 10, :name => 'jon')
|
21
|
+
@block[10, 20].entries
|
22
|
+
|
23
|
+
@klass.expects(:find).with(:limit => 20, :offset => 20, :name => 'jon')
|
24
|
+
@block[20, 20].entries
|
25
|
+
end
|
26
|
+
|
27
|
+
xspecify "slice is an alias of []" do
|
28
|
+
@klass.expects(:find).with(:limit => 20, :offset => 10, :name => 'jon')
|
29
|
+
@block.slice(10, 20).entries
|
30
|
+
end
|
31
|
+
|
32
|
+
xspecify "[] with range" do
|
33
|
+
@klass.expects(:find).with(:limit => 10, :offset => 10, :name => 'jon')
|
34
|
+
@block[11..20].entries
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
context "<%= adapter_module %> Adapter :: Sort" do
|
4
|
+
setup do
|
5
|
+
@klass = User
|
6
|
+
@block = @klass.select { |m| m.name == 'jon' }
|
7
|
+
end
|
8
|
+
|
9
|
+
xspecify "order" do
|
10
|
+
string = @block.sort_by { |m| m.name }.to_s
|
11
|
+
string.should == "foo"
|
12
|
+
end
|
13
|
+
|
14
|
+
xspecify "combined order" do
|
15
|
+
string = @block.sort_by { |m| [ m.name, m.age ] }.to_s
|
16
|
+
string.should == "foo"
|
17
|
+
end
|
18
|
+
|
19
|
+
xspecify "combined order with single reverse" do
|
20
|
+
string = @block.sort_by { |m| [ m.name, -m.age ] }.to_s
|
21
|
+
string.should == "foo"
|
22
|
+
end
|
23
|
+
|
24
|
+
xspecify "combined order with two reverses" do
|
25
|
+
string = @block.sort_by { |m| [ -m.name, -m.age ] }.to_s
|
26
|
+
string.should == "foo"
|
27
|
+
end
|
28
|
+
|
29
|
+
xspecify "reverse order with -" do
|
30
|
+
string = @block.sort_by { |m| -m.age }.to_s
|
31
|
+
string.should == "foo"
|
32
|
+
end
|
33
|
+
|
34
|
+
xspecify "reverse order with #reverse" do
|
35
|
+
# TODO: not implemented
|
36
|
+
string = @block.sort_by { |m| m.age }.reverse.to_s
|
37
|
+
string.should == "foo"
|
38
|
+
end
|
39
|
+
|
40
|
+
xspecify "random order" do
|
41
|
+
string = @block.sort_by { rand }.to_s
|
42
|
+
string.should == "foo"
|
43
|
+
end
|
44
|
+
|
45
|
+
xspecify "non-existent method to sort by" do
|
46
|
+
should.raise(NoMethodError) { @block.sort_by { foo }.to_s }
|
47
|
+
end
|
48
|
+
|
49
|
+
xspecify "Symbol#to_proc" do
|
50
|
+
string = @klass.sort_by(&:name).to_s
|
51
|
+
string.should == "foo"
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rubigen'
|
4
|
+
|
5
|
+
if %w(-v --version).include? ARGV.first
|
6
|
+
version = File.read('./Rakefile').scan(/Version = '(.+)'/).first.first
|
7
|
+
puts "#{File.basename($0)} #{version}"
|
8
|
+
exit(0)
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'rubigen/scripts/generate'
|
12
|
+
RubiGen::Base.use_application_sources! :ambition_adapter
|
13
|
+
RubiGen::Scripts::Generate.new.run(ARGV, :generator => 'ambition_adapter')
|
data/lib/ambition.rb
CHANGED
@@ -1,15 +1,10 @@
|
|
1
|
-
|
2
|
-
require 'rubygems'
|
3
|
-
require 'active_record'
|
4
|
-
end
|
1
|
+
require 'ambition/enumerable'
|
5
2
|
require 'ambition/api'
|
6
|
-
require 'ambition/
|
7
|
-
require 'ambition/
|
8
|
-
require 'ambition/ruby_processor'
|
9
|
-
require 'ambition/select_processor'
|
10
|
-
require 'ambition/sort_processor'
|
11
|
-
require 'ambition/simple_processor'
|
12
|
-
require 'ambition/query'
|
13
|
-
require 'ambition/database_statements'
|
3
|
+
require 'ambition/context'
|
4
|
+
require 'ambition/core_ext'
|
14
5
|
|
15
|
-
|
6
|
+
require 'ambition/processors/base'
|
7
|
+
require 'ambition/processors/select'
|
8
|
+
require 'ambition/processors/sort'
|
9
|
+
require 'ambition/processors/slice'
|
10
|
+
require 'ambition/processors/ruby'
|
data/lib/ambition/api.rb
CHANGED
@@ -2,57 +2,43 @@ module Ambition
|
|
2
2
|
module API
|
3
3
|
include Enumerable
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
##
|
6
|
+
# Entry methods
|
7
|
+
def select(&block)
|
8
|
+
context = ambition_context
|
9
|
+
context << Processors::Select.new(context, block)
|
8
10
|
end
|
9
11
|
|
10
|
-
def
|
11
|
-
|
12
|
+
def sort_by(&block)
|
13
|
+
context = ambition_context
|
14
|
+
context << Processors::Sort.new(context, block)
|
12
15
|
end
|
13
|
-
alias_method :to_a, :entries
|
14
16
|
|
15
|
-
def
|
16
|
-
|
17
|
+
def entries
|
18
|
+
ambition_context.kick
|
17
19
|
end
|
20
|
+
alias_method :to_a, :entries
|
18
21
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
if limit == 1
|
23
|
-
find(:first, query_context.to_hash)
|
24
|
-
else
|
25
|
-
query_context
|
26
|
-
end
|
22
|
+
def size
|
23
|
+
ambition_context.size
|
27
24
|
end
|
28
25
|
|
29
|
-
def slice(
|
30
|
-
|
31
|
-
|
32
|
-
limit -= 1 if offset.exclude_end?
|
33
|
-
offset = offset.first
|
34
|
-
limit -= offset
|
35
|
-
elsif limit.nil?
|
36
|
-
return find(offset)
|
37
|
-
end
|
38
|
-
|
39
|
-
query_context.add SimpleProcessor.new(:offset => offset)
|
40
|
-
query_context.add SimpleProcessor.new(:limit => limit)
|
26
|
+
def slice(start, length = nil)
|
27
|
+
context = ambition_context
|
28
|
+
context << Processors::Slice.new(context, start, length)
|
41
29
|
end
|
42
30
|
alias_method :[], :slice
|
43
31
|
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
|
32
|
+
##
|
33
|
+
# Convenience methods
|
48
34
|
def detect(&block)
|
49
35
|
select(&block).first
|
50
36
|
end
|
51
37
|
|
52
|
-
def
|
53
|
-
count
|
38
|
+
def first(count = 1)
|
39
|
+
sliced = slice(0, count)
|
40
|
+
count == 1 ? sliced.kick : sliced
|
54
41
|
end
|
55
|
-
alias_method :length, :size
|
56
42
|
|
57
43
|
def each(&block)
|
58
44
|
entries.each(&block)
|
@@ -69,5 +55,26 @@ module Ambition
|
|
69
55
|
def empty?
|
70
56
|
size.zero?
|
71
57
|
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Plumbing
|
61
|
+
def ambition_context
|
62
|
+
Context.new(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
def ambition_adapter
|
66
|
+
name = respond_to?(:name) ? name : self.class.name
|
67
|
+
parent = respond_to?(:superclass) ? superclass : self.class.superclass
|
68
|
+
@@ambition_adapter[name] || @@ambition_adapter[parent.name]
|
69
|
+
end
|
70
|
+
|
71
|
+
def ambition_adapter=(klass)
|
72
|
+
@@ambition_adapter ||= {}
|
73
|
+
@@ambition_adapter[name] = klass
|
74
|
+
end
|
75
|
+
|
76
|
+
def ambition_owner
|
77
|
+
@owner || self
|
78
|
+
end
|
72
79
|
end
|
73
80
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Ambition
|
2
|
+
class Context
|
3
|
+
include API
|
4
|
+
|
5
|
+
##
|
6
|
+
# These are the methods your Query and Translator classes will
|
7
|
+
# want to access.
|
8
|
+
#
|
9
|
+
# +owner+ The class everything was called on. Like `User'
|
10
|
+
#
|
11
|
+
# +clauses+ A hash of arrays, one key per processor.
|
12
|
+
# So, if someone called User.select, your
|
13
|
+
# +clauses+ hash would have a :select key with
|
14
|
+
# an array of translated strings via your Select
|
15
|
+
# class.
|
16
|
+
#
|
17
|
+
# +stash+ A place for you to stick stuff. Available to
|
18
|
+
# all Translators and your Query class.
|
19
|
+
attr_reader :clauses, :owner, :stash
|
20
|
+
|
21
|
+
def initialize(owner)
|
22
|
+
@owner = owner
|
23
|
+
@clauses = {}
|
24
|
+
@stash = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def ambition_context
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def <<(clause)
|
32
|
+
@clauses[clause.key] ||= []
|
33
|
+
@clauses[clause.key] << clause.to_s
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def adapter_query
|
38
|
+
Processors::Base.translator(self, :Query)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_hash
|
42
|
+
adapter_query.to_hash
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
adapter_query.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def kick
|
50
|
+
adapter_query.kick
|
51
|
+
end
|
52
|
+
|
53
|
+
def size
|
54
|
+
adapter_query.size
|
55
|
+
end
|
56
|
+
alias_method :length, :size
|
57
|
+
|
58
|
+
def inspect
|
59
|
+
"(Query object: call #to_s or #to_hash to inspect, call an Enumerable (such as #each or #first) to request data)"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -3,6 +3,19 @@
|
|
3
3
|
require 'parse_tree'
|
4
4
|
require 'ruby2ruby'
|
5
5
|
|
6
|
+
class Object
|
7
|
+
def to_sexp
|
8
|
+
instance_eval <<-end_eval
|
9
|
+
return proc { #{inspect} }.to_sexp.last
|
10
|
+
end_eval
|
11
|
+
end
|
12
|
+
|
13
|
+
def metaclass; (class << self; self end) end
|
14
|
+
def meta_eval(&blk) metaclass.instance_eval(&blk) end
|
15
|
+
def meta_def(name, &blk) meta_eval { define_method name, &blk } end
|
16
|
+
def class_def(name, &blk) class_eval { define_method name, &blk } end
|
17
|
+
end
|
18
|
+
|
6
19
|
class ProcHolder
|
7
20
|
end
|
8
21
|
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Processors
|
3
|
+
class Base
|
4
|
+
##
|
5
|
+
# Processing methods
|
6
|
+
def process_proc(exp)
|
7
|
+
# puts "=> #{exp.inspect}"
|
8
|
+
receiver, body = process(exp.shift), exp.shift
|
9
|
+
process(body)
|
10
|
+
end
|
11
|
+
|
12
|
+
def process_dasgn_curr(exp)
|
13
|
+
@receiver = exp.first
|
14
|
+
@receiver.to_s
|
15
|
+
end
|
16
|
+
alias_method :process_dasgn, :process_dasgn_curr
|
17
|
+
|
18
|
+
def process_array(exp)
|
19
|
+
# Branch on whether this is straight Ruby or a real array
|
20
|
+
if ruby = rubify(exp)
|
21
|
+
ruby
|
22
|
+
else
|
23
|
+
exp.map { |m| process(m) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def process_str(exp)
|
28
|
+
exp.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def process_lit(exp)
|
32
|
+
exp.first
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_nil(exp)
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def process_true(exp)
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_false(exp)
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def process_dvar(exp)
|
48
|
+
target = exp.shift
|
49
|
+
value(target.to_s[0..-1])
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_ivar(exp)
|
53
|
+
value(exp.shift.to_s[0..-1])
|
54
|
+
end
|
55
|
+
|
56
|
+
def process_lvar(exp)
|
57
|
+
value(exp.shift.to_s)
|
58
|
+
end
|
59
|
+
|
60
|
+
def process_vcall(exp)
|
61
|
+
value(exp.shift.to_s)
|
62
|
+
end
|
63
|
+
|
64
|
+
def process_gvar(exp)
|
65
|
+
value(exp.shift.to_s)
|
66
|
+
end
|
67
|
+
|
68
|
+
def process(node)
|
69
|
+
node ||= []
|
70
|
+
|
71
|
+
if node.is_a? Symbol
|
72
|
+
node
|
73
|
+
elsif respond_to?(method = "process_#{node.first}")
|
74
|
+
send(method, node[1..-1])
|
75
|
+
elsif node.blank?
|
76
|
+
''
|
77
|
+
else
|
78
|
+
raise "Missing process method for sexp: #{node.inspect}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Helper methods
|
84
|
+
def to_s
|
85
|
+
process @block.to_sexp
|
86
|
+
end
|
87
|
+
|
88
|
+
def key
|
89
|
+
self.class.name.split('::').last.downcase.intern
|
90
|
+
end
|
91
|
+
|
92
|
+
def value(variable)
|
93
|
+
eval variable, @block
|
94
|
+
end
|
95
|
+
|
96
|
+
def translator
|
97
|
+
@translator ||= self.class.translator(@context)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.translator(context, name = nil)
|
101
|
+
name ||= self.name.split('::').last
|
102
|
+
klass = context.owner.ambition_adapter.const_get(name)
|
103
|
+
instance = klass.new
|
104
|
+
|
105
|
+
unless instance.respond_to? :context
|
106
|
+
klass.class_eval do
|
107
|
+
attr_accessor :context
|
108
|
+
def owner; @context.owner end
|
109
|
+
def clauses; @context.clauses end
|
110
|
+
def stash; @context.stash end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
instance.context = context
|
115
|
+
instance
|
116
|
+
end
|
117
|
+
|
118
|
+
def rubify(exp)
|
119
|
+
# TODO: encapsulate this check in Ruby.should_process?(exp)
|
120
|
+
if exp.first.first == :call && exp.first[1].last != @receiver && Array(exp.first[1][1]).last != @receiver
|
121
|
+
value Ruby.process(exp.first)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|