arel 0.4.0 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/README.markdown +24 -0
  2. data/lib/arel.rb +3 -1
  3. data/lib/arel/algebra/attributes/attribute.rb +175 -141
  4. data/lib/arel/algebra/core_extensions.rb +0 -1
  5. data/lib/arel/algebra/core_extensions/hash.rb +5 -9
  6. data/lib/arel/algebra/core_extensions/object.rb +0 -4
  7. data/lib/arel/algebra/expression.rb +37 -24
  8. data/lib/arel/algebra/header.rb +5 -6
  9. data/lib/arel/algebra/ordering.rb +13 -5
  10. data/lib/arel/algebra/predicates.rb +143 -27
  11. data/lib/arel/algebra/relations.rb +0 -1
  12. data/lib/arel/algebra/relations/operations/from.rb +10 -2
  13. data/lib/arel/algebra/relations/operations/group.rb +8 -6
  14. data/lib/arel/algebra/relations/operations/having.rb +3 -6
  15. data/lib/arel/algebra/relations/operations/join.rb +52 -18
  16. data/lib/arel/algebra/relations/operations/lock.rb +4 -6
  17. data/lib/arel/algebra/relations/operations/order.rb +11 -7
  18. data/lib/arel/algebra/relations/operations/project.rb +10 -10
  19. data/lib/arel/algebra/relations/operations/skip.rb +10 -3
  20. data/lib/arel/algebra/relations/operations/take.rb +10 -3
  21. data/lib/arel/algebra/relations/operations/where.rb +12 -6
  22. data/lib/arel/algebra/relations/relation.rb +161 -92
  23. data/lib/arel/algebra/relations/row.rb +8 -5
  24. data/lib/arel/algebra/relations/utilities/compound.rb +34 -33
  25. data/lib/arel/algebra/relations/utilities/externalization.rb +10 -8
  26. data/lib/arel/algebra/relations/writes.rb +24 -13
  27. data/lib/arel/algebra/value.rb +41 -2
  28. data/lib/arel/engines/memory.rb +0 -2
  29. data/lib/arel/engines/memory/engine.rb +3 -9
  30. data/lib/arel/engines/memory/relations.rb +0 -3
  31. data/lib/arel/engines/memory/relations/array.rb +5 -3
  32. data/lib/arel/engines/memory/relations/operations.rb +2 -60
  33. data/lib/arel/engines/sql.rb +0 -2
  34. data/lib/arel/engines/sql/christener.rb +12 -6
  35. data/lib/arel/engines/sql/compilers/oracle_compiler.rb +34 -23
  36. data/lib/arel/engines/sql/compilers/postgresql_compiler.rb +23 -15
  37. data/lib/arel/engines/sql/engine.rb +19 -27
  38. data/lib/arel/engines/sql/formatters.rb +26 -10
  39. data/lib/arel/engines/sql/relations.rb +0 -7
  40. data/lib/arel/engines/sql/relations/compiler.rb +70 -35
  41. data/lib/arel/engines/sql/relations/table.rb +44 -32
  42. data/lib/arel/{engines/sql/relations/utilities/recursion.rb → recursion/base_case.rb} +0 -0
  43. data/lib/arel/session.rb +24 -40
  44. data/lib/arel/sql_literal.rb +13 -0
  45. data/lib/arel/version.rb +1 -1
  46. data/spec/algebra/unit/predicates/inequality_spec.rb +32 -0
  47. data/spec/algebra/unit/predicates/predicate_spec.rb +22 -0
  48. data/spec/algebra/unit/primitives/attribute_spec.rb +3 -9
  49. data/spec/algebra/unit/primitives/expression_spec.rb +1 -7
  50. data/spec/algebra/unit/relations/join_spec.rb +0 -7
  51. data/spec/algebra/unit/relations/project_spec.rb +3 -3
  52. data/spec/algebra/unit/relations/relation_spec.rb +74 -25
  53. data/spec/algebra/unit/session/session_spec.rb +7 -7
  54. data/spec/engines/memory/integration/joins/cross_engine_spec.rb +20 -10
  55. data/spec/engines/memory/unit/relations/array_spec.rb +6 -5
  56. data/spec/engines/memory/unit/relations/join_spec.rb +7 -6
  57. data/spec/engines/memory/unit/relations/order_spec.rb +7 -6
  58. data/spec/engines/memory/unit/relations/project_spec.rb +6 -6
  59. data/spec/engines/memory/unit/relations/skip_spec.rb +10 -5
  60. data/spec/engines/memory/unit/relations/take_spec.rb +7 -5
  61. data/spec/engines/memory/unit/relations/where_spec.rb +13 -9
  62. data/spec/engines/sql/unit/engine_spec.rb +20 -0
  63. data/spec/engines/sql/unit/relations/group_spec.rb +2 -2
  64. data/spec/engines/sql/unit/relations/order_spec.rb +5 -5
  65. data/spec/engines/sql/unit/relations/project_spec.rb +4 -4
  66. data/spec/engines/sql/unit/relations/table_spec.rb +0 -7
  67. data/spec/engines/sql/unit/relations/take_spec.rb +26 -0
  68. data/spec/engines/sql/unit/relations/where_spec.rb +1 -1
  69. data/spec/spec_helper.rb +1 -4
  70. data/spec/sql/christener_spec.rb +70 -0
  71. data/spec/support/model.rb +7 -2
  72. metadata +109 -23
  73. data/lib/arel/algebra/core_extensions/class.rb +0 -32
  74. data/lib/arel/algebra/relations/operations/alias.rb +0 -7
  75. data/lib/arel/engines/memory/predicates.rb +0 -99
  76. data/lib/arel/engines/memory/primitives.rb +0 -27
  77. data/lib/arel/engines/memory/relations/compound.rb +0 -9
  78. data/lib/arel/engines/memory/relations/writes.rb +0 -7
  79. data/lib/arel/engines/sql/predicates.rb +0 -103
  80. data/lib/arel/engines/sql/primitives.rb +0 -97
  81. data/lib/arel/engines/sql/relations/operations/alias.rb +0 -5
  82. data/lib/arel/engines/sql/relations/operations/join.rb +0 -33
  83. data/lib/arel/engines/sql/relations/relation.rb +0 -65
  84. data/lib/arel/engines/sql/relations/utilities/compound.rb +0 -10
  85. data/lib/arel/engines/sql/relations/utilities/externalization.rb +0 -14
  86. data/lib/arel/engines/sql/relations/writes.rb +0 -19
@@ -24,11 +24,16 @@ module Arel
24
24
  .join(@photos) \
25
25
  .on(@users[:id].eq(@photos[:user_id])) \
26
26
  .project(@users[:name], @photos[:camera_id]) \
27
- .let do |relation|
28
- relation.call.should == [
29
- Row.new(relation, ['bryan', @adapter_returns_integer ? 6 : '6']),
30
- Row.new(relation, ['emilio', @adapter_returns_integer ? 42 : '42'])
31
- ]
27
+ .tap do |relation|
28
+ rows = relation.call
29
+ rows.length.should == 2
30
+ [
31
+ ['bryan', @adapter_returns_integer ? 6 : '6'],
32
+ ['emilio', @adapter_returns_integer ? 42 : '42']
33
+ ].zip(rows).each do |tuple, row|
34
+ row.relation.should == relation
35
+ row.tuple.should == tuple
36
+ end
32
37
  end
33
38
  end
34
39
  end
@@ -39,11 +44,16 @@ module Arel
39
44
  .join(@users) \
40
45
  .on(@users[:id].eq(@photos[:user_id])) \
41
46
  .project(@users[:name], @photos[:camera_id]) \
42
- .let do |relation|
43
- relation.call.should == [
44
- Row.new(relation, ['bryan', @adapter_returns_integer ? 6 : '6']),
45
- Row.new(relation, ['emilio', @adapter_returns_integer ? 42 : '42'])
46
- ]
47
+ .tap do |relation|
48
+ rows = relation.call
49
+ rows.length.should == 2
50
+ [
51
+ ['bryan', @adapter_returns_integer ? 6 : '6'],
52
+ ['emilio', @adapter_returns_integer ? 42 : '42']
53
+ ].zip(rows).each do |tuple, row|
54
+ row.relation.should == relation
55
+ row.tuple.should == tuple
56
+ end
47
57
  end
48
58
  end
49
59
  end
@@ -21,11 +21,12 @@ module Arel
21
21
 
22
22
  describe '#call' do
23
23
  it "manufactures an array of hashes of attributes to values" do
24
- @relation.call.should == [
25
- Row.new(@relation, [1, 'duck']),
26
- Row.new(@relation, [2, 'duck']),
27
- Row.new(@relation, [3, 'goose'])
28
- ]
24
+ rows = @relation.call
25
+ rows.length.should == 3
26
+ @relation.array.zip(rows).each do |tuple, row|
27
+ row.relation.should == @relation
28
+ row.tuple.should == tuple
29
+ end
29
30
  end
30
31
  end
31
32
  end
@@ -17,12 +17,13 @@ module Arel
17
17
  @relation1 \
18
18
  .join(@relation2) \
19
19
  .on(@relation1[:id].eq(@relation2[:id])) \
20
- .let do |relation|
21
- relation.call.should == [
22
- Row.new(relation, [1, 'duck', 1, 'duck' ]),
23
- Row.new(relation, [2, 'duck', 2, 'duck' ]),
24
- Row.new(relation, [3, 'goose', 3, 'goose'])
25
- ]
20
+ .tap do |relation|
21
+ rows = relation.call
22
+ rows.length.should == 3
23
+ @relation1.array.zip(rows).each do |tuple, row|
24
+ row.relation.should == relation
25
+ row.tuple.should == (tuple * 2)
26
+ end
26
27
  end
27
28
  end
28
29
  end
@@ -14,12 +14,13 @@ module Arel
14
14
  it 'sorts the relation with the provided ordering' do
15
15
  @relation \
16
16
  .order(@relation[:id].desc) \
17
- .let do |relation|
18
- relation.call.should == [
19
- Row.new(relation, [3, 'goose']),
20
- Row.new(relation, [2, 'duck' ]),
21
- Row.new(relation, [1, 'duck' ])
22
- ]
17
+ .tap do |relation|
18
+ rows = relation.call
19
+ rows.length.should == 3
20
+ @relation.array.reverse.zip(rows) do |tuple, row|
21
+ row.relation.should == relation
22
+ row.tuple.should == tuple
23
+ end
23
24
  end
24
25
  end
25
26
  end
@@ -14,12 +14,12 @@ module Arel
14
14
  it 'retains only the attributes that are provided' do
15
15
  @relation \
16
16
  .project(@relation[:id]) \
17
- .let do |relation|
18
- relation.call.should == [
19
- Row.new(relation, [1]),
20
- Row.new(relation, [2]),
21
- Row.new(relation, [3])
22
- ]
17
+ .tap do |relation|
18
+ rows = relation.call
19
+ @relation.array.zip(rows) do |tuple, row|
20
+ row.relation.should == relation
21
+ row.tuple.should == [tuple.first]
22
+ end
23
23
  end
24
24
  end
25
25
  end
@@ -14,11 +14,16 @@ module Arel
14
14
  it 'removes the first n rows' do
15
15
  @relation \
16
16
  .skip(1) \
17
- .let do |relation|
18
- relation.call.should == [
19
- Row.new(relation, [2, 'duck']),
20
- Row.new(relation, [3, 'goose']),
21
- ]
17
+ .tap do |relation|
18
+ rows = relation.call
19
+ rows.length.should == 2
20
+ one, two = *rows
21
+
22
+ one.relation.should == relation
23
+ one.tuple.should == [2, 'duck']
24
+
25
+ two.relation.should == relation
26
+ two.tuple.should == [3, 'goose']
22
27
  end
23
28
  end
24
29
  end
@@ -14,11 +14,13 @@ module Arel
14
14
  it 'removes the rows after the first n' do
15
15
  @relation \
16
16
  .take(2) \
17
- .let do |relation|
18
- relation.call.should == [
19
- Row.new(relation, [1, 'duck']),
20
- Row.new(relation, [2, 'duck']),
21
- ]
17
+ .tap do |relation|
18
+ rows = relation.call
19
+ rows.length.should == 2
20
+ rows.each_with_index do |row, i|
21
+ row.relation.should == relation
22
+ row.tuple.should == [i + 1, 'duck']
23
+ end
22
24
  end
23
25
  end
24
26
  end
@@ -14,11 +14,13 @@ module Arel
14
14
  it 'filters the relation with the provided predicate' do
15
15
  @relation \
16
16
  .where(@relation[:id].lt(3)) \
17
- .let do |relation|
18
- relation.call.should == [
19
- Row.new(relation, [1, 'duck']),
20
- Row.new(relation, [2, 'duck']),
21
- ]
17
+ .tap do |relation|
18
+ rows = relation.call
19
+ rows.length.should == 2
20
+ rows.each_with_index do |row, i|
21
+ row.relation.should == relation
22
+ row.tuple.should == [i + 1, 'duck']
23
+ end
22
24
  end
23
25
  end
24
26
 
@@ -27,10 +29,12 @@ module Arel
27
29
  @relation \
28
30
  .where(@relation[:id].gt(1)) \
29
31
  .where(@relation[:id].lt(3)) \
30
- .let do |relation|
31
- relation.call.should == [
32
- Row.new(relation, [2, 'duck'])
33
- ]
32
+ .tap do |relation|
33
+ rows = relation.call
34
+ rows.length.should == 1
35
+ row = rows.first
36
+ row.relation.should == relation
37
+ row.tuple.should == [2, 'duck']
34
38
  end
35
39
  end
36
40
  end
@@ -1,12 +1,32 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Arel
4
+ FakeAR = Struct.new(:connection)
5
+ class FakeConnection < Struct.new :called
6
+ def initialize c = []; super; end
7
+
8
+ def method_missing name, *args, &block
9
+ called << [name, args, block]
10
+ end
11
+ end
12
+
4
13
  describe Sql::Engine do
5
14
  before do
6
15
  @users = Table.new(:users)
7
16
  @users.delete
8
17
  end
9
18
 
19
+ describe "method missing" do
20
+ it "should ask for a connection" do
21
+ conn = FakeConnection.new
22
+ ar = FakeAR.new conn
23
+ engine = Arel::Sql::Engine.new ar
24
+
25
+ ar.connection = nil
26
+ lambda { engine.foo }.should raise_error
27
+ end
28
+ end
29
+
10
30
  describe "CRUD" do
11
31
  describe "#create" do
12
32
  it "inserts into the relation" do
@@ -10,7 +10,7 @@ module Arel
10
10
  describe '#to_sql' do
11
11
  describe 'when given a predicate' do
12
12
  it "manufactures sql with where clause conditions" do
13
- sql = Group.new(@relation, @attribute).to_sql
13
+ sql = Group.new(@relation, [@attribute]).to_sql
14
14
 
15
15
  adapter_is :mysql do
16
16
  sql.should be_like(%Q{
@@ -40,7 +40,7 @@ module Arel
40
40
 
41
41
  describe 'when given a string' do
42
42
  it "passes the string through to the where clause" do
43
- sql = Group.new(@relation, 'asdf').to_sql
43
+ sql = Group.new(@relation, ['asdf']).to_sql
44
44
 
45
45
  adapter_is :mysql do
46
46
  sql.should be_like(%Q{
@@ -10,7 +10,7 @@ module Arel
10
10
  describe '#to_sql' do
11
11
  describe "when given an attribute" do
12
12
  it "manufactures sql with an order clause populated by the attribute" do
13
- sql = Order.new(@relation, @attribute).to_sql
13
+ sql = Order.new(@relation, [@attribute]).to_sql
14
14
 
15
15
  adapter_is :mysql do
16
16
  sql.should be_like(%Q{
@@ -60,7 +60,7 @@ module Arel
60
60
  end
61
61
 
62
62
  it "manufactures sql with an order clause populated by comma-separated attributes" do
63
- sql = Order.new(@relation, @attribute, @another_attribute).to_sql
63
+ sql = Order.new(@relation, [@attribute, @another_attribute]).to_sql
64
64
 
65
65
  adapter_is :mysql do
66
66
  sql.should be_like(%Q{
@@ -94,7 +94,7 @@ module Arel
94
94
  end
95
95
 
96
96
  it "passes the string through to the order clause" do
97
- sql = Order.new(@relation, @string).to_sql
97
+ sql = Order.new(@relation, [@string]).to_sql
98
98
 
99
99
  adapter_is :mysql do
100
100
  sql.should be_like(%Q{
@@ -124,12 +124,12 @@ module Arel
124
124
 
125
125
  describe "when ordering an ordered relation" do
126
126
  before do
127
- @ordered_relation = Order.new(@relation, @attribute)
127
+ @ordered_relation = Order.new(@relation, [@attribute])
128
128
  @another_attribute = @relation[:name]
129
129
  end
130
130
 
131
131
  it "manufactures sql with the order clause of the last ordering preceding the first ordering" do
132
- sql = Order.new(@ordered_relation, @another_attribute).to_sql
132
+ sql = Order.new(@ordered_relation, [@another_attribute]).to_sql
133
133
 
134
134
  adapter_is :mysql do
135
135
  sql.should be_like(%Q{
@@ -10,7 +10,7 @@ module Arel
10
10
  describe '#to_sql' do
11
11
  describe 'when given an attribute' do
12
12
  it "manufactures sql with a limited select clause" do
13
- sql = Project.new(@relation, @attribute).to_sql
13
+ sql = Project.new(@relation, [@attribute]).to_sql
14
14
 
15
15
  adapter_is :mysql do
16
16
  sql.should be_like(%Q{
@@ -37,11 +37,11 @@ module Arel
37
37
 
38
38
  describe 'when given a relation' do
39
39
  before do
40
- @scalar_relation = Project.new(@relation, @relation[:name])
40
+ @scalar_relation = Project.new(@relation, [@relation[:name]])
41
41
  end
42
42
 
43
43
  it "manufactures sql with scalar selects" do
44
- sql = Project.new(@relation, @scalar_relation).to_sql
44
+ sql = Project.new(@relation, [@scalar_relation]).to_sql
45
45
 
46
46
  adapter_is :mysql do
47
47
  sql.should be_like(%Q{
@@ -65,7 +65,7 @@ module Arel
65
65
 
66
66
  describe 'when given a string' do
67
67
  it "passes the string through to the select clause" do
68
- sql = Project.new(@relation, 'asdf').to_sql
68
+ sql = Project.new(@relation, ['asdf']).to_sql
69
69
 
70
70
  adapter_is :mysql do
71
71
  sql.should be_like(%Q{
@@ -108,13 +108,6 @@ module Arel
108
108
  end
109
109
  end
110
110
 
111
- describe 'hashing' do
112
- it "implements hash equality" do
113
- Table.new(:users).should hash_the_same_as(Table.new(:users))
114
- Table.new(:users).should_not hash_the_same_as(Table.new(:photos))
115
- end
116
- end
117
-
118
111
  describe '#engine' do
119
112
  it "defaults to global engine" do
120
113
  Table.engine = engine = Sql::Engine.new
@@ -34,6 +34,14 @@ module Arel
34
34
  ORDER BY "USERS"."ID" ASC)
35
35
  where rownum <= 4
36
36
  })
37
+
38
+ sql_with_distinct = Take.new(@relation.project('DISTINCT "USERS"."ID"'), @taken).to_sql
39
+ sql_with_distinct.should be_like(%Q{
40
+ select * from
41
+ (SELECT DISTINCT "USERS"."ID"
42
+ FROM "USERS")
43
+ where rownum <= 4
44
+ })
37
45
  end
38
46
 
39
47
  adapter_is_not :mysql, :oracle do
@@ -44,6 +52,24 @@ module Arel
44
52
  })
45
53
  end
46
54
  end
55
+
56
+ it "manufactures count sql with limit" do
57
+ sql = Take.new(@relation.project(@relation[:id].count), @taken).to_sql
58
+
59
+ adapter_is :mysql do
60
+ sql.should be_like(%Q{
61
+ SELECT COUNT(*) AS count_id
62
+ FROM (SELECT 1 FROM `users` LIMIT 4) AS subquery
63
+ })
64
+ end
65
+
66
+ adapter_is_not :mysql, :oracle do
67
+ sql.should be_like(%Q{
68
+ SELECT COUNT(*) AS count_id
69
+ FROM (SELECT 1 FROM "users" LIMIT 4) AS subquery
70
+ })
71
+ end
72
+ end
47
73
  end
48
74
  end
49
75
  end
@@ -10,7 +10,7 @@ module Arel
10
10
  describe '#to_sql' do
11
11
  describe 'when given a predicate' do
12
12
  it "manufactures sql with where clause conditions" do
13
- sql = Where.new(@relation, @predicate).to_sql
13
+ sql = Where.new(@relation, [@predicate]).to_sql
14
14
 
15
15
  adapter_is :mysql do
16
16
  sql.should be_like(%Q{
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,3 @@
1
- dir = File.dirname(__FILE__)
2
- $LOAD_PATH.unshift "#{dir}/../lib"
3
-
4
1
  require 'rubygems'
5
2
  require 'spec'
6
3
  require 'pp'
@@ -11,7 +8,7 @@ if adapter = ENV['ADAPTER']
11
8
  require "support/connections/#{adapter}_connection.rb"
12
9
  end
13
10
 
14
- Dir["#{dir}/{support,shared}/*.rb"].each do |file|
11
+ Dir["spec/{support,shared}/*.rb"].each do |file|
15
12
  require file
16
13
  end
17
14
 
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ module Arel
4
+ module Sql
5
+ describe "Christener" do
6
+ it "returns the first name" do
7
+ christener = Christener.new
8
+ table = Table.new 'users'
9
+ table2 = Table.new 'pictures'
10
+ christener.name_for(table).should == 'users'
11
+ christener.name_for(table2).should == 'pictures'
12
+ christener.name_for(table).should == 'users'
13
+ end
14
+
15
+ it "returns a unique name for an alias" do
16
+ christener = Christener.new
17
+ table = Table.new 'users'
18
+ table2 = Table.new 'users', :as => 'friends'
19
+ christener.name_for(table).should == 'users'
20
+ christener.name_for(table2).should == 'friends'
21
+ end
22
+
23
+ it "returns a unique name for an alias with same name" do
24
+ christener = Christener.new
25
+ table = Table.new 'users'
26
+ table2 = Table.new 'friends', :as => 'users'
27
+ christener.name_for(table).should == 'users'
28
+ christener.name_for(table2).should == 'users_2'
29
+ end
30
+
31
+ it "returns alias name" do
32
+ christener = Christener.new
33
+ table = Table.new 'users'
34
+ aliaz = Alias.new table
35
+
36
+ christener.name_for(table).should == 'users'
37
+ christener.name_for(aliaz).should == 'users_2'
38
+ end
39
+
40
+ it "returns alias first" do
41
+ christener = Christener.new
42
+ table = Table.new 'users'
43
+ aliaz = Alias.new table
44
+
45
+ christener.name_for(aliaz).should == 'users'
46
+ christener.name_for(table).should == 'users_2'
47
+ end
48
+
49
+ it "returns externalization name" do
50
+ christener = Christener.new
51
+ table = Table.new 'users'
52
+ ext = Externalization.new table
53
+
54
+ christener.name_for(table).should == 'users'
55
+ christener.name_for(ext).should == 'users_external'
56
+ end
57
+
58
+ it "returns aliases externalizations and tables" do
59
+ christener = Christener.new
60
+ table = Table.new 'users'
61
+ aliaz = Alias.new table
62
+ ext = Externalization.new table
63
+
64
+ christener.name_for(table).should == 'users'
65
+ christener.name_for(aliaz).should == 'users_2'
66
+ christener.name_for(ext).should == 'users_external'
67
+ end
68
+ end
69
+ end
70
+ end