directiverecord 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +9 -0
  3. data/CHANGELOG.rdoc +5 -0
  4. data/Gemfile +3 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +49 -0
  7. data/Rakefile +10 -0
  8. data/VERSION +1 -0
  9. data/directiverecord.gemspec +27 -0
  10. data/lib/directive_record.rb +6 -0
  11. data/lib/directive_record/gem_ext.rb +1 -0
  12. data/lib/directive_record/gem_ext/active_record.rb +2 -0
  13. data/lib/directive_record/gem_ext/active_record/base.rb +13 -0
  14. data/lib/directive_record/gem_ext/active_record/relation.rb +17 -0
  15. data/lib/directive_record/query.rb +25 -0
  16. data/lib/directive_record/query/monetdb.rb +54 -0
  17. data/lib/directive_record/query/mysql.rb +36 -0
  18. data/lib/directive_record/query/sql.rb +291 -0
  19. data/lib/directive_record/relation.rb +70 -0
  20. data/lib/directive_record/version.rb +7 -0
  21. data/lib/directiverecord.rb +1 -0
  22. data/test/application/app/models/customer.rb +6 -0
  23. data/test/application/app/models/employee.rb +5 -0
  24. data/test/application/app/models/office.rb +14 -0
  25. data/test/application/app/models/order.rb +4 -0
  26. data/test/application/app/models/order_detail.rb +4 -0
  27. data/test/application/app/models/payment.rb +3 -0
  28. data/test/application/app/models/product.rb +3 -0
  29. data/test/application/app/models/product_line.rb +3 -0
  30. data/test/application/app/models/tag.rb +3 -0
  31. data/test/application/boot.rb +27 -0
  32. data/test/application/config/database.yml +8 -0
  33. data/test/application/db/database.sql +327 -0
  34. data/test/test_helper.rb +17 -0
  35. data/test/test_helper/coverage.rb +13 -0
  36. data/test/unit/gem_ext/active_record/test_base.rb +30 -0
  37. data/test/unit/gem_ext/active_record/test_relation.rb +42 -0
  38. data/test/unit/query/test_monetdb.rb +27 -0
  39. data/test/unit/query/test_mysql.rb +238 -0
  40. data/test/unit/query/test_sql.rb +46 -0
  41. data/test/unit/test_directive_record.rb +15 -0
  42. data/test/unit/test_query.rb +40 -0
  43. data/test/unit/test_relation.rb +42 -0
  44. metadata +221 -0
@@ -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