sequel 4.45.0 → 4.46.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 +4 -4
- data/CHANGELOG +108 -0
- data/doc/release_notes/4.46.0.txt +404 -0
- data/doc/security.rdoc +9 -0
- data/doc/sql.rdoc +2 -2
- data/doc/testing.rdoc +1 -1
- data/doc/validations.rdoc +1 -2
- data/lib/sequel/adapters/ado.rb +8 -3
- data/lib/sequel/adapters/ado/access.rb +8 -4
- data/lib/sequel/adapters/ado/mssql.rb +3 -1
- data/lib/sequel/adapters/amalgalite.rb +5 -0
- data/lib/sequel/adapters/cubrid.rb +16 -7
- data/lib/sequel/adapters/do.rb +7 -1
- data/lib/sequel/adapters/do/mysql.rb +8 -4
- data/lib/sequel/adapters/ibmdb.rb +10 -5
- data/lib/sequel/adapters/jdbc.rb +8 -2
- data/lib/sequel/adapters/jdbc/as400.rb +10 -3
- data/lib/sequel/adapters/jdbc/db2.rb +27 -16
- data/lib/sequel/adapters/jdbc/derby.rb +47 -20
- data/lib/sequel/adapters/jdbc/h2.rb +13 -7
- data/lib/sequel/adapters/jdbc/hsqldb.rb +18 -9
- data/lib/sequel/adapters/jdbc/mssql.rb +5 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +3 -2
- data/lib/sequel/adapters/jdbc/oracle.rb +3 -2
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -3
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +2 -1
- data/lib/sequel/adapters/jdbc/sqlite.rb +10 -3
- data/lib/sequel/adapters/jdbc/sqlserver.rb +23 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +16 -10
- data/lib/sequel/adapters/mock.rb +5 -0
- data/lib/sequel/adapters/mysql.rb +8 -1
- data/lib/sequel/adapters/mysql2.rb +6 -1
- data/lib/sequel/adapters/odbc.rb +20 -8
- data/lib/sequel/adapters/odbc/mssql.rb +6 -3
- data/lib/sequel/adapters/oracle.rb +12 -6
- data/lib/sequel/adapters/postgres.rb +20 -8
- data/lib/sequel/adapters/shared/access.rb +76 -47
- data/lib/sequel/adapters/shared/cubrid.rb +16 -11
- data/lib/sequel/adapters/shared/db2.rb +46 -19
- data/lib/sequel/adapters/shared/firebird.rb +20 -8
- data/lib/sequel/adapters/shared/informix.rb +6 -3
- data/lib/sequel/adapters/shared/mssql.rb +132 -72
- data/lib/sequel/adapters/shared/mysql.rb +112 -65
- data/lib/sequel/adapters/shared/oracle.rb +36 -21
- data/lib/sequel/adapters/shared/postgres.rb +91 -56
- data/lib/sequel/adapters/shared/sqlanywhere.rb +65 -37
- data/lib/sequel/adapters/shared/sqlite.rb +67 -32
- data/lib/sequel/adapters/sqlanywhere.rb +9 -1
- data/lib/sequel/adapters/sqlite.rb +8 -1
- data/lib/sequel/adapters/swift.rb +5 -0
- data/lib/sequel/adapters/swift/mysql.rb +4 -2
- data/lib/sequel/adapters/swift/sqlite.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +10 -3
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +1 -1
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +1 -1
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -0
- data/lib/sequel/adapters/utils/pg_types.rb +14 -6
- data/lib/sequel/adapters/utils/replace.rb +4 -2
- data/lib/sequel/connection_pool/single.rb +2 -2
- data/lib/sequel/core.rb +24 -11
- data/lib/sequel/database/connecting.rb +9 -3
- data/lib/sequel/database/dataset_defaults.rb +7 -1
- data/lib/sequel/database/logging.rb +1 -0
- data/lib/sequel/database/misc.rb +5 -2
- data/lib/sequel/database/query.rb +7 -5
- data/lib/sequel/database/schema_generator.rb +1 -0
- data/lib/sequel/database/schema_methods.rb +50 -27
- data/lib/sequel/database/transactions.rb +19 -9
- data/lib/sequel/dataset/actions.rb +15 -6
- data/lib/sequel/dataset/graph.rb +15 -5
- data/lib/sequel/dataset/misc.rb +12 -4
- data/lib/sequel/dataset/mutation.rb +17 -8
- data/lib/sequel/dataset/prepared_statements.rb +3 -2
- data/lib/sequel/dataset/query.rb +84 -38
- data/lib/sequel/dataset/sql.rb +302 -191
- data/lib/sequel/deprecated.rb +26 -17
- data/lib/sequel/extensions/_deprecated_identifier_mangling.rb +2 -2
- data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
- data/lib/sequel/extensions/from_block.rb +1 -0
- data/lib/sequel/extensions/graph_each.rb +1 -1
- data/lib/sequel/extensions/identifier_mangling.rb +2 -2
- data/lib/sequel/extensions/migration.rb +28 -4
- data/lib/sequel/extensions/no_auto_literal_strings.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +4 -4
- data/lib/sequel/extensions/sequel_3_dataset_methods.rb +5 -3
- data/lib/sequel/extensions/set_overrides.rb +2 -0
- data/lib/sequel/extensions/split_array_nil.rb +2 -2
- data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
- data/lib/sequel/model.rb +11 -7
- data/lib/sequel/model/associations.rb +5 -7
- data/lib/sequel/model/base.rb +47 -45
- data/lib/sequel/model/dataset_module.rb +9 -14
- data/lib/sequel/model/plugins.rb +3 -0
- data/lib/sequel/no_core_ext.rb +1 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -1
- data/lib/sequel/plugins/boolean_subsets.rb +7 -5
- data/lib/sequel/plugins/class_table_inheritance.rb +47 -10
- data/lib/sequel/plugins/dataset_associations.rb +1 -1
- data/lib/sequel/plugins/def_dataset_method.rb +90 -0
- data/lib/sequel/plugins/finder.rb +240 -0
- data/lib/sequel/plugins/inverted_subsets.rb +19 -12
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +1 -1
- data/lib/sequel/plugins/schema.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +7 -1
- data/lib/sequel/plugins/subset_conditions.rb +11 -3
- data/lib/sequel/plugins/whitelist_security.rb +118 -0
- data/lib/sequel/sql.rb +80 -36
- data/lib/sequel/timezones.rb +2 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +20 -0
- data/spec/adapters/mysql_spec.rb +1 -1
- data/spec/adapters/oracle_spec.rb +12 -8
- data/spec/adapters/postgres_spec.rb +1 -1
- data/spec/adapters/spec_helper.rb +1 -1
- data/spec/adapters/sqlite_spec.rb +36 -34
- data/spec/core/connection_pool_spec.rb +2 -1
- data/spec/core/database_spec.rb +87 -9
- data/spec/core/dataset_spec.rb +501 -129
- data/spec/core/deprecated_spec.rb +1 -1
- data/spec/core/expression_filters_spec.rb +146 -60
- data/spec/core/mock_adapter_spec.rb +1 -1
- data/spec/core/object_graph_spec.rb +61 -9
- data/spec/core/placeholder_literalizer_spec.rb +20 -2
- data/spec/core/schema_generator_spec.rb +6 -6
- data/spec/core/schema_spec.rb +54 -5
- data/spec/core_extensions_spec.rb +122 -18
- data/spec/deprecation_helper.rb +27 -2
- data/spec/extensions/_deprecated_identifier_mangling_spec.rb +6 -6
- data/spec/extensions/association_proxies_spec.rb +2 -2
- data/spec/extensions/auto_literal_strings_spec.rb +212 -0
- data/spec/extensions/blacklist_security_spec.rb +1 -0
- data/spec/extensions/class_table_inheritance_spec.rb +1037 -39
- data/spec/extensions/column_select_spec.rb +20 -8
- data/spec/extensions/columns_introspection_spec.rb +3 -3
- data/spec/extensions/core_refinements_spec.rb +29 -12
- data/spec/extensions/dataset_associations_spec.rb +12 -12
- data/spec/extensions/def_dataset_method_spec.rb +100 -0
- data/spec/extensions/error_sql_spec.rb +1 -1
- data/spec/extensions/finder_spec.rb +260 -0
- data/spec/extensions/graph_each_spec.rb +2 -2
- data/spec/extensions/identifier_mangling_spec.rb +14 -8
- data/spec/extensions/inverted_subsets_spec.rb +4 -4
- data/spec/extensions/lazy_attributes_spec.rb +7 -0
- data/spec/extensions/many_through_many_spec.rb +38 -14
- data/spec/extensions/nested_attributes_spec.rb +18 -6
- data/spec/extensions/no_auto_literal_strings_spec.rb +1 -1
- data/spec/extensions/pg_enum_spec.rb +16 -1
- data/spec/extensions/pg_interval_spec.rb +11 -2
- data/spec/extensions/pg_loose_count_spec.rb +5 -0
- data/spec/extensions/pg_row_spec.rb +25 -0
- data/spec/extensions/prepared_statements_spec.rb +10 -1
- data/spec/extensions/query_spec.rb +2 -2
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +2 -2
- data/spec/extensions/set_overrides_spec.rb +7 -3
- data/spec/extensions/sql_expr_spec.rb +0 -1
- data/spec/extensions/subset_conditions_spec.rb +6 -6
- data/spec/extensions/table_select_spec.rb +24 -12
- data/spec/extensions/to_dot_spec.rb +4 -4
- data/spec/extensions/whitelist_security_spec.rb +131 -0
- data/spec/integration/dataset_test.rb +9 -5
- data/spec/integration/model_test.rb +2 -0
- data/spec/integration/plugin_test.rb +2 -2
- data/spec/integration/spec_helper.rb +1 -1
- data/spec/model/associations_spec.rb +39 -11
- data/spec/model/base_spec.rb +44 -24
- data/spec/model/class_dataset_methods_spec.rb +18 -16
- data/spec/model/dataset_methods_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +84 -24
- data/spec/model/model_spec.rb +97 -63
- data/spec/model/record_spec.rb +21 -13
- metadata +13 -2
|
@@ -24,26 +24,29 @@ describe "Sequel::Plugins::ColumnSelect" do
|
|
|
24
24
|
@Album.dataset.sql.must_equal 'SELECT albs.id, albs.a, albs.b, albs.c FROM albs'
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
with_symbol_splitting "should handle splittable symbols" do
|
|
28
28
|
@Album.dataset = :s__albums
|
|
29
29
|
@Album.plugin :column_select
|
|
30
30
|
@Album.dataset.sql.must_equal 'SELECT s.albums.id, s.albums.a, s.albums.b, s.albums.c FROM s.albums'
|
|
31
31
|
|
|
32
|
+
@Album.dataset = :albums___a
|
|
33
|
+
@Album.dataset.sql.must_equal 'SELECT a.id, a.a, a.b, a.c FROM albums AS a'
|
|
34
|
+
|
|
35
|
+
@Album.dataset = :s__albums___a
|
|
36
|
+
@Album.dataset.sql.must_equal 'SELECT a.id, a.a, a.b, a.c FROM s.albums AS a'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should handle qualified tables" do
|
|
32
40
|
@Album.dataset = Sequel.qualify(:s2, :albums)
|
|
41
|
+
@Album.plugin :column_select
|
|
33
42
|
@Album.dataset.sql.must_equal 'SELECT s2.albums.id, s2.albums.a, s2.albums.b, s2.albums.c FROM s2.albums'
|
|
34
43
|
end
|
|
35
44
|
|
|
36
45
|
it "should handle aliases" do
|
|
37
|
-
@Album.dataset = :albums___a
|
|
38
46
|
@Album.plugin :column_select
|
|
39
|
-
@Album.dataset.sql.must_equal 'SELECT a.id, a.a, a.b, a.c FROM albums AS a'
|
|
40
|
-
|
|
41
47
|
@Album.dataset = Sequel.as(:albums, :b)
|
|
42
48
|
@Album.dataset.sql.must_equal 'SELECT b.id, b.a, b.b, b.c FROM albums AS b'
|
|
43
49
|
|
|
44
|
-
@Album.dataset = :s__albums___a
|
|
45
|
-
@Album.dataset.sql.must_equal 'SELECT a.id, a.a, a.b, a.c FROM s.albums AS a'
|
|
46
|
-
|
|
47
50
|
@Album.dataset = @Album.db[:albums].from_self
|
|
48
51
|
@Album.dataset.sql.must_equal 'SELECT t1.id, t1.a, t1.b, t1.c FROM (SELECT * FROM albums) AS t1'
|
|
49
52
|
|
|
@@ -60,7 +63,7 @@ describe "Sequel::Plugins::ColumnSelect" do
|
|
|
60
63
|
@Album.dataset.sql.must_equal 'SELECT name, artist FROM albums'
|
|
61
64
|
end
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
deprecated "should not add a explicit column selection on existing dataset with multiple tables" do
|
|
64
67
|
@Album.dataset = @Album.db.from(:a1, :a2)
|
|
65
68
|
@Album.plugin :column_select
|
|
66
69
|
@Album.dataset.sql.must_equal 'SELECT * FROM a1, a2'
|
|
@@ -69,6 +72,15 @@ describe "Sequel::Plugins::ColumnSelect" do
|
|
|
69
72
|
@Album.dataset.sql.must_equal 'SELECT * FROM a1 CROSS JOIN a2'
|
|
70
73
|
end
|
|
71
74
|
|
|
75
|
+
it "should add a explicit column selection on existing dataset with a subquery" do
|
|
76
|
+
@Album.dataset = @Album.db.from(:a1, :a2).from_self(:alias=>:foo)
|
|
77
|
+
@Album.plugin :column_select
|
|
78
|
+
@Album.dataset.sql.must_equal 'SELECT foo.id, foo.a, foo.b, foo.c FROM (SELECT * FROM a1, a2) AS foo'
|
|
79
|
+
|
|
80
|
+
@Album.dataset = @Album.db.from(:a1).cross_join(:a2).from_self(:alias=>:foo)
|
|
81
|
+
@Album.dataset.sql.must_equal 'SELECT foo.id, foo.a, foo.b, foo.c FROM (SELECT * FROM a1 CROSS JOIN a2) AS foo'
|
|
82
|
+
end
|
|
83
|
+
|
|
72
84
|
it "should use explicit column selection for many_to_many associations" do
|
|
73
85
|
@Album.plugin :column_select
|
|
74
86
|
@Album.many_to_many :albums, :class=>@Album, :left_key=>:l, :right_key=>:r, :join_table=>:j
|
|
@@ -20,17 +20,17 @@ describe "columns_introspection extension" do
|
|
|
20
20
|
@db.sqls.length.must_equal 0
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
with_symbol_splitting "should handle qualified symbols without a database query" do
|
|
24
24
|
@ds.select(:t__x).columns.must_equal [:x]
|
|
25
25
|
@db.sqls.length.must_equal 0
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
with_symbol_splitting "should handle aliased symbols without a database query" do
|
|
29
29
|
@ds.select(:x___a).columns.must_equal [:a]
|
|
30
30
|
@db.sqls.length.must_equal 0
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
with_symbol_splitting "should handle qualified and aliased symbols without a database query" do
|
|
34
34
|
@ds.select(:t__x___a).columns.must_equal [:a]
|
|
35
35
|
@db.sqls.length.must_equal 0
|
|
36
36
|
end
|
|
@@ -20,6 +20,9 @@ describe "Core refinements" do
|
|
|
20
20
|
|
|
21
21
|
it "should support NOT via Symbol#~" do
|
|
22
22
|
@d.l(~:x).must_equal 'NOT x'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
with_symbol_splitting "should support NOT via Symbol#~ for splittable symbols" do
|
|
23
26
|
@d.l(~:x__y).must_equal 'NOT x.y'
|
|
24
27
|
end
|
|
25
28
|
|
|
@@ -97,7 +100,7 @@ describe "Core refinements" do
|
|
|
97
100
|
@d.lit([:x.sql_function(1), 'y.z'.lit].sql_string_join(', ')).must_equal "(x(1) || ', ' || y.z)"
|
|
98
101
|
@d.lit([:x, 1, :y].sql_string_join).must_equal "(x || '1' || y)"
|
|
99
102
|
@d.lit([:x, 1, :y].sql_string_join(', ')).must_equal "(x || ', ' || '1' || ', ' || y)"
|
|
100
|
-
@d.lit([:x, 1, :y].sql_string_join(:
|
|
103
|
+
@d.lit([:x, 1, :y].sql_string_join(Sequel[:y][:z])).must_equal "(x || y.z || '1' || y.z || y)"
|
|
101
104
|
@d.lit([:x, 1, :y].sql_string_join(1)).must_equal "(x || '1' || '1' || '1' || y)"
|
|
102
105
|
@d.lit([:x, :y].sql_string_join('y.x || x.y'.lit)).must_equal "(x || y.x || x.y || y)"
|
|
103
106
|
@d.lit([[:x, :y].sql_string_join, [:a, :b].sql_string_join].sql_string_join).must_equal "(x || y || a || b)"
|
|
@@ -148,7 +151,7 @@ describe "Array#case and Hash#case" do
|
|
|
148
151
|
@d.literal([[:x, :y]].case(:z)).must_equal '(CASE WHEN x THEN y ELSE z END)'
|
|
149
152
|
@d.literal([[:x, :y], [:a, :b]].case(:z)).must_equal '(CASE WHEN x THEN y WHEN a THEN b ELSE z END)'
|
|
150
153
|
@d.literal([[:x, :y], [:a, :b]].case(:z, :exp)).must_equal '(CASE exp WHEN x THEN y WHEN a THEN b ELSE z END)'
|
|
151
|
-
@d.literal([[:x, :y], [:a, :b]].case(:z, :
|
|
154
|
+
@d.literal([[:x, :y], [:a, :b]].case(:z, Sequel[:exp][:w])).must_equal '(CASE exp.w WHEN x THEN y WHEN a THEN b ELSE z END)'
|
|
152
155
|
end
|
|
153
156
|
|
|
154
157
|
it "should return SQL CASE expression with expression even if nil" do
|
|
@@ -173,8 +176,8 @@ describe "Array#sql_value_list and #sql_array" do
|
|
|
173
176
|
end
|
|
174
177
|
|
|
175
178
|
it "should treat the array as an SQL value list instead of conditions when used as a placeholder value" do
|
|
176
|
-
@d.filter("(a, b) IN ?", [[:x, 1], [:y, 2]]).sql.must_equal 'SELECT * WHERE ((a, b) IN ((x = 1) AND (y = 2)))'
|
|
177
|
-
@d.filter("(a, b) IN ?", [[:x, 1], [:y, 2]].sql_value_list).sql.must_equal 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
|
|
179
|
+
@d.filter(Sequel.lit("(a, b) IN ?", [[:x, 1], [:y, 2]])).sql.must_equal 'SELECT * WHERE ((a, b) IN ((x = 1) AND (y = 2)))'
|
|
180
|
+
@d.filter(Sequel.lit("(a, b) IN ?", [[:x, 1], [:y, 2]].sql_value_list)).sql.must_equal 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
|
|
178
181
|
end
|
|
179
182
|
|
|
180
183
|
it "should be no difference when used as a hash value" do
|
|
@@ -237,7 +240,9 @@ describe "#desc" do
|
|
|
237
240
|
|
|
238
241
|
it "should format a DESC clause for a column ref" do
|
|
239
242
|
@ds.literal(:test.desc).must_equal 'test DESC'
|
|
243
|
+
end
|
|
240
244
|
|
|
245
|
+
with_symbol_splitting "should format a DESC clause for a column ref with splittable symbol" do
|
|
241
246
|
@ds.literal(:items__price.desc).must_equal 'items.price DESC'
|
|
242
247
|
end
|
|
243
248
|
|
|
@@ -253,7 +258,9 @@ describe "#asc" do
|
|
|
253
258
|
|
|
254
259
|
it "should format a ASC clause for a column ref" do
|
|
255
260
|
@ds.literal(:test.asc).must_equal 'test ASC'
|
|
261
|
+
end
|
|
256
262
|
|
|
263
|
+
with_symbol_splitting "should format a ASC clause for a column ref with splittable symbol" do
|
|
257
264
|
@ds.literal(:items__price.asc).must_equal 'items.price ASC'
|
|
258
265
|
end
|
|
259
266
|
|
|
@@ -269,7 +276,9 @@ describe "#as" do
|
|
|
269
276
|
|
|
270
277
|
it "should format a AS clause for a column ref" do
|
|
271
278
|
@ds.literal(:test.as(:t)).must_equal 'test AS t'
|
|
279
|
+
end
|
|
272
280
|
|
|
281
|
+
with_symbol_splitting "should format a AS clause for a column ref with splittable symbols" do
|
|
273
282
|
@ds.literal(:items__price.as(:p)).must_equal 'items.price AS p'
|
|
274
283
|
end
|
|
275
284
|
|
|
@@ -289,13 +298,7 @@ describe "Column references" do
|
|
|
289
298
|
|
|
290
299
|
it "should be quoted properly" do
|
|
291
300
|
@ds.literal(:xyz).must_equal "`xyz`"
|
|
292
|
-
@ds.literal(:xyz__abc).must_equal "`xyz`.`abc`"
|
|
293
|
-
|
|
294
301
|
@ds.literal(:xyz.as(:x)).must_equal "`xyz` AS `x`"
|
|
295
|
-
@ds.literal(:xyz__abc.as(:x)).must_equal "`xyz`.`abc` AS `x`"
|
|
296
|
-
|
|
297
|
-
@ds.literal(:xyz___x).must_equal "`xyz` AS `x`"
|
|
298
|
-
@ds.literal(:xyz__abc___x).must_equal "`xyz`.`abc` AS `x`"
|
|
299
302
|
end
|
|
300
303
|
|
|
301
304
|
it "should be quoted properly in SQL functions" do
|
|
@@ -311,6 +314,13 @@ describe "Column references" do
|
|
|
311
314
|
|
|
312
315
|
it "should be quoted properly in a cast function" do
|
|
313
316
|
@ds.literal(:x.cast(:integer)).must_equal "CAST(`x` AS integer)"
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
with_symbol_splitting "should be quoted properly when using splittable symbols" do
|
|
320
|
+
@ds.literal(:xyz__abc).must_equal "`xyz`.`abc`"
|
|
321
|
+
@ds.literal(:xyz__abc.as(:x)).must_equal "`xyz`.`abc` AS `x`"
|
|
322
|
+
@ds.literal(:xyz___x).must_equal "`xyz` AS `x`"
|
|
323
|
+
@ds.literal(:xyz__abc___x).must_equal "`xyz`.`abc` AS `x`"
|
|
314
324
|
@ds.literal(:x__y.cast('varchar(20)')).must_equal "CAST(`x`.`y` AS varchar(20))"
|
|
315
325
|
end
|
|
316
326
|
end
|
|
@@ -337,7 +347,7 @@ describe "Symbol#*" do
|
|
|
337
347
|
@ds.literal(:abc.*(5)).must_equal '(abc * 5)'
|
|
338
348
|
end
|
|
339
349
|
|
|
340
|
-
|
|
350
|
+
with_symbol_splitting "should support qualified symbols if no argument" do
|
|
341
351
|
@ds.literal(:xyz__abc.*).must_equal 'xyz.abc.*'
|
|
342
352
|
end
|
|
343
353
|
end
|
|
@@ -360,6 +370,10 @@ describe "Symbol" do
|
|
|
360
370
|
end
|
|
361
371
|
|
|
362
372
|
it "should be able to qualify an identifier" do
|
|
373
|
+
@ds.literal(:xyz.identifier.qualify(Sequel[:xyz][:abc])).must_equal '"xyz"."abc"."xyz"'
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
with_symbol_splitting "should be able to qualify an identifier with qualified symbol" do
|
|
363
377
|
@ds.literal(:xyz.identifier.qualify(:xyz__abc)).must_equal '"xyz"."abc"."xyz"'
|
|
364
378
|
end
|
|
365
379
|
|
|
@@ -398,9 +412,12 @@ describe "Symbol" do
|
|
|
398
412
|
@ds.literal(:abc.cast(:integer)).must_equal "CAST(abc AS integer)"
|
|
399
413
|
end
|
|
400
414
|
|
|
415
|
+
with_symbol_splitting "should support sql array accesses via sql_subscript for splittable symbols" do
|
|
416
|
+
@ds.literal(:abc__def.sql_subscript(1)).must_equal "abc.def[1]"
|
|
417
|
+
end
|
|
418
|
+
|
|
401
419
|
it "should support sql array accesses via sql_subscript" do
|
|
402
420
|
@ds.literal(:abc.sql_subscript(1)).must_equal "abc[1]"
|
|
403
|
-
@ds.literal(:abc__def.sql_subscript(1)).must_equal "abc.def[1]"
|
|
404
421
|
@ds.literal(:abc.sql_subscript(1)|2).must_equal "abc[1, 2]"
|
|
405
422
|
@ds.literal(:abc.sql_subscript(1)[2]).must_equal "abc[1][2]"
|
|
406
423
|
end
|
|
@@ -100,7 +100,7 @@ describe "Sequel::Plugins::DatasetAssociations" do
|
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
it "should work for many_to_many associations with :dataset_association_join=>true" do
|
|
103
|
-
@Album.many_to_many :tags, :clone=>:tags, :dataset_associations_join=>true, :select=>[Sequel.expr(:tags).*, :
|
|
103
|
+
@Album.many_to_many :tags, :clone=>:tags, :dataset_associations_join=>true, :select=>[Sequel.expr(:tags).*, Sequel[:albums_tags][:foo]]
|
|
104
104
|
ds = @Album.tags
|
|
105
105
|
ds.must_be_kind_of(Sequel::Dataset)
|
|
106
106
|
ds.model.must_equal @Tag
|
|
@@ -108,7 +108,7 @@ describe "Sequel::Plugins::DatasetAssociations" do
|
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
it "should work for one_through_one associations with :dataset_association_join=>true" do
|
|
111
|
-
@Album.one_through_one :first_tag, :clone=>:first_tag, :dataset_associations_join=>true, :select=>[Sequel.expr(:tags).*, :
|
|
111
|
+
@Album.one_through_one :first_tag, :clone=>:first_tag, :dataset_associations_join=>true, :select=>[Sequel.expr(:tags).*, Sequel[:albums_tags][:foo]]
|
|
112
112
|
ds = @Album.first_tags
|
|
113
113
|
ds.must_be_kind_of(Sequel::Dataset)
|
|
114
114
|
ds.model.must_equal @Tag
|
|
@@ -116,7 +116,7 @@ describe "Sequel::Plugins::DatasetAssociations" do
|
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
it "should work for many_through_many associations with :dataset_association_join=>true" do
|
|
119
|
-
@Artist.many_through_many :tags, :clone=>:tags, :dataset_associations_join=>true, :select=>[Sequel.expr(:tags).*, :
|
|
119
|
+
@Artist.many_through_many :tags, :clone=>:tags, :dataset_associations_join=>true, :select=>[Sequel.expr(:tags).*, Sequel[:albums_tags][:foo]]
|
|
120
120
|
ds = @Artist.tags
|
|
121
121
|
ds.must_be_kind_of(Sequel::Dataset)
|
|
122
122
|
ds.model.must_equal @Tag
|
|
@@ -124,7 +124,7 @@ describe "Sequel::Plugins::DatasetAssociations" do
|
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
it "should work for one_through_many associations with :dataset_association_join=>true" do
|
|
127
|
-
@Artist.one_through_many :otag, :clone=>:otag, :dataset_associations_join=>true, :select=>[Sequel.expr(:tags).*, :
|
|
127
|
+
@Artist.one_through_many :otag, :clone=>:otag, :dataset_associations_join=>true, :select=>[Sequel.expr(:tags).*, Sequel[:albums_tags][:foo]]
|
|
128
128
|
ds = @Artist.otags
|
|
129
129
|
ds.must_be_kind_of(Sequel::Dataset)
|
|
130
130
|
ds.model.must_equal @Tag
|
|
@@ -227,22 +227,22 @@ describe "Sequel::Plugins::DatasetAssociations" do
|
|
|
227
227
|
end
|
|
228
228
|
|
|
229
229
|
it "should deal correctly with :order option for one_through_one associations" do
|
|
230
|
-
@Album.one_through_one :first_tag, :clone=>:first_tag, :order
|
|
230
|
+
@Album.one_through_one :first_tag, :clone=>:first_tag, :order=>Sequel[:tags][:name]
|
|
231
231
|
@Album.first_tags.sql.must_equal 'SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE (((albums_tags.album_id) IN (SELECT albums.id FROM albums)) AND ((albums_tags.album_id, tags.id) IN (SELECT DISTINCT ON (albums_tags.album_id) albums_tags.album_id, tags.id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) ORDER BY albums_tags.album_id, tags.name))))) ORDER BY tags.name'
|
|
232
232
|
end
|
|
233
233
|
|
|
234
234
|
it "should deal correctly with :limit option for many_to_many associations" do
|
|
235
|
-
@Album.many_to_many :tags, :clone=>:tags, :limit=>10, :order
|
|
235
|
+
@Album.many_to_many :tags, :clone=>:tags, :limit=>10, :order=>Sequel[:tags][:name]
|
|
236
236
|
@Album.tags.sql.must_equal 'SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE (((albums_tags.album_id) IN (SELECT albums.id FROM albums)) AND ((albums_tags.album_id, tags.id) IN (SELECT b, c FROM (SELECT albums_tags.album_id AS b, tags.id AS c, row_number() OVER (PARTITION BY albums_tags.album_id ORDER BY tags.name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id)) AS t1 WHERE (x_sequel_row_number_x <= 10)))))) ORDER BY tags.name'
|
|
237
237
|
end
|
|
238
238
|
|
|
239
239
|
it "should deal correctly with :order option for one_through_many associations" do
|
|
240
|
-
@Artist.one_through_many :otag, :clone=>:otag, :order
|
|
240
|
+
@Artist.one_through_many :otag, :clone=>:otag, :order=>Sequel[:tags][:name]
|
|
241
241
|
@Artist.otags.sql.must_equal 'SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM artists INNER JOIN albums ON (albums.artist_id = artists.id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) INNER JOIN tags ON (tags.id = albums_tags.tag_id) WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND ((albums.artist_id, tags.id) IN (SELECT DISTINCT ON (albums.artist_id) albums.artist_id, tags.id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) ORDER BY albums.artist_id, tags.name))))) ORDER BY tags.name'
|
|
242
242
|
end
|
|
243
243
|
|
|
244
244
|
it "should deal correctly with :limit option for many_through_many associations" do
|
|
245
|
-
@Artist.many_through_many :tags, :clone=>:tags, :limit=>10, :order
|
|
245
|
+
@Artist.many_through_many :tags, :clone=>:tags, :limit=>10, :order=>Sequel[:tags][:name]
|
|
246
246
|
@Artist.tags.sql.must_equal 'SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM artists INNER JOIN albums ON (albums.artist_id = artists.id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) INNER JOIN tags ON (tags.id = albums_tags.tag_id) WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND ((albums.artist_id, tags.id) IN (SELECT b, c FROM (SELECT albums.artist_id AS b, tags.id AS c, row_number() OVER (PARTITION BY albums.artist_id ORDER BY tags.name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id)) AS t1 WHERE (x_sequel_row_number_x <= 10)))))) ORDER BY tags.name'
|
|
247
247
|
end
|
|
248
248
|
end
|
|
@@ -331,22 +331,22 @@ describe "Sequel::Plugins::DatasetAssociations with composite keys" do
|
|
|
331
331
|
end
|
|
332
332
|
|
|
333
333
|
it "should deal correctly with :order option for one_through_one associations" do
|
|
334
|
-
@Album.one_through_one :first_tag, :clone=>:first_tag, :order
|
|
334
|
+
@Album.one_through_one :first_tag, :clone=>:first_tag, :order=>Sequel[:tags][:name]
|
|
335
335
|
@Album.first_tags.sql.must_equal 'SELECT tags.* FROM tags WHERE ((tags.id1, tags.id2) IN (SELECT albums_tags.tag_id1, albums_tags.tag_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.id1) AND (albums_tags.tag_id2 = tags.id2)) WHERE (((albums_tags.album_id1, albums_tags.album_id2) IN (SELECT albums.id1, albums.id2 FROM albums)) AND ((albums_tags.album_id1, albums_tags.album_id2, tags.id1, tags.id2) IN (SELECT DISTINCT ON (albums_tags.album_id1, albums_tags.album_id2) albums_tags.album_id1, albums_tags.album_id2, tags.id1, tags.id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.id1) AND (albums_tags.tag_id2 = tags.id2)) ORDER BY albums_tags.album_id1, albums_tags.album_id2, tags.name))))) ORDER BY tags.name'
|
|
336
336
|
end
|
|
337
337
|
|
|
338
338
|
it "should deal correctly with :limit option for many_to_many associations" do
|
|
339
|
-
@Album.many_to_many :tags, :clone=>:tags, :limit=>10, :order
|
|
339
|
+
@Album.many_to_many :tags, :clone=>:tags, :limit=>10, :order=>Sequel[:tags][:name]
|
|
340
340
|
@Album.tags.sql.must_equal 'SELECT tags.* FROM tags WHERE ((tags.id1, tags.id2) IN (SELECT albums_tags.tag_id1, albums_tags.tag_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.id1) AND (albums_tags.tag_id2 = tags.id2)) WHERE (((albums_tags.album_id1, albums_tags.album_id2) IN (SELECT albums.id1, albums.id2 FROM albums)) AND ((albums_tags.album_id1, albums_tags.album_id2, tags.id1, tags.id2) IN (SELECT b, c, d, e FROM (SELECT albums_tags.album_id1 AS b, albums_tags.album_id2 AS c, tags.id1 AS d, tags.id2 AS e, row_number() OVER (PARTITION BY albums_tags.album_id1, albums_tags.album_id2 ORDER BY tags.name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.id1) AND (albums_tags.tag_id2 = tags.id2))) AS t1 WHERE (x_sequel_row_number_x <= 10)))))) ORDER BY tags.name'
|
|
341
341
|
end
|
|
342
342
|
|
|
343
343
|
it "should deal correctly with :order option for one_through_many associations" do
|
|
344
|
-
@Artist.one_through_many :otag, :clone=>:otag, :order
|
|
344
|
+
@Artist.one_through_many :otag, :clone=>:otag, :order=>Sequel[:tags][:name]
|
|
345
345
|
@Artist.otags.sql.must_equal 'SELECT tags.* FROM tags WHERE ((tags.id1, tags.id2) IN (SELECT albums_tags.tag_id1, albums_tags.tag_id2 FROM artists INNER JOIN albums ON ((albums.artist_id1 = artists.id1) AND (albums.artist_id2 = artists.id2)) INNER JOIN albums_tags ON ((albums_tags.album_id1 = albums.id1) AND (albums_tags.album_id2 = albums.id2)) INNER JOIN tags ON ((tags.id1 = albums_tags.tag_id1) AND (tags.id2 = albums_tags.tag_id2)) WHERE (((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists)) AND ((albums.artist_id1, albums.artist_id2, tags.id1, tags.id2) IN (SELECT DISTINCT ON (albums.artist_id1, albums.artist_id2) albums.artist_id1, albums.artist_id2, tags.id1, tags.id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.id1) AND (albums_tags.tag_id2 = tags.id2)) INNER JOIN albums ON ((albums.id1 = albums_tags.album_id1) AND (albums.id2 = albums_tags.album_id2)) ORDER BY albums.artist_id1, albums.artist_id2, tags.name))))) ORDER BY tags.name'
|
|
346
346
|
end
|
|
347
347
|
|
|
348
348
|
it "should deal correctly with :limit option for many_through_many associations" do
|
|
349
|
-
@Artist.many_through_many :tags, :clone=>:tags, :limit=>10, :order
|
|
349
|
+
@Artist.many_through_many :tags, :clone=>:tags, :limit=>10, :order=>Sequel[:tags][:name]
|
|
350
350
|
@Artist.tags.sql.must_equal 'SELECT tags.* FROM tags WHERE ((tags.id1, tags.id2) IN (SELECT albums_tags.tag_id1, albums_tags.tag_id2 FROM artists INNER JOIN albums ON ((albums.artist_id1 = artists.id1) AND (albums.artist_id2 = artists.id2)) INNER JOIN albums_tags ON ((albums_tags.album_id1 = albums.id1) AND (albums_tags.album_id2 = albums.id2)) INNER JOIN tags ON ((tags.id1 = albums_tags.tag_id1) AND (tags.id2 = albums_tags.tag_id2)) WHERE (((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists)) AND ((albums.artist_id1, albums.artist_id2, tags.id1, tags.id2) IN (SELECT b, c, d, e FROM (SELECT albums.artist_id1 AS b, albums.artist_id2 AS c, tags.id1 AS d, tags.id2 AS e, row_number() OVER (PARTITION BY albums.artist_id1, albums.artist_id2 ORDER BY tags.name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.id1) AND (albums_tags.tag_id2 = tags.id2)) INNER JOIN albums ON ((albums.id1 = albums_tags.album_id1) AND (albums.id2 = albums_tags.album_id2))) AS t1 WHERE (x_sequel_row_number_x <= 10)))))) ORDER BY tags.name'
|
|
351
351
|
end
|
|
352
352
|
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe Sequel::Model, ".def_dataset_method" do
|
|
4
|
+
before do
|
|
5
|
+
@c = Class.new(Sequel::Model(:items))
|
|
6
|
+
@c.plugin :def_dataset_method
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should add a method to the dataset and model if called with a block argument" do
|
|
10
|
+
@c.def_dataset_method(:return_3){3}
|
|
11
|
+
@c.return_3.must_equal 3
|
|
12
|
+
@c.dataset.return_3.must_equal 3
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should handle weird method names" do
|
|
16
|
+
@c.def_dataset_method(:"return 3"){3}
|
|
17
|
+
@c.send(:"return 3").must_equal 3
|
|
18
|
+
@c.dataset.send(:"return 3").must_equal 3
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should not add a model method if the model already responds to the method" do
|
|
22
|
+
@c.instance_eval do
|
|
23
|
+
def foo
|
|
24
|
+
1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def bar
|
|
30
|
+
2
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def_dataset_method(:foo){3}
|
|
34
|
+
def_dataset_method(:bar){4}
|
|
35
|
+
end
|
|
36
|
+
@c.foo.must_equal 1
|
|
37
|
+
@c.dataset.foo.must_equal 3
|
|
38
|
+
@c.send(:bar).must_equal 2
|
|
39
|
+
@c.dataset.bar.must_equal 4
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should add all passed methods to the model if called without a block argument" do
|
|
43
|
+
@c.def_dataset_method(:return_3, :return_4)
|
|
44
|
+
proc{@c.return_3}.must_raise(NoMethodError)
|
|
45
|
+
proc{@c.return_4}.must_raise(NoMethodError)
|
|
46
|
+
@c.dataset = @c.dataset.with_extend do
|
|
47
|
+
def return_3; 3; end
|
|
48
|
+
def return_4; 4; end
|
|
49
|
+
end
|
|
50
|
+
@c.return_3.must_equal 3
|
|
51
|
+
@c.return_4.must_equal 4
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "should cache calls and readd methods if set_dataset is used" do
|
|
55
|
+
@c.def_dataset_method(:return_3){3}
|
|
56
|
+
@c.set_dataset :items
|
|
57
|
+
@c.return_3.must_equal 3
|
|
58
|
+
@c.dataset.return_3.must_equal 3
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "should readd methods to subclasses, if set_dataset is used in a subclass" do
|
|
62
|
+
@c.def_dataset_method(:return_3){3}
|
|
63
|
+
c = Class.new(@c)
|
|
64
|
+
c.set_dataset :items
|
|
65
|
+
c.return_3.must_equal 3
|
|
66
|
+
c.dataset.return_3.must_equal 3
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe Sequel::Model, ".subset" do
|
|
71
|
+
before do
|
|
72
|
+
@c = Class.new(Sequel::Model(:items))
|
|
73
|
+
@c.plugin :def_dataset_method
|
|
74
|
+
DB.reset
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "should create a filter on the underlying dataset" do
|
|
78
|
+
proc {@c.new_only}.must_raise(NoMethodError)
|
|
79
|
+
|
|
80
|
+
@c.subset(:new_only){age < 'new'}
|
|
81
|
+
|
|
82
|
+
@c.new_only.sql.must_equal "SELECT * FROM items WHERE (age < 'new')"
|
|
83
|
+
@c.dataset.new_only.sql.must_equal "SELECT * FROM items WHERE (age < 'new')"
|
|
84
|
+
|
|
85
|
+
@c.subset(:pricey){price > 100}
|
|
86
|
+
|
|
87
|
+
@c.pricey.sql.must_equal "SELECT * FROM items WHERE (price > 100)"
|
|
88
|
+
@c.dataset.pricey.sql.must_equal "SELECT * FROM items WHERE (price > 100)"
|
|
89
|
+
|
|
90
|
+
@c.pricey.new_only.sql.must_equal "SELECT * FROM items WHERE ((price > 100) AND (age < 'new'))"
|
|
91
|
+
@c.new_only.pricey.sql.must_equal "SELECT * FROM items WHERE ((age < 'new') AND (price > 100))"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should not override existing model methods" do
|
|
95
|
+
def @c.active() true end
|
|
96
|
+
@c.subset(:active, :active)
|
|
97
|
+
@c.active.must_equal true
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
@@ -2,7 +2,7 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
|
2
2
|
|
|
3
3
|
describe "error_sql extension" do
|
|
4
4
|
before do
|
|
5
|
-
@db = Sequel.mock(:fetch=>proc{|sql| @db.
|
|
5
|
+
@db = Sequel.mock(:fetch=>proc{|sql| @db.log_connection_yield(sql, nil){raise StandardError}}).extension(:error_sql)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
it "should have Sequel::DatabaseError#sql give the SQL causing the error" do
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe Sequel::Model, ".finder" do
|
|
4
|
+
before do
|
|
5
|
+
@h = {:id=>1}
|
|
6
|
+
@db = Sequel.mock(:fetch=>@h)
|
|
7
|
+
@c = Class.new(Sequel::Model(@db[:items]))
|
|
8
|
+
@c.instance_eval do
|
|
9
|
+
def foo(a, b)
|
|
10
|
+
where(:bar=>a).order(b)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
@c.plugin :finder
|
|
14
|
+
@o = @c.load(@h)
|
|
15
|
+
@db.sqls
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should create a method that calls the method given and returns the first instance" do
|
|
19
|
+
@c.finder :foo
|
|
20
|
+
@c.first_foo(1, 2).must_equal @o
|
|
21
|
+
@c.first_foo(3, 4).must_equal @o
|
|
22
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2 LIMIT 1", "SELECT * FROM items WHERE (bar = 3) ORDER BY 4 LIMIT 1"]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should work correctly when subclassing" do
|
|
26
|
+
@c.finder(:foo)
|
|
27
|
+
@sc = Class.new(@c)
|
|
28
|
+
@sc.set_dataset :foos
|
|
29
|
+
@db.sqls
|
|
30
|
+
@sc.first_foo(1, 2).must_equal @sc.load(@h)
|
|
31
|
+
@sc.first_foo(3, 4).must_equal @sc.load(@h)
|
|
32
|
+
@db.sqls.must_equal ["SELECT * FROM foos WHERE (bar = 1) ORDER BY 2 LIMIT 1", "SELECT * FROM foos WHERE (bar = 3) ORDER BY 4 LIMIT 1"]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should work correctly when dataset is modified" do
|
|
36
|
+
@c.finder(:foo)
|
|
37
|
+
@c.first_foo(1, 2).must_equal @o
|
|
38
|
+
@c.set_dataset :foos
|
|
39
|
+
@c.first_foo(3, 4).must_equal @o
|
|
40
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2 LIMIT 1", "SELECT * FROM foos LIMIT 1", "SELECT * FROM foos WHERE (bar = 3) ORDER BY 4 LIMIT 1"]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should create a method based on the given block if no method symbol provided" do
|
|
44
|
+
@c.finder(:name=>:first_foo){|pl, ds| ds.where(pl.arg).limit(1)}
|
|
45
|
+
@c.first_foo(:id=>1).must_equal @o
|
|
46
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (id = 1) LIMIT 1"]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should raise an error if both a block and method symbol given" do
|
|
50
|
+
proc{@c.finder(:foo, :name=>:first_foo){|pl, ds| ds.where(pl.arg)}}.must_raise(Sequel::Error)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should raise an error if two option hashes are provided" do
|
|
54
|
+
proc{@c.finder({:name2=>:foo}, :name=>:first_foo){|pl, ds| ds.where(pl.arg)}}.must_raise(Sequel::Error)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should support :type option" do
|
|
58
|
+
@c.finder :foo, :type=>:all
|
|
59
|
+
@c.finder :foo, :type=>:each
|
|
60
|
+
@c.finder :foo, :type=>:get
|
|
61
|
+
|
|
62
|
+
a = []
|
|
63
|
+
@c.all_foo(1, 2){|r| a << r}.must_equal [@o]
|
|
64
|
+
a.must_equal [@o]
|
|
65
|
+
|
|
66
|
+
a = []
|
|
67
|
+
@c.each_foo(3, 4){|r| a << r}
|
|
68
|
+
a.must_equal [@o]
|
|
69
|
+
|
|
70
|
+
@c.get_foo(5, 6).must_equal 1
|
|
71
|
+
|
|
72
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2", "SELECT * FROM items WHERE (bar = 3) ORDER BY 4", "SELECT * FROM items WHERE (bar = 5) ORDER BY 6 LIMIT 1"]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "should support :name option" do
|
|
76
|
+
@c.finder :foo, :name=>:find_foo
|
|
77
|
+
@c.find_foo(1, 2).must_equal @o
|
|
78
|
+
@c.find_foo(3, 4).must_equal @o
|
|
79
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2 LIMIT 1", "SELECT * FROM items WHERE (bar = 3) ORDER BY 4 LIMIT 1"]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "should support :arity option" do
|
|
83
|
+
def @c.foobar(*b)
|
|
84
|
+
ds = dataset
|
|
85
|
+
b.each_with_index do |a, i|
|
|
86
|
+
ds = ds.where(i=>a)
|
|
87
|
+
end
|
|
88
|
+
ds
|
|
89
|
+
end
|
|
90
|
+
@c.finder :foobar, :arity=>1, :name=>:find_foobar_1
|
|
91
|
+
@c.finder :foobar, :arity=>2, :name=>:find_foobar_2
|
|
92
|
+
@c.find_foobar_1(:a)
|
|
93
|
+
@c.find_foobar_2(:a, :b)
|
|
94
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (0 = a) LIMIT 1", "SELECT * FROM items WHERE ((0 = a) AND (1 = b)) LIMIT 1"]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "should support :mod option" do
|
|
98
|
+
m = Module.new
|
|
99
|
+
@c.finder :foo, :mod=>m
|
|
100
|
+
proc{@c.first_foo}.must_raise NoMethodError
|
|
101
|
+
@c.extend m
|
|
102
|
+
@c.first_foo(1, 2).must_equal @o
|
|
103
|
+
@c.first_foo(3, 4).must_equal @o
|
|
104
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2 LIMIT 1", "SELECT * FROM items WHERE (bar = 3) ORDER BY 4 LIMIT 1"]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should raise error when calling with the wrong arity" do
|
|
108
|
+
@c.finder :foo
|
|
109
|
+
proc{@c.first_foo(1)}.must_raise Sequel::Error
|
|
110
|
+
proc{@c.first_foo(1,2,3)}.must_raise Sequel::Error
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe Sequel::Model, ".prepared_finder" do
|
|
115
|
+
before do
|
|
116
|
+
@h = {:id=>1}
|
|
117
|
+
@db = Sequel.mock(:fetch=>@h)
|
|
118
|
+
@db.extend_datasets do
|
|
119
|
+
def select_sql
|
|
120
|
+
sql = super
|
|
121
|
+
sql << ' -- prepared' if is_a?(Sequel::Dataset::PreparedStatementMethods)
|
|
122
|
+
sql
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
@c = Class.new(Sequel::Model(@db[:items]))
|
|
126
|
+
@c.instance_eval do
|
|
127
|
+
def foo(a, b)
|
|
128
|
+
where(:bar=>a).order(b)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
@c.plugin :finder
|
|
132
|
+
@o = @c.load(@h)
|
|
133
|
+
@db.sqls
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "should create a method that calls the method given and returns the first instance" do
|
|
137
|
+
@c.prepared_finder :foo
|
|
138
|
+
@c.first_foo(1, 2).must_equal @o
|
|
139
|
+
@c.first_foo(3, 4).must_equal @o
|
|
140
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2 LIMIT 1 -- prepared", "SELECT * FROM items WHERE (bar = 3) ORDER BY 4 LIMIT 1 -- prepared"]
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "should work correctly when subclassing" do
|
|
144
|
+
@c.prepared_finder(:foo)
|
|
145
|
+
@sc = Class.new(@c)
|
|
146
|
+
@sc.set_dataset :foos
|
|
147
|
+
@db.sqls
|
|
148
|
+
@sc.first_foo(1, 2).must_equal @sc.load(@h)
|
|
149
|
+
@sc.first_foo(3, 4).must_equal @sc.load(@h)
|
|
150
|
+
@db.sqls.must_equal ["SELECT * FROM foos WHERE (bar = 1) ORDER BY 2 LIMIT 1 -- prepared", "SELECT * FROM foos WHERE (bar = 3) ORDER BY 4 LIMIT 1 -- prepared"]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "should work correctly when dataset is modified" do
|
|
154
|
+
@c.prepared_finder(:foo)
|
|
155
|
+
@c.first_foo(1, 2).must_equal @o
|
|
156
|
+
@c.set_dataset :foos
|
|
157
|
+
@c.first_foo(3, 4).must_equal @o
|
|
158
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2 LIMIT 1 -- prepared", "SELECT * FROM foos LIMIT 1", "SELECT * FROM foos WHERE (bar = 3) ORDER BY 4 LIMIT 1 -- prepared"]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should create a method based on the given block if no method symbol provided" do
|
|
162
|
+
@c.prepared_finder(:name=>:first_foo){|a1| where(:id=>a1).limit(1)}
|
|
163
|
+
@c.first_foo(1).must_equal @o
|
|
164
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (id = 1) LIMIT 1 -- prepared"]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "should raise an error if both a block and method symbol given" do
|
|
168
|
+
proc{@c.prepared_finder(:foo, :name=>:first_foo){|pl, ds| ds.where(pl.arg)}}.must_raise(Sequel::Error)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "should raise an error if two option hashes are provided" do
|
|
172
|
+
proc{@c.prepared_finder({:name2=>:foo}, :name=>:first_foo){|pl, ds| ds.where(pl.arg)}}.must_raise(Sequel::Error)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "should support :type option" do
|
|
176
|
+
@c.prepared_finder :foo, :type=>:all
|
|
177
|
+
@c.prepared_finder :foo, :type=>:each
|
|
178
|
+
|
|
179
|
+
a = []
|
|
180
|
+
@c.all_foo(1, 2){|r| a << r}.must_equal [@o]
|
|
181
|
+
a.must_equal [@o]
|
|
182
|
+
|
|
183
|
+
a = []
|
|
184
|
+
@c.each_foo(3, 4){|r| a << r}
|
|
185
|
+
a.must_equal [@o]
|
|
186
|
+
|
|
187
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2 -- prepared", "SELECT * FROM items WHERE (bar = 3) ORDER BY 4 -- prepared"]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it "should support :name option" do
|
|
191
|
+
@c.prepared_finder :foo, :name=>:find_foo
|
|
192
|
+
@c.find_foo(1, 2).must_equal @o
|
|
193
|
+
@c.find_foo(3, 4).must_equal @o
|
|
194
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2 LIMIT 1 -- prepared", "SELECT * FROM items WHERE (bar = 3) ORDER BY 4 LIMIT 1 -- prepared"]
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it "should support :arity option" do
|
|
198
|
+
def @c.foobar(*b)
|
|
199
|
+
ds = dataset
|
|
200
|
+
b.each_with_index do |a, i|
|
|
201
|
+
ds = ds.where(i=>a)
|
|
202
|
+
end
|
|
203
|
+
ds
|
|
204
|
+
end
|
|
205
|
+
@c.prepared_finder :foobar, :arity=>1, :name=>:find_foobar_1
|
|
206
|
+
@c.prepared_finder :foobar, :arity=>2, :name=>:find_foobar_2
|
|
207
|
+
@c.find_foobar_1(:a)
|
|
208
|
+
@c.find_foobar_2(:a, :b)
|
|
209
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (0 = a) LIMIT 1 -- prepared", "SELECT * FROM items WHERE ((0 = a) AND (1 = b)) LIMIT 1 -- prepared"]
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it "should support :mod option" do
|
|
213
|
+
m = Module.new
|
|
214
|
+
@c.prepared_finder :foo, :mod=>m
|
|
215
|
+
proc{@c.first_foo}.must_raise NoMethodError
|
|
216
|
+
@c.extend m
|
|
217
|
+
@c.first_foo(1, 2).must_equal @o
|
|
218
|
+
@c.first_foo(3, 4).must_equal @o
|
|
219
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2 LIMIT 1 -- prepared", "SELECT * FROM items WHERE (bar = 3) ORDER BY 4 LIMIT 1 -- prepared"]
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it "should handle models with names" do
|
|
223
|
+
def @c.name; 'foobar' end
|
|
224
|
+
@c.prepared_finder :foo
|
|
225
|
+
@c.first_foo(1, 2).must_equal @o
|
|
226
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (bar = 1) ORDER BY 2 LIMIT 1 -- prepared"]
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
describe "Sequel::Model.freeze" do
|
|
231
|
+
it "should freeze the model class and not allow any changes" do
|
|
232
|
+
model = Class.new(Sequel::Model(:items))
|
|
233
|
+
model.plugin :finder
|
|
234
|
+
model.finder(:name=>:f_by_name){|pl, ds| ds.where(:name=>pl.arg).limit(1)}
|
|
235
|
+
model.freeze
|
|
236
|
+
model.f_by_name('a').must_equal model.call(:id=>1, :x=>1)
|
|
237
|
+
proc{model.finder(:name=>:first_by_name){|pl, ds| ds.where(:name=>pl.arg).limit(1)}}.must_raise RuntimeError, TypeError
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it "should freeze a model class without a dataset without breaking" do
|
|
241
|
+
model = Class.new(Sequel::Model)
|
|
242
|
+
model.plugin :finder
|
|
243
|
+
model.freeze
|
|
244
|
+
proc{model.finder(:name=>:first_by_name){|pl, ds| ds.where(:name=>pl.arg).limit(1)}}.must_raise RuntimeError, TypeError
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it "should allow subclasses of frozen model classes to work correctly" do
|
|
248
|
+
model = Class.new(Sequel::Model(:items))
|
|
249
|
+
model.plugin :finder
|
|
250
|
+
model.freeze
|
|
251
|
+
model = Class.new(model)
|
|
252
|
+
model.dataset = :items2
|
|
253
|
+
|
|
254
|
+
model.dataset_module{}
|
|
255
|
+
model.plugin Module.new
|
|
256
|
+
model.finder(:name=>:first_by_name){|pl, ds| ds.where(:name=>pl.arg).limit(1)}
|
|
257
|
+
model.first_by_name('a').values.must_equal(:id=>1, :x=>1)
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|