sequel 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +100 -0
- data/README.rdoc +3 -3
- data/bin/sequel +102 -19
- data/doc/reflection.rdoc +83 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/lib/sequel/adapters/ado.rb +11 -0
- data/lib/sequel/adapters/amalgalite.rb +5 -20
- data/lib/sequel/adapters/do.rb +44 -36
- data/lib/sequel/adapters/firebird.rb +29 -43
- data/lib/sequel/adapters/jdbc.rb +17 -27
- data/lib/sequel/adapters/mysql.rb +35 -40
- data/lib/sequel/adapters/odbc.rb +4 -23
- data/lib/sequel/adapters/oracle.rb +22 -19
- data/lib/sequel/adapters/postgres.rb +6 -15
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +29 -10
- data/lib/sequel/adapters/shared/oracle.rb +6 -8
- data/lib/sequel/adapters/shared/postgres.rb +28 -72
- data/lib/sequel/adapters/shared/sqlite.rb +5 -3
- data/lib/sequel/adapters/sqlite.rb +5 -20
- data/lib/sequel/adapters/utils/savepoint_transactions.rb +80 -0
- data/lib/sequel/adapters/utils/unsupported.rb +0 -12
- data/lib/sequel/core.rb +12 -3
- data/lib/sequel/core_sql.rb +1 -8
- data/lib/sequel/database.rb +107 -43
- data/lib/sequel/database/schema_generator.rb +1 -0
- data/lib/sequel/database/schema_methods.rb +38 -4
- data/lib/sequel/dataset.rb +6 -0
- data/lib/sequel/dataset/convenience.rb +2 -2
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/prepared_statements.rb +3 -8
- data/lib/sequel/dataset/sql.rb +93 -19
- data/lib/sequel/extensions/blank.rb +2 -1
- data/lib/sequel/extensions/inflector.rb +4 -3
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pretty_table.rb +4 -0
- data/lib/sequel/extensions/query.rb +4 -0
- data/lib/sequel/extensions/schema_dumper.rb +100 -24
- data/lib/sequel/extensions/string_date_time.rb +3 -4
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +96 -38
- data/lib/sequel/model/base.rb +14 -14
- data/lib/sequel/model/plugins.rb +32 -21
- data/lib/sequel/plugins/caching.rb +13 -15
- data/lib/sequel/plugins/identity_map.rb +107 -0
- data/lib/sequel/plugins/lazy_attributes.rb +65 -0
- data/lib/sequel/plugins/many_through_many.rb +188 -0
- data/lib/sequel/plugins/schema.rb +13 -0
- data/lib/sequel/plugins/serialization.rb +53 -37
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/validation_class_methods.rb +28 -7
- data/lib/sequel/plugins/validation_helpers.rb +31 -24
- data/lib/sequel/sql.rb +16 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/ado_spec.rb +47 -1
- data/spec/adapters/firebird_spec.rb +39 -36
- data/spec/adapters/mysql_spec.rb +25 -9
- data/spec/adapters/postgres_spec.rb +11 -24
- data/spec/core/database_spec.rb +54 -13
- data/spec/core/dataset_spec.rb +147 -29
- data/spec/core/object_graph_spec.rb +6 -1
- data/spec/core/schema_spec.rb +34 -0
- data/spec/core/spec_helper.rb +0 -2
- data/spec/extensions/caching_spec.rb +7 -0
- data/spec/extensions/identity_map_spec.rb +158 -0
- data/spec/extensions/lazy_attributes_spec.rb +113 -0
- data/spec/extensions/many_through_many_spec.rb +813 -0
- data/spec/extensions/migration_spec.rb +4 -4
- data/spec/extensions/schema_dumper_spec.rb +114 -13
- data/spec/extensions/schema_spec.rb +19 -3
- data/spec/extensions/serialization_spec.rb +28 -0
- data/spec/extensions/single_table_inheritance_spec.rb +25 -1
- data/spec/extensions/spec_helper.rb +2 -7
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/validation_class_methods_spec.rb +10 -5
- data/spec/integration/dataset_test.rb +39 -6
- data/spec/integration/eager_loader_test.rb +7 -7
- data/spec/integration/spec_helper.rb +0 -1
- data/spec/integration/transaction_test.rb +28 -1
- data/spec/model/association_reflection_spec.rb +29 -3
- data/spec/model/associations_spec.rb +1 -0
- data/spec/model/eager_loading_spec.rb +70 -1
- data/spec/model/plugins_spec.rb +236 -50
- data/spec/model/spec_helper.rb +0 -2
- metadata +18 -5
@@ -135,7 +135,7 @@ context "Sequel::Migrator" do
|
|
135
135
|
File.open('001_create_sessions.rb', 'w') {|f| f << MIGRATION_001}
|
136
136
|
File.open('002_create_nodes.rb', 'w') {|f| f << MIGRATION_002}
|
137
137
|
File.open('003_create_users.rb', 'w') {|f| f << MIGRATION_003}
|
138
|
-
File.open('
|
138
|
+
File.open('005_5_create_attributes.rb', 'w') {|f| f << MIGRATION_005}
|
139
139
|
|
140
140
|
@db[:schema_info].version = nil
|
141
141
|
end
|
@@ -149,7 +149,7 @@ context "Sequel::Migrator" do
|
|
149
149
|
File.delete('001_create_sessions.rb')
|
150
150
|
File.delete('002_create_nodes.rb')
|
151
151
|
File.delete('003_create_users.rb')
|
152
|
-
File.delete('
|
152
|
+
File.delete('005_5_create_attributes.rb')
|
153
153
|
end
|
154
154
|
|
155
155
|
specify "should return the list of files for a specified version range" do
|
@@ -159,8 +159,8 @@ context "Sequel::Migrator" do
|
|
159
159
|
Sequel::Migrator.migration_files('.', 1..3).should == \
|
160
160
|
['./001_create_sessions.rb', './002_create_nodes.rb', './003_create_users.rb']
|
161
161
|
|
162
|
-
Sequel::Migrator.migration_files('.', 3..
|
163
|
-
['./003_create_users.rb', './
|
162
|
+
Sequel::Migrator.migration_files('.', 3..6).should == \
|
163
|
+
['./003_create_users.rb', './005_5_create_attributes.rb']
|
164
164
|
|
165
165
|
Sequel::Migrator.migration_files('.', 7..8).should == []
|
166
166
|
end
|
@@ -78,11 +78,6 @@ describe "Sequel::Database dump methods" do
|
|
78
78
|
when :t3
|
79
79
|
[[:c1, {:db_type=>'date', :default=>"'now()'", :allow_null=>true}],
|
80
80
|
[:c2, {:db_type=>'datetime', :allow_null=>false}]]
|
81
|
-
when :t4
|
82
|
-
[[:c1, {:db_type=>'boolean', :default=>"false", :allow_null=>true}],
|
83
|
-
[:c2, {:db_type=>'boolean', :default=>"true", :allow_null=>true}],
|
84
|
-
[:c3, {:db_type=>'varchar', :default=>"'blah'", :allow_null=>true}],
|
85
|
-
[:c4, {:db_type=>'integer', :default=>"35", :allow_null=>true}]]
|
86
81
|
when :t5
|
87
82
|
[[:c1, {:db_type=>'blahblah', :allow_null=>true}]]
|
88
83
|
end
|
@@ -102,7 +97,7 @@ describe "Sequel::Database dump methods" do
|
|
102
97
|
{:i1=>{:columns=>[:c1], :unique=>false},
|
103
98
|
:t1_c2_c1_index=>{:columns=>[:c2, :c1], :unique=>true}}
|
104
99
|
end
|
105
|
-
@d.dump_table_schema(:t1).should == "create_table(:t1) do\n primary_key :c1\n String :c2, :size=>20\n \n index [:c1], :name=>:i1\n index [:c2, :c1], :unique=>true\nend"
|
100
|
+
@d.dump_table_schema(:t1).should == "create_table(:t1, :ignore_index_errors=>true) do\n primary_key :c1\n String :c2, :size=>20\n \n index [:c1], :name=>:i1\n index [:c2, :c1], :unique=>true\nend"
|
106
101
|
end
|
107
102
|
|
108
103
|
it "should support dumping the whole database as a migration" do
|
@@ -129,6 +124,31 @@ end
|
|
129
124
|
END_MIG
|
130
125
|
end
|
131
126
|
|
127
|
+
it "should sort table names when dumping a migration" do
|
128
|
+
@d.meta_def(:tables){[:t2, :t1]}
|
129
|
+
@d.dump_schema_migration.should == <<-END_MIG
|
130
|
+
Class.new(Sequel::Migration) do
|
131
|
+
def up
|
132
|
+
create_table(:t1) do
|
133
|
+
primary_key :c1
|
134
|
+
String :c2, :size=>20
|
135
|
+
end
|
136
|
+
|
137
|
+
create_table(:t2) do
|
138
|
+
Integer :c1, :null=>false
|
139
|
+
BigDecimal :c2, :null=>false
|
140
|
+
|
141
|
+
primary_key [:c1, :c2]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def down
|
146
|
+
drop_table(:t1, :t2)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
END_MIG
|
150
|
+
end
|
151
|
+
|
132
152
|
it "should honor the :same_db option to not convert types" do
|
133
153
|
@d.dump_table_schema(:t1, :same_db=>true).should == "create_table(:t1) do\n primary_key :c1\n column :c2, \"varchar(20)\"\nend"
|
134
154
|
@d.dump_schema_migration(:same_db=>true).should == <<-END_MIG
|
@@ -192,24 +212,105 @@ END_MIG
|
|
192
212
|
@d.dump_indexes_migration.should == <<-END_MIG
|
193
213
|
Class.new(Sequel::Migration) do
|
194
214
|
def up
|
195
|
-
add_index :t1, [:c1], :name=>:i1
|
196
|
-
add_index :t1, [:c2, :c1], :unique=>true
|
215
|
+
add_index :t1, [:c1], :ignore_errors=>true, :name=>:i1
|
216
|
+
add_index :t1, [:c2, :c1], :ignore_errors=>true, :unique=>true
|
197
217
|
end
|
198
218
|
|
199
219
|
def down
|
200
|
-
drop_index :t1, [:c1], :name=>:i1
|
201
|
-
drop_index :t1, [:c2, :c1], :unique=>true
|
220
|
+
drop_index :t1, [:c1], :ignore_errors=>true, :name=>:i1
|
221
|
+
drop_index :t1, [:c2, :c1], :ignore_errors=>true, :unique=>true
|
202
222
|
end
|
203
223
|
end
|
204
224
|
END_MIG
|
205
225
|
end
|
206
226
|
|
207
227
|
it "should handle not null values and defaults" do
|
208
|
-
@d.dump_table_schema(:t3).should == "create_table(:t3) do\n Date :c1
|
228
|
+
@d.dump_table_schema(:t3).should == "create_table(:t3) do\n Date :c1\n DateTime :c2, :null=>false\nend"
|
209
229
|
end
|
210
|
-
|
230
|
+
|
231
|
+
it "should handle converting many default formats" do
|
232
|
+
m = @d.method(:column_schema_to_ruby_default)
|
233
|
+
m.call("adf", :string, :same_db=>true).inspect.should == '"adf".lit'
|
234
|
+
p = lambda{|d,t| m.call(d,t,{})}
|
235
|
+
p[nil, :integer].should == nil
|
236
|
+
p['1', :integer].should == 1
|
237
|
+
p['-1', :integer].should == -1
|
238
|
+
p['1.0', :float].should == 1.0
|
239
|
+
p['-1.0', :float].should == -1.0
|
240
|
+
p['1.0', :decimal].should == BigDecimal.new('1.0')
|
241
|
+
p['-1.0', :decimal].should == BigDecimal.new('-1.0')
|
242
|
+
p['1', :boolean].should == true
|
243
|
+
p['0', :boolean].should == false
|
244
|
+
p['true', :boolean].should == true
|
245
|
+
p['false', :boolean].should == false
|
246
|
+
p["'t'", :boolean].should == true
|
247
|
+
p["'f'", :boolean].should == false
|
248
|
+
p["'a'", :string].should == 'a'
|
249
|
+
p["'a'", :blob].should == 'a'.to_sequel_blob
|
250
|
+
p["'a'", :blob].should be_a_kind_of(Sequel::SQL::Blob)
|
251
|
+
p["''", :string].should == ''
|
252
|
+
p["'\\a''b'", :string].should == "\\a'b"
|
253
|
+
p["'NULL'", :string].should == "NULL"
|
254
|
+
p["'2009-10-29'", :date].should == Date.new(2009,10,29)
|
255
|
+
p["CURRENT_TIMESTAMP", :date].should == nil
|
256
|
+
p["today()", :date].should == nil
|
257
|
+
p["'2009-10-29T10:20:30-07:00'", :datetime].should == DateTime.parse('2009-10-29T10:20:30-07:00')
|
258
|
+
p["'2009-10-29 10:20:30'", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
|
259
|
+
p["'10:20:30'", :time].should == Time.parse('10:20:30')
|
260
|
+
p["NaN", :float].should == nil
|
261
|
+
end
|
262
|
+
|
211
263
|
it "should handle converting common defaults" do
|
212
|
-
@d.
|
264
|
+
@d.meta_def(:schema) do |t, *os|
|
265
|
+
[[:c1, {:db_type=>'boolean', :default=>"false", :type=>:boolean, :allow_null=>true}],
|
266
|
+
[:c2, {:db_type=>'varchar', :default=>"'blah'", :type=>:string, :allow_null=>true}],
|
267
|
+
[:c3, {:db_type=>'integer', :default=>"-1", :type=>:integer, :allow_null=>true}],
|
268
|
+
[:c4, {:db_type=>'float', :default=>"1.0", :type=>:float, :allow_null=>true}],
|
269
|
+
[:c5, {:db_type=>'decimal', :default=>"100.50", :type=>:decimal, :allow_null=>true}],
|
270
|
+
[:c6, {:db_type=>'blob', :default=>"'blah'", :type=>:blob, :allow_null=>true}],
|
271
|
+
[:c7, {:db_type=>'date', :default=>"'2008-10-29'", :type=>:date, :allow_null=>true}],
|
272
|
+
[:c8, {:db_type=>'datetime', :default=>"'2008-10-29 10:20:30'", :type=>:datetime, :allow_null=>true}],
|
273
|
+
[:c9, {:db_type=>'time', :default=>"'10:20:30'", :type=>:time, :allow_null=>true}],
|
274
|
+
[:c10, {:db_type=>'interval', :default=>"'6 weeks'", :type=>:interval, :allow_null=>true}]]
|
275
|
+
end
|
276
|
+
@d.dump_table_schema(:t4).gsub(/[+-]\d\d:\d\d"\)/, '")').should == "create_table(:t4) do\n TrueClass :c1, :default=>false\n String :c2, :default=>\"blah\"\n Integer :c3, :default=>-1\n Float :c4, :default=>1.0\n BigDecimal :c5, :default=>BigDecimal.new(\"0.1005E3\")\n File :c6, :default=>Sequel::SQL::Blob.new(\"blah\")\n Date :c7, :default=>Date.parse(\"2008-10-29\")\n DateTime :c8, :default=>DateTime.parse(\"2008-10-29T10:20:30\")\n Time :c9, :default=>Time.parse(\"10:20:30\"), :only_time=>true\n String :c10\nend"
|
277
|
+
@d.dump_table_schema(:t4, :same_db=>true).gsub(/[+-]\d\d:\d\d"\)/, '")').should == "create_table(:t4) do\n column :c1, \"boolean\", :default=>false\n column :c2, \"varchar\", :default=>\"blah\"\n column :c3, \"integer\", :default=>-1\n column :c4, \"float\", :default=>1.0\n column :c5, \"decimal\", :default=>BigDecimal.new(\"0.1005E3\")\n column :c6, \"blob\", :default=>Sequel::SQL::Blob.new(\"blah\")\n column :c7, \"date\", :default=>Date.parse(\"2008-10-29\")\n column :c8, \"datetime\", :default=>DateTime.parse(\"2008-10-29T10:20:30\")\n column :c9, \"time\", :default=>Time.parse(\"10:20:30\")\n column :c10, \"interval\", :default=>\"'6 weeks'\".lit\nend"
|
278
|
+
end
|
279
|
+
|
280
|
+
it "should handle converting PostgreSQL specific default formats" do
|
281
|
+
m = @d.method(:column_schema_to_ruby_default)
|
282
|
+
@d.meta_def(:database_type){:postgres}
|
283
|
+
p = lambda{|d,t| m.call(d,t,{})}
|
284
|
+
p["''::text", :string].should == ""
|
285
|
+
p["'\\a''b'::character varying", :string].should == "\\a'b"
|
286
|
+
p["'a'::bpchar", :string].should == "a"
|
287
|
+
p["(-1)", :integer].should == -1
|
288
|
+
p["(-1.0)", :float].should == -1.0
|
289
|
+
p['(-1.0)', :decimal].should == BigDecimal.new('-1.0')
|
290
|
+
p["'a'::bytea", :blob].should == 'a'.to_sequel_blob
|
291
|
+
p["'a'::bytea", :blob].should be_a_kind_of(Sequel::SQL::Blob)
|
292
|
+
p["'2009-10-29'::date", :date].should == Date.new(2009,10,29)
|
293
|
+
p["'2009-10-29 10:20:30.241343'::timestamp without time zone", :datetime].should == DateTime.parse('2009-10-29 10:20:30.241343')
|
294
|
+
p["'10:20:30'::time without time zone", :time].should == Time.parse('10:20:30')
|
295
|
+
end
|
296
|
+
|
297
|
+
it "should handle converting MySQL specific default formats" do
|
298
|
+
m = @d.method(:column_schema_to_ruby_default)
|
299
|
+
@d.meta_def(:database_type){:mysql}
|
300
|
+
p = lambda{|d,t| m.call(d,t,{})}
|
301
|
+
s = lambda{|d,t| m.call(d,t,{:same_db=>true})}
|
302
|
+
p["\\a'b", :string].should == "\\a'b"
|
303
|
+
p["a", :string].should == "a"
|
304
|
+
p["NULL", :string].should == "NULL"
|
305
|
+
p["-1", :float].should == -1.0
|
306
|
+
p['-1', :decimal].should == BigDecimal.new('-1.0')
|
307
|
+
p["2009-10-29", :date].should == Date.new(2009,10,29)
|
308
|
+
p["2009-10-29 10:20:30", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
|
309
|
+
p["10:20:30", :time].should == Time.parse('10:20:30')
|
310
|
+
p["CURRENT_DATE", :date].should == nil
|
311
|
+
p["CURRENT_TIMESTAMP", :datetime].should == nil
|
312
|
+
s["CURRENT_DATE", :date].inspect.should == "\"CURRENT_DATE\".lit"
|
313
|
+
s["CURRENT_TIMESTAMP", :datetime].inspect.should == "\"CURRENT_TIMESTAMP\".lit"
|
213
314
|
end
|
214
315
|
|
215
316
|
it "should convert unknown database types to strings" do
|
@@ -95,8 +95,7 @@ describe Sequel::Model, "drop_table" do
|
|
95
95
|
end
|
96
96
|
|
97
97
|
describe Sequel::Model, "create_table!" do
|
98
|
-
|
99
|
-
before(:each) do
|
98
|
+
before do
|
100
99
|
MODEL_DB.reset
|
101
100
|
@model = Class.new(Sequel::Model(:items))
|
102
101
|
end
|
@@ -104,8 +103,25 @@ describe Sequel::Model, "create_table!" do
|
|
104
103
|
it "should drop table if it exists and then create the table" do
|
105
104
|
@model.should_receive(:drop_table).and_return(true)
|
106
105
|
@model.should_receive(:create_table).and_return(true)
|
107
|
-
|
108
106
|
@model.create_table!
|
109
107
|
end
|
108
|
+
end
|
110
109
|
|
110
|
+
describe Sequel::Model, "create_table?" do
|
111
|
+
before do
|
112
|
+
MODEL_DB.reset
|
113
|
+
@model = Class.new(Sequel::Model(:items))
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should not create the table if it already exists" do
|
117
|
+
@model.should_receive(:table_exists?).and_return(true)
|
118
|
+
@model.should_not_receive(:create_table)
|
119
|
+
@model.create_table?.should == nil
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should create the table if it doesn't exist" do
|
123
|
+
@model.should_receive(:table_exists?).and_return(false)
|
124
|
+
@model.should_receive(:create_table).and_return(3)
|
125
|
+
@model.create_table?.should == 3
|
126
|
+
end
|
111
127
|
end
|
@@ -14,6 +14,16 @@ describe "Serialization plugin" do
|
|
14
14
|
end
|
15
15
|
MODEL_DB.reset
|
16
16
|
end
|
17
|
+
|
18
|
+
it "should allow setting additional serializable attributes via plugin :serialization call" do
|
19
|
+
@c.plugin :serialization, :yaml, :abc
|
20
|
+
@c.create(:abc => 1, :def=> 2)
|
21
|
+
MODEL_DB.sqls.last.should =~ /INSERT INTO items \((abc, def|def, abc)\) VALUES \(('--- 1\n', 2|2, '--- 1\n')\)/
|
22
|
+
|
23
|
+
@c.plugin :serialization, :marshal, :def
|
24
|
+
@c.create(:abc => 1, :def=> 1)
|
25
|
+
MODEL_DB.sqls.last.should =~ /INSERT INTO items \((abc, def|def, abc)\) VALUES \(('--- 1\n', 'BAhpBg==\n'|'BAhpBg==\n', '--- 1\n')\)/
|
26
|
+
end
|
17
27
|
|
18
28
|
it "should allow serializing attributes to yaml" do
|
19
29
|
@c.plugin :serialization, :yaml, :abc
|
@@ -26,6 +36,16 @@ describe "Serialization plugin" do
|
|
26
36
|
]
|
27
37
|
end
|
28
38
|
|
39
|
+
it "serialization_format should be the serialization format used" do
|
40
|
+
@c.plugin :serialization, :yaml, :abc
|
41
|
+
@c.serialization_format.should == :yaml
|
42
|
+
end
|
43
|
+
|
44
|
+
it "serialized_columns should be the columns serialized" do
|
45
|
+
@c.plugin :serialization, :yaml, :abc
|
46
|
+
@c.serialized_columns.should == [:abc]
|
47
|
+
end
|
48
|
+
|
29
49
|
it "should allow serializing attributes to marshal" do
|
30
50
|
@c.plugin :serialization, :marshal, :abc
|
31
51
|
@c.create(:abc => 1)
|
@@ -115,4 +135,12 @@ describe "Serialization plugin" do
|
|
115
135
|
o.refresh
|
116
136
|
o.deserialized_values.length.should == 0
|
117
137
|
end
|
138
|
+
|
139
|
+
it "should raise an error if calling internal serialization methods with bad columns" do
|
140
|
+
@c.set_primary_key :id
|
141
|
+
@c.plugin :serialization
|
142
|
+
o = @c.load(:id => 1, :abc => "--- 1\n", :def => "--- hello\n")
|
143
|
+
lambda{o.send(:serialize_value, :abc, 1)}.should raise_error(Sequel::Error)
|
144
|
+
lambda{o.send(:deserialize_value, :abc, "--- hello\n")}.should raise_error(Sequel::Error)
|
145
|
+
end
|
118
146
|
end
|
@@ -13,13 +13,37 @@ describe Sequel::Model, "#sti_key" do
|
|
13
13
|
end
|
14
14
|
@ds = StiTest.dataset
|
15
15
|
MODEL_DB.reset
|
16
|
-
end
|
16
|
+
end
|
17
|
+
after do
|
18
|
+
Object.send(:remove_const, :StiTestSub1)
|
19
|
+
Object.send(:remove_const, :StiTestSub2)
|
20
|
+
Object.send(:remove_const, :StiTest)
|
21
|
+
end
|
17
22
|
|
18
23
|
specify "should have simple_table = nil" do
|
19
24
|
StiTest.simple_table.should == nil
|
20
25
|
StiTestSub1.simple_table.should == nil
|
21
26
|
end
|
22
27
|
|
28
|
+
it "should allow changing the inheritance column via a plugin :single_table_inheritance call" do
|
29
|
+
StiTest.plugin :single_table_inheritance, :blah
|
30
|
+
Object.send(:remove_const, :StiTestSub1)
|
31
|
+
Object.send(:remove_const, :StiTestSub2)
|
32
|
+
class ::StiTestSub1 < StiTest
|
33
|
+
end
|
34
|
+
class ::StiTestSub2 < StiTest
|
35
|
+
end
|
36
|
+
def @ds.fetch_rows(sql)
|
37
|
+
yield({:blah=>'StiTest'})
|
38
|
+
yield({:blah=>'StiTestSub1'})
|
39
|
+
yield({:blah=>'StiTestSub2'})
|
40
|
+
end
|
41
|
+
StiTest.all.collect{|x| x.class}.should == [StiTest, StiTestSub1, StiTestSub2]
|
42
|
+
StiTest.dataset.sql.should == "SELECT * FROM sti_tests"
|
43
|
+
StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (blah = 'StiTestSub1')"
|
44
|
+
StiTestSub2.dataset.sql.should == "SELECT * FROM sti_tests WHERE (blah = 'StiTestSub2')"
|
45
|
+
end
|
46
|
+
|
23
47
|
it "should return rows with the correct class based on the polymorphic_key value" do
|
24
48
|
def @ds.fetch_rows(sql)
|
25
49
|
yield({:kind=>'StiTest'})
|
@@ -8,13 +8,8 @@ unless Sequel.const_defined?('Model')
|
|
8
8
|
require 'sequel/model'
|
9
9
|
end
|
10
10
|
|
11
|
-
Sequel.
|
12
|
-
|
13
|
-
extensions = %w'string_date_time inflector pagination query pretty_table blank migration schema_dumper'
|
14
|
-
plugins = {:hook_class_methods=>[], :schema=>[], :validation_class_methods=>[]}
|
15
|
-
|
16
|
-
extensions.each{|e| require "sequel/extensions/#{e}"}
|
17
|
-
plugins.each{|p, opts| Sequel::Model.plugin(p, *opts)}
|
11
|
+
Sequel.extension(*%w'string_date_time inflector pagination query pretty_table blank migration schema_dumper')
|
12
|
+
{:hook_class_methods=>[], :schema=>[], :validation_class_methods=>[]}.each{|p, opts| Sequel::Model.plugin(p, *opts)}
|
18
13
|
|
19
14
|
class MockDataset < Sequel::Dataset
|
20
15
|
def insert(*args)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe "Sequel::Plugins::TacticalEagerLoading" do
|
4
|
+
before do
|
5
|
+
class ::TaticalEagerLoadingModel < Sequel::Model
|
6
|
+
plugin :tactical_eager_loading
|
7
|
+
columns :id, :parent_id
|
8
|
+
many_to_one :parent, :class=>self
|
9
|
+
one_to_many :children, :class=>self, :key=>:parent_id
|
10
|
+
ds = dataset
|
11
|
+
def ds.fetch_rows(sql)
|
12
|
+
execute(sql)
|
13
|
+
where = @opts[:where]
|
14
|
+
if !where
|
15
|
+
yield(:id=>1, :parent_id=>101)
|
16
|
+
yield(:id=>2, :parent_id=>102)
|
17
|
+
yield(:id=>101, :parent_id=>nil)
|
18
|
+
yield(:id=>102, :parent_id=>nil)
|
19
|
+
elsif where.args.first.column == :id
|
20
|
+
Array(where.args.last).each do |x|
|
21
|
+
yield(:id=>x, :parent_id=>nil)
|
22
|
+
end
|
23
|
+
elsif where.args.first.column == :parent_id
|
24
|
+
Array(where.args.last).each do |x|
|
25
|
+
yield(:id=>x-100, :parent_id=>x) if x > 100
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@c = ::TaticalEagerLoadingModel
|
31
|
+
@ds = TaticalEagerLoadingModel.dataset
|
32
|
+
MODEL_DB.reset
|
33
|
+
end
|
34
|
+
after do
|
35
|
+
Object.send(:remove_const, :TaticalEagerLoadingModel)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "Dataset#all should set the retrieved_by and reteived_with attributes" do
|
39
|
+
ts = @c.all
|
40
|
+
ts.map{|x| [x.retrieved_by, x.retrieved_with]}.should == [[@ds,ts], [@ds,ts], [@ds,ts], [@ds,ts]]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "Dataset#all shouldn't raise an error if a Sequel::Model instance is not returned" do
|
44
|
+
proc{@c.naked.all}.should_not raise_error
|
45
|
+
end
|
46
|
+
|
47
|
+
it "association getter methods should eagerly load the association if the association isn't cached" do
|
48
|
+
MODEL_DB.sqls.length.should == 0
|
49
|
+
ts = @c.all
|
50
|
+
MODEL_DB.sqls.length.should == 1
|
51
|
+
ts.map{|x| x.parent}.should == [ts[2], ts[3], nil, nil]
|
52
|
+
MODEL_DB.sqls.length.should == 2
|
53
|
+
ts.map{|x| x.children}.should == [[], [], [ts[0]], [ts[1]]]
|
54
|
+
MODEL_DB.sqls.length.should == 3
|
55
|
+
end
|
56
|
+
|
57
|
+
it "association getter methods should not eagerly load the association if the association is cached" do
|
58
|
+
MODEL_DB.sqls.length.should == 0
|
59
|
+
ts = @c.all
|
60
|
+
MODEL_DB.sqls.length.should == 1
|
61
|
+
ts.map{|x| x.parent}.should == [ts[2], ts[3], nil, nil]
|
62
|
+
@ds.should_not_receive(:eager_load)
|
63
|
+
ts.map{|x| x.parent}.should == [ts[2], ts[3], nil, nil]
|
64
|
+
end
|
65
|
+
end
|
@@ -116,7 +116,8 @@ end
|
|
116
116
|
|
117
117
|
describe Sequel::Model do
|
118
118
|
before do
|
119
|
-
@c = Class.new(Sequel::Model)
|
119
|
+
@c = Class.new(Sequel::Model)
|
120
|
+
@c.class_eval do
|
120
121
|
columns :score
|
121
122
|
validates_each :score do |o, a, v|
|
122
123
|
o.errors[a] << 'too low' if v < 87
|
@@ -551,12 +552,14 @@ end
|
|
551
552
|
|
552
553
|
context "Superclass validations" do
|
553
554
|
before do
|
554
|
-
@c1 = Class.new(Sequel::Model)
|
555
|
+
@c1 = Class.new(Sequel::Model)
|
556
|
+
@c1.class_eval do
|
555
557
|
columns :value
|
556
558
|
validates_length_of :value, :minimum => 5
|
557
559
|
end
|
558
560
|
|
559
|
-
@c2 = Class.new(@c1)
|
561
|
+
@c2 = Class.new(@c1)
|
562
|
+
@c2.class_eval do
|
560
563
|
columns :value
|
561
564
|
validates_format_of :value, :with => /^[a-z]+$/
|
562
565
|
end
|
@@ -601,7 +604,8 @@ end
|
|
601
604
|
|
602
605
|
context ".validates with block" do
|
603
606
|
specify "should support calling .each" do
|
604
|
-
@c = Class.new(Sequel::Model)
|
607
|
+
@c = Class.new(Sequel::Model)
|
608
|
+
@c.class_eval do
|
605
609
|
columns :vvv
|
606
610
|
validates do
|
607
611
|
each :vvv do |o, a, v|
|
@@ -938,7 +942,8 @@ end
|
|
938
942
|
|
939
943
|
describe "Model#save" do
|
940
944
|
before do
|
941
|
-
@c = Class.new(Sequel::Model(:people))
|
945
|
+
@c = Class.new(Sequel::Model(:people))
|
946
|
+
@c.class_eval do
|
942
947
|
columns :id
|
943
948
|
|
944
949
|
validates_each :id do |o, a, v|
|
@@ -120,7 +120,9 @@ describe Sequel::Dataset do
|
|
120
120
|
proc {@d.literal(true)}.should_not raise_error
|
121
121
|
proc {@d.literal(false)}.should_not raise_error
|
122
122
|
end
|
123
|
+
end
|
123
124
|
|
125
|
+
describe Sequel::Database do
|
124
126
|
specify "should correctly escape strings" do
|
125
127
|
INTEGRATION_DB.get("\\dingo".as(:a)) == "\\dingo"
|
126
128
|
end
|
@@ -132,6 +134,14 @@ describe Sequel::Dataset do
|
|
132
134
|
specify "should properly escape binary data" do
|
133
135
|
INTEGRATION_DB.get("\1\2\3".to_sequel_blob.as(:a)) == "\1\2\3"
|
134
136
|
end
|
137
|
+
|
138
|
+
specify "should have a working table_exists?" do
|
139
|
+
t = :basdfdsafsaddsaf
|
140
|
+
INTEGRATION_DB.drop_table(t) rescue nil
|
141
|
+
INTEGRATION_DB.table_exists?(t).should == false
|
142
|
+
INTEGRATION_DB.create_table(t){Integer :a}
|
143
|
+
INTEGRATION_DB.table_exists?(t).should == true
|
144
|
+
end
|
135
145
|
end
|
136
146
|
|
137
147
|
context "An SQLite dataset" do
|
@@ -208,7 +218,7 @@ describe "Simple Dataset operations in transactions" do
|
|
208
218
|
specify "should insert correctly with a primary key specified inside a transaction" do
|
209
219
|
INTEGRATION_DB.transaction do
|
210
220
|
@ds.insert(:id=>100, :number=>20)
|
211
|
-
sqls_should_be('
|
221
|
+
sqls_should_be('BEGIN', /INSERT INTO items_insert_in_transaction \((number, id|id, number)\) VALUES \((100, 20|20, 100)\)/)
|
212
222
|
@ds.count.should == 1
|
213
223
|
@ds.order(:id).all.should == [{:id=>100, :number=>20}]
|
214
224
|
end
|
@@ -217,7 +227,7 @@ describe "Simple Dataset operations in transactions" do
|
|
217
227
|
specify "should have insert return primary key value inside a transaction" do
|
218
228
|
INTEGRATION_DB.transaction do
|
219
229
|
@ds.insert(:number=>20).should == 1
|
220
|
-
sqls_should_be('
|
230
|
+
sqls_should_be('BEGIN', /INSERT INTO items_insert_in_transaction \(number\) VALUES \(20\)/)
|
221
231
|
@ds.count.should == 1
|
222
232
|
@ds.order(:id).all.should == [{:id=>1, :number=>20}]
|
223
233
|
end
|
@@ -228,16 +238,12 @@ describe "Dataset UNION, EXCEPT, and INTERSECT" do
|
|
228
238
|
before do
|
229
239
|
INTEGRATION_DB.create_table!(:i1){integer :number}
|
230
240
|
INTEGRATION_DB.create_table!(:i2){integer :number}
|
231
|
-
INTEGRATION_DB.create_table!(:i3){integer :number}
|
232
241
|
@ds1 = INTEGRATION_DB[:i1]
|
233
242
|
@ds1.insert(:number=>10)
|
234
243
|
@ds1.insert(:number=>20)
|
235
244
|
@ds2 = INTEGRATION_DB[:i2]
|
236
245
|
@ds2.insert(:number=>10)
|
237
246
|
@ds2.insert(:number=>30)
|
238
|
-
@ds3 = INTEGRATION_DB[:i3]
|
239
|
-
@ds3.insert(:number=>10)
|
240
|
-
@ds3.insert(:number=>40)
|
241
247
|
clear_sqls
|
242
248
|
end
|
243
249
|
|
@@ -249,7 +255,34 @@ describe "Dataset UNION, EXCEPT, and INTERSECT" do
|
|
249
255
|
end
|
250
256
|
end
|
251
257
|
|
258
|
+
specify "should give the correct results for UNION, EXCEPT, and INTERSECT when used with ordering and limits" do
|
259
|
+
@ds1.insert(:number=>8)
|
260
|
+
@ds2.insert(:number=>9)
|
261
|
+
@ds1.insert(:number=>38)
|
262
|
+
@ds2.insert(:number=>39)
|
263
|
+
|
264
|
+
@ds1.order(:number.desc).union(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'8 9 10 20 30 38 39'
|
265
|
+
@ds1.union(@ds2.order(:number.desc)).order(:number).map{|x| x[:number].to_s}.should == %w'8 9 10 20 30 38 39'
|
266
|
+
|
267
|
+
@ds1.order(:number.desc).limit(1).union(@ds2).order(:number).map{|x| x[:number].to_s}.should == %w'9 10 30 38 39'
|
268
|
+
@ds2.order(:number.desc).limit(1).union(@ds1).order(:number).map{|x| x[:number].to_s}.should == %w'8 10 20 38 39'
|
269
|
+
|
270
|
+
@ds1.union(@ds2.order(:number).limit(1)).order(:number).map{|x| x[:number].to_s}.should == %w'8 9 10 20 38'
|
271
|
+
@ds2.union(@ds1.order(:number).limit(1)).order(:number).map{|x| x[:number].to_s}.should == %w'8 9 10 30 39'
|
272
|
+
|
273
|
+
@ds1.union(@ds2).limit(2).order(:number).map{|x| x[:number].to_s}.should == %w'8 9'
|
274
|
+
@ds2.union(@ds1).order(:number.desc).limit(2).map{|x| x[:number].to_s}.should == %w'39 38'
|
275
|
+
|
276
|
+
@ds1.order(:number.desc).limit(2).union(@ds2.order(:number.desc).limit(2)).order(:number).limit(3).map{|x| x[:number].to_s}.should == %w'20 30 38'
|
277
|
+
@ds2.order(:number).limit(2).union(@ds1.order(:number).limit(2)).order(:number.desc).limit(3).map{|x| x[:number].to_s}.should == %w'10 9 8'
|
278
|
+
end
|
279
|
+
|
252
280
|
specify "should give the correct results for compound UNION, EXCEPT, and INTERSECT" do
|
281
|
+
INTEGRATION_DB.create_table!(:i3){integer :number}
|
282
|
+
@ds3 = INTEGRATION_DB[:i3]
|
283
|
+
@ds3.insert(:number=>10)
|
284
|
+
@ds3.insert(:number=>40)
|
285
|
+
|
253
286
|
@ds1.union(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30 40'
|
254
287
|
@ds1.union(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.should == %w'10 20 30 40'
|
255
288
|
unless defined?(Sequel::Dataset::UnsupportedIntersectExcept) and @ds1.class.ancestors.include?(Sequel::Dataset::UnsupportedIntersectExcept)
|