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