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