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