sequel 4.45.0 → 4.46.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|