ambition 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/Manifest +41 -48
  2. data/README +6 -203
  3. data/ambition.gemspec +111 -0
  4. data/app_generators/ambition_adapter/USAGE +1 -0
  5. data/app_generators/ambition_adapter/ambition_adapter_generator.rb +66 -0
  6. data/app_generators/ambition_adapter/templates/LICENSE +18 -0
  7. data/app_generators/ambition_adapter/templates/README +6 -0
  8. data/app_generators/ambition_adapter/templates/Rakefile +31 -0
  9. data/app_generators/ambition_adapter/templates/lib/base.rb.erb +12 -0
  10. data/app_generators/ambition_adapter/templates/lib/init.rb.erb +22 -0
  11. data/app_generators/ambition_adapter/templates/lib/query.rb.erb +52 -0
  12. data/app_generators/ambition_adapter/templates/lib/select.rb.erb +100 -0
  13. data/app_generators/ambition_adapter/templates/lib/slice.rb.erb +19 -0
  14. data/app_generators/ambition_adapter/templates/lib/sort.rb.erb +43 -0
  15. data/app_generators/ambition_adapter/templates/test/helper.rb.erb +9 -0
  16. data/app_generators/ambition_adapter/templates/test/select_test.rb.erb +157 -0
  17. data/app_generators/ambition_adapter/templates/test/slice_test.rb.erb +36 -0
  18. data/app_generators/ambition_adapter/templates/test/sort_test.rb.erb +53 -0
  19. data/bin/ambition_adapter +13 -0
  20. data/lib/ambition.rb +8 -13
  21. data/lib/ambition/api.rb +42 -35
  22. data/lib/ambition/context.rb +62 -0
  23. data/lib/ambition/{proc_to_ruby.rb → core_ext.rb} +13 -0
  24. data/lib/ambition/enumerable.rb +6 -0
  25. data/lib/ambition/processors/base.rb +126 -0
  26. data/lib/ambition/processors/ruby.rb +24 -0
  27. data/lib/ambition/processors/select.rb +105 -0
  28. data/lib/ambition/processors/slice.rb +15 -0
  29. data/lib/ambition/processors/sort.rb +51 -0
  30. data/test/adapters/exemplar/association_test.rb +34 -0
  31. data/test/adapters/exemplar/count_test.rb +0 -0
  32. data/test/adapters/exemplar/detect_test.rb +9 -0
  33. data/test/adapters/exemplar/enumerable_test.rb +0 -0
  34. data/test/adapters/exemplar/helper.rb +3 -0
  35. data/test/adapters/exemplar/index_operator.rb +6 -0
  36. data/test/adapters/exemplar/reject_test.rb +0 -0
  37. data/test/adapters/exemplar/select_test.rb +151 -0
  38. data/test/adapters/exemplar/slice_test.rb +0 -0
  39. data/test/adapters/exemplar/sort_test.rb +0 -0
  40. data/test/debug +9 -0
  41. data/test/helper.rb +2 -52
  42. metadata +56 -71
  43. data/Rakefile +0 -64
  44. data/init.rb +0 -1
  45. data/lib/ambition/database_statements.rb +0 -31
  46. data/lib/ambition/processor.rb +0 -123
  47. data/lib/ambition/query.rb +0 -91
  48. data/lib/ambition/ruby_processor.rb +0 -22
  49. data/lib/ambition/select_processor.rb +0 -149
  50. data/lib/ambition/simple_processor.rb +0 -10
  51. data/lib/ambition/sort_processor.rb +0 -47
  52. data/lib/ambition/source.rb +0 -53
  53. data/test/benchmark.rb +0 -68
  54. data/test/chaining_test.rb +0 -34
  55. data/test/console +0 -9
  56. data/test/count_test.rb +0 -17
  57. data/test/databases/boot.rb +0 -3
  58. data/test/databases/database.yml +0 -17
  59. data/test/databases/fixtures/admin.rb +0 -3
  60. data/test/databases/fixtures/companies.yml +0 -24
  61. data/test/databases/fixtures/company.rb +0 -23
  62. data/test/databases/fixtures/developer.rb +0 -11
  63. data/test/databases/fixtures/developers_projects.yml +0 -13
  64. data/test/databases/fixtures/project.rb +0 -4
  65. data/test/databases/fixtures/projects.yml +0 -7
  66. data/test/databases/fixtures/replies.yml +0 -20
  67. data/test/databases/fixtures/reply.rb +0 -5
  68. data/test/databases/fixtures/topic.rb +0 -19
  69. data/test/databases/fixtures/topics.yml +0 -32
  70. data/test/databases/fixtures/user.rb +0 -2
  71. data/test/databases/fixtures/users.yml +0 -35
  72. data/test/databases/lib/activerecord_test_connector.rb +0 -65
  73. data/test/databases/lib/load_fixtures.rb +0 -13
  74. data/test/databases/lib/schema.rb +0 -41
  75. data/test/enumerable_test.rb +0 -95
  76. data/test/join_test.rb +0 -61
  77. data/test/limit_test.rb +0 -41
  78. data/test/order_test.rb +0 -52
  79. data/test/profiler.rb +0 -34
  80. data/test/ruby_test.rb +0 -9
  81. data/test/source_test.rb +0 -43
  82. data/test/types_test.rb +0 -59
  83. 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,15 @@
1
+ module Ambition
2
+ module Processors
3
+ class Slice < Base
4
+ def initialize(context, start, length=nil)
5
+ @context = context
6
+ @start = start
7
+ @length = length
8
+ end
9
+
10
+ def to_s
11
+ translator.slice(@start, @length)
12
+ end
13
+ end
14
+ end
15
+ 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
@@ -0,0 +1,9 @@
1
+ context "Exemplar Adapter :: Detect" do
2
+ xspecify "simple ==" do
3
+ User.detect { |m| m.name == 'chris' }
4
+ end
5
+
6
+ xspecify "nothing found" do
7
+ User.detect { |m| m.name == 'chris' }.should.be.nil
8
+ end
9
+ end
File without changes
@@ -0,0 +1,3 @@
1
+ require File.dirname(__FILE__) + '/../../helper'
2
+
3
+ # require 'ambition/adapters/exemplar'
@@ -0,0 +1,6 @@
1
+ xcontext "Exemplar Adapter :: [] operator" do
2
+ specify "finds a single row" do
3
+ User.expects(:find).with(1)
4
+ User[1]
5
+ end
6
+ end
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 'rubygems'
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
- begin require 'redgreen'; rescue LoadError; end unless $NO_TEST
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