sequel 3.46.0 → 3.47.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +96 -0
  3. data/Rakefile +7 -1
  4. data/bin/sequel +6 -4
  5. data/doc/active_record.rdoc +1 -1
  6. data/doc/advanced_associations.rdoc +14 -35
  7. data/doc/association_basics.rdoc +66 -4
  8. data/doc/migration.rdoc +4 -0
  9. data/doc/opening_databases.rdoc +6 -0
  10. data/doc/postgresql.rdoc +302 -0
  11. data/doc/release_notes/3.47.0.txt +270 -0
  12. data/doc/security.rdoc +6 -0
  13. data/lib/sequel/adapters/ibmdb.rb +9 -9
  14. data/lib/sequel/adapters/jdbc.rb +22 -7
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
  16. data/lib/sequel/adapters/mock.rb +2 -0
  17. data/lib/sequel/adapters/postgres.rb +44 -13
  18. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  19. data/lib/sequel/adapters/shared/mysql.rb +2 -2
  20. data/lib/sequel/adapters/shared/postgres.rb +94 -55
  21. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  22. data/lib/sequel/adapters/sqlite.rb +2 -2
  23. data/lib/sequel/adapters/utils/pg_types.rb +1 -14
  24. data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
  25. data/lib/sequel/connection_pool/threaded.rb +1 -1
  26. data/lib/sequel/core.rb +1 -1
  27. data/lib/sequel/database/connecting.rb +2 -2
  28. data/lib/sequel/database/features.rb +5 -0
  29. data/lib/sequel/database/misc.rb +47 -5
  30. data/lib/sequel/database/query.rb +2 -2
  31. data/lib/sequel/dataset/actions.rb +4 -2
  32. data/lib/sequel/dataset/misc.rb +1 -1
  33. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  34. data/lib/sequel/dataset/query.rb +8 -6
  35. data/lib/sequel/dataset/sql.rb +8 -6
  36. data/lib/sequel/extensions/constraint_validations.rb +5 -2
  37. data/lib/sequel/extensions/migration.rb +10 -8
  38. data/lib/sequel/extensions/pagination.rb +3 -0
  39. data/lib/sequel/extensions/pg_array.rb +85 -25
  40. data/lib/sequel/extensions/pg_hstore.rb +8 -1
  41. data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
  42. data/lib/sequel/extensions/pg_inet.rb +16 -13
  43. data/lib/sequel/extensions/pg_interval.rb +6 -2
  44. data/lib/sequel/extensions/pg_json.rb +18 -11
  45. data/lib/sequel/extensions/pg_range.rb +17 -2
  46. data/lib/sequel/extensions/pg_range_ops.rb +7 -5
  47. data/lib/sequel/extensions/pg_row.rb +29 -12
  48. data/lib/sequel/extensions/pretty_table.rb +3 -0
  49. data/lib/sequel/extensions/query.rb +3 -0
  50. data/lib/sequel/extensions/schema_caching.rb +2 -0
  51. data/lib/sequel/extensions/schema_dumper.rb +3 -1
  52. data/lib/sequel/extensions/select_remove.rb +3 -0
  53. data/lib/sequel/model.rb +8 -2
  54. data/lib/sequel/model/associations.rb +39 -27
  55. data/lib/sequel/model/base.rb +99 -38
  56. data/lib/sequel/model/plugins.rb +25 -0
  57. data/lib/sequel/plugins/association_autoreloading.rb +27 -22
  58. data/lib/sequel/plugins/association_dependencies.rb +1 -7
  59. data/lib/sequel/plugins/auto_validations.rb +110 -0
  60. data/lib/sequel/plugins/boolean_readers.rb +1 -6
  61. data/lib/sequel/plugins/caching.rb +6 -13
  62. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  63. data/lib/sequel/plugins/composition.rb +14 -7
  64. data/lib/sequel/plugins/constraint_validations.rb +2 -13
  65. data/lib/sequel/plugins/defaults_setter.rb +1 -6
  66. data/lib/sequel/plugins/dirty.rb +8 -0
  67. data/lib/sequel/plugins/error_splitter.rb +54 -0
  68. data/lib/sequel/plugins/force_encoding.rb +1 -5
  69. data/lib/sequel/plugins/hook_class_methods.rb +1 -6
  70. data/lib/sequel/plugins/input_transformer.rb +79 -0
  71. data/lib/sequel/plugins/instance_filters.rb +7 -1
  72. data/lib/sequel/plugins/instance_hooks.rb +7 -1
  73. data/lib/sequel/plugins/json_serializer.rb +5 -10
  74. data/lib/sequel/plugins/lazy_attributes.rb +20 -7
  75. data/lib/sequel/plugins/list.rb +1 -6
  76. data/lib/sequel/plugins/many_through_many.rb +1 -2
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
  78. data/lib/sequel/plugins/optimistic_locking.rb +1 -5
  79. data/lib/sequel/plugins/pg_row.rb +4 -2
  80. data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
  81. data/lib/sequel/plugins/prepared_statements.rb +1 -5
  82. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
  83. data/lib/sequel/plugins/rcte_tree.rb +2 -2
  84. data/lib/sequel/plugins/serialization.rb +11 -13
  85. data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
  86. data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
  87. data/lib/sequel/plugins/static_cache.rb +67 -19
  88. data/lib/sequel/plugins/string_stripper.rb +7 -27
  89. data/lib/sequel/plugins/subclasses.rb +3 -5
  90. data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
  91. data/lib/sequel/plugins/timestamps.rb +2 -7
  92. data/lib/sequel/plugins/touch.rb +5 -8
  93. data/lib/sequel/plugins/tree.rb +1 -6
  94. data/lib/sequel/plugins/typecast_on_load.rb +1 -5
  95. data/lib/sequel/plugins/update_primary_key.rb +26 -14
  96. data/lib/sequel/plugins/validation_class_methods.rb +31 -16
  97. data/lib/sequel/plugins/validation_helpers.rb +50 -26
  98. data/lib/sequel/plugins/xml_serializer.rb +3 -6
  99. data/lib/sequel/sql.rb +1 -1
  100. data/lib/sequel/version.rb +1 -1
  101. data/spec/adapters/postgres_spec.rb +131 -15
  102. data/spec/adapters/sqlite_spec.rb +1 -1
  103. data/spec/core/connection_pool_spec.rb +16 -17
  104. data/spec/core/database_spec.rb +111 -40
  105. data/spec/core/dataset_spec.rb +65 -74
  106. data/spec/core/expression_filters_spec.rb +6 -5
  107. data/spec/core/object_graph_spec.rb +0 -1
  108. data/spec/core/schema_spec.rb +23 -23
  109. data/spec/core/spec_helper.rb +5 -1
  110. data/spec/extensions/association_dependencies_spec.rb +1 -1
  111. data/spec/extensions/association_proxies_spec.rb +1 -1
  112. data/spec/extensions/auto_validations_spec.rb +90 -0
  113. data/spec/extensions/caching_spec.rb +6 -0
  114. data/spec/extensions/class_table_inheritance_spec.rb +8 -1
  115. data/spec/extensions/composition_spec.rb +12 -5
  116. data/spec/extensions/constraint_validations_spec.rb +4 -4
  117. data/spec/extensions/core_refinements_spec.rb +29 -79
  118. data/spec/extensions/dirty_spec.rb +14 -0
  119. data/spec/extensions/error_splitter_spec.rb +18 -0
  120. data/spec/extensions/identity_map_spec.rb +0 -1
  121. data/spec/extensions/input_transformer_spec.rb +54 -0
  122. data/spec/extensions/instance_filters_spec.rb +6 -0
  123. data/spec/extensions/instance_hooks_spec.rb +12 -1
  124. data/spec/extensions/json_serializer_spec.rb +0 -1
  125. data/spec/extensions/lazy_attributes_spec.rb +64 -55
  126. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  127. data/spec/extensions/many_through_many_spec.rb +3 -4
  128. data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
  129. data/spec/extensions/migration_spec.rb +16 -0
  130. data/spec/extensions/null_dataset_spec.rb +1 -1
  131. data/spec/extensions/pg_array_spec.rb +48 -1
  132. data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
  133. data/spec/extensions/pg_hstore_spec.rb +5 -0
  134. data/spec/extensions/pg_inet_spec.rb +5 -0
  135. data/spec/extensions/pg_interval_spec.rb +7 -3
  136. data/spec/extensions/pg_json_spec.rb +6 -1
  137. data/spec/extensions/pg_range_ops_spec.rb +4 -1
  138. data/spec/extensions/pg_range_spec.rb +5 -0
  139. data/spec/extensions/pg_row_plugin_spec.rb +13 -0
  140. data/spec/extensions/pg_row_spec.rb +28 -19
  141. data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
  142. data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
  143. data/spec/extensions/query_literals_spec.rb +1 -1
  144. data/spec/extensions/rcte_tree_spec.rb +2 -2
  145. data/spec/extensions/schema_spec.rb +2 -2
  146. data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
  147. data/spec/extensions/serialization_spec.rb +15 -1
  148. data/spec/extensions/sharding_spec.rb +1 -1
  149. data/spec/extensions/single_table_inheritance_spec.rb +1 -1
  150. data/spec/extensions/static_cache_spec.rb +59 -9
  151. data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
  152. data/spec/extensions/update_primary_key_spec.rb +17 -1
  153. data/spec/extensions/validation_class_methods_spec.rb +25 -0
  154. data/spec/extensions/validation_helpers_spec.rb +59 -3
  155. data/spec/integration/associations_test.rb +5 -5
  156. data/spec/integration/eager_loader_test.rb +32 -63
  157. data/spec/integration/model_test.rb +2 -2
  158. data/spec/integration/plugin_test.rb +88 -56
  159. data/spec/integration/prepared_statement_test.rb +1 -1
  160. data/spec/integration/schema_test.rb +1 -1
  161. data/spec/integration/timezone_test.rb +0 -1
  162. data/spec/integration/transaction_test.rb +0 -1
  163. data/spec/model/association_reflection_spec.rb +1 -1
  164. data/spec/model/associations_spec.rb +106 -84
  165. data/spec/model/base_spec.rb +4 -4
  166. data/spec/model/eager_loading_spec.rb +8 -8
  167. data/spec/model/model_spec.rb +27 -9
  168. data/spec/model/plugins_spec.rb +71 -0
  169. data/spec/model/record_spec.rb +99 -13
  170. metadata +12 -2
@@ -18,12 +18,17 @@ describe Sequel::Model, "PgTypecastOnLoad plugin" do
18
18
  @c.first.refresh.values.should == {:id=>1, :b=>true, :y=>0}
19
19
  end
20
20
 
21
+ specify "should not fail if schema oid does not have a related conversion proc" do
22
+ @c.db_schema[:b][:oid] = 0
23
+ @c.first.refresh.values.should == {:id=>1, :b=>"t", :y=>0}
24
+ end
25
+
21
26
  specify "should call the database conversion proc with value when automatically reloading the object on creation via insert_select" do
22
27
  @c.dataset.meta_def(:insert_select){|h| insert(h); first}
23
28
  @c.create.values.should == {:id=>1, :b=>true, :y=>0}
24
29
  end
25
30
 
26
- specify "should allowing setting columns separately via add_typecast_on_load_columns" do
31
+ specify "should allowing setting columns separately via add_pg_typecast_on_load_columns" do
27
32
  @c = Class.new(Sequel::Model(@db[:items]))
28
33
  @c.plugin :pg_typecast_on_load
29
34
  @c.first.values.should == {:id=>1, :b=>"t", :y=>"0"}
@@ -1,6 +1,6 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
- describe "Sequel::Plugins::AssociationPks" do
3
+ describe "Sequel::Plugins::PreparedStatementsAssociations" do
4
4
  before do
5
5
  @db = Sequel.mock
6
6
  @db.extend_datasets do
@@ -34,7 +34,7 @@ describe "query_literals extension" do
34
34
  @ds.select_more('a, b, c').sql.should == 'SELECT d, a, b, c FROM t'
35
35
  end
36
36
 
37
- it "should have #select_ use placeholder literal string if given a string and additional arguments" do
37
+ it "should have #select_more use placeholder literal string if given a string and additional arguments" do
38
38
  @ds.select_more('a, b, ?', 1).sql.should == 'SELECT d, a, b, 1 FROM t'
39
39
  end
40
40
 
@@ -144,7 +144,7 @@ describe Sequel::Model, "rcte_tree" do
144
144
  [{:id=>2, :name=>'AA', :parent_id=>1, :x_root_x=>2},
145
145
  {:id=>1, :name=>'00', :parent_id=>8, :x_root_x=>1}, {:id=>1, :name=>'00', :parent_id=>8, :x_root_x=>2},
146
146
  {:id=>8, :name=>'?', :parent_id=>nil, :x_root_x=>2}, {:id=>8, :name=>'?', :parent_id=>nil, :x_root_x=>1}]]
147
- os = @ds.eager(:ancestors).all
147
+ @ds.eager(:ancestors).all
148
148
  sqls = MODEL_DB.sqls
149
149
  sqls.first.should == "SELECT * FROM nodes"
150
150
  sqls.last.should =~ /WITH t AS \(SELECT id AS x_root_x, nodes\.\* FROM nodes WHERE \(\(id IN \([12], [12]\)\) AND \(i = 1\)\) UNION ALL SELECT t\.x_root_x, nodes\.\* FROM nodes INNER JOIN t ON \(t\.parent_id = nodes\.id\) WHERE \(i = 1\)\) SELECT \* FROM t AS nodes WHERE \(i = 1\)/
@@ -236,7 +236,7 @@ describe Sequel::Model, "rcte_tree" do
236
236
  [{:id=>6, :parent_id=>2, :name=>'C', :x_root_x=>2}, {:id=>9, :parent_id=>2, :name=>'E', :x_root_x=>2},
237
237
  {:id=>3, :name=>'00', :parent_id=>6, :x_root_x=>6}, {:id=>3, :name=>'00', :parent_id=>6, :x_root_x=>2},
238
238
  {:id=>4, :name=>'?', :parent_id=>7, :x_root_x=>7}, {:id=>5, :name=>'?', :parent_id=>4, :x_root_x=>7}]]
239
- os = @ds.eager(:descendants).all
239
+ @ds.eager(:descendants).all
240
240
  sqls = MODEL_DB.sqls
241
241
  sqls.first.should == "SELECT * FROM nodes"
242
242
  sqls.last.should =~ /WITH t AS \(SELECT parent_id AS x_root_x, nodes\.\* FROM nodes WHERE \(\(parent_id IN \([267], [267], [267]\)\) AND \(i = 1\)\) UNION ALL SELECT t\.x_root_x, nodes\.\* FROM nodes INNER JOIN t ON \(t\.id = nodes\.parent_id\) WHERE \(i = 1\)\) SELECT \* FROM t AS nodes WHERE \(i = 1\)/
@@ -1,6 +1,6 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
- describe Sequel::Model, "dataset & schema" do
3
+ describe Sequel::Model, "set_schema" do
4
4
  before do
5
5
  @model = Class.new(Sequel::Model(:items))
6
6
  @model.plugin :schema
@@ -8,7 +8,7 @@ describe Sequel::Model, "dataset & schema" do
8
8
 
9
9
  specify "sets schema with implicit table name" do
10
10
  @model.set_schema do
11
- primary_key :ssn, :string
11
+ primary_key :ssn, :type=>:string
12
12
  end
13
13
  @model.primary_key.should == :ssn
14
14
  @model.table_name.should == :items
@@ -69,4 +69,12 @@ describe "serialization_modification_detection plugin" do
69
69
  @o4.save
70
70
  @o4.changed_columns.should == []
71
71
  end
72
+
73
+ it "should work with frozen objects" do
74
+ @o1.changed_columns.should == []
75
+ @o1.h.should == {}
76
+ @o1.freeze
77
+ @o1.h[1] = 2
78
+ @o1.changed_columns.should == [:h]
79
+ end
72
80
  end
@@ -76,7 +76,6 @@ describe "Serialization plugin" do
76
76
  it "should translate values to and from yaml serialization format using accessor methods" do
77
77
  @c.set_primary_key :id
78
78
  @c.plugin :serialization, :yaml, :abc, :def
79
- vals = nil
80
79
  @c.dataset._fetch = {:id => 1, :abc => "--- 1\n", :def => "--- hello\n"}
81
80
 
82
81
  o = @c.first
@@ -290,4 +289,19 @@ describe "Serialization plugin" do
290
289
  o = @c.load(:abc => 3)
291
290
  o.abc.should == 9
292
291
  end
292
+
293
+ it "should work correctly with frozen instances" do
294
+ @c.set_primary_key :id
295
+ @c.plugin :serialization, :yaml, :abc, :def
296
+ @c.dataset._fetch = {:id => 1, :abc => "--- 1\n", :def => "--- hello\n"}
297
+
298
+ o = @c.first
299
+ o.freeze
300
+ o.abc.should == 1
301
+ o.abc.should == 1
302
+ o.def.should == "hello"
303
+ o.def.should == "hello"
304
+ proc{o.abc = 2}.should raise_error
305
+ proc{o.def = 'h'}.should raise_error
306
+ end
293
307
  end
@@ -161,7 +161,7 @@ describe "sharding plugin" do
161
161
  end
162
162
 
163
163
  specify "should not override a server already set on an associated object" do
164
- album = @Album.server(:s1).first
164
+ @Album.server(:s1).first
165
165
  artist = @Artist.server(:s2).first
166
166
  @db.sqls.should == ["SELECT * FROM albums LIMIT 1 -- s1", "SELECT * FROM artists LIMIT 1 -- s2"]
167
167
 
@@ -1,6 +1,6 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
- describe Sequel::Model, "#sti_key" do
3
+ describe Sequel::Model, "single table inheritance plugin" do
4
4
  before do
5
5
  class ::StiTest < Sequel::Model
6
6
  columns :id, :kind, :blah
@@ -67,13 +67,27 @@ describe "Sequel::Plugins::StaticCache" do
67
67
  @db.sqls.should == []
68
68
  end
69
69
 
70
- it "should have map send a query if given an argument" do
70
+ it "should have count with no argument or block not issue a query" do
71
+ @c.count.should == 2
72
+ @db.sqls.should == []
73
+ end
74
+
75
+ it "should have count with argument or block not issue a query" do
76
+ @db.fetch = [[{:count=>1}], [{:count=>2}]]
77
+ @c.count(:a).should == 1
78
+ @c.count{b}.should == 2
79
+ @db.sqls.should == ["SELECT COUNT(a) AS count FROM t LIMIT 1", "SELECT COUNT(b) AS count FROM t LIMIT 1"]
80
+ end
81
+
82
+ it "should have map not send a query if given an argument" do
71
83
  @c.map(:id).sort.should == [1, 2]
72
- @db.sqls.should == ["SELECT * FROM t"]
84
+ @db.sqls.should == []
85
+ @c.map([:id,:id]).sort.should == [[1,1], [2,2]]
86
+ @db.sqls.should == []
73
87
  end
74
88
 
75
89
  it "should have map without a block or argument not raise an exception or issue a query" do
76
- @c.map
90
+ @c.map.to_a.should == @c.all
77
91
  @db.sqls.should == []
78
92
  end
79
93
 
@@ -81,6 +95,10 @@ describe "Sequel::Plugins::StaticCache" do
81
95
  @c.map.frozen?.should be_false
82
96
  end
83
97
 
98
+ it "should have map with a block and argument raise" do
99
+ proc{@c.map(:id){}}.should raise_error(Sequel::Error)
100
+ end
101
+
84
102
  it "should have other enumerable methods work without sending a query" do
85
103
  a = @c.sort_by{|o| o.id}
86
104
  a.first.should equal(@c1)
@@ -88,7 +106,7 @@ describe "Sequel::Plugins::StaticCache" do
88
106
  @db.sqls.should == []
89
107
  end
90
108
 
91
- it "should have all just return the hashes' values" do
109
+ it "should have all just return the cached values" do
92
110
  a = @c.all.sort_by{|o| o.id}
93
111
  a.first.should equal(@c1)
94
112
  a.last.should equal(@c2)
@@ -105,22 +123,54 @@ describe "Sequel::Plugins::StaticCache" do
105
123
 
106
124
  it "should have to_hash without arguments return the cached objects without a query" do
107
125
  a = @c.to_hash
126
+ a.should == {1=>@c1, 2=>@c2}
108
127
  a[1].should equal(@c1)
109
128
  a[2].should equal(@c2)
110
129
  @db.sqls.should == []
111
130
  end
112
131
 
113
- it "should have to_hash with any arguments use a query" do
114
- @c.to_hash(:id).should == {1=>@c1, 2=>@c2}
115
- @db.sqls.should == ['SELECT * FROM t']
132
+ it "should have to_hash with arguments return the cached objects without a query" do
133
+ a = @c.to_hash(:id)
134
+ a.should == {1=>@c1, 2=>@c2}
135
+ a[1].should equal(@c1)
136
+ a[2].should equal(@c2)
137
+
138
+ a = @c.to_hash([:id])
139
+ a.should == {[1]=>@c1, [2]=>@c2}
140
+ a[[1]].should equal(@c1)
141
+ a[[2]].should equal(@c2)
142
+
116
143
  @c.to_hash(:id, :id).should == {1=>1, 2=>2}
117
- @db.sqls.should == ['SELECT * FROM t']
144
+ @c.to_hash([:id], :id).should == {[1]=>1, [2]=>2}
145
+ @c.to_hash(:id, [:id]).should == {1=>[1], 2=>[2]}
146
+ @c.to_hash([:id], [:id]).should == {[1]=>[1], [2]=>[2]}
147
+
148
+ @db.sqls.should == []
118
149
  end
119
150
 
120
- it "should have all not return a frozen object" do
151
+ it "should have to_hash not return a frozen object" do
121
152
  @c.to_hash.frozen?.should be_false
122
153
  end
123
154
 
155
+ it "should have to_hash_groups without arguments return the cached objects without a query" do
156
+ a = @c.to_hash_groups(:id)
157
+ a.should == {1=>[@c1], 2=>[@c2]}
158
+ a[1].first.should equal(@c1)
159
+ a[2].first.should equal(@c2)
160
+
161
+ a = @c.to_hash_groups([:id])
162
+ a.should == {[1]=>[@c1], [2]=>[@c2]}
163
+ a[[1]].first.should equal(@c1)
164
+ a[[2]].first.should equal(@c2)
165
+
166
+ @c.to_hash_groups(:id, :id).should == {1=>[1], 2=>[2]}
167
+ @c.to_hash_groups([:id], :id).should == {[1]=>[1], [2]=>[2]}
168
+ @c.to_hash_groups(:id, [:id]).should == {1=>[[1]], 2=>[[2]]}
169
+ @c.to_hash_groups([:id], [:id]).should == {[1]=>[[1]], [2]=>[[2]]}
170
+
171
+ @db.sqls.should == []
172
+ end
173
+
124
174
  it "all of the static cache values (model instances) should be frozen" do
125
175
  @c.all.all?{|o| o.frozen?}.should be_true
126
176
  end
@@ -2,7 +2,7 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "Sequel::Plugins::TacticalEagerLoading" do
4
4
  before do
5
- class ::TaticalEagerLoadingModel < Sequel::Model
5
+ class ::TacticalEagerLoadingModel < Sequel::Model
6
6
  plugin :tactical_eager_loading
7
7
  columns :id, :parent_id
8
8
  many_to_one :parent, :class=>self
@@ -10,6 +10,8 @@ describe "Sequel::Plugins::TacticalEagerLoading" do
10
10
  dataset._fetch = proc do |sql|
11
11
  if sql !~ /WHERE/
12
12
  [{:id=>1, :parent_id=>101}, {:id=>2, :parent_id=>102}, {:id=>101, :parent_id=>nil}, {:id=>102, :parent_id=>nil}]
13
+ elsif sql =~ /WHERE.*\bid = (\d+)/
14
+ [{:id=>$1.to_i, :parent_id=>nil}]
13
15
  elsif sql =~ /WHERE.*\bid IN \(([\d, ]*)\)/
14
16
  $1.split(', ').map{|x| {:id=>x.to_i, :parent_id=>nil}}
15
17
  elsif sql =~ /WHERE.*\bparent_id IN \(([\d, ]*)\)/
@@ -17,12 +19,12 @@ describe "Sequel::Plugins::TacticalEagerLoading" do
17
19
  end
18
20
  end
19
21
  end
20
- @c = ::TaticalEagerLoadingModel
21
- @ds = TaticalEagerLoadingModel.dataset
22
+ @c = ::TacticalEagerLoadingModel
23
+ @ds = TacticalEagerLoadingModel.dataset
22
24
  MODEL_DB.reset
23
25
  end
24
26
  after do
25
- Object.send(:remove_const, :TaticalEagerLoadingModel)
27
+ Object.send(:remove_const, :TacticalEagerLoadingModel)
26
28
  end
27
29
 
28
30
  it "Dataset#all should set the retrieved_by and retrieved_with attributes" do
@@ -60,4 +62,17 @@ describe "Sequel::Plugins::TacticalEagerLoading" do
60
62
  @c.all{|x| x.parent2 if x.is_a?(c)}
61
63
  end
62
64
 
65
+ it "association getter methods should not eagerly load the association if an instance is frozen" do
66
+ ts = @c.all
67
+ ts.first.freeze
68
+ MODEL_DB.sqls.length.should == 1
69
+ ts.map{|x| x.parent}.should == [ts[2], ts[3], nil, nil]
70
+ MODEL_DB.sqls.length.should == 2
71
+ ts.map{|x| x.children}.should == [[], [], [ts[0]], [ts[1]]]
72
+ MODEL_DB.sqls.length.should == 2
73
+ ts.map{|x| x.parent}.should == [ts[2], ts[3], nil, nil]
74
+ MODEL_DB.sqls.length.should == 1
75
+ ts.map{|x| x.children}.should == [[], [], [ts[0]], [ts[1]]]
76
+ MODEL_DB.sqls.length.should == 1
77
+ end
63
78
  end
@@ -7,7 +7,6 @@ describe "Sequel::Plugins::UpdatePrimaryKey" do
7
7
  @c.columns :a, :b
8
8
  @c.set_primary_key :a
9
9
  @c.unrestrict_primary_key
10
- @o = @c.new
11
10
  @ds = @c.dataset
12
11
  MODEL_DB.reset
13
12
  end
@@ -68,4 +67,21 @@ describe "Sequel::Plugins::UpdatePrimaryKey" do
68
67
  @c.all.should == [@c.load(:a=>2, :b=>5)]
69
68
  MODEL_DB.sqls.should == ["SELECT * FROM a LIMIT 1", "UPDATE a SET a = 2 WHERE (a = 1)", "UPDATE a SET b = 4 WHERE (a = 2)", "UPDATE a SET b = 5 WHERE (a = 2)", "SELECT * FROM a"]
70
69
  end
70
+
71
+ specify "should clear the associations cache of non-many_to_one associations when changing the primary key" do
72
+ @c.one_to_many :cs, :class=>@c
73
+ @c.many_to_one :c, :class=>@c
74
+ o = @c.new(:a=>1)
75
+ o.associations[:cs] = @c.new
76
+ o.associations[:c] = o2 = @c.new
77
+ o.a = 2
78
+ o.associations.should == {:c=>o2}
79
+ end
80
+
81
+ specify "should handle frozen instances" do
82
+ o = @c.new
83
+ o.a = 1
84
+ o.freeze
85
+ o.pk_hash.should == {:a=>1}
86
+ end
71
87
  end
@@ -799,6 +799,31 @@ describe Sequel::Model, "Validations" do
799
799
  @person.should be_valid
800
800
  end
801
801
 
802
+ it "should validate that a column has the correct type for the schema column" do
803
+ p = model_class.call Sequel::Model do
804
+ columns :age, :d
805
+ self.raise_on_typecast_failure = false
806
+ validates_schema_type :age
807
+ validates_schema_type :d, :message=>'is a bad choice'
808
+ @db_schema = {:age=>{:type=>:integer}, :d=>{:type=>:date}}
809
+ end
810
+
811
+ @person = p.new
812
+ @person.should be_valid
813
+
814
+ @person.age = 'a'
815
+ @person.should_not be_valid
816
+ @person.errors.full_messages.should == ['age is not a valid integer']
817
+ @person.age = 1
818
+ @person.should be_valid
819
+
820
+ @person.d = 'a'
821
+ @person.should_not be_valid
822
+ @person.errors.full_messages.should == ['d is a bad choice']
823
+ @person.d = Date.today
824
+ @person.should be_valid
825
+ end
826
+
802
827
  it "should validate numericality of column" do
803
828
  class ::Person < Sequel::Model
804
829
  validations.clear
@@ -247,6 +247,22 @@ describe "Sequel::Plugins::ValidationHelpers" do
247
247
  @m.errors.full_messages.should == ['value is not a valid integer']
248
248
  end
249
249
 
250
+ specify "should support validates_schema_types" do
251
+ @c.set_validations{validates_schema_types}
252
+ @m.value = 123
253
+ @m.should be_valid
254
+ @m.value = '123'
255
+ @m.should be_valid
256
+ @m.meta_def(:db_schema){{:value=>{:type=>:integer}}}
257
+ @m.should_not be_valid
258
+ @m.errors.full_messages.should == ['value is not a valid integer']
259
+
260
+ @c.set_validations{validates_schema_types(:value)}
261
+ @m.meta_def(:db_schema){{:value=>{:type=>:integer}}}
262
+ @m.should_not be_valid
263
+ @m.errors.full_messages.should == ['value is not a valid integer']
264
+ end
265
+
250
266
  specify "should support validates_numeric" do
251
267
  @c.set_validations{validates_numeric(:value)}
252
268
  @m.value = 'blah'
@@ -279,29 +295,57 @@ describe "Sequel::Plugins::ValidationHelpers" do
279
295
  @m.should be_valid
280
296
  @m.value = '123'
281
297
  @m.should_not be_valid
282
- @m.errors.full_messages.should == ['value is not a Integer']
298
+ @m.errors.full_messages.should == ['value is not a valid integer']
283
299
 
284
300
  @c.set_validations{validates_type(:String, :value)}
285
301
  @m.value = '123'
286
302
  @m.should be_valid
287
303
  @m.value = 123
288
304
  @m.should_not be_valid
289
- @m.errors.full_messages.should == ['value is not a String']
305
+ @m.errors.full_messages.should == ['value is not a valid string']
290
306
 
291
307
  @c.set_validations{validates_type('Integer', :value)}
292
308
  @m.value = 123
293
309
  @m.should be_valid
294
310
  @m.value = 123.05
295
311
  @m.should_not be_valid
296
- @m.errors.full_messages.should == ['value is not a Integer']
312
+ @m.errors.full_messages.should == ['value is not a valid integer']
297
313
 
298
314
  @c.set_validations{validates_type(Integer, :value)}
299
315
  @m.value = nil
300
316
  @m.should be_valid
301
317
  @m.value = false
302
318
  @m.should_not be_valid
319
+
320
+ @c.set_validations{validates_type([Integer, Float], :value)}
321
+ @m.value = nil
322
+ @m.should be_valid
323
+ @m.value = 1
324
+ @m.should be_valid
325
+ @m.value = 1.0
326
+ @m.should be_valid
327
+ @m.value = BigDecimal.new('1.0')
328
+ @m.should_not be_valid
329
+ @m.errors.full_messages.should == ['value is not a valid integer or float']
303
330
  end
304
331
 
332
+ specify "should support validates_not_null" do
333
+ @c.set_validations{validates_not_null(:value)}
334
+ @m.should_not be_valid
335
+ @m.value = ''
336
+ @m.should be_valid
337
+ @m.value = 1234
338
+ @m.should be_valid
339
+ @m.value = nil
340
+ @m.should_not be_valid
341
+ @m.value = true
342
+ @m.should be_valid
343
+ @m.value = false
344
+ @m.should be_valid
345
+ @m.value = Time.now
346
+ @m.should be_valid
347
+ end
348
+
305
349
  specify "should support validates_presence" do
306
350
  @c.set_validations{validates_presence(:value)}
307
351
  @m.should_not be_valid
@@ -466,4 +510,16 @@ describe "Sequel::Plugins::ValidationHelpers" do
466
510
  m.should be_valid
467
511
  MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '2') AND (password = '1') AND (id != 3)) LIMIT 1"]
468
512
  end
513
+
514
+ it "should not attempt a database query if the underlying columns have validation errors" do
515
+ @c.columns(:id, :username, :password)
516
+ @c.set_dataset MODEL_DB[:items]
517
+ @c.set_validations{validates_not_string(:username); validates_unique([:username, :password])}
518
+ @c.dataset._fetch = {:v=>0}
519
+
520
+ MODEL_DB.reset
521
+ m = @c.new(:username => "1", :password => "anothertest")
522
+ m.should_not be_valid
523
+ MODEL_DB.sqls.should == []
524
+ end
469
525
  end