arel 0.4.0 → 1.0.0.rc1

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