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
|
@@ -111,9 +111,9 @@ describe "identifier_mangling extension" do
|
|
|
111
111
|
deprecated "should respect the quote_indentifiers_default method if Sequel.quote_identifiers = nil" do
|
|
112
112
|
Sequel.quote_identifiers = nil
|
|
113
113
|
Sequel::Database.new(:identifier_mangling=>true).quote_identifiers?.must_equal true
|
|
114
|
-
x = Class.new(Sequel::Database){def quote_identifiers_default; false end}
|
|
114
|
+
x = Class.new(Sequel::Database){def dataset_class_default; Sequel::Dataset end; def quote_identifiers_default; false end}
|
|
115
115
|
x.new(:identifier_mangling=>true).quote_identifiers?.must_equal false
|
|
116
|
-
y = Class.new(Sequel::Database){def quote_identifiers_default; true end}
|
|
116
|
+
y = Class.new(Sequel::Database){def dataset_class_default; Sequel::Dataset end; def quote_identifiers_default; true end}
|
|
117
117
|
y.new(:identifier_mangling=>true).quote_identifiers?.must_equal true
|
|
118
118
|
end
|
|
119
119
|
|
|
@@ -121,9 +121,9 @@ describe "identifier_mangling extension" do
|
|
|
121
121
|
class Sequel::Database
|
|
122
122
|
@identifier_input_method = nil
|
|
123
123
|
end
|
|
124
|
-
x = Class.new(Sequel::Database){def identifier_input_method_default; :downcase end}
|
|
124
|
+
x = Class.new(Sequel::Database){def dataset_class_default; Sequel::Dataset end; def identifier_input_method_default; :downcase end}
|
|
125
125
|
x.new(:identifier_mangling=>true).identifier_input_method.must_equal :downcase
|
|
126
|
-
y = Class.new(Sequel::Database){def identifier_input_method_default; :camelize end}
|
|
126
|
+
y = Class.new(Sequel::Database){def dataset_class_default; Sequel::Dataset end; def identifier_input_method_default; :camelize end}
|
|
127
127
|
y.new(:identifier_mangling=>true).identifier_input_method.must_equal :camelize
|
|
128
128
|
end
|
|
129
129
|
|
|
@@ -131,9 +131,9 @@ describe "identifier_mangling extension" do
|
|
|
131
131
|
class Sequel::Database
|
|
132
132
|
@identifier_output_method = nil
|
|
133
133
|
end
|
|
134
|
-
x = Class.new(Sequel::Database){def identifier_output_method_default; :upcase end}
|
|
134
|
+
x = Class.new(Sequel::Database){def dataset_class_default; Sequel::Dataset end; def identifier_output_method_default; :upcase end}
|
|
135
135
|
x.new(:identifier_mangling=>true).identifier_output_method.must_equal :upcase
|
|
136
|
-
y = Class.new(Sequel::Database){def identifier_output_method_default; :underscore end}
|
|
136
|
+
y = Class.new(Sequel::Database){def dataset_class_default; Sequel::Dataset end; def identifier_output_method_default; :underscore end}
|
|
137
137
|
y.new(:identifier_mangling=>true).identifier_output_method.must_equal :underscore
|
|
138
138
|
end
|
|
139
139
|
end
|
|
@@ -31,11 +31,11 @@ describe "Sequel::Plugins::AssociationProxies" do
|
|
|
31
31
|
|
|
32
32
|
it "should accept block to plugin to specify which methods to proxy to dataset" do
|
|
33
33
|
Item.plugin :association_proxies do |opts|
|
|
34
|
-
opts[:method] == :where || opts[:arguments].
|
|
34
|
+
opts[:method] == :where || opts[:arguments].first.is_a?(Sequel::LiteralString) || opts[:block]
|
|
35
35
|
end
|
|
36
36
|
@i.associations.has_key?(:tags).must_equal false
|
|
37
37
|
@t.where(:a=>1).sql.must_equal "SELECT tags.* FROM tags INNER JOIN items_tags ON (items_tags.tag_id = tags.id) WHERE ((items_tags.item_id = 1) AND (a = 1))"
|
|
38
|
-
@t.filter('a =
|
|
38
|
+
@t.filter(Sequel.lit('a = 1')).sql.must_equal "SELECT tags.* FROM tags INNER JOIN items_tags ON (items_tags.tag_id = tags.id) WHERE ((items_tags.item_id = 1) AND (a = 1))"
|
|
39
39
|
@t.filter{{:a=>1}}.sql.must_equal "SELECT tags.* FROM tags INNER JOIN items_tags ON (items_tags.tag_id = tags.id) WHERE ((items_tags.item_id = 1) AND (a = 1))"
|
|
40
40
|
|
|
41
41
|
@i.associations.has_key?(:tags).must_equal false
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "Dataset#where" do
|
|
4
|
+
before do
|
|
5
|
+
@dataset = Sequel.mock[:test].extension(:auto_literal_strings)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "should work with a string with placeholders and arguments for those placeholders" do
|
|
9
|
+
@dataset.where('price < ? AND id in ?', 100, [1, 2, 3]).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "should not modify passed array with placeholders" do
|
|
13
|
+
a = ['price < ? AND id in ?', 100, 1, 2, 3]
|
|
14
|
+
b = a.dup
|
|
15
|
+
@dataset.where(a)
|
|
16
|
+
b.must_equal a
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should work with strings (custom SQL expressions)" do
|
|
20
|
+
@dataset.where('(a = 1 AND b = 2)').select_sql.must_equal "SELECT * FROM test WHERE ((a = 1 AND b = 2))"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should work with a string with named placeholders and a hash of placeholder value arguments" do
|
|
24
|
+
@dataset.where('price < :price AND id in :ids', :price=>100, :ids=>[1, 2, 3]).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should not modify passed array with named placeholders" do
|
|
28
|
+
a = ['price < :price AND id in :ids', {:price=>100}]
|
|
29
|
+
b = a.dup
|
|
30
|
+
@dataset.where(a)
|
|
31
|
+
b.must_equal a
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should not replace named placeholders that don't exist in the hash" do
|
|
35
|
+
@dataset.where('price < :price AND id in :ids', :price=>100).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id in :ids)"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "should raise an error for a mismatched number of placeholders" do
|
|
39
|
+
proc{@dataset.where('price < ? AND id in ?', 100).select_sql}.must_raise(Sequel::Error)
|
|
40
|
+
proc{@dataset.where('price < ? AND id in ?', 100, [1, 2, 3], 4).select_sql}.must_raise(Sequel::Error)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should handle partial names" do
|
|
44
|
+
@dataset.where('price < :price AND id = :p', :p=>2, :price=>100).select_sql.must_equal "SELECT * FROM test WHERE (price < 100 AND id = 2)"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should handle ::cast syntax when no parameters are supplied" do
|
|
48
|
+
@dataset.where('price::float = 10', {}).select_sql.must_equal "SELECT * FROM test WHERE (price::float = 10)"
|
|
49
|
+
@dataset.where('price::float ? 10', {}).select_sql.must_equal "SELECT * FROM test WHERE (price::float ? 10)"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should affect select, delete and update statements when using strings" do
|
|
53
|
+
@d2 = @dataset.where('region = ?', 'Asia')
|
|
54
|
+
@d2.select_sql.must_equal "SELECT * FROM test WHERE (region = 'Asia')"
|
|
55
|
+
@d2.delete_sql.must_equal "DELETE FROM test WHERE (region = 'Asia')"
|
|
56
|
+
@d2.update_sql(:GDP => 0).must_equal "UPDATE test SET GDP = 0 WHERE (region = 'Asia')"
|
|
57
|
+
|
|
58
|
+
@d3 = @dataset.where("a = 1")
|
|
59
|
+
@d3.select_sql.must_equal "SELECT * FROM test WHERE (a = 1)"
|
|
60
|
+
@d3.delete_sql.must_equal "DELETE FROM test WHERE (a = 1)"
|
|
61
|
+
@d3.update_sql(:GDP => 0).must_equal "UPDATE test SET GDP = 0 WHERE (a = 1)"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "should be composable using AND operator (for scoping) when using strings" do
|
|
65
|
+
@d2 = @dataset.where('region = ?', 'Asia')
|
|
66
|
+
@d2.where('GDP > ?', 1000).select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
|
|
67
|
+
@d2.where(:name => ['Japan', 'China']).select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (name IN ('Japan', 'China')))"
|
|
68
|
+
@d2.where('GDP > ?').select_sql.must_equal "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > ?))"
|
|
69
|
+
|
|
70
|
+
@d3 = @dataset.where("a = 1")
|
|
71
|
+
@d3.where('b = 2').select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (b = 2))"
|
|
72
|
+
@d3.where(:c => 3).select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (c = 3))"
|
|
73
|
+
@d3.where('d = ?', 4).select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (d = 4))"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should be composable using AND operator (for scoping) with block and string" do
|
|
77
|
+
@dataset.where("a = 1").where{e < 5}.select_sql.must_equal "SELECT * FROM test WHERE ((a = 1) AND (e < 5))"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe "Dataset #first and #last" do
|
|
82
|
+
before do
|
|
83
|
+
@d = Sequel.mock(:fetch=>proc{|s| {:s=>s}})[:test].extension(:auto_literal_strings)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should combine block and standard argument filters if argument is not an Integer" do
|
|
87
|
+
ds = @d.order(:name).freeze
|
|
88
|
+
5.times do
|
|
89
|
+
@d.first('y = 25'){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 25) AND (z > 26)) LIMIT 1')
|
|
90
|
+
ds.last('y = 16'){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 16) AND (z > 26)) ORDER BY name DESC LIMIT 1')
|
|
91
|
+
@d.first('y = ?', 25){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 25) AND (z > 26)) LIMIT 1')
|
|
92
|
+
ds.last('y = ?', 16){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 16) AND (z > 26)) ORDER BY name DESC LIMIT 1')
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "Dataset#exclude" do
|
|
98
|
+
before do
|
|
99
|
+
@dataset = Sequel.mock.dataset.from(:test).extension(:auto_literal_strings)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should parenthesize a single string condition correctly" do
|
|
103
|
+
@dataset.exclude("region = 'Asia' AND name = 'Japan'").select_sql.must_equal "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "should parenthesize an array condition correctly" do
|
|
107
|
+
@dataset.exclude('region = ? AND name = ?', 'Asia', 'Japan').select_sql.must_equal "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe "Dataset#and" do
|
|
112
|
+
before do
|
|
113
|
+
@dataset = Sequel.mock.dataset.from(:test).extension(:auto_literal_strings)
|
|
114
|
+
@d1 = @dataset.where(:x => 1)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "should accept string filters with placeholders" do
|
|
118
|
+
@d1.and('y > ?', 2).sql.must_equal 'SELECT * FROM test WHERE ((x = 1) AND (y > 2))'
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
describe "Dataset#or" do
|
|
123
|
+
before do
|
|
124
|
+
@dataset = Sequel.mock.dataset.from(:test).extension(:auto_literal_strings)
|
|
125
|
+
@d1 = @dataset.where(:x => 1)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "should accept string filters" do
|
|
129
|
+
@d1.or('y > ?', 2).sql.must_equal 'SELECT * FROM test WHERE ((x = 1) OR (y > 2))'
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
describe "Dataset#having" do
|
|
134
|
+
before do
|
|
135
|
+
@dataset = Sequel.mock.dataset.from(:test).extension(:auto_literal_strings)
|
|
136
|
+
@grouped = @dataset.group(:region).select(:region, Sequel.function(:sum, :population), Sequel.function(:avg, :gdp))
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "should handle string arguments" do
|
|
140
|
+
@grouped.having('sum(population) > 10').select_sql.must_equal "SELECT region, sum(population), avg(gdp) FROM test GROUP BY region HAVING (sum(population) > 10)"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
describe "Dataset#join_table" do
|
|
145
|
+
before do
|
|
146
|
+
@d = Sequel.mock.dataset.from(:items).with_quote_identifiers(true).extension(:auto_literal_strings)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "should support using a string as the join condition" do
|
|
150
|
+
@d.join(:categories, "c.item_id = items.id", :table_alias=>:c).sql.must_equal 'SELECT * FROM "items" INNER JOIN "categories" AS "c" ON (c.item_id = items.id)'
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
describe "Dataset prepared statements and bound variables " do
|
|
155
|
+
before do
|
|
156
|
+
@db = Sequel.mock
|
|
157
|
+
@ds = @db[:items].with_extend{def insert_select_sql(*v) "#{insert_sql(*v)} RETURNING *" end}.extension(:auto_literal_strings)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "should handle literal strings" do
|
|
161
|
+
@ds.filter("num = ?", :$n).call(:select, :n=>1)
|
|
162
|
+
@db.sqls.must_equal ['SELECT * FROM items WHERE (num = 1)']
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "should handle subselects with strings" do
|
|
166
|
+
@ds.filter(:$b).filter(:num=>@ds.select(:num).filter("num = ?", :$n)).call(:select, :n=>1, :b=>0)
|
|
167
|
+
@db.sqls.must_equal ['SELECT * FROM items WHERE (0 AND (num IN (SELECT num FROM items WHERE (num = 1))))']
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
describe "Dataset#update_sql" do
|
|
172
|
+
before do
|
|
173
|
+
@ds = Sequel.mock.dataset.from(:items).extension(:auto_literal_strings)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "should accept strings" do
|
|
177
|
+
@ds.update_sql("a = b").must_equal "UPDATE items SET a = b"
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "should accept literal strings" do
|
|
181
|
+
@ds.update_sql(Sequel.lit("a = b")).must_equal "UPDATE items SET a = b"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "should accept hash" do
|
|
185
|
+
@ds.update_sql(:c => 'd').must_equal "UPDATE items SET c = 'd'"
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
describe "Dataset::PlaceholderLiteralizer" do
|
|
190
|
+
before do
|
|
191
|
+
@c = Sequel::Dataset::PlaceholderLiteralizer
|
|
192
|
+
@db = Sequel.mock
|
|
193
|
+
@ds = @db[:items].extension(:auto_literal_strings)
|
|
194
|
+
@h = {:id=>1}
|
|
195
|
+
@ds.db.fetch = @h
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it "should handle calls with a placeholders used as filter arguments" do
|
|
199
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(pl.arg)}
|
|
200
|
+
loader.first(:id=>1).must_equal @h
|
|
201
|
+
loader.first(Sequel.expr{a(b)}).must_equal @h
|
|
202
|
+
loader.first("a = 1").must_equal @h
|
|
203
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE (id = 1)", "SELECT * FROM items WHERE a(b)", "SELECT * FROM items WHERE (a = 1)"]
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it "should handle calls with a placeholder used multiple times in different capacities" do
|
|
207
|
+
loader = @c.loader(@ds){|pl, ds| a = pl.arg; ds.where(a).where(:b=>a)}
|
|
208
|
+
loader.first("a = 1").must_equal @h
|
|
209
|
+
loader.first(["a = ?", 2]).must_equal @h
|
|
210
|
+
@db.sqls.must_equal ["SELECT * FROM items WHERE ((a = 1) AND (b = 'a = 1'))", "SELECT * FROM items WHERE ((a = 2) AND (b IN ('a = ?', 2)))"]
|
|
211
|
+
end
|
|
212
|
+
end
|
|
@@ -72,6 +72,7 @@ describe Sequel::Model, ".restricted_columns " do
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
it "should have allowed take precedence over restricted" do
|
|
75
|
+
@c.plugin :whitelist_security
|
|
75
76
|
@c.set_allowed_columns :x, :y
|
|
76
77
|
@c.set_restricted_columns :y, :z
|
|
77
78
|
i = @c.new(:x => 1, :y => 2, :z => 3)
|
|
@@ -30,6 +30,1004 @@ describe "class_table_inheritance plugin" do
|
|
|
30
30
|
end
|
|
31
31
|
plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}
|
|
32
32
|
end
|
|
33
|
+
deprecated do
|
|
34
|
+
class ::Manager < Employee
|
|
35
|
+
one_to_many :staff_members, :class=>:Staff
|
|
36
|
+
end
|
|
37
|
+
class ::Executive < Manager
|
|
38
|
+
end
|
|
39
|
+
class ::Ceo < Executive
|
|
40
|
+
end
|
|
41
|
+
class ::Staff < Employee
|
|
42
|
+
many_to_one :manager
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
@ds = Employee.dataset
|
|
46
|
+
@db.sqls
|
|
47
|
+
end
|
|
48
|
+
after do
|
|
49
|
+
Object.send(:remove_const, :Ceo)
|
|
50
|
+
Object.send(:remove_const, :Executive)
|
|
51
|
+
Object.send(:remove_const, :Manager)
|
|
52
|
+
Object.send(:remove_const, :Staff)
|
|
53
|
+
Object.send(:remove_const, :Employee)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
deprecated "should freeze CTI information when freezing model class" do
|
|
57
|
+
Employee.freeze
|
|
58
|
+
Employee.cti_models.frozen?.must_equal true
|
|
59
|
+
Employee.cti_tables.frozen?.must_equal true
|
|
60
|
+
Employee.cti_instance_dataset.frozen?.must_equal true
|
|
61
|
+
Employee.cti_table_columns.frozen?.must_equal true
|
|
62
|
+
Employee.cti_table_map.frozen?.must_equal true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
deprecated "should not attempt to use prepared statements" do
|
|
66
|
+
Manager.plugin :prepared_statements
|
|
67
|
+
Manager[1]
|
|
68
|
+
@db.sqls.must_equal ["SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id = 1) LIMIT 1"]
|
|
69
|
+
Manager.load(:id=>1, :kind=>'Manager', :num_staff=>2).save
|
|
70
|
+
@db.sqls.must_equal ["UPDATE employees SET kind = 'Manager' WHERE (id = 1)", "UPDATE managers SET num_staff = 2 WHERE (id = 1)"]
|
|
71
|
+
@db.fetch = {:id=>1, :kind=>'Manager', :num_staff=>2}
|
|
72
|
+
Manager.load(:id=>1, :kind=>'Manager', :num_staff=>2).refresh
|
|
73
|
+
@db.sqls.must_equal ["SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id = 1) LIMIT 1"]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
deprecated "#cti_base_model should be the model that loaded the plugin" do
|
|
77
|
+
Executive.cti_base_model.must_equal Employee
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
deprecated "#cti_columns should be a mapping of table names to columns" do
|
|
81
|
+
Executive.cti_columns.must_equal(:employees=>[:id, :name, :kind], :managers=>[:id, :num_staff], :executives=>[:id, :num_managers])
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
deprecated "should have simple_table = nil for all subclasses" do
|
|
85
|
+
Manager.simple_table.must_be_nil
|
|
86
|
+
Executive.simple_table.must_be_nil
|
|
87
|
+
Ceo.simple_table.must_be_nil
|
|
88
|
+
Staff.simple_table.must_be_nil
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
deprecated "should have working row_proc if using set_dataset in subclass to remove columns" do
|
|
92
|
+
Manager.set_dataset(Manager.dataset.select(*(Manager.columns - [:blah])))
|
|
93
|
+
Manager.dataset = Manager.dataset.with_fetch(:id=>1, :kind=>'Ceo')
|
|
94
|
+
Manager[1].must_equal Ceo.load(:id=>1, :kind=>'Ceo')
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
deprecated "should use a joined dataset in subclasses" do
|
|
98
|
+
Employee.dataset.sql.must_equal 'SELECT * FROM employees'
|
|
99
|
+
Manager.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)'
|
|
100
|
+
Executive.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)'
|
|
101
|
+
Ceo.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (employees.kind IN (\'Ceo\'))'
|
|
102
|
+
Staff.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)'
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
deprecated "should return rows with the correct class based on the polymorphic_key value" do
|
|
106
|
+
@ds.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}]).all.collect{|x| x.class}.must_equal [Employee, Manager, Executive, Ceo, Staff]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
deprecated "should return rows with the correct class based on the polymorphic_key value for subclasses" do
|
|
110
|
+
Manager.dataset.with_fetch([{:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}]).all.collect{|x| x.class}.must_equal [Manager, Executive, Ceo]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
deprecated "should have refresh return all columns in subclass after loading from superclass" do
|
|
114
|
+
Employee.dataset = Employee.dataset.with_fetch([{:id=>1, :name=>'A', :kind=>'Ceo'}])
|
|
115
|
+
Ceo.dataset = Ceo.dataset.with_fetch([{:id=>1, :name=>'A', :kind=>'Ceo', :num_staff=>3, :num_managers=>2}])
|
|
116
|
+
a = Employee.first
|
|
117
|
+
a.class.must_equal Ceo
|
|
118
|
+
a.values.must_equal(:id=>1, :name=>'A', :kind=>'Ceo')
|
|
119
|
+
a.refresh.values.must_equal(:id=>1, :name=>'A', :kind=>'Ceo', :num_staff=>3, :num_managers=>2)
|
|
120
|
+
@db.sqls.must_equal ["SELECT * FROM employees LIMIT 1",
|
|
121
|
+
"SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE ((employees.kind IN ('Ceo')) AND (executives.id = 1)) LIMIT 1"]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
deprecated "should return rows with the current class if cti_key is nil" do
|
|
125
|
+
Employee.plugin(:class_table_inheritance)
|
|
126
|
+
Employee.dataset.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}]).all.map{|x| x.class}.must_equal [Employee, Employee, Employee, Employee, Employee]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
deprecated "should return rows with the current class if cti_key is nil in subclasses" do
|
|
130
|
+
Employee.plugin(:class_table_inheritance)
|
|
131
|
+
Object.send(:remove_const, :Executive)
|
|
132
|
+
Object.send(:remove_const, :Manager)
|
|
133
|
+
class ::Manager < Employee; end
|
|
134
|
+
class ::Executive < Manager; end
|
|
135
|
+
Manager.dataset.with_fetch([{:kind=>'Manager'}, {:kind=>'Executive'}]).all.map{|x| x.class}.must_equal [Manager, Manager]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
deprecated "should handle a model map with integer values" do
|
|
139
|
+
Employee.plugin(:class_table_inheritance, :key=>:kind, :model_map=>{0=>:Employee, 1=>:Manager, 2=>:Executive, 3=>:Ceo})
|
|
140
|
+
Object.send(:remove_const, :Ceo)
|
|
141
|
+
Object.send(:remove_const, :Executive)
|
|
142
|
+
Object.send(:remove_const, :Manager)
|
|
143
|
+
class ::Manager < Employee; end
|
|
144
|
+
class ::Executive < Manager; end
|
|
145
|
+
class ::Ceo < Executive; end
|
|
146
|
+
Employee.dataset = Employee.dataset.with_fetch([{:kind=>nil},{:kind=>0},{:kind=>1}, {:kind=>2}, {:kind=>3}])
|
|
147
|
+
Employee.all.collect{|x| x.class}.must_equal [Employee, Employee, Manager, Executive, Ceo]
|
|
148
|
+
Manager.dataset = Manager.dataset.with_fetch([{:kind=>nil},{:kind=>0},{:kind=>1}, {:kind=>2}, {:kind=>3}])
|
|
149
|
+
Manager.all.collect{|x| x.class}.must_equal [Manager, Employee, Manager, Executive, Ceo]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
deprecated "should fallback to the main class if the given class does not exist" do
|
|
153
|
+
@ds.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Blah'}, {:kind=>'Staff'}]).all.map{|x| x.class}.must_equal [Employee, Manager, Employee, Staff]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
deprecated "should fallback to the main class if the given class does not exist in subclasses" do
|
|
157
|
+
Manager.dataset.with_fetch([{:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Blah'}]).all.map{|x| x.class}.must_equal [Manager, Executive, Ceo, Manager]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
deprecated "should sets the model class name for the key when creating new parent class records" do
|
|
161
|
+
Employee.create
|
|
162
|
+
@db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Employee')"]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
deprecated "should sets the model class name for the key when creating new subclass records" do
|
|
166
|
+
Ceo.create
|
|
167
|
+
@db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Ceo')",
|
|
168
|
+
"INSERT INTO managers (id) VALUES (1)",
|
|
169
|
+
"INSERT INTO executives (id) VALUES (1)"]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
deprecated "should ignore existing cti_key value when creating new records" do
|
|
173
|
+
Employee.create(:kind=>'Manager')
|
|
174
|
+
@db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Employee')"]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
deprecated "should ignore existing cti_key value in subclasses" do
|
|
178
|
+
Manager.create(:kind=>'Executive')
|
|
179
|
+
@db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Manager')",
|
|
180
|
+
"INSERT INTO managers (id) VALUES (1)"]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
deprecated "should handle validations on the type column field" do
|
|
184
|
+
o = Employee.new
|
|
185
|
+
def o.validate
|
|
186
|
+
errors.add(:kind, 'not present') unless kind
|
|
187
|
+
end
|
|
188
|
+
o.valid?.must_equal true
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
deprecated "should set the type column field even when not validating" do
|
|
192
|
+
Employee.new.save(:validate=>false)
|
|
193
|
+
@db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Employee')"]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
deprecated "should allow specifying a map of names to tables to override implicit mapping" do
|
|
197
|
+
Manager.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)'
|
|
198
|
+
Staff.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)'
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
deprecated "should lazily load attributes for columns in subclass tables" do
|
|
202
|
+
Manager.dataset = Manager.dataset.with_fetch(:id=>1, :name=>'J', :kind=>'Ceo', :num_staff=>2)
|
|
203
|
+
m = Manager[1]
|
|
204
|
+
@db.sqls.must_equal ['SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id = 1) LIMIT 1']
|
|
205
|
+
@db.fetch = {:num_managers=>3}
|
|
206
|
+
m.must_be_kind_of Ceo
|
|
207
|
+
m.num_managers.must_equal 3
|
|
208
|
+
@db.sqls.must_equal ['SELECT executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (executives.id = 1) LIMIT 1']
|
|
209
|
+
m.values.must_equal(:id=>1, :name=>'J', :kind=>'Ceo', :num_staff=>2, :num_managers=>3)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
deprecated "should lazily load columns in middle classes correctly when loaded from parent class" do
|
|
213
|
+
Employee.dataset = Employee.dataset.with_fetch(:id=>1, :kind=>'Ceo')
|
|
214
|
+
@db.fetch = [[:num_staff=>2]]
|
|
215
|
+
e = Employee[1]
|
|
216
|
+
e.must_be_kind_of(Ceo)
|
|
217
|
+
@db.sqls.must_equal ["SELECT * FROM employees WHERE (id = 1) LIMIT 1"]
|
|
218
|
+
e.num_staff.must_equal 2
|
|
219
|
+
@db.sqls.must_equal ["SELECT managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id = 1) LIMIT 1"]
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
deprecated "should eagerly load lazily columns in subclasses when loaded from parent class" do
|
|
223
|
+
Employee.dataset = Employee.dataset.with_fetch(:id=>1, :kind=>'Ceo')
|
|
224
|
+
@db.fetch = [[{:id=>1, :num_staff=>2}], [{:id=>1, :num_managers=>3}]]
|
|
225
|
+
e = Employee.all.first
|
|
226
|
+
e.must_be_kind_of(Ceo)
|
|
227
|
+
@db.sqls.must_equal ["SELECT * FROM employees"]
|
|
228
|
+
e.num_staff.must_equal 2
|
|
229
|
+
@db.sqls.must_equal ["SELECT managers.id, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id IN (1))"]
|
|
230
|
+
e.num_managers.must_equal 3
|
|
231
|
+
@db.sqls.must_equal ['SELECT executives.id, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (executives.id IN (1))']
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
deprecated "should include schema for columns for tables for ancestor classes" do
|
|
235
|
+
Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string})
|
|
236
|
+
Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer})
|
|
237
|
+
Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer}, :num_managers=>{:type=>:integer})
|
|
238
|
+
Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :manager_id=>{:type=>:integer})
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
deprecated "should use the correct primary key (which should have the same name in all subclasses)" do
|
|
242
|
+
[Employee, Manager, Executive, Ceo, Staff].each{|c| c.primary_key.must_equal :id}
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
deprecated "should have table_name return the table name of the most specific table" do
|
|
246
|
+
Employee.table_name.must_equal :employees
|
|
247
|
+
Manager.table_name.must_equal :managers
|
|
248
|
+
Executive.table_name.must_equal :executives
|
|
249
|
+
Ceo.table_name.must_equal :executives
|
|
250
|
+
Staff.table_name.must_equal :staff
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
deprecated "should delete the correct rows from all tables when deleting" do
|
|
254
|
+
Ceo.load(:id=>1).delete
|
|
255
|
+
@db.sqls.must_equal ["DELETE FROM executives WHERE (id = 1)", "DELETE FROM managers WHERE (id = 1)", "DELETE FROM employees WHERE (id = 1)"]
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
deprecated "should not allow deletion of frozen object" do
|
|
259
|
+
o = Ceo.load(:id=>1)
|
|
260
|
+
o.freeze
|
|
261
|
+
proc{o.delete}.must_raise(Sequel::Error)
|
|
262
|
+
@db.sqls.must_equal []
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
deprecated "should insert the correct rows into all tables when inserting" do
|
|
266
|
+
Ceo.create(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
267
|
+
sqls = @db.sqls
|
|
268
|
+
sqls.length.must_equal 3
|
|
269
|
+
sqls[0].must_match(/INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Ceo)', '(E|Ceo)'\)/)
|
|
270
|
+
sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \([12], [12]\)/)
|
|
271
|
+
sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([13], [13]\)/)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
deprecated "should insert the correct rows into all tables when inserting when insert_select is supported" do
|
|
275
|
+
[Executive, Manager, Employee].each do |klass|
|
|
276
|
+
klass.instance_variable_set(:@cti_instance_dataset, klass.cti_instance_dataset.with_extend do
|
|
277
|
+
def supports_insert_select?; true; end
|
|
278
|
+
def insert_select(v)
|
|
279
|
+
db.run(insert_sql(v) + " RETURNING *")
|
|
280
|
+
v.merge(:id=>1)
|
|
281
|
+
end
|
|
282
|
+
end)
|
|
283
|
+
end
|
|
284
|
+
Ceo.create(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
285
|
+
sqls = @db.sqls
|
|
286
|
+
sqls.length.must_equal 3
|
|
287
|
+
sqls[0].must_match(/INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Ceo)', '(E|Ceo)'\) RETURNING \*/)
|
|
288
|
+
sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \([12], [12]\) RETURNING \*/)
|
|
289
|
+
sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([13], [13]\) RETURNING \*/)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
deprecated "should insert the correct rows into all tables with a given primary key" do
|
|
293
|
+
e = Ceo.new(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
294
|
+
e.id = 2
|
|
295
|
+
e.save
|
|
296
|
+
sqls = @db.sqls
|
|
297
|
+
sqls.length.must_equal 3
|
|
298
|
+
sqls[0].must_match(/INSERT INTO employees \((name|kind|id), (name|kind|id), (name|kind|id)\) VALUES \(('E'|'Ceo'|2), ('E'|'Ceo'|2), ('E'|'Ceo'|2)\)/)
|
|
299
|
+
sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \(2, 2\)/)
|
|
300
|
+
sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([23], [23]\)/)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
deprecated "should update the correct rows in all tables when updating" do
|
|
304
|
+
Ceo.load(:id=>2).update(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
305
|
+
@db.sqls.must_equal ["UPDATE employees SET name = 'E' WHERE (id = 2)", "UPDATE managers SET num_staff = 2 WHERE (id = 2)", "UPDATE executives SET num_managers = 3 WHERE (id = 2)"]
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
deprecated "should handle many_to_one relationships correctly" do
|
|
309
|
+
Manager.dataset = Manager.dataset.with_fetch(:id=>3, :name=>'E', :kind=>'Ceo', :num_managers=>3)
|
|
310
|
+
Staff.load(:manager_id=>3).manager.must_equal Ceo.load(:id=>3, :name=>'E', :kind=>'Ceo', :num_managers=>3)
|
|
311
|
+
@db.sqls.must_equal ['SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id = 3) LIMIT 1']
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
deprecated "should handle one_to_many relationships correctly" do
|
|
315
|
+
Staff.dataset = Staff.dataset.with_fetch(:id=>1, :name=>'S', :kind=>'Staff', :manager_id=>3)
|
|
316
|
+
Ceo.load(:id=>3).staff_members.must_equal [Staff.load(:id=>1, :name=>'S', :kind=>'Staff', :manager_id=>3)]
|
|
317
|
+
@db.sqls.must_equal ['SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id) WHERE (staff.manager_id = 3)']
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
describe "class_table_inheritance plugin without sti_key" do
|
|
322
|
+
before do
|
|
323
|
+
@db = Sequel.mock(:autoid=>proc{|sql| 1})
|
|
324
|
+
def @db.supports_schema_parsing?() true end
|
|
325
|
+
def @db.schema(table, opts={})
|
|
326
|
+
{:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}]],
|
|
327
|
+
:managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}]],
|
|
328
|
+
:executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}]],
|
|
329
|
+
:staff=>[[:id, {:type=>:integer}], [:manager_id, {:type=>:integer}]],
|
|
330
|
+
}[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
|
|
331
|
+
end
|
|
332
|
+
@db.extend_datasets do
|
|
333
|
+
def columns
|
|
334
|
+
{[:employees]=>[:id, :name],
|
|
335
|
+
[:managers]=>[:id, :num_staff],
|
|
336
|
+
[:executives]=>[:id, :num_managers],
|
|
337
|
+
[:staff]=>[:id, :manager_id],
|
|
338
|
+
[:employees, :managers]=>[:id, :name, :num_staff],
|
|
339
|
+
[:employees, :managers, :executives]=>[:id, :name, :num_staff, :num_managers],
|
|
340
|
+
[:employees, :staff]=>[:id, :name, :manager_id],
|
|
341
|
+
}[opts[:from] + (opts[:join] || []).map{|x| x.table}]
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
class ::Employee < Sequel::Model(@db)
|
|
345
|
+
def _save_refresh; @values[:id] = 1 end
|
|
346
|
+
def self.columns
|
|
347
|
+
dataset.columns
|
|
348
|
+
end
|
|
349
|
+
plugin :class_table_inheritance, :table_map=>{:Staff=>:staff}
|
|
350
|
+
end
|
|
351
|
+
deprecated do
|
|
352
|
+
class ::Manager < Employee
|
|
353
|
+
one_to_many :staff_members, :class=>:Staff
|
|
354
|
+
end
|
|
355
|
+
class ::Executive < Manager
|
|
356
|
+
end
|
|
357
|
+
class ::Staff < Employee
|
|
358
|
+
many_to_one :manager
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
@ds = Employee.dataset
|
|
362
|
+
@db.sqls
|
|
363
|
+
end
|
|
364
|
+
after do
|
|
365
|
+
Object.send(:remove_const, :Executive)
|
|
366
|
+
Object.send(:remove_const, :Manager)
|
|
367
|
+
Object.send(:remove_const, :Staff)
|
|
368
|
+
Object.send(:remove_const, :Employee)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
deprecated "should have simple_table = nil for all subclasses" do
|
|
372
|
+
Manager.simple_table.must_be_nil
|
|
373
|
+
Executive.simple_table.must_be_nil
|
|
374
|
+
Staff.simple_table.must_be_nil
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
deprecated "should have working row_proc if using set_dataset in subclass to remove columns" do
|
|
378
|
+
Manager.set_dataset(Manager.dataset.select(*(Manager.columns - [:blah])))
|
|
379
|
+
Manager.dataset = Manager.dataset.with_fetch(:id=>1)
|
|
380
|
+
Manager[1].must_equal Manager.load(:id=>1)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
deprecated "should use a dataset in subclasses" do
|
|
384
|
+
Employee.dataset.sql.must_equal 'SELECT * FROM employees'
|
|
385
|
+
Manager.dataset.sql.must_equal 'SELECT employees.id, employees.name, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)'
|
|
386
|
+
Executive.dataset.sql.must_equal 'SELECT employees.id, employees.name, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)'
|
|
387
|
+
Staff.dataset.sql.must_equal 'SELECT employees.id, employees.name, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)'
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
deprecated "should return rows with the current class if cti_key is nil" do
|
|
391
|
+
Employee.plugin(:class_table_inheritance)
|
|
392
|
+
Employee.dataset = Employee.dataset.with_fetch([{}])
|
|
393
|
+
Employee.first.class.must_equal Employee
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
deprecated "should include schema for columns for tables for ancestor classes" do
|
|
398
|
+
Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string})
|
|
399
|
+
Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :num_staff=>{:type=>:integer})
|
|
400
|
+
Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :num_staff=>{:type=>:integer}, :num_managers=>{:type=>:integer})
|
|
401
|
+
Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :manager_id=>{:type=>:integer})
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
deprecated "should use the correct primary key (which should have the same name in all subclasses)" do
|
|
405
|
+
[Employee, Manager, Executive, Staff].each{|c| c.primary_key.must_equal :id}
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
deprecated "should have table_name return the table name of the most specific table" do
|
|
409
|
+
Employee.table_name.must_equal :employees
|
|
410
|
+
Manager.table_name.must_equal :managers
|
|
411
|
+
Executive.table_name.must_equal :executives
|
|
412
|
+
Staff.table_name.must_equal :staff
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
deprecated "should delete the correct rows from all tables when deleting" do
|
|
416
|
+
Executive.load(:id=>1).delete
|
|
417
|
+
@db.sqls.must_equal ["DELETE FROM executives WHERE (id = 1)", "DELETE FROM managers WHERE (id = 1)", "DELETE FROM employees WHERE (id = 1)"]
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
deprecated "should not allow deletion of frozen object" do
|
|
421
|
+
o = Executive.load(:id=>1)
|
|
422
|
+
o.freeze
|
|
423
|
+
proc{o.delete}.must_raise(Sequel::Error)
|
|
424
|
+
@db.sqls.must_equal []
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
deprecated "should insert the correct rows into all tables when inserting" do
|
|
428
|
+
Executive.create(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
429
|
+
sqls = @db.sqls
|
|
430
|
+
sqls.length.must_equal 3
|
|
431
|
+
sqls[0].must_match(/INSERT INTO employees \(name\) VALUES \('E'\)/)
|
|
432
|
+
sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \([12], [12]\)/)
|
|
433
|
+
sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([13], [13]\)/)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
deprecated "should insert the correct rows into all tables with a given primary key" do
|
|
437
|
+
e = Executive.new(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
438
|
+
e.id = 2
|
|
439
|
+
e.save
|
|
440
|
+
sqls = @db.sqls
|
|
441
|
+
sqls.length.must_equal 3
|
|
442
|
+
sqls[0].must_match(/INSERT INTO employees \((name|id), (name|id)\) VALUES \(('E'|2), ('E'|2)\)/)
|
|
443
|
+
sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \(2, 2\)/)
|
|
444
|
+
sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([23], [23]\)/)
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
deprecated "should update the correct rows in all tables when updating" do
|
|
448
|
+
Executive.load(:id=>2).update(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
449
|
+
@db.sqls.must_equal ["UPDATE employees SET name = 'E' WHERE (id = 2)", "UPDATE managers SET num_staff = 2 WHERE (id = 2)", "UPDATE executives SET num_managers = 3 WHERE (id = 2)"]
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
deprecated "should handle many_to_one relationships correctly" do
|
|
453
|
+
Manager.dataset = Manager.dataset.with_fetch(:id=>3, :name=>'E', :num_staff=>3)
|
|
454
|
+
Staff.load(:manager_id=>3).manager.must_equal Manager.load(:id=>3, :name=>'E', :num_staff=>3)
|
|
455
|
+
@db.sqls.must_equal ['SELECT employees.id, employees.name, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id = 3) LIMIT 1']
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
deprecated "should handle one_to_many relationships correctly" do
|
|
459
|
+
Staff.dataset = Staff.dataset.with_fetch(:id=>1, :name=>'S', :manager_id=>3)
|
|
460
|
+
Executive.load(:id=>3).staff_members.must_equal [Staff.load(:id=>1, :name=>'S', :manager_id=>3)]
|
|
461
|
+
@db.sqls.must_equal ['SELECT employees.id, employees.name, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id) WHERE (staff.manager_id = 3)']
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
describe "class_table_inheritance plugin with duplicate columns" do
|
|
466
|
+
before do
|
|
467
|
+
@db = Sequel.mock(:autoid=>proc{|sql| 1})
|
|
468
|
+
def @db.supports_schema_parsing?() true end
|
|
469
|
+
def @db.schema(table, opts={})
|
|
470
|
+
{:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]],
|
|
471
|
+
:managers=>[[:id, {:type=>:integer}], [:name, {:type=>:string}]],
|
|
472
|
+
}[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
|
|
473
|
+
end
|
|
474
|
+
@db.extend_datasets do
|
|
475
|
+
def columns
|
|
476
|
+
{[:employees]=>[:id, :name, :kind],
|
|
477
|
+
[:managers]=>[:id, :name],
|
|
478
|
+
}[opts[:from] + (opts[:join] || []).map{|x| x.table}]
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
class ::Employee < Sequel::Model(@db)
|
|
482
|
+
def _save_refresh; @values[:id] = 1 end
|
|
483
|
+
def self.columns
|
|
484
|
+
dataset.columns
|
|
485
|
+
end
|
|
486
|
+
plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}
|
|
487
|
+
end
|
|
488
|
+
deprecated do
|
|
489
|
+
class ::Manager < Employee; end
|
|
490
|
+
end
|
|
491
|
+
@ds = Employee.dataset
|
|
492
|
+
@db.sqls
|
|
493
|
+
end
|
|
494
|
+
after do
|
|
495
|
+
Object.send(:remove_const, :Manager)
|
|
496
|
+
Object.send(:remove_const, :Employee)
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
deprecated "should select names from both tables" do
|
|
500
|
+
Manager.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, managers.name FROM employees INNER JOIN managers ON (managers.id = employees.id)'
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
describe "class_table_inheritance plugin with :alias option" do
|
|
505
|
+
before do
|
|
506
|
+
@db = Sequel.mock(:autoid=>proc{|sql| 1})
|
|
507
|
+
def @db.supports_schema_parsing?() true end
|
|
508
|
+
def @db.schema(table, opts={})
|
|
509
|
+
{:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]],
|
|
510
|
+
:managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}]],
|
|
511
|
+
:executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}]],
|
|
512
|
+
:staff=>[[:id, {:type=>:integer}], [:manager_id, {:type=>:integer}]],
|
|
513
|
+
}[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
|
|
514
|
+
end
|
|
515
|
+
@db.extend_datasets do
|
|
516
|
+
def columns
|
|
517
|
+
{[:employees]=>[:id, :name, :kind],
|
|
518
|
+
[:managers]=>[:id, :num_staff],
|
|
519
|
+
[:executives]=>[:id, :num_managers],
|
|
520
|
+
[:staff]=>[:id, :manager_id],
|
|
521
|
+
[:employees, :managers]=>[:id, :name, :kind, :num_staff],
|
|
522
|
+
[:employees, :managers, :executives]=>[:id, :name, :kind, :num_staff, :num_managers],
|
|
523
|
+
[:employees, :staff]=>[:id, :name, :kind, :manager_id],
|
|
524
|
+
}[opts[:from] + (opts[:join] || []).map{|x| x.table}]
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
class ::Employee < Sequel::Model(@db)
|
|
528
|
+
def _save_refresh; @values[:id] = 1 end
|
|
529
|
+
def self.columns
|
|
530
|
+
dataset.columns || dataset.opts[:from].first.expression.columns
|
|
531
|
+
end
|
|
532
|
+
plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}, :alias=>:employees
|
|
533
|
+
end
|
|
534
|
+
class ::Manager < Employee
|
|
535
|
+
one_to_many :staff_members, :class=>:Staff
|
|
536
|
+
end
|
|
537
|
+
class ::Executive < Manager
|
|
538
|
+
end
|
|
539
|
+
class ::Ceo < Executive
|
|
540
|
+
end
|
|
541
|
+
class ::Staff < Employee
|
|
542
|
+
many_to_one :manager
|
|
543
|
+
end
|
|
544
|
+
@ds = Employee.dataset
|
|
545
|
+
@db.sqls
|
|
546
|
+
end
|
|
547
|
+
after do
|
|
548
|
+
Object.send(:remove_const, :Ceo)
|
|
549
|
+
Object.send(:remove_const, :Executive)
|
|
550
|
+
Object.send(:remove_const, :Manager)
|
|
551
|
+
Object.send(:remove_const, :Staff)
|
|
552
|
+
Object.send(:remove_const, :Employee)
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
it "should freeze CTI information when freezing model class" do
|
|
556
|
+
Employee.freeze
|
|
557
|
+
Employee.cti_models.frozen?.must_equal true
|
|
558
|
+
Employee.cti_tables.frozen?.must_equal true
|
|
559
|
+
Employee.cti_instance_dataset.frozen?.must_equal true
|
|
560
|
+
Employee.cti_table_columns.frozen?.must_equal true
|
|
561
|
+
Employee.cti_table_map.frozen?.must_equal true
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
it "should not attempt to use prepared statements" do
|
|
565
|
+
Manager.plugin :prepared_statements
|
|
566
|
+
Manager[1]
|
|
567
|
+
@db.sqls.must_equal ["SELECT id, name, kind, num_staff FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (id = 1) LIMIT 1"]
|
|
568
|
+
Manager.load(:id=>1, :kind=>'Manager', :num_staff=>2).save
|
|
569
|
+
@db.sqls.must_equal ["UPDATE employees SET kind = 'Manager' WHERE (id = 1)", "UPDATE managers SET num_staff = 2 WHERE (id = 1)"]
|
|
570
|
+
@db.fetch = {:id=>1, :kind=>'Manager', :num_staff=>2}
|
|
571
|
+
Manager.load(:id=>1, :kind=>'Manager', :num_staff=>2).refresh
|
|
572
|
+
@db.sqls.must_equal ["SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (id = 1) LIMIT 1"]
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
it "#cti_base_model should be the model that loaded the plugin" do
|
|
576
|
+
Executive.cti_base_model.must_equal Employee
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
it "#cti_columns should be a mapping of table names to columns" do
|
|
580
|
+
Executive.cti_columns.must_equal(:employees=>[:id, :name, :kind], :managers=>[:id, :num_staff], :executives=>[:id, :num_managers])
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
it "should have simple_table = nil for all subclasses" do
|
|
584
|
+
Manager.simple_table.must_be_nil
|
|
585
|
+
Executive.simple_table.must_be_nil
|
|
586
|
+
Ceo.simple_table.must_be_nil
|
|
587
|
+
Staff.simple_table.must_be_nil
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
it "should have working row_proc if using set_dataset in subclass to remove columns" do
|
|
591
|
+
Manager.set_dataset(Manager.dataset.select(*(Manager.columns - [:blah])))
|
|
592
|
+
Manager.dataset = Manager.dataset.with_fetch(:id=>1, :kind=>'Ceo')
|
|
593
|
+
Manager[1].must_equal Ceo.load(:id=>1, :kind=>'Ceo')
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
it "should use a subquery in subclasses" do
|
|
597
|
+
Employee.dataset.sql.must_equal 'SELECT * FROM employees'
|
|
598
|
+
Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees'
|
|
599
|
+
Executive.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)) AS employees'
|
|
600
|
+
Ceo.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (employees.kind IN (\'Ceo\'))) AS employees'
|
|
601
|
+
Staff.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees'
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
it "should return rows with the correct class based on the polymorphic_key value" do
|
|
605
|
+
@ds.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}]).all.collect{|x| x.class}.must_equal [Employee, Manager, Executive, Ceo, Staff]
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
it "should return rows with the correct class based on the polymorphic_key value for subclasses" do
|
|
609
|
+
Manager.dataset.with_fetch([{:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}]).all.collect{|x| x.class}.must_equal [Manager, Executive, Ceo]
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
it "should have refresh return all columns in subclass after loading from superclass" do
|
|
613
|
+
Employee.dataset = Employee.dataset.with_fetch([{:id=>1, :name=>'A', :kind=>'Ceo'}])
|
|
614
|
+
Ceo.dataset = Ceo.dataset.with_fetch([{:id=>1, :name=>'A', :kind=>'Ceo', :num_staff=>3, :num_managers=>2}])
|
|
615
|
+
a = Employee.first
|
|
616
|
+
a.class.must_equal Ceo
|
|
617
|
+
a.values.must_equal(:id=>1, :name=>'A', :kind=>'Ceo')
|
|
618
|
+
a.refresh.values.must_equal(:id=>1, :name=>'A', :kind=>'Ceo', :num_staff=>3, :num_managers=>2)
|
|
619
|
+
@db.sqls.must_equal ["SELECT * FROM employees LIMIT 1",
|
|
620
|
+
"SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (employees.kind IN ('Ceo'))) AS employees WHERE (id = 1) LIMIT 1"]
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
it "should return rows with the current class if cti_key is nil" do
|
|
624
|
+
Employee.plugin(:class_table_inheritance, :alias=>:employees)
|
|
625
|
+
Employee.dataset.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}]).all.map{|x| x.class}.must_equal [Employee, Employee, Employee, Employee, Employee]
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
it "should return rows with the current class if cti_key is nil in subclasses" do
|
|
629
|
+
Employee.plugin(:class_table_inheritance, :alias=>:employees)
|
|
630
|
+
Object.send(:remove_const, :Executive)
|
|
631
|
+
Object.send(:remove_const, :Manager)
|
|
632
|
+
class ::Manager < Employee; end
|
|
633
|
+
class ::Executive < Manager; end
|
|
634
|
+
Manager.dataset.with_fetch([{:kind=>'Manager'}, {:kind=>'Executive'}]).all.map{|x| x.class}.must_equal [Manager, Manager]
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
it "should handle a model map with integer values" do
|
|
638
|
+
Employee.plugin(:class_table_inheritance, :key=>:kind, :model_map=>{0=>:Employee, 1=>:Manager, 2=>:Executive, 3=>:Ceo}, :alias=>:employees)
|
|
639
|
+
Object.send(:remove_const, :Ceo)
|
|
640
|
+
Object.send(:remove_const, :Executive)
|
|
641
|
+
Object.send(:remove_const, :Manager)
|
|
642
|
+
class ::Manager < Employee; end
|
|
643
|
+
class ::Executive < Manager; end
|
|
644
|
+
class ::Ceo < Executive; end
|
|
645
|
+
Employee.dataset = Employee.dataset.with_fetch([{:kind=>nil},{:kind=>0},{:kind=>1}, {:kind=>2}, {:kind=>3}])
|
|
646
|
+
Employee.all.collect{|x| x.class}.must_equal [Employee, Employee, Manager, Executive, Ceo]
|
|
647
|
+
Manager.dataset = Manager.dataset.with_fetch([{:kind=>nil},{:kind=>0},{:kind=>1}, {:kind=>2}, {:kind=>3}])
|
|
648
|
+
Manager.all.collect{|x| x.class}.must_equal [Manager, Employee, Manager, Executive, Ceo]
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
it "should fallback to the main class if the given class does not exist" do
|
|
652
|
+
@ds.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Blah'}, {:kind=>'Staff'}]).all.map{|x| x.class}.must_equal [Employee, Manager, Employee, Staff]
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
it "should fallback to the main class if the given class does not exist in subclasses" do
|
|
656
|
+
Manager.dataset.with_fetch([{:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Blah'}]).all.map{|x| x.class}.must_equal [Manager, Executive, Ceo, Manager]
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
it "should sets the model class name for the key when creating new parent class records" do
|
|
660
|
+
Employee.create
|
|
661
|
+
@db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Employee')"]
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
it "should sets the model class name for the key when creating new subclass records" do
|
|
665
|
+
Ceo.create
|
|
666
|
+
@db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Ceo')",
|
|
667
|
+
"INSERT INTO managers (id) VALUES (1)",
|
|
668
|
+
"INSERT INTO executives (id) VALUES (1)"]
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
it "should ignore existing cti_key value when creating new records" do
|
|
672
|
+
Employee.create(:kind=>'Manager')
|
|
673
|
+
@db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Employee')"]
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
it "should ignore existing cti_key value in subclasses" do
|
|
677
|
+
Manager.create(:kind=>'Executive')
|
|
678
|
+
@db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Manager')",
|
|
679
|
+
"INSERT INTO managers (id) VALUES (1)"]
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
it "should handle validations on the type column field" do
|
|
683
|
+
o = Employee.new
|
|
684
|
+
def o.validate
|
|
685
|
+
errors.add(:kind, 'not present') unless kind
|
|
686
|
+
end
|
|
687
|
+
o.valid?.must_equal true
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
it "should set the type column field even when not validating" do
|
|
691
|
+
Employee.new.save(:validate=>false)
|
|
692
|
+
@db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Employee')"]
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
it "should allow specifying a map of names to tables to override implicit mapping" do
|
|
696
|
+
Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees'
|
|
697
|
+
Staff.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees'
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
it "should lazily load attributes for columns in subclass tables" do
|
|
701
|
+
Manager.dataset = Manager.dataset.with_fetch(:id=>1, :name=>'J', :kind=>'Ceo', :num_staff=>2)
|
|
702
|
+
m = Manager[1]
|
|
703
|
+
@db.sqls.must_equal ['SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (id = 1) LIMIT 1']
|
|
704
|
+
@db.fetch = {:num_managers=>3}
|
|
705
|
+
m.must_be_kind_of Ceo
|
|
706
|
+
m.num_managers.must_equal 3
|
|
707
|
+
@db.sqls.must_equal ['SELECT employees.num_managers FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)) AS employees WHERE (employees.id = 1) LIMIT 1']
|
|
708
|
+
m.values.must_equal(:id=>1, :name=>'J', :kind=>'Ceo', :num_staff=>2, :num_managers=>3)
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
it "should lazily load columns in middle classes correctly when loaded from parent class" do
|
|
712
|
+
Employee.dataset = Employee.dataset.with_fetch(:id=>1, :kind=>'Ceo')
|
|
713
|
+
@db.fetch = [[:num_staff=>2]]
|
|
714
|
+
e = Employee[1]
|
|
715
|
+
e.must_be_kind_of(Ceo)
|
|
716
|
+
@db.sqls.must_equal ["SELECT * FROM employees WHERE (id = 1) LIMIT 1"]
|
|
717
|
+
e.num_staff.must_equal 2
|
|
718
|
+
@db.sqls.must_equal ["SELECT employees.num_staff FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (employees.id = 1) LIMIT 1"]
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
it "should eagerly load lazily columns in subclasses when loaded from parent class" do
|
|
722
|
+
Employee.dataset = Employee.dataset.with_fetch(:id=>1, :kind=>'Ceo')
|
|
723
|
+
@db.fetch = [[{:id=>1, :num_staff=>2}], [{:id=>1, :num_managers=>3}]]
|
|
724
|
+
e = Employee.all.first
|
|
725
|
+
e.must_be_kind_of(Ceo)
|
|
726
|
+
@db.sqls.must_equal ["SELECT * FROM employees"]
|
|
727
|
+
e.num_staff.must_equal 2
|
|
728
|
+
@db.sqls.must_equal ["SELECT employees.id, employees.num_staff FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (employees.id IN (1))"]
|
|
729
|
+
e.num_managers.must_equal 3
|
|
730
|
+
@db.sqls.must_equal ['SELECT employees.id, employees.num_managers FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)) AS employees WHERE (employees.id IN (1))']
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
it "should include schema for columns for tables for ancestor classes" do
|
|
734
|
+
Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string})
|
|
735
|
+
Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer})
|
|
736
|
+
Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer}, :num_managers=>{:type=>:integer})
|
|
737
|
+
Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :manager_id=>{:type=>:integer})
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
it "should use the correct primary key (which should have the same name in all subclasses)" do
|
|
741
|
+
[Employee, Manager, Executive, Ceo, Staff].each{|c| c.primary_key.must_equal :id}
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
it "should have table_name return the table name of the most specific table" do
|
|
745
|
+
Employee.table_name.must_equal :employees
|
|
746
|
+
Manager.table_name.must_equal :employees
|
|
747
|
+
Executive.table_name.must_equal :employees
|
|
748
|
+
Ceo.table_name.must_equal :employees
|
|
749
|
+
Staff.table_name.must_equal :employees
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
it "should delete the correct rows from all tables when deleting" do
|
|
753
|
+
Ceo.load(:id=>1).delete
|
|
754
|
+
@db.sqls.must_equal ["DELETE FROM executives WHERE (id = 1)", "DELETE FROM managers WHERE (id = 1)", "DELETE FROM employees WHERE (id = 1)"]
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
it "should not allow deletion of frozen object" do
|
|
758
|
+
o = Ceo.load(:id=>1)
|
|
759
|
+
o.freeze
|
|
760
|
+
proc{o.delete}.must_raise(Sequel::Error)
|
|
761
|
+
@db.sqls.must_equal []
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
it "should insert the correct rows into all tables when inserting" do
|
|
765
|
+
Ceo.create(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
766
|
+
sqls = @db.sqls
|
|
767
|
+
sqls.length.must_equal 3
|
|
768
|
+
sqls[0].must_match(/INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Ceo)', '(E|Ceo)'\)/)
|
|
769
|
+
sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \([12], [12]\)/)
|
|
770
|
+
sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([13], [13]\)/)
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
it "should insert the correct rows into all tables when inserting when insert_select is supported" do
|
|
774
|
+
[Executive, Manager, Employee].each do |klass|
|
|
775
|
+
klass.instance_variable_set(:@cti_instance_dataset, klass.cti_instance_dataset.with_extend do
|
|
776
|
+
def supports_insert_select?; true; end
|
|
777
|
+
def insert_select(v)
|
|
778
|
+
db.run(insert_sql(v) + " RETURNING *")
|
|
779
|
+
v.merge(:id=>1)
|
|
780
|
+
end
|
|
781
|
+
end)
|
|
782
|
+
end
|
|
783
|
+
Ceo.create(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
784
|
+
sqls = @db.sqls
|
|
785
|
+
sqls.length.must_equal 3
|
|
786
|
+
sqls[0].must_match(/INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Ceo)', '(E|Ceo)'\) RETURNING \*/)
|
|
787
|
+
sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \([12], [12]\) RETURNING \*/)
|
|
788
|
+
sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([13], [13]\) RETURNING \*/)
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
it "should insert the correct rows into all tables with a given primary key" do
|
|
792
|
+
e = Ceo.new(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
793
|
+
e.id = 2
|
|
794
|
+
e.save
|
|
795
|
+
sqls = @db.sqls
|
|
796
|
+
sqls.length.must_equal 3
|
|
797
|
+
sqls[0].must_match(/INSERT INTO employees \((name|kind|id), (name|kind|id), (name|kind|id)\) VALUES \(('E'|'Ceo'|2), ('E'|'Ceo'|2), ('E'|'Ceo'|2)\)/)
|
|
798
|
+
sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \(2, 2\)/)
|
|
799
|
+
sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([23], [23]\)/)
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
it "should update the correct rows in all tables when updating" do
|
|
803
|
+
Ceo.load(:id=>2).update(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
804
|
+
@db.sqls.must_equal ["UPDATE employees SET name = 'E' WHERE (id = 2)", "UPDATE managers SET num_staff = 2 WHERE (id = 2)", "UPDATE executives SET num_managers = 3 WHERE (id = 2)"]
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
it "should handle many_to_one relationships correctly" do
|
|
808
|
+
Manager.dataset = Manager.dataset.with_fetch(:id=>3, :name=>'E', :kind=>'Ceo', :num_managers=>3)
|
|
809
|
+
Staff.load(:manager_id=>3).manager.must_equal Ceo.load(:id=>3, :name=>'E', :kind=>'Ceo', :num_managers=>3)
|
|
810
|
+
@db.sqls.must_equal ['SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (id = 3) LIMIT 1']
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
it "should handle one_to_many relationships correctly" do
|
|
814
|
+
Staff.dataset = Staff.dataset.with_fetch(:id=>1, :name=>'S', :kind=>'Staff', :manager_id=>3)
|
|
815
|
+
Ceo.load(:id=>3).staff_members.must_equal [Staff.load(:id=>1, :name=>'S', :kind=>'Staff', :manager_id=>3)]
|
|
816
|
+
@db.sqls.must_equal ['SELECT * FROM (SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees WHERE (employees.manager_id = 3)']
|
|
817
|
+
end
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
describe "class_table_inheritance plugin without sti_key with :alias option" do
|
|
821
|
+
before do
|
|
822
|
+
@db = Sequel.mock(:autoid=>proc{|sql| 1})
|
|
823
|
+
def @db.supports_schema_parsing?() true end
|
|
824
|
+
def @db.schema(table, opts={})
|
|
825
|
+
{:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}]],
|
|
826
|
+
:managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}]],
|
|
827
|
+
:executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}]],
|
|
828
|
+
:staff=>[[:id, {:type=>:integer}], [:manager_id, {:type=>:integer}]],
|
|
829
|
+
}[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
|
|
830
|
+
end
|
|
831
|
+
@db.extend_datasets do
|
|
832
|
+
def columns
|
|
833
|
+
{[:employees]=>[:id, :name],
|
|
834
|
+
[:managers]=>[:id, :num_staff],
|
|
835
|
+
[:executives]=>[:id, :num_managers],
|
|
836
|
+
[:staff]=>[:id, :manager_id],
|
|
837
|
+
[:employees, :managers]=>[:id, :name, :num_staff],
|
|
838
|
+
[:employees, :managers, :executives]=>[:id, :name, :num_staff, :num_managers],
|
|
839
|
+
[:employees, :staff]=>[:id, :name, :manager_id],
|
|
840
|
+
}[opts[:from] + (opts[:join] || []).map{|x| x.table}]
|
|
841
|
+
end
|
|
842
|
+
end
|
|
843
|
+
class ::Employee < Sequel::Model(@db)
|
|
844
|
+
def _save_refresh; @values[:id] = 1 end
|
|
845
|
+
def self.columns
|
|
846
|
+
dataset.columns || dataset.opts[:from].first.expression.columns
|
|
847
|
+
end
|
|
848
|
+
plugin :class_table_inheritance, :table_map=>{:Staff=>:staff}, :alias=>:employees
|
|
849
|
+
end
|
|
850
|
+
class ::Manager < Employee
|
|
851
|
+
one_to_many :staff_members, :class=>:Staff
|
|
852
|
+
end
|
|
853
|
+
class ::Executive < Manager
|
|
854
|
+
end
|
|
855
|
+
class ::Staff < Employee
|
|
856
|
+
many_to_one :manager
|
|
857
|
+
end
|
|
858
|
+
@ds = Employee.dataset
|
|
859
|
+
@db.sqls
|
|
860
|
+
end
|
|
861
|
+
after do
|
|
862
|
+
Object.send(:remove_const, :Executive)
|
|
863
|
+
Object.send(:remove_const, :Manager)
|
|
864
|
+
Object.send(:remove_const, :Staff)
|
|
865
|
+
Object.send(:remove_const, :Employee)
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
it "should have simple_table = nil for all subclasses" do
|
|
869
|
+
Manager.simple_table.must_be_nil
|
|
870
|
+
Executive.simple_table.must_be_nil
|
|
871
|
+
Staff.simple_table.must_be_nil
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
it "should have working row_proc if using set_dataset in subclass to remove columns" do
|
|
875
|
+
Manager.set_dataset(Manager.dataset.select(*(Manager.columns - [:blah])))
|
|
876
|
+
Manager.dataset = Manager.dataset.with_fetch(:id=>1)
|
|
877
|
+
Manager[1].must_equal Manager.load(:id=>1)
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
it "should use a joined dataset in subclasses" do
|
|
881
|
+
Employee.dataset.sql.must_equal 'SELECT * FROM employees'
|
|
882
|
+
Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees'
|
|
883
|
+
Executive.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)) AS employees'
|
|
884
|
+
Staff.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees'
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
it "should return rows with the current class if cti_key is nil" do
|
|
888
|
+
Employee.plugin(:class_table_inheritance)
|
|
889
|
+
Employee.dataset = Employee.dataset.with_fetch([{}])
|
|
890
|
+
Employee.first.class.must_equal Employee
|
|
891
|
+
end
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
it "should include schema for columns for tables for ancestor classes" do
|
|
895
|
+
Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string})
|
|
896
|
+
Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :num_staff=>{:type=>:integer})
|
|
897
|
+
Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :num_staff=>{:type=>:integer}, :num_managers=>{:type=>:integer})
|
|
898
|
+
Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :manager_id=>{:type=>:integer})
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
it "should use the correct primary key (which should have the same name in all subclasses)" do
|
|
902
|
+
[Employee, Manager, Executive, Staff].each{|c| c.primary_key.must_equal :id}
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
it "should have table_name return the table name of the most specific table" do
|
|
906
|
+
Employee.table_name.must_equal :employees
|
|
907
|
+
Manager.table_name.must_equal :employees
|
|
908
|
+
Executive.table_name.must_equal :employees
|
|
909
|
+
Staff.table_name.must_equal :employees
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
it "should delete the correct rows from all tables when deleting" do
|
|
913
|
+
Executive.load(:id=>1).delete
|
|
914
|
+
@db.sqls.must_equal ["DELETE FROM executives WHERE (id = 1)", "DELETE FROM managers WHERE (id = 1)", "DELETE FROM employees WHERE (id = 1)"]
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
it "should not allow deletion of frozen object" do
|
|
918
|
+
o = Executive.load(:id=>1)
|
|
919
|
+
o.freeze
|
|
920
|
+
proc{o.delete}.must_raise(Sequel::Error)
|
|
921
|
+
@db.sqls.must_equal []
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
it "should insert the correct rows into all tables when inserting" do
|
|
925
|
+
Executive.create(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
926
|
+
sqls = @db.sqls
|
|
927
|
+
sqls.length.must_equal 3
|
|
928
|
+
sqls[0].must_match(/INSERT INTO employees \(name\) VALUES \('E'\)/)
|
|
929
|
+
sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \([12], [12]\)/)
|
|
930
|
+
sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([13], [13]\)/)
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
it "should insert the correct rows into all tables with a given primary key" do
|
|
934
|
+
e = Executive.new(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
935
|
+
e.id = 2
|
|
936
|
+
e.save
|
|
937
|
+
sqls = @db.sqls
|
|
938
|
+
sqls.length.must_equal 3
|
|
939
|
+
sqls[0].must_match(/INSERT INTO employees \((name|id), (name|id)\) VALUES \(('E'|2), ('E'|2)\)/)
|
|
940
|
+
sqls[1].must_match(/INSERT INTO managers \((num_staff|id), (num_staff|id)\) VALUES \(2, 2\)/)
|
|
941
|
+
sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([23], [23]\)/)
|
|
942
|
+
end
|
|
943
|
+
|
|
944
|
+
it "should update the correct rows in all tables when updating" do
|
|
945
|
+
Executive.load(:id=>2).update(:num_managers=>3, :num_staff=>2, :name=>'E')
|
|
946
|
+
@db.sqls.must_equal ["UPDATE employees SET name = 'E' WHERE (id = 2)", "UPDATE managers SET num_staff = 2 WHERE (id = 2)", "UPDATE executives SET num_managers = 3 WHERE (id = 2)"]
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
it "should handle many_to_one relationships correctly" do
|
|
950
|
+
Manager.dataset = Manager.dataset.with_fetch(:id=>3, :name=>'E', :num_staff=>3)
|
|
951
|
+
Staff.load(:manager_id=>3).manager.must_equal Manager.load(:id=>3, :name=>'E', :num_staff=>3)
|
|
952
|
+
@db.sqls.must_equal ['SELECT * FROM (SELECT employees.id, employees.name, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (id = 3) LIMIT 1']
|
|
953
|
+
end
|
|
954
|
+
|
|
955
|
+
it "should handle one_to_many relationships correctly" do
|
|
956
|
+
Staff.dataset = Staff.dataset.with_fetch(:id=>1, :name=>'S', :manager_id=>3)
|
|
957
|
+
Executive.load(:id=>3).staff_members.must_equal [Staff.load(:id=>1, :name=>'S', :manager_id=>3)]
|
|
958
|
+
@db.sqls.must_equal ['SELECT * FROM (SELECT employees.id, employees.name, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees WHERE (employees.manager_id = 3)']
|
|
959
|
+
end
|
|
960
|
+
end
|
|
961
|
+
|
|
962
|
+
describe "class_table_inheritance plugin with duplicate columns with :alias option" do
|
|
963
|
+
before do
|
|
964
|
+
@db = Sequel.mock(:autoid=>proc{|sql| 1})
|
|
965
|
+
def @db.supports_schema_parsing?() true end
|
|
966
|
+
def @db.schema(table, opts={})
|
|
967
|
+
{:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]],
|
|
968
|
+
:managers=>[[:id, {:type=>:integer}], [:name, {:type=>:string}]],
|
|
969
|
+
}[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
|
|
970
|
+
end
|
|
971
|
+
@db.extend_datasets do
|
|
972
|
+
def columns
|
|
973
|
+
{[:employees]=>[:id, :name, :kind],
|
|
974
|
+
[:managers]=>[:id, :name],
|
|
975
|
+
}[opts[:from] + (opts[:join] || []).map{|x| x.table}]
|
|
976
|
+
end
|
|
977
|
+
end
|
|
978
|
+
class ::Employee < Sequel::Model(@db)
|
|
979
|
+
def _save_refresh; @values[:id] = 1 end
|
|
980
|
+
def self.columns
|
|
981
|
+
dataset.columns || dataset.opts[:from].first.expression.columns
|
|
982
|
+
end
|
|
983
|
+
plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}, :alias=>:employees
|
|
984
|
+
end
|
|
985
|
+
deprecated do
|
|
986
|
+
class ::Manager < Employee; end
|
|
987
|
+
end
|
|
988
|
+
@ds = Employee.dataset
|
|
989
|
+
@db.sqls
|
|
990
|
+
end
|
|
991
|
+
after do
|
|
992
|
+
Object.send(:remove_const, :Manager)
|
|
993
|
+
Object.send(:remove_const, :Employee)
|
|
994
|
+
end
|
|
995
|
+
|
|
996
|
+
it "should select names from both tables" do
|
|
997
|
+
Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.name FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees'
|
|
998
|
+
end
|
|
999
|
+
end
|
|
1000
|
+
|
|
1001
|
+
describe "class_table_inheritance plugin with :alias option" do
|
|
1002
|
+
before do
|
|
1003
|
+
@db = Sequel.mock(:autoid=>proc{|sql| 1})
|
|
1004
|
+
def @db.supports_schema_parsing?() true end
|
|
1005
|
+
def @db.schema(table, opts={})
|
|
1006
|
+
{:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]],
|
|
1007
|
+
:managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}]],
|
|
1008
|
+
:executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}]],
|
|
1009
|
+
:staff=>[[:id, {:type=>:integer}], [:manager_id, {:type=>:integer}]],
|
|
1010
|
+
}[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
|
|
1011
|
+
end
|
|
1012
|
+
@db.extend_datasets do
|
|
1013
|
+
def columns
|
|
1014
|
+
{[:employees]=>[:id, :name, :kind],
|
|
1015
|
+
[:managers]=>[:id, :num_staff],
|
|
1016
|
+
[:executives]=>[:id, :num_managers],
|
|
1017
|
+
[:staff]=>[:id, :manager_id],
|
|
1018
|
+
[:employees, :managers]=>[:id, :name, :kind, :num_staff],
|
|
1019
|
+
[:employees, :managers, :executives]=>[:id, :name, :kind, :num_staff, :num_managers],
|
|
1020
|
+
[:employees, :staff]=>[:id, :name, :kind, :manager_id],
|
|
1021
|
+
}[opts[:from] + (opts[:join] || []).map{|x| x.table}]
|
|
1022
|
+
end
|
|
1023
|
+
end
|
|
1024
|
+
class ::Employee < Sequel::Model(@db)
|
|
1025
|
+
def _save_refresh; @values[:id] = 1 end
|
|
1026
|
+
def self.columns
|
|
1027
|
+
dataset.columns || dataset.opts[:from].first.expression.columns
|
|
1028
|
+
end
|
|
1029
|
+
plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}, :alias=>:employees
|
|
1030
|
+
end
|
|
33
1031
|
class ::Manager < Employee
|
|
34
1032
|
one_to_many :staff_members, :class=>:Staff
|
|
35
1033
|
end
|
|
@@ -63,12 +1061,12 @@ describe "class_table_inheritance plugin" do
|
|
|
63
1061
|
it "should not attempt to use prepared statements" do
|
|
64
1062
|
Manager.plugin :prepared_statements
|
|
65
1063
|
Manager[1]
|
|
66
|
-
@db.sqls.must_equal ["SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (
|
|
1064
|
+
@db.sqls.must_equal ["SELECT id, name, kind, num_staff FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (id = 1) LIMIT 1"]
|
|
67
1065
|
Manager.load(:id=>1, :kind=>'Manager', :num_staff=>2).save
|
|
68
1066
|
@db.sqls.must_equal ["UPDATE employees SET kind = 'Manager' WHERE (id = 1)", "UPDATE managers SET num_staff = 2 WHERE (id = 1)"]
|
|
69
1067
|
@db.fetch = {:id=>1, :kind=>'Manager', :num_staff=>2}
|
|
70
1068
|
Manager.load(:id=>1, :kind=>'Manager', :num_staff=>2).refresh
|
|
71
|
-
@db.sqls.must_equal ["SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (
|
|
1069
|
+
@db.sqls.must_equal ["SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (id = 1) LIMIT 1"]
|
|
72
1070
|
end
|
|
73
1071
|
|
|
74
1072
|
it "#cti_base_model should be the model that loaded the plugin" do
|
|
@@ -92,12 +1090,12 @@ describe "class_table_inheritance plugin" do
|
|
|
92
1090
|
Manager[1].must_equal Ceo.load(:id=>1, :kind=>'Ceo')
|
|
93
1091
|
end
|
|
94
1092
|
|
|
95
|
-
it "should use a
|
|
1093
|
+
it "should use a subquery in subclasses" do
|
|
96
1094
|
Employee.dataset.sql.must_equal 'SELECT * FROM employees'
|
|
97
|
-
Manager.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)'
|
|
98
|
-
Executive.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)'
|
|
99
|
-
Ceo.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (employees.kind IN (\'Ceo\'))'
|
|
100
|
-
Staff.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)'
|
|
1095
|
+
Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees'
|
|
1096
|
+
Executive.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)) AS employees'
|
|
1097
|
+
Ceo.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (employees.kind IN (\'Ceo\'))) AS employees'
|
|
1098
|
+
Staff.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees'
|
|
101
1099
|
end
|
|
102
1100
|
|
|
103
1101
|
it "should return rows with the correct class based on the polymorphic_key value" do
|
|
@@ -116,16 +1114,16 @@ describe "class_table_inheritance plugin" do
|
|
|
116
1114
|
a.values.must_equal(:id=>1, :name=>'A', :kind=>'Ceo')
|
|
117
1115
|
a.refresh.values.must_equal(:id=>1, :name=>'A', :kind=>'Ceo', :num_staff=>3, :num_managers=>2)
|
|
118
1116
|
@db.sqls.must_equal ["SELECT * FROM employees LIMIT 1",
|
|
119
|
-
"SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (
|
|
1117
|
+
"SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (employees.kind IN ('Ceo'))) AS employees WHERE (id = 1) LIMIT 1"]
|
|
120
1118
|
end
|
|
121
1119
|
|
|
122
1120
|
it "should return rows with the current class if cti_key is nil" do
|
|
123
|
-
Employee.plugin(:class_table_inheritance)
|
|
1121
|
+
Employee.plugin(:class_table_inheritance, :alias=>:employees)
|
|
124
1122
|
Employee.dataset.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}]).all.map{|x| x.class}.must_equal [Employee, Employee, Employee, Employee, Employee]
|
|
125
1123
|
end
|
|
126
1124
|
|
|
127
1125
|
it "should return rows with the current class if cti_key is nil in subclasses" do
|
|
128
|
-
Employee.plugin(:class_table_inheritance)
|
|
1126
|
+
Employee.plugin(:class_table_inheritance, :alias=>:employees)
|
|
129
1127
|
Object.send(:remove_const, :Executive)
|
|
130
1128
|
Object.send(:remove_const, :Manager)
|
|
131
1129
|
class ::Manager < Employee; end
|
|
@@ -134,7 +1132,7 @@ describe "class_table_inheritance plugin" do
|
|
|
134
1132
|
end
|
|
135
1133
|
|
|
136
1134
|
it "should handle a model map with integer values" do
|
|
137
|
-
Employee.plugin(:class_table_inheritance, :key=>:kind, :model_map=>{0=>:Employee, 1=>:Manager, 2=>:Executive, 3=>:Ceo})
|
|
1135
|
+
Employee.plugin(:class_table_inheritance, :key=>:kind, :model_map=>{0=>:Employee, 1=>:Manager, 2=>:Executive, 3=>:Ceo}, :alias=>:employees)
|
|
138
1136
|
Object.send(:remove_const, :Ceo)
|
|
139
1137
|
Object.send(:remove_const, :Executive)
|
|
140
1138
|
Object.send(:remove_const, :Manager)
|
|
@@ -192,18 +1190,18 @@ describe "class_table_inheritance plugin" do
|
|
|
192
1190
|
end
|
|
193
1191
|
|
|
194
1192
|
it "should allow specifying a map of names to tables to override implicit mapping" do
|
|
195
|
-
Manager.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)'
|
|
196
|
-
Staff.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)'
|
|
1193
|
+
Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees'
|
|
1194
|
+
Staff.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees'
|
|
197
1195
|
end
|
|
198
1196
|
|
|
199
1197
|
it "should lazily load attributes for columns in subclass tables" do
|
|
200
1198
|
Manager.dataset = Manager.dataset.with_fetch(:id=>1, :name=>'J', :kind=>'Ceo', :num_staff=>2)
|
|
201
1199
|
m = Manager[1]
|
|
202
|
-
@db.sqls.must_equal ['SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (
|
|
1200
|
+
@db.sqls.must_equal ['SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (id = 1) LIMIT 1']
|
|
203
1201
|
@db.fetch = {:num_managers=>3}
|
|
204
1202
|
m.must_be_kind_of Ceo
|
|
205
1203
|
m.num_managers.must_equal 3
|
|
206
|
-
@db.sqls.must_equal ['SELECT executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (
|
|
1204
|
+
@db.sqls.must_equal ['SELECT employees.num_managers FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)) AS employees WHERE (employees.id = 1) LIMIT 1']
|
|
207
1205
|
m.values.must_equal(:id=>1, :name=>'J', :kind=>'Ceo', :num_staff=>2, :num_managers=>3)
|
|
208
1206
|
end
|
|
209
1207
|
|
|
@@ -214,7 +1212,7 @@ describe "class_table_inheritance plugin" do
|
|
|
214
1212
|
e.must_be_kind_of(Ceo)
|
|
215
1213
|
@db.sqls.must_equal ["SELECT * FROM employees WHERE (id = 1) LIMIT 1"]
|
|
216
1214
|
e.num_staff.must_equal 2
|
|
217
|
-
@db.sqls.must_equal ["SELECT managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (
|
|
1215
|
+
@db.sqls.must_equal ["SELECT employees.num_staff FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (employees.id = 1) LIMIT 1"]
|
|
218
1216
|
end
|
|
219
1217
|
|
|
220
1218
|
it "should eagerly load lazily columns in subclasses when loaded from parent class" do
|
|
@@ -224,9 +1222,9 @@ describe "class_table_inheritance plugin" do
|
|
|
224
1222
|
e.must_be_kind_of(Ceo)
|
|
225
1223
|
@db.sqls.must_equal ["SELECT * FROM employees"]
|
|
226
1224
|
e.num_staff.must_equal 2
|
|
227
|
-
@db.sqls.must_equal ["SELECT
|
|
1225
|
+
@db.sqls.must_equal ["SELECT employees.id, employees.num_staff FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (employees.id IN (1))"]
|
|
228
1226
|
e.num_managers.must_equal 3
|
|
229
|
-
@db.sqls.must_equal ['SELECT
|
|
1227
|
+
@db.sqls.must_equal ['SELECT employees.id, employees.num_managers FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)) AS employees WHERE (employees.id IN (1))']
|
|
230
1228
|
end
|
|
231
1229
|
|
|
232
1230
|
it "should include schema for columns for tables for ancestor classes" do
|
|
@@ -242,10 +1240,10 @@ describe "class_table_inheritance plugin" do
|
|
|
242
1240
|
|
|
243
1241
|
it "should have table_name return the table name of the most specific table" do
|
|
244
1242
|
Employee.table_name.must_equal :employees
|
|
245
|
-
Manager.table_name.must_equal :
|
|
246
|
-
Executive.table_name.must_equal :
|
|
247
|
-
Ceo.table_name.must_equal :
|
|
248
|
-
Staff.table_name.must_equal :
|
|
1243
|
+
Manager.table_name.must_equal :employees
|
|
1244
|
+
Executive.table_name.must_equal :employees
|
|
1245
|
+
Ceo.table_name.must_equal :employees
|
|
1246
|
+
Staff.table_name.must_equal :employees
|
|
249
1247
|
end
|
|
250
1248
|
|
|
251
1249
|
it "should delete the correct rows from all tables when deleting" do
|
|
@@ -306,17 +1304,17 @@ describe "class_table_inheritance plugin" do
|
|
|
306
1304
|
it "should handle many_to_one relationships correctly" do
|
|
307
1305
|
Manager.dataset = Manager.dataset.with_fetch(:id=>3, :name=>'E', :kind=>'Ceo', :num_managers=>3)
|
|
308
1306
|
Staff.load(:manager_id=>3).manager.must_equal Ceo.load(:id=>3, :name=>'E', :kind=>'Ceo', :num_managers=>3)
|
|
309
|
-
@db.sqls.must_equal ['SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (
|
|
1307
|
+
@db.sqls.must_equal ['SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (id = 3) LIMIT 1']
|
|
310
1308
|
end
|
|
311
1309
|
|
|
312
1310
|
it "should handle one_to_many relationships correctly" do
|
|
313
1311
|
Staff.dataset = Staff.dataset.with_fetch(:id=>1, :name=>'S', :kind=>'Staff', :manager_id=>3)
|
|
314
1312
|
Ceo.load(:id=>3).staff_members.must_equal [Staff.load(:id=>1, :name=>'S', :kind=>'Staff', :manager_id=>3)]
|
|
315
|
-
@db.sqls.must_equal ['SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id) WHERE (
|
|
1313
|
+
@db.sqls.must_equal ['SELECT * FROM (SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees WHERE (employees.manager_id = 3)']
|
|
316
1314
|
end
|
|
317
1315
|
end
|
|
318
1316
|
|
|
319
|
-
describe "class_table_inheritance plugin without sti_key" do
|
|
1317
|
+
describe "class_table_inheritance plugin without sti_key with :alias option" do
|
|
320
1318
|
before do
|
|
321
1319
|
@db = Sequel.mock(:autoid=>proc{|sql| 1})
|
|
322
1320
|
def @db.supports_schema_parsing?() true end
|
|
@@ -342,9 +1340,9 @@ describe "class_table_inheritance plugin without sti_key" do
|
|
|
342
1340
|
class ::Employee < Sequel::Model(@db)
|
|
343
1341
|
def _save_refresh; @values[:id] = 1 end
|
|
344
1342
|
def self.columns
|
|
345
|
-
dataset.columns
|
|
1343
|
+
dataset.columns || dataset.opts[:from].first.expression.columns
|
|
346
1344
|
end
|
|
347
|
-
plugin :class_table_inheritance, :table_map=>{:Staff=>:staff}
|
|
1345
|
+
plugin :class_table_inheritance, :table_map=>{:Staff=>:staff}, :alias=>:employees
|
|
348
1346
|
end
|
|
349
1347
|
class ::Manager < Employee
|
|
350
1348
|
one_to_many :staff_members, :class=>:Staff
|
|
@@ -378,9 +1376,9 @@ describe "class_table_inheritance plugin without sti_key" do
|
|
|
378
1376
|
|
|
379
1377
|
it "should use a joined dataset in subclasses" do
|
|
380
1378
|
Employee.dataset.sql.must_equal 'SELECT * FROM employees'
|
|
381
|
-
Manager.dataset.sql.must_equal 'SELECT employees.id, employees.name, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)'
|
|
382
|
-
Executive.dataset.sql.must_equal 'SELECT employees.id, employees.name, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)'
|
|
383
|
-
Staff.dataset.sql.must_equal 'SELECT employees.id, employees.name, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)'
|
|
1379
|
+
Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees'
|
|
1380
|
+
Executive.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)) AS employees'
|
|
1381
|
+
Staff.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees'
|
|
384
1382
|
end
|
|
385
1383
|
|
|
386
1384
|
it "should return rows with the current class if cti_key is nil" do
|
|
@@ -403,9 +1401,9 @@ describe "class_table_inheritance plugin without sti_key" do
|
|
|
403
1401
|
|
|
404
1402
|
it "should have table_name return the table name of the most specific table" do
|
|
405
1403
|
Employee.table_name.must_equal :employees
|
|
406
|
-
Manager.table_name.must_equal :
|
|
407
|
-
Executive.table_name.must_equal :
|
|
408
|
-
Staff.table_name.must_equal :
|
|
1404
|
+
Manager.table_name.must_equal :employees
|
|
1405
|
+
Executive.table_name.must_equal :employees
|
|
1406
|
+
Staff.table_name.must_equal :employees
|
|
409
1407
|
end
|
|
410
1408
|
|
|
411
1409
|
it "should delete the correct rows from all tables when deleting" do
|
|
@@ -448,17 +1446,17 @@ describe "class_table_inheritance plugin without sti_key" do
|
|
|
448
1446
|
it "should handle many_to_one relationships correctly" do
|
|
449
1447
|
Manager.dataset = Manager.dataset.with_fetch(:id=>3, :name=>'E', :num_staff=>3)
|
|
450
1448
|
Staff.load(:manager_id=>3).manager.must_equal Manager.load(:id=>3, :name=>'E', :num_staff=>3)
|
|
451
|
-
@db.sqls.must_equal ['SELECT employees.id, employees.name, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (
|
|
1449
|
+
@db.sqls.must_equal ['SELECT * FROM (SELECT employees.id, employees.name, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees WHERE (id = 3) LIMIT 1']
|
|
452
1450
|
end
|
|
453
1451
|
|
|
454
1452
|
it "should handle one_to_many relationships correctly" do
|
|
455
1453
|
Staff.dataset = Staff.dataset.with_fetch(:id=>1, :name=>'S', :manager_id=>3)
|
|
456
1454
|
Executive.load(:id=>3).staff_members.must_equal [Staff.load(:id=>1, :name=>'S', :manager_id=>3)]
|
|
457
|
-
@db.sqls.must_equal ['SELECT employees.id, employees.name, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id) WHERE (
|
|
1455
|
+
@db.sqls.must_equal ['SELECT * FROM (SELECT employees.id, employees.name, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees WHERE (employees.manager_id = 3)']
|
|
458
1456
|
end
|
|
459
1457
|
end
|
|
460
1458
|
|
|
461
|
-
describe "class_table_inheritance plugin with duplicate columns" do
|
|
1459
|
+
describe "class_table_inheritance plugin with duplicate columns with :alias option" do
|
|
462
1460
|
before do
|
|
463
1461
|
@db = Sequel.mock(:autoid=>proc{|sql| 1})
|
|
464
1462
|
def @db.supports_schema_parsing?() true end
|
|
@@ -477,9 +1475,9 @@ describe "class_table_inheritance plugin with duplicate columns" do
|
|
|
477
1475
|
class ::Employee < Sequel::Model(@db)
|
|
478
1476
|
def _save_refresh; @values[:id] = 1 end
|
|
479
1477
|
def self.columns
|
|
480
|
-
dataset.columns
|
|
1478
|
+
dataset.columns || dataset.opts[:from].first.expression.columns
|
|
481
1479
|
end
|
|
482
|
-
plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}
|
|
1480
|
+
plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff}, :alias=>:employees
|
|
483
1481
|
end
|
|
484
1482
|
deprecated do
|
|
485
1483
|
class ::Manager < Employee; end
|
|
@@ -493,6 +1491,6 @@ describe "class_table_inheritance plugin with duplicate columns" do
|
|
|
493
1491
|
end
|
|
494
1492
|
|
|
495
1493
|
it "should select names from both tables" do
|
|
496
|
-
Manager.dataset.sql.must_equal 'SELECT employees.id, employees.name, employees.kind, managers.name FROM employees INNER JOIN managers ON (managers.id = employees.id)'
|
|
1494
|
+
Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.name FROM employees INNER JOIN managers ON (managers.id = employees.id)) AS employees'
|
|
497
1495
|
end
|
|
498
1496
|
end
|