directiverecord 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +9 -0
- data/CHANGELOG.rdoc +5 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.md +49 -0
- data/Rakefile +10 -0
- data/VERSION +1 -0
- data/directiverecord.gemspec +27 -0
- data/lib/directive_record.rb +6 -0
- data/lib/directive_record/gem_ext.rb +1 -0
- data/lib/directive_record/gem_ext/active_record.rb +2 -0
- data/lib/directive_record/gem_ext/active_record/base.rb +13 -0
- data/lib/directive_record/gem_ext/active_record/relation.rb +17 -0
- data/lib/directive_record/query.rb +25 -0
- data/lib/directive_record/query/monetdb.rb +54 -0
- data/lib/directive_record/query/mysql.rb +36 -0
- data/lib/directive_record/query/sql.rb +291 -0
- data/lib/directive_record/relation.rb +70 -0
- data/lib/directive_record/version.rb +7 -0
- data/lib/directiverecord.rb +1 -0
- data/test/application/app/models/customer.rb +6 -0
- data/test/application/app/models/employee.rb +5 -0
- data/test/application/app/models/office.rb +14 -0
- data/test/application/app/models/order.rb +4 -0
- data/test/application/app/models/order_detail.rb +4 -0
- data/test/application/app/models/payment.rb +3 -0
- data/test/application/app/models/product.rb +3 -0
- data/test/application/app/models/product_line.rb +3 -0
- data/test/application/app/models/tag.rb +3 -0
- data/test/application/boot.rb +27 -0
- data/test/application/config/database.yml +8 -0
- data/test/application/db/database.sql +327 -0
- data/test/test_helper.rb +17 -0
- data/test/test_helper/coverage.rb +13 -0
- data/test/unit/gem_ext/active_record/test_base.rb +30 -0
- data/test/unit/gem_ext/active_record/test_relation.rb +42 -0
- data/test/unit/query/test_monetdb.rb +27 -0
- data/test/unit/query/test_mysql.rb +238 -0
- data/test/unit/query/test_sql.rb +46 -0
- data/test/unit/test_directive_record.rb +15 -0
- data/test/unit/test_query.rb +40 -0
- data/test/unit/test_relation.rb +42 -0
- metadata +221 -0
data/test/test_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative "test_helper/coverage"
|
2
|
+
|
3
|
+
require "minitest/autorun"
|
4
|
+
require "mocha/setup"
|
5
|
+
|
6
|
+
require "bundler"
|
7
|
+
Bundler.require :default, :development
|
8
|
+
|
9
|
+
require_relative "application/boot"
|
10
|
+
|
11
|
+
def project_file(path)
|
12
|
+
File.expand_path "../../#{path}", __FILE__
|
13
|
+
end
|
14
|
+
|
15
|
+
def strip(sql)
|
16
|
+
sql.strip.gsub(/^\s+/m, "")
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
if Dir.pwd == File.expand_path("../../..", __FILE__)
|
2
|
+
|
3
|
+
require "simplecov"
|
4
|
+
|
5
|
+
SimpleCov.coverage_dir "test/coverage"
|
6
|
+
SimpleCov.start do
|
7
|
+
add_group "DirectiveRecord", "lib"
|
8
|
+
add_group "App models", "app/models"
|
9
|
+
add_group "Test suite", "test"
|
10
|
+
add_filter "boot.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative "../../../test_helper"
|
2
|
+
|
3
|
+
module Unit
|
4
|
+
module GemExt
|
5
|
+
module ActiveRecord
|
6
|
+
class TestBase < MiniTest::Test
|
7
|
+
|
8
|
+
describe ::ActiveRecord::Base do
|
9
|
+
describe ".to_qry" do
|
10
|
+
it "initiates a DirectiveRecord::Query instance and returns the query SQL" do
|
11
|
+
query = mock
|
12
|
+
query.expects(:to_sql).with(:select => "city").returns("SELECT city FROM offices")
|
13
|
+
DirectiveRecord::Query.expects(:new).with(Office).returns(query)
|
14
|
+
assert_equal "SELECT city FROM offices", Office.to_qry(:select => "city")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe ".qry" do
|
19
|
+
it "selects rows with the generated query" do
|
20
|
+
Office.expects(:to_qry).with("city").returns("SELECT city FROM offices")
|
21
|
+
Office.connection.expects(:select_rows).with("SELECT city FROM offices").returns(%w(NYC))
|
22
|
+
assert_equal %w(NYC), Office.qry("city")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative "../../../test_helper"
|
2
|
+
|
3
|
+
module Unit
|
4
|
+
module GemExt
|
5
|
+
module ActiveRecord
|
6
|
+
class TestRelation < MiniTest::Test
|
7
|
+
|
8
|
+
describe ::ActiveRecord::Relation do
|
9
|
+
before do
|
10
|
+
@relation = Office.where(:id => 1)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#qry_options" do
|
14
|
+
it "initiates a DirectiveRecord::Relation instance and returns the query options" do
|
15
|
+
relation = mock
|
16
|
+
relation.expects(:qry_options).returns(:where => "id = 1")
|
17
|
+
DirectiveRecord::Relation.expects(:new).with(@relation).returns(relation)
|
18
|
+
assert_equal({:where => "id = 1"}, @relation.qry_options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#to_qry" do
|
23
|
+
it "delegates to its klass with qry_options" do
|
24
|
+
@relation.expects(:qry_options).with("city").returns(:select => "city", :where => ["id = 1"])
|
25
|
+
Office.expects(:to_qry).with(:select => "city", :where => ["id = 1"]).returns("SELECT city FROM offices WHERE id = 1")
|
26
|
+
assert_equal "SELECT city FROM offices WHERE id = 1", @relation.to_qry("city")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#qry" do
|
31
|
+
it "delegates to its klass with qry_options" do
|
32
|
+
@relation.expects(:qry_options).with("city").returns(:select => "city", :where => ["id = 1"])
|
33
|
+
Office.expects(:qry).with(:select => "city", :where => ["id = 1"]).returns(%w(NYC))
|
34
|
+
assert_equal %w(NYC), @relation.qry("city")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative "../../test_helper"
|
2
|
+
|
3
|
+
module Unit
|
4
|
+
module Query
|
5
|
+
class TestMonetDB < MiniTest::Test
|
6
|
+
|
7
|
+
describe DirectiveRecord::Query::MonetDB do
|
8
|
+
before do
|
9
|
+
DirectiveRecord::Query.expects(:class_for).returns(DirectiveRecord::Query::MonetDB).at_least_once
|
10
|
+
end
|
11
|
+
|
12
|
+
it "generates the expected SQL" do
|
13
|
+
assert_equal(
|
14
|
+
strip(
|
15
|
+
%Q{
|
16
|
+
SELECT o.id, o.city
|
17
|
+
FROM offices o
|
18
|
+
}
|
19
|
+
),
|
20
|
+
Office.to_qry("id, city")
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
require_relative "../../test_helper"
|
2
|
+
|
3
|
+
module Unit
|
4
|
+
module Query
|
5
|
+
class TestMySQL < MiniTest::Test
|
6
|
+
|
7
|
+
describe DirectiveRecord::Query::MySQL do
|
8
|
+
before do
|
9
|
+
DirectiveRecord::Query.expects(:class_for).returns(DirectiveRecord::Query::MySQL).at_least_once
|
10
|
+
end
|
11
|
+
|
12
|
+
it "generates the expected SQL" do
|
13
|
+
assert_equal(
|
14
|
+
strip(
|
15
|
+
%Q{
|
16
|
+
SELECT `o`.id, `o`.city
|
17
|
+
FROM offices `o`
|
18
|
+
}
|
19
|
+
),
|
20
|
+
Office.to_qry("id, city")
|
21
|
+
)
|
22
|
+
|
23
|
+
assert_equal(
|
24
|
+
strip(
|
25
|
+
%Q{
|
26
|
+
SELECT `e`.*
|
27
|
+
FROM employees `e`
|
28
|
+
WHERE (`e`.office_id = 1) AND (`e`.first_name LIKE '%y')
|
29
|
+
}
|
30
|
+
),
|
31
|
+
Employee.where(:office_id => 1).where(Employee.arel_table[:first_name].matches("%y")).to_qry
|
32
|
+
)
|
33
|
+
|
34
|
+
assert_equal(
|
35
|
+
strip(
|
36
|
+
%Q{
|
37
|
+
SELECT `e`.*
|
38
|
+
FROM employees `e`
|
39
|
+
WHERE ((`e`.office_id = 1) AND (`e`.first_name LIKE '%y'))
|
40
|
+
}
|
41
|
+
),
|
42
|
+
Employee.where("(office_id = 1) AND (first_name LIKE '%y')").to_qry
|
43
|
+
)
|
44
|
+
|
45
|
+
assert_equal(
|
46
|
+
strip(
|
47
|
+
%Q{
|
48
|
+
SELECT `c`.id, `c`.name, COUNT(`orders`.id) AS order_count, GROUP_CONCAT(DISTINCT `tags`.name) AS tags
|
49
|
+
FROM customers `c`
|
50
|
+
LEFT JOIN orders `orders` ON `orders`.customer_id = `c`.id
|
51
|
+
LEFT JOIN customers_tags `tags_bridge_table` ON `tags_bridge_table`.customer_id = `c`.id
|
52
|
+
LEFT JOIN tags `tags` ON `tags`.id = `tags_bridge_table`.tag_id
|
53
|
+
GROUP BY `c`.id
|
54
|
+
ORDER BY COUNT(DISTINCT `tags`.id) DESC
|
55
|
+
LIMIT 5
|
56
|
+
}
|
57
|
+
),
|
58
|
+
Customer.to_qry("id, name, COUNT(orders.id) AS order_count, GROUP_CONCAT(DISTINCT tags.name) AS tags", :group_by => "id", :order_by => "COUNT(DISTINCT tags.id) DESC", :limit => 5)
|
59
|
+
)
|
60
|
+
|
61
|
+
assert_equal(
|
62
|
+
strip(
|
63
|
+
%Q{
|
64
|
+
SELECT `c`.*
|
65
|
+
FROM customers `c`
|
66
|
+
LEFT JOIN customers_tags `tags_bridge_table` ON `tags_bridge_table`.customer_id = `c`.id
|
67
|
+
LEFT JOIN tags `tags` ON `tags`.id = `tags_bridge_table`.tag_id
|
68
|
+
WHERE (`tags`.name LIKE '%gifts%')
|
69
|
+
}
|
70
|
+
),
|
71
|
+
Customer.where("tags.name LIKE ?", "%gifts%").to_qry
|
72
|
+
)
|
73
|
+
|
74
|
+
assert_equal(
|
75
|
+
strip(
|
76
|
+
%Q{
|
77
|
+
SELECT `tags`.*
|
78
|
+
FROM customers `c`
|
79
|
+
LEFT JOIN customers_tags `tags_bridge_table` ON `tags_bridge_table`.customer_id = `c`.id
|
80
|
+
LEFT JOIN tags `tags` ON `tags`.id = `tags_bridge_table`.tag_id
|
81
|
+
WHERE (`tags`.name LIKE '%gifts%')
|
82
|
+
GROUP BY `tags`.id
|
83
|
+
ORDER BY `tags`.id
|
84
|
+
}
|
85
|
+
),
|
86
|
+
Customer.where("tags.name LIKE ?", "%gifts%").group("tags.id").to_qry("tags.*")
|
87
|
+
)
|
88
|
+
|
89
|
+
assert_equal(
|
90
|
+
strip(
|
91
|
+
%Q{
|
92
|
+
SELECT `c`.id, `c`.name, COUNT(`orders`.id) AS order_count
|
93
|
+
FROM customers `c`
|
94
|
+
LEFT JOIN orders `orders` ON `orders`.customer_id = `c`.id
|
95
|
+
GROUP BY `c`.id
|
96
|
+
HAVING (order_count > 3)
|
97
|
+
ORDER BY `c`.id
|
98
|
+
}
|
99
|
+
),
|
100
|
+
Customer.to_qry("id, name, COUNT(orders.id) AS order_count", :where => "order_count > 3", :group_by => "id")
|
101
|
+
)
|
102
|
+
|
103
|
+
$default_office_scope = {:id => [1, 3, 6]}
|
104
|
+
|
105
|
+
assert_equal(
|
106
|
+
strip(
|
107
|
+
%Q{
|
108
|
+
SELECT `o`.id AS c1, `o`.city AS c2
|
109
|
+
FROM offices `o`
|
110
|
+
WHERE (`o`.id IN (1, 3, 6))
|
111
|
+
}
|
112
|
+
),
|
113
|
+
Office.to_qry("id", "city", :numerize_aliases => true)
|
114
|
+
)
|
115
|
+
|
116
|
+
$default_office_scope = nil
|
117
|
+
|
118
|
+
assert_equal(
|
119
|
+
strip(
|
120
|
+
%Q{
|
121
|
+
SELECT `o`.id AS c1, `o`.city AS c2
|
122
|
+
FROM offices `o`
|
123
|
+
}
|
124
|
+
),
|
125
|
+
Office.to_qry("id", "city", :numerize_aliases => true)
|
126
|
+
)
|
127
|
+
|
128
|
+
assert_equal(
|
129
|
+
strip(
|
130
|
+
%Q{
|
131
|
+
SELECT `c`.id, `c`.name, MAX(`orders`.order_date) AS `max:orders.order_date`
|
132
|
+
FROM customers `c`
|
133
|
+
LEFT JOIN orders `orders` ON `orders`.customer_id = `c`.id
|
134
|
+
}
|
135
|
+
),
|
136
|
+
Customer.to_qry("id", "name", "orders.order_date", :aggregates => {"orders.order_date" => :max})
|
137
|
+
)
|
138
|
+
|
139
|
+
assert_equal(
|
140
|
+
strip(
|
141
|
+
%Q{
|
142
|
+
SELECT `c`.id, `c`.name, MAX(`orders`.order_date) AS `max:orders.order_date`
|
143
|
+
FROM customers `c`
|
144
|
+
LEFT JOIN orders `orders` ON `orders`.customer_id = `c`.id
|
145
|
+
ORDER BY MAX(`orders`.order_date)
|
146
|
+
}
|
147
|
+
),
|
148
|
+
Customer.to_qry("id", "name", "orders.order_date", :aggregates => {"orders.order_date" => :max}, :order_by => "orders.order_date")
|
149
|
+
)
|
150
|
+
|
151
|
+
assert_equal(
|
152
|
+
strip(
|
153
|
+
%Q{
|
154
|
+
SELECT ROUND(SUM(`od`.price_each), 2) AS `sum:price_each`
|
155
|
+
FROM order_details `od`
|
156
|
+
GROUP BY NULL
|
157
|
+
}
|
158
|
+
),
|
159
|
+
OrderDetail.to_qry("price_each", :aggregates => {"price_each" => :sum}, :group_by => :all)
|
160
|
+
)
|
161
|
+
|
162
|
+
assert_equal(
|
163
|
+
strip(
|
164
|
+
%Q{
|
165
|
+
SELECT ROUND(SUM(`od`.price_each), 2) AS `sum:price_each`
|
166
|
+
FROM order_details `od`
|
167
|
+
GROUP BY `od`.order_id
|
168
|
+
ORDER BY `od`.order_id
|
169
|
+
}
|
170
|
+
),
|
171
|
+
OrderDetail.to_qry("price_each", :aggregates => {"price_each" => :sum}, :group_by => "order_id")
|
172
|
+
)
|
173
|
+
|
174
|
+
assert_equal(
|
175
|
+
strip(
|
176
|
+
%Q{
|
177
|
+
SELECT `order`.id AS c1, ROUND(SUM(`od`.price_each), 2) AS c2
|
178
|
+
FROM order_details `od`
|
179
|
+
LEFT JOIN orders `order` ON `order`.id = `od`.order_id
|
180
|
+
GROUP BY c1
|
181
|
+
ORDER BY c1
|
182
|
+
}
|
183
|
+
),
|
184
|
+
OrderDetail.to_qry("order.id", "price_each", :aggregates => {"price_each" => :sum}, :group_by => "order.id", :numerize_aliases => true)
|
185
|
+
)
|
186
|
+
|
187
|
+
assert_equal(
|
188
|
+
strip(
|
189
|
+
%Q{
|
190
|
+
SELECT `e`.*
|
191
|
+
FROM employees `e`
|
192
|
+
WHERE (`e`.first_name LIKE '%y')
|
193
|
+
}
|
194
|
+
),
|
195
|
+
Employee.where(["first_name LIKE ?", "%y"]).to_qry
|
196
|
+
)
|
197
|
+
|
198
|
+
assert_equal(
|
199
|
+
strip(
|
200
|
+
%Q{
|
201
|
+
SELECT `e`.id
|
202
|
+
FROM employees `e`
|
203
|
+
LEFT JOIN offices `office` ON `office`.id = `e`.office_id
|
204
|
+
ORDER BY `office`.city, `e`.last_name, `e`.first_name
|
205
|
+
}
|
206
|
+
),
|
207
|
+
Employee.to_qry("id", :order_by => "office.city, last_name, first_name")
|
208
|
+
)
|
209
|
+
|
210
|
+
assert_equal(
|
211
|
+
strip(
|
212
|
+
%Q{
|
213
|
+
SELECT `e`.id, `office`.city
|
214
|
+
FROM employees `e`
|
215
|
+
LEFT JOIN offices `office` ON `office`.id = `e`.office_id
|
216
|
+
WHERE (`e`.id IN (1143, 1076, 1165, 1002, 1056, 1166, 0))
|
217
|
+
ORDER BY `office`.city, `e`.last_name, `e`.first_name
|
218
|
+
}
|
219
|
+
),
|
220
|
+
Employee.to_qry("id", "office.city", :where => "office_id = 1", :order_by => "office.city, last_name, first_name")
|
221
|
+
)
|
222
|
+
|
223
|
+
assert_equal(
|
224
|
+
strip(
|
225
|
+
%Q{
|
226
|
+
SELECT `o`.*
|
227
|
+
FROM offices `o`
|
228
|
+
WHERE (`o`.country = 'USA')
|
229
|
+
}
|
230
|
+
),
|
231
|
+
Office.usa.to_qry
|
232
|
+
)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "../../test_helper"
|
2
|
+
|
3
|
+
module Unit
|
4
|
+
module Query
|
5
|
+
class TestSQL < MiniTest::Test
|
6
|
+
|
7
|
+
describe DirectiveRecord::Query::SQL do
|
8
|
+
before do
|
9
|
+
@base = mock
|
10
|
+
@directive_query = DirectiveRecord::Query::SQL.new(@base)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#initialize" do
|
14
|
+
it "stores the passed base class as an instance variable" do
|
15
|
+
assert_equal @base, @directive_query.instance_variable_get(:@base)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#path_delimiter" do
|
20
|
+
it "raises an NotImplementedError" do
|
21
|
+
assert_raises NotImplementedError do
|
22
|
+
@directive_query.send :path_delimiter
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#aggregate_delimiter" do
|
28
|
+
it "raises an NotImplementedError" do
|
29
|
+
assert_raises NotImplementedError do
|
30
|
+
@directive_query.send :aggregate_delimiter
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#group_by_all_sql" do
|
36
|
+
it "raises an NotImplementedError" do
|
37
|
+
assert_raises NotImplementedError do
|
38
|
+
@directive_query.send :group_by_all_sql
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
module Unit
|
4
|
+
class TestDirectiveRecord < MiniTest::Test
|
5
|
+
|
6
|
+
describe DirectiveRecord do
|
7
|
+
it "has the current version" do
|
8
|
+
version = File.read(project_file("VERSION")).strip
|
9
|
+
assert_equal version, DirectiveRecord::VERSION
|
10
|
+
assert File.read(project_file("CHANGELOG.rdoc")).include?("Version #{version} ")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
module Unit
|
4
|
+
class TestQuery < MiniTest::Test
|
5
|
+
|
6
|
+
describe DirectiveRecord::Query do
|
7
|
+
describe ".new" do
|
8
|
+
it "returns the appropriate query instance" do
|
9
|
+
instance = mock
|
10
|
+
instance.expects(:new).with(Office).returns("SELECT * FROM offices")
|
11
|
+
DirectiveRecord::Query.expects(:class_for).with("activerecord::connectionadapters::mysql2adapter").returns(instance)
|
12
|
+
assert_equal "SELECT * FROM offices", DirectiveRecord::Query.new(Office)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".class_for" do
|
17
|
+
describe "when MySQL" do
|
18
|
+
it "returns the DirectiveRecord::Query::MySQL class" do
|
19
|
+
assert_equal DirectiveRecord::Query::MySQL, DirectiveRecord::Query.send(:class_for, "activerecord::connectionadapters::mysql2adapter")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "when MonetDB" do
|
24
|
+
it "returns the DirectiveRecord::Query::MonetDB class" do
|
25
|
+
assert_equal DirectiveRecord::Query::MonetDB, DirectiveRecord::Query.send(:class_for, "monetdb::connection")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "when else" do
|
30
|
+
it "raises a NotImplementedError" do
|
31
|
+
assert_raises NotImplementedError do
|
32
|
+
DirectiveRecord::Query.send(:class_for, "foobar::connection")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|