sequel 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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)
|