sequel 3.21.0 → 3.32.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.
Files changed (234) hide show
  1. data/CHANGELOG +539 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +20 -6
  4. data/Rakefile +37 -19
  5. data/bin/sequel +1 -5
  6. data/doc/advanced_associations.rdoc +17 -0
  7. data/doc/association_basics.rdoc +293 -43
  8. data/doc/dataset_basics.rdoc +4 -4
  9. data/doc/dataset_filtering.rdoc +8 -0
  10. data/doc/mass_assignment.rdoc +54 -0
  11. data/doc/migration.rdoc +15 -538
  12. data/doc/model_hooks.rdoc +72 -27
  13. data/doc/opening_databases.rdoc +89 -50
  14. data/doc/prepared_statements.rdoc +40 -13
  15. data/doc/reflection.rdoc +8 -2
  16. data/doc/release_notes/3.22.0.txt +39 -0
  17. data/doc/release_notes/3.23.0.txt +172 -0
  18. data/doc/release_notes/3.24.0.txt +420 -0
  19. data/doc/release_notes/3.25.0.txt +88 -0
  20. data/doc/release_notes/3.26.0.txt +88 -0
  21. data/doc/release_notes/3.27.0.txt +82 -0
  22. data/doc/release_notes/3.28.0.txt +304 -0
  23. data/doc/release_notes/3.29.0.txt +459 -0
  24. data/doc/release_notes/3.30.0.txt +135 -0
  25. data/doc/release_notes/3.31.0.txt +146 -0
  26. data/doc/release_notes/3.32.0.txt +202 -0
  27. data/doc/schema_modification.rdoc +547 -0
  28. data/doc/sharding.rdoc +7 -1
  29. data/doc/testing.rdoc +129 -0
  30. data/doc/transactions.rdoc +137 -0
  31. data/doc/virtual_rows.rdoc +2 -2
  32. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  33. data/lib/sequel/adapters/ado.rb +40 -18
  34. data/lib/sequel/adapters/amalgalite.rb +17 -9
  35. data/lib/sequel/adapters/db2.rb +175 -86
  36. data/lib/sequel/adapters/dbi.rb +15 -15
  37. data/lib/sequel/adapters/do/mysql.rb +5 -7
  38. data/lib/sequel/adapters/do/postgres.rb +0 -5
  39. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  40. data/lib/sequel/adapters/do.rb +17 -36
  41. data/lib/sequel/adapters/firebird.rb +27 -208
  42. data/lib/sequel/adapters/ibmdb.rb +448 -0
  43. data/lib/sequel/adapters/informix.rb +6 -23
  44. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  45. data/lib/sequel/adapters/jdbc/db2.rb +52 -0
  46. data/lib/sequel/adapters/jdbc/derby.rb +260 -0
  47. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  48. data/lib/sequel/adapters/jdbc/h2.rb +66 -30
  49. data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
  50. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  51. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  52. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  53. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  54. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  56. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  57. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  58. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  59. data/lib/sequel/adapters/jdbc.rb +231 -78
  60. data/lib/sequel/adapters/mock.rb +339 -0
  61. data/lib/sequel/adapters/mysql.rb +85 -184
  62. data/lib/sequel/adapters/mysql2.rb +26 -25
  63. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  64. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  65. data/lib/sequel/adapters/odbc.rb +28 -9
  66. data/lib/sequel/adapters/openbase.rb +3 -5
  67. data/lib/sequel/adapters/oracle.rb +355 -53
  68. data/lib/sequel/adapters/postgres.rb +194 -33
  69. data/lib/sequel/adapters/shared/access.rb +32 -9
  70. data/lib/sequel/adapters/shared/db2.rb +328 -0
  71. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  72. data/lib/sequel/adapters/shared/informix.rb +53 -0
  73. data/lib/sequel/adapters/shared/mssql.rb +278 -107
  74. data/lib/sequel/adapters/shared/mysql.rb +203 -65
  75. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  76. data/lib/sequel/adapters/shared/oracle.rb +237 -46
  77. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  78. data/lib/sequel/adapters/shared/progress.rb +5 -8
  79. data/lib/sequel/adapters/shared/sqlite.rb +200 -50
  80. data/lib/sequel/adapters/sqlite.rb +29 -21
  81. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  82. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  83. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  84. data/lib/sequel/adapters/swift.rb +5 -5
  85. data/lib/sequel/adapters/tinytds.rb +160 -19
  86. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  87. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  88. data/lib/sequel/ast_transformer.rb +190 -0
  89. data/lib/sequel/connection_pool/threaded.rb +3 -2
  90. data/lib/sequel/connection_pool.rb +1 -1
  91. data/lib/sequel/core.rb +68 -5
  92. data/lib/sequel/database/connecting.rb +5 -6
  93. data/lib/sequel/database/dataset.rb +4 -4
  94. data/lib/sequel/database/dataset_defaults.rb +59 -1
  95. data/lib/sequel/database/logging.rb +1 -1
  96. data/lib/sequel/database/misc.rb +104 -19
  97. data/lib/sequel/database/query.rb +163 -59
  98. data/lib/sequel/database/schema_generator.rb +17 -7
  99. data/lib/sequel/database/schema_methods.rb +59 -29
  100. data/lib/sequel/dataset/actions.rb +240 -76
  101. data/lib/sequel/dataset/features.rb +91 -8
  102. data/lib/sequel/dataset/graph.rb +38 -11
  103. data/lib/sequel/dataset/misc.rb +50 -23
  104. data/lib/sequel/dataset/mutation.rb +12 -7
  105. data/lib/sequel/dataset/prepared_statements.rb +59 -27
  106. data/lib/sequel/dataset/query.rb +236 -43
  107. data/lib/sequel/dataset/sql.rb +676 -267
  108. data/lib/sequel/dataset.rb +8 -0
  109. data/lib/sequel/exceptions.rb +4 -0
  110. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  111. data/lib/sequel/extensions/migration.rb +6 -4
  112. data/lib/sequel/extensions/named_timezones.rb +5 -0
  113. data/lib/sequel/extensions/query.rb +1 -1
  114. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  115. data/lib/sequel/extensions/to_dot.rb +95 -83
  116. data/lib/sequel/model/associations.rb +1081 -344
  117. data/lib/sequel/model/base.rb +420 -133
  118. data/lib/sequel/model/errors.rb +1 -1
  119. data/lib/sequel/model/exceptions.rb +5 -1
  120. data/lib/sequel/model.rb +20 -10
  121. data/lib/sequel/plugins/active_model.rb +13 -1
  122. data/lib/sequel/plugins/association_pks.rb +22 -4
  123. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  124. data/lib/sequel/plugins/composition.rb +43 -10
  125. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  126. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  127. data/lib/sequel/plugins/force_encoding.rb +6 -6
  128. data/lib/sequel/plugins/identity_map.rb +114 -7
  129. data/lib/sequel/plugins/many_through_many.rb +68 -7
  130. data/lib/sequel/plugins/nested_attributes.rb +65 -10
  131. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  132. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  133. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  134. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  135. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  136. data/lib/sequel/plugins/serialization.rb +98 -49
  137. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  138. data/lib/sequel/plugins/sharding.rb +21 -54
  139. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  140. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  141. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  142. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  143. data/lib/sequel/sql.rb +109 -52
  144. data/lib/sequel/timezones.rb +57 -38
  145. data/lib/sequel/version.rb +1 -1
  146. data/spec/adapters/db2_spec.rb +146 -0
  147. data/spec/adapters/mssql_spec.rb +135 -30
  148. data/spec/adapters/mysql_spec.rb +244 -212
  149. data/spec/adapters/oracle_spec.rb +67 -77
  150. data/spec/adapters/postgres_spec.rb +267 -78
  151. data/spec/adapters/spec_helper.rb +6 -5
  152. data/spec/adapters/sqlite_spec.rb +35 -21
  153. data/spec/core/connection_pool_spec.rb +71 -92
  154. data/spec/core/core_sql_spec.rb +21 -30
  155. data/spec/core/database_spec.rb +789 -512
  156. data/spec/core/dataset_spec.rb +1176 -1061
  157. data/spec/core/expression_filters_spec.rb +159 -42
  158. data/spec/core/mock_adapter_spec.rb +419 -0
  159. data/spec/core/object_graph_spec.rb +86 -114
  160. data/spec/core/schema_generator_spec.rb +3 -3
  161. data/spec/core/schema_spec.rb +298 -38
  162. data/spec/core/spec_helper.rb +6 -48
  163. data/spec/extensions/active_model_spec.rb +13 -0
  164. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  165. data/spec/extensions/association_dependencies_spec.rb +2 -12
  166. data/spec/extensions/association_pks_spec.rb +66 -32
  167. data/spec/extensions/caching_spec.rb +23 -50
  168. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  169. data/spec/extensions/columns_introspection_spec.rb +91 -0
  170. data/spec/extensions/composition_spec.rb +18 -13
  171. data/spec/extensions/dataset_associations_spec.rb +199 -0
  172. data/spec/extensions/defaults_setter_spec.rb +64 -0
  173. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  174. data/spec/extensions/identity_map_spec.rb +114 -22
  175. data/spec/extensions/instance_filters_spec.rb +11 -21
  176. data/spec/extensions/instance_hooks_spec.rb +53 -0
  177. data/spec/extensions/json_serializer_spec.rb +4 -5
  178. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  179. data/spec/extensions/list_spec.rb +17 -39
  180. data/spec/extensions/many_through_many_spec.rb +283 -250
  181. data/spec/extensions/migration_spec.rb +35 -32
  182. data/spec/extensions/named_timezones_spec.rb +23 -3
  183. data/spec/extensions/nested_attributes_spec.rb +97 -88
  184. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  185. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  186. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  187. data/spec/extensions/prepared_statements_spec.rb +53 -0
  188. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  189. data/spec/extensions/pretty_table_spec.rb +1 -6
  190. data/spec/extensions/query_spec.rb +6 -0
  191. data/spec/extensions/rcte_tree_spec.rb +41 -43
  192. data/spec/extensions/schema_dumper_spec.rb +5 -8
  193. data/spec/extensions/schema_spec.rb +13 -21
  194. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  195. data/spec/extensions/serialization_spec.rb +81 -43
  196. data/spec/extensions/sharding_spec.rb +66 -140
  197. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  198. data/spec/extensions/spec_helper.rb +13 -61
  199. data/spec/extensions/sql_expr_spec.rb +20 -60
  200. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  201. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  202. data/spec/extensions/timestamps_spec.rb +6 -6
  203. data/spec/extensions/to_dot_spec.rb +4 -7
  204. data/spec/extensions/touch_spec.rb +13 -14
  205. data/spec/extensions/tree_spec.rb +11 -26
  206. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  207. data/spec/extensions/update_primary_key_spec.rb +30 -24
  208. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  209. data/spec/extensions/validation_helpers_spec.rb +16 -35
  210. data/spec/extensions/xml_serializer_spec.rb +16 -4
  211. data/spec/integration/associations_test.rb +661 -21
  212. data/spec/integration/database_test.rb +4 -2
  213. data/spec/integration/dataset_test.rb +500 -67
  214. data/spec/integration/eager_loader_test.rb +19 -21
  215. data/spec/integration/model_test.rb +80 -1
  216. data/spec/integration/plugin_test.rb +367 -119
  217. data/spec/integration/prepared_statement_test.rb +222 -125
  218. data/spec/integration/schema_test.rb +217 -39
  219. data/spec/integration/spec_helper.rb +39 -30
  220. data/spec/integration/timezone_test.rb +38 -12
  221. data/spec/integration/transaction_test.rb +182 -5
  222. data/spec/integration/type_test.rb +17 -3
  223. data/spec/model/association_reflection_spec.rb +201 -7
  224. data/spec/model/associations_spec.rb +955 -662
  225. data/spec/model/base_spec.rb +275 -95
  226. data/spec/model/dataset_methods_spec.rb +7 -27
  227. data/spec/model/eager_loading_spec.rb +677 -676
  228. data/spec/model/hooks_spec.rb +349 -67
  229. data/spec/model/model_spec.rb +222 -168
  230. data/spec/model/plugins_spec.rb +24 -13
  231. data/spec/model/record_spec.rb +381 -218
  232. data/spec/model/spec_helper.rb +13 -71
  233. data/spec/model/validations_spec.rb +11 -0
  234. metadata +117 -73
@@ -12,13 +12,48 @@ describe "Sequel::Model()" do
12
12
  c.dataset.should == ds
13
13
  end
14
14
 
15
- it "should return a model subclass with a dataset with the default database and given table name if given a symbol" do
15
+ it "should return a model subclass with a dataset with the default database and given table name if given a Symbol" do
16
16
  c = Sequel::Model(:blah)
17
17
  c.superclass.should == Sequel::Model
18
18
  c.db.should == @db
19
19
  c.table_name.should == :blah
20
20
  end
21
21
 
22
+ it "should return a model subclass with a dataset with the default database and given table name if given a LiteralString" do
23
+ c = Sequel::Model('blah'.lit)
24
+ c.superclass.should == Sequel::Model
25
+ c.db.should == @db
26
+ c.table_name.should == 'blah'.lit
27
+ end
28
+
29
+ it "should return a model subclass with a dataset with the default database and given table name if given an SQL::Identifier" do
30
+ c = Sequel::Model(:blah.identifier)
31
+ c.superclass.should == Sequel::Model
32
+ c.db.should == @db
33
+ c.table_name.should == :blah.identifier
34
+ end
35
+
36
+ it "should return a model subclass with a dataset with the default database and given table name if given an SQL::QualifiedIdentifier" do
37
+ c = Sequel::Model(:blah.qualify(:boo))
38
+ c.superclass.should == Sequel::Model
39
+ c.db.should == @db
40
+ c.table_name.should == :blah.qualify(:boo)
41
+ end
42
+
43
+ it "should return a model subclass with a dataset with the default database and given table name if given an SQL::AliasedExpression" do
44
+ c = Sequel::Model(:blah.as(:boo))
45
+ c.superclass.should == Sequel::Model
46
+ c.db.should == @db
47
+ c.table_name.should == :boo
48
+ end
49
+
50
+ it "should return a model subclass with the given dataset if given a dataset using an SQL::Identifier" do
51
+ ds = @db[:blah.identifier]
52
+ c = Sequel::Model(ds)
53
+ c.superclass.should == Sequel::Model
54
+ c.dataset.should == ds
55
+ end
56
+
22
57
  it "should return a model subclass associated to the given database if given a database" do
23
58
  db = Sequel::Database.new
24
59
  c = Sequel::Model(db)
@@ -43,6 +78,34 @@ describe "Sequel::Model()" do
43
78
  end.should_not raise_error
44
79
  end
45
80
 
81
+ it "should work without raising an exception with an SQL::Identifier " do
82
+ proc do
83
+ class ::Album < Sequel::Model(:table.identifier); end
84
+ class ::Album < Sequel::Model(:table.identifier); end
85
+ end.should_not raise_error
86
+ end
87
+
88
+ it "should work without raising an exception with an SQL::QualifiedIdentifier " do
89
+ proc do
90
+ class ::Album < Sequel::Model(:table.qualify(:schema)); end
91
+ class ::Album < Sequel::Model(:table.qualify(:schema)); end
92
+ end.should_not raise_error
93
+ end
94
+
95
+ it "should work without raising an exception with an SQL::AliasedExpression" do
96
+ proc do
97
+ class ::Album < Sequel::Model(:table.as(:alias)); end
98
+ class ::Album < Sequel::Model(:table.as(:alias)); end
99
+ end.should_not raise_error
100
+ end
101
+
102
+ it "should work without raising an exception with an LiteralString" do
103
+ proc do
104
+ class ::Album < Sequel::Model('table'.lit); end
105
+ class ::Album < Sequel::Model('table'.lit); end
106
+ end.should_not raise_error
107
+ end
108
+
46
109
  it "should work without raising an exception with a database" do
47
110
  proc do
48
111
  class ::Album < Sequel::Model(@db); end
@@ -56,6 +119,13 @@ describe "Sequel::Model()" do
56
119
  class ::Album < Sequel::Model(@db[:table]); end
57
120
  end.should_not raise_error
58
121
  end
122
+
123
+ it "should work without raising an exception with a dataset with an SQL::Identifier" do
124
+ proc do
125
+ class ::Album < Sequel::Model(@db[:table.identifier]); end
126
+ class ::Album < Sequel::Model(@db[:table.identifier]); end
127
+ end.should_not raise_error
128
+ end
59
129
  end
60
130
  end
61
131
 
@@ -70,17 +140,16 @@ describe Sequel::Model do
70
140
  it "should be associated with a dataset" do
71
141
  model_a = Class.new(Sequel::Model) { set_dataset MODEL_DB[:as] }
72
142
 
73
- model_a.dataset.should be_a_kind_of(MockDataset)
143
+ model_a.dataset.should be_a_kind_of(Sequel::Mock::Dataset)
74
144
  model_a.dataset.opts[:from].should == [:as]
75
145
 
76
146
  model_b = Class.new(Sequel::Model) { set_dataset MODEL_DB[:bs] }
77
147
 
78
- model_b.dataset.should be_a_kind_of(MockDataset)
148
+ model_b.dataset.should be_a_kind_of(Sequel::Mock::Dataset)
79
149
  model_b.dataset.opts[:from].should == [:bs]
80
150
 
81
151
  model_a.dataset.opts[:from].should == [:as]
82
152
  end
83
-
84
153
  end
85
154
 
86
155
  describe Sequel::Model, "dataset & schema" do
@@ -106,12 +175,36 @@ describe Sequel::Model, "dataset & schema" do
106
175
  @model.table_name.should == :foo
107
176
  end
108
177
 
109
- it "set_dataset should take a symbol" do
178
+ it "set_dataset should take a Symbol" do
110
179
  @model.db = MODEL_DB
111
180
  @model.set_dataset(:foo)
112
181
  @model.table_name.should == :foo
113
182
  end
114
183
 
184
+ it "set_dataset should take a LiteralString" do
185
+ @model.db = MODEL_DB
186
+ @model.set_dataset('foo'.lit)
187
+ @model.table_name.should == 'foo'.lit
188
+ end
189
+
190
+ it "set_dataset should take an SQL::Identifier" do
191
+ @model.db = MODEL_DB
192
+ @model.set_dataset(:foo.identifier)
193
+ @model.table_name.should == :foo.identifier
194
+ end
195
+
196
+ it "set_dataset should take an SQL::QualifiedIdentifier" do
197
+ @model.db = MODEL_DB
198
+ @model.set_dataset(:foo.qualify(:bar))
199
+ @model.table_name.should == :foo.qualify(:bar)
200
+ end
201
+
202
+ it "set_dataset should take an SQL::AliasedExpression" do
203
+ @model.db = MODEL_DB
204
+ @model.set_dataset(:foo.as(:bar))
205
+ @model.table_name.should == :bar
206
+ end
207
+
115
208
  it "table_name should respect table aliases" do
116
209
  @model.set_dataset(:foo___x)
117
210
  @model.table_name.should == :x
@@ -121,16 +214,30 @@ describe Sequel::Model, "dataset & schema" do
121
214
  proc{@model.set_dataset(Object.new)}.should raise_error(Sequel::Error)
122
215
  end
123
216
 
124
- it "set_dataset should add the destroy method to the dataset" do
217
+ it "set_dataset should add the destroy method to the dataset that destroys each object" do
125
218
  ds = MODEL_DB[:foo]
126
219
  ds.should_not respond_to(:destroy)
127
220
  @model.set_dataset(ds)
128
221
  ds.should respond_to(:destroy)
222
+ MODEL_DB.sqls
223
+ ds._fetch = [{:id=>1}, {:id=>2}]
224
+ ds.destroy.should == 2
225
+ MODEL_DB.sqls.should == ["SELECT * FROM foo", "DELETE FROM foo WHERE (id = 1)", "DELETE FROM foo WHERE (id = 2)"]
226
+ end
227
+
228
+ it "set_dataset should add the destroy method that respects sharding with transactions" do
229
+ db = Sequel.mock(:servers=>{:s1=>{}})
230
+ ds = db[:foo].server(:s1)
231
+ @model.use_transactions = true
232
+ @model.set_dataset(ds)
233
+ db.sqls
234
+ ds.destroy.should == 0
235
+ db.sqls.should == ["BEGIN -- s1", "SELECT * FROM foo -- s1", "COMMIT -- s1"]
129
236
  end
130
237
 
131
238
  it "should raise an error on set_dataset if there is an error connecting to the database" do
132
239
  @model.meta_def(:columns){raise Sequel::DatabaseConnectionError}
133
- proc{@model.set_dataset(MODEL_DB[:foo].join(:blah))}.should raise_error
240
+ proc{@model.set_dataset(Sequel::Database.new[:foo].join(:blah))}.should raise_error
134
241
  end
135
242
 
136
243
  it "should not raise an error if there is a problem getting the columns for a dataset" do
@@ -149,9 +256,8 @@ describe Sequel::Model, "dataset & schema" do
149
256
  end
150
257
  end
151
258
 
152
- describe Sequel::Model, "constructor" do
153
-
154
- before(:each) do
259
+ describe Sequel::Model, "constructors" do
260
+ before do
155
261
  @m = Class.new(Sequel::Model)
156
262
  @m.columns :a, :b
157
263
  end
@@ -170,11 +276,38 @@ describe Sequel::Model, "constructor" do
170
276
  m.values[:a].should == 1
171
277
  end
172
278
 
279
+ it "should have dataset row_proc create an existing object" do
280
+ @m.dataset = Sequel::Dataset.new(nil)
281
+ o = @m.dataset.row_proc.call(:a=>1)
282
+ o.should be_a_kind_of(@m)
283
+ o.values.should == {:a=>1}
284
+ o.new?.should be_false
285
+ end
286
+
287
+ it "should have .call create an existing object" do
288
+ o = @m.call(:a=>1)
289
+ o.should be_a_kind_of(@m)
290
+ o.values.should == {:a=>1}
291
+ o.new?.should be_false
292
+ end
293
+
294
+ it "should have .load create an existing object" do
295
+ o = @m.load(:a=>1)
296
+ o.should be_a_kind_of(@m)
297
+ o.values.should == {:a=>1}
298
+ o.new?.should be_false
299
+ end
300
+
301
+ it "should have .new with a second true argument create an existing object" do
302
+ o = @m.new({:a=>1}, true)
303
+ o.should be_a_kind_of(@m)
304
+ o.values.should == {:a=>1}
305
+ o.new?.should be_false
306
+ end
173
307
  end
174
308
 
175
309
  describe Sequel::Model, "new" do
176
-
177
- before(:each) do
310
+ before do
178
311
  @m = Class.new(Sequel::Model) do
179
312
  set_dataset MODEL_DB[:items]
180
313
  columns :x, :id
@@ -194,15 +327,8 @@ describe Sequel::Model, "new" do
194
327
  end
195
328
 
196
329
  it "should use the last inserted id as primary key if not in values" do
197
- d = @m.dataset
198
- def d.insert(*args)
199
- super
200
- 1234
201
- end
202
-
203
- def d.first
204
- {:x => 1, :id => 1234}
205
- end
330
+ @m.dataset._fetch = {:x => 1, :id => 1234}
331
+ @m.dataset.autoid = 1234
206
332
 
207
333
  o = @m.new(:x => 1)
208
334
  o.save
@@ -212,14 +338,12 @@ describe Sequel::Model, "new" do
212
338
  o.save
213
339
  o.id.should == 333
214
340
  end
215
-
216
341
  end
217
342
 
218
343
  describe Sequel::Model, ".subset" do
219
344
  before do
220
- MODEL_DB.reset
221
-
222
345
  @c = Class.new(Sequel::Model(:items))
346
+ MODEL_DB.reset
223
347
  end
224
348
 
225
349
  specify "should create a filter on the underlying dataset" do
@@ -247,44 +371,31 @@ describe Sequel::Model, ".subset" do
247
371
  end
248
372
 
249
373
  describe Sequel::Model, ".find" do
250
-
251
- before(:each) do
252
- MODEL_DB.reset
253
-
374
+ before do
254
375
  @c = Class.new(Sequel::Model(:items))
255
-
256
- $cache_dataset_row = {:name => 'sharon', :id => 1}
257
- @dataset = @c.dataset
258
- $sqls = []
259
- @dataset.extend(Module.new {
260
- def fetch_rows(sql)
261
- $sqls << sql
262
- yield $cache_dataset_row
263
- end
264
- })
376
+ @c.dataset._fetch = {:name => 'sharon', :id => 1}
377
+ MODEL_DB.reset
265
378
  end
266
379
 
267
380
  it "should return the first record matching the given filter" do
268
381
  @c.find(:name => 'sharon').should be_a_kind_of(@c)
269
- $sqls.last.should == "SELECT * FROM items WHERE (name = 'sharon') LIMIT 1"
382
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (name = 'sharon') LIMIT 1"]
270
383
 
271
384
  @c.find(:name.like('abc%')).should be_a_kind_of(@c)
272
- $sqls.last.should == "SELECT * FROM items WHERE (name LIKE 'abc%') LIMIT 1"
385
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (name LIKE 'abc%') LIMIT 1"]
273
386
  end
274
387
 
275
388
  specify "should accept filter blocks" do
276
389
  @c.find{:id.sql_number > 1}.should be_a_kind_of(@c)
277
- $sqls.last.should == "SELECT * FROM items WHERE (id > 1) LIMIT 1"
390
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (id > 1) LIMIT 1"]
278
391
 
279
392
  @c.find {(:x.sql_number > 1) & (:y.sql_number < 2)}.should be_a_kind_of(@c)
280
- $sqls.last.should == "SELECT * FROM items WHERE ((x > 1) AND (y < 2)) LIMIT 1"
393
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE ((x > 1) AND (y < 2)) LIMIT 1"]
281
394
  end
282
-
283
395
  end
284
396
 
285
397
  describe Sequel::Model, ".fetch" do
286
-
287
- before(:each) do
398
+ before do
288
399
  MODEL_DB.reset
289
400
  @c = Class.new(Sequel::Model(:items))
290
401
  end
@@ -301,80 +412,53 @@ describe Sequel::Model, ".fetch" do
301
412
  end
302
413
 
303
414
  describe Sequel::Model, ".find_or_create" do
304
-
305
- before(:each) do
306
- MODEL_DB.reset
415
+ before do
307
416
  @c = Class.new(Sequel::Model(:items)) do
308
- no_primary_key
417
+ set_primary_key :id
309
418
  columns :x
310
419
  end
420
+ MODEL_DB.reset
311
421
  end
312
422
 
313
423
  it "should find the record" do
314
- @c.find_or_create(:x => 1)
424
+ @c.find_or_create(:x => 1).should == @c.load(:x=>1, :id=>1)
315
425
  MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (x = 1) LIMIT 1"]
316
-
317
- MODEL_DB.reset
318
426
  end
319
427
 
320
428
  it "should create the record if not found" do
321
- @c.meta_def(:find) do |*args|
322
- dataset.filter(*args).first
323
- nil
324
- end
325
-
326
- @c.find_or_create(:x => 1)
327
- MODEL_DB.sqls.should == [
328
- "SELECT * FROM items WHERE (x = 1) LIMIT 1",
329
- "INSERT INTO items (x) VALUES (1)"
330
- ]
429
+ @c.dataset._fetch = [[], {:x=>1, :id=>1}]
430
+ @c.dataset.autoid = 1
431
+ @c.find_or_create(:x => 1).should == @c.load(:x=>1, :id=>1)
432
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE (x = 1) LIMIT 1",
433
+ "INSERT INTO items (x) VALUES (1)",
434
+ "SELECT * FROM items WHERE (id = 1) LIMIT 1"]
331
435
  end
332
436
 
333
437
  it "should pass the new record to be created to the block if no record is found" do
334
- @c.meta_def(:find){|*|}
335
- @c.find_or_create(:x => 1){|x| x[:y] = 2}
336
- ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].should include(MODEL_DB.sqls.first)
438
+ @c.dataset._fetch = [[], {:x=>1, :id=>1}]
439
+ @c.dataset.autoid = 1
440
+ @c.find_or_create(:x => 1){|x| x[:y] = 2}.should == @c.load(:x=>1, :id=>1)
441
+ sqls = MODEL_DB.sqls
442
+ sqls.first.should == "SELECT * FROM items WHERE (x = 1) LIMIT 1"
443
+ ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].should include(sqls[1])
444
+ sqls.last.should == "SELECT * FROM items WHERE (id = 1) LIMIT 1"
337
445
  end
338
446
  end
339
447
 
340
448
  describe Sequel::Model, ".all" do
341
-
342
- before(:each) do
343
- MODEL_DB.reset
344
- @c = Class.new(Sequel::Model(:items)) do
345
- no_primary_key
346
- end
347
-
348
- @c.dataset.meta_def(:all) {1234}
349
- end
350
-
351
449
  it "should return all records in the dataset" do
352
- @c.all.should == 1234
353
- end
354
-
355
- end
356
-
357
- class DummyModelBased < Sequel::Model(:blog)
358
- end
359
-
360
- describe Sequel::Model, "(:tablename)" do
361
-
362
- it "should allow reopening of descendant classes" do
363
- proc do
364
- eval "class DummyModelBased < Sequel::Model(:blog); end"
365
- end.should_not raise_error
450
+ c = Class.new(Sequel::Model(:items))
451
+ c.all.should == [c.load(:x=>1, :id=>1)]
366
452
  end
367
-
368
453
  end
369
454
 
370
455
  describe Sequel::Model, "A model class without a primary key" do
371
-
372
- before(:each) do
373
- MODEL_DB.reset
456
+ before do
374
457
  @c = Class.new(Sequel::Model(:items)) do
375
458
  columns :x
376
459
  no_primary_key
377
460
  end
461
+ MODEL_DB.reset
378
462
  end
379
463
 
380
464
  it "should be able to insert records without selecting them back" do
@@ -387,8 +471,11 @@ describe Sequel::Model, "A model class without a primary key" do
387
471
  end
388
472
 
389
473
  it "should raise when deleting" do
390
- o = @c.new
391
- proc {o.delete}.should raise_error
474
+ proc{@c.load(:x=>1).delete}.should raise_error
475
+ end
476
+
477
+ it "should raise when updating" do
478
+ proc{@c.load(:x=>1).update(:x=>2)}.should raise_error
392
479
  end
393
480
 
394
481
  it "should insert a record when saving" do
@@ -397,24 +484,17 @@ describe Sequel::Model, "A model class without a primary key" do
397
484
  o.save
398
485
  MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (2)']
399
486
  end
400
-
401
487
  end
402
488
 
403
489
  describe Sequel::Model, "attribute accessors" do
404
490
  before do
405
- MODEL_DB.reset
406
- @dataset = Sequel::Dataset.new(MODEL_DB)
407
- def @dataset.columns; [:x, :y]; end
408
- @c = Class.new(Sequel::Model) do
409
- def self.db_schema
410
- set_columns(Array(@columns))
411
- @db_schema = {:x=>{:type=>:integer}, :y=>{:type=>:integer}}
412
- end
413
- def self.set_dataset(ds, opts={})
414
- @columns = ds.columns
415
- db_schema
416
- end
491
+ db = Sequel.mock
492
+ def db.schema(*)
493
+ [[:x, {:type=>:integer}], [:y, {:type=>:integer}]]
417
494
  end
495
+ @dataset = db[:items].columns(:x, :y)
496
+ @c = Class.new(Sequel::Model)
497
+ MODEL_DB.reset
418
498
  end
419
499
 
420
500
  it "should be created on set_dataset" do
@@ -446,7 +526,7 @@ describe Sequel::Model, "attribute accessors" do
446
526
  end
447
527
 
448
528
  it "should have a working typecasting setter even if the column is not selected" do
449
- @c.set_dataset(@dataset.select(:y))
529
+ @c.set_dataset(@dataset.select(:y).columns(:y))
450
530
  o = @c.new
451
531
 
452
532
  o.x = '34'
@@ -454,7 +534,7 @@ describe Sequel::Model, "attribute accessors" do
454
534
  end
455
535
 
456
536
  it "should typecast if the new value is the same as the existing but has a different class" do
457
- @c.set_dataset(@dataset.select(:y))
537
+ @c.set_dataset(@dataset.select(:y).columns(:y))
458
538
  o = @c.new
459
539
 
460
540
  o.x = 34
@@ -466,58 +546,51 @@ describe Sequel::Model, "attribute accessors" do
466
546
  end
467
547
 
468
548
  describe Sequel::Model, ".[]" do
469
-
470
- before(:each) do
471
- MODEL_DB.reset
472
-
549
+ before do
473
550
  @c = Class.new(Sequel::Model(:items))
474
-
475
- $cache_dataset_row = {:name => 'sharon', :id => 1}
476
- @dataset = @c.dataset
477
- $sqls = []
478
- @dataset.extend(Module.new {
479
- def fetch_rows(sql)
480
- $sqls << sql
481
- yield $cache_dataset_row
482
- end
483
- })
551
+ @c.dataset._fetch = {:name => 'sharon', :id => 1}
552
+ MODEL_DB.reset
484
553
  end
485
554
 
486
555
  it "should return the first record for the given pk" do
487
- @c[1].should be_a_kind_of(@c)
488
- $sqls.last.should == "SELECT * FROM items WHERE (id = 1) LIMIT 1"
489
- @c[9999].should be_a_kind_of(@c)
490
- $sqls.last.should == "SELECT * FROM items WHERE (id = 9999) LIMIT 1"
556
+ @c[1].should == @c.load(:name => 'sharon', :id => 1)
557
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE id = 1"]
558
+ @c[9999].should == @c.load(:name => 'sharon', :id => 1)
559
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE id = 9999"]
491
560
  end
492
561
 
493
562
  it "should work correctly for custom primary key" do
494
563
  @c.set_primary_key :name
495
- @c['sharon'].should be_a_kind_of(@c)
496
- $sqls.last.should == "SELECT * FROM items WHERE (name = 'sharon') LIMIT 1"
564
+ @c['sharon'].should == @c.load(:name => 'sharon', :id => 1)
565
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE name = 'sharon'"]
566
+ end
567
+
568
+ it "should return the first record for the given pk for a filtered dataset" do
569
+ @c.dataset = @c.dataset.filter(:active=>true)
570
+ @c[1].should == @c.load(:name => 'sharon', :id => 1)
571
+ MODEL_DB.sqls.should == ["SELECT * FROM items WHERE ((active IS TRUE) AND (id = 1)) LIMIT 1"]
497
572
  end
498
573
 
499
574
  it "should work correctly for composite primary key specified as array" do
500
575
  @c.set_primary_key [:node_id, :kind]
501
576
  @c[3921, 201].should be_a_kind_of(@c)
502
- $sqls.last.should =~ \
503
- /^SELECT \* FROM items WHERE \((\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\))\) LIMIT 1$/
577
+ sqls = MODEL_DB.sqls
578
+ sqls.length.should == 1
579
+ sqls.first.should =~ /^SELECT \* FROM items WHERE \((\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\))\) LIMIT 1$/
504
580
  end
505
581
 
506
582
  it "should work correctly for composite primary key specified as separate arguments" do
507
583
  @c.set_primary_key :node_id, :kind
508
584
  @c[3921, 201].should be_a_kind_of(@c)
509
- $sqls.last.should =~ \
510
- /^SELECT \* FROM items WHERE \((\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\))\) LIMIT 1$/
585
+ sqls = MODEL_DB.sqls
586
+ sqls.length.should == 1
587
+ sqls.first.should =~ /^SELECT \* FROM items WHERE \((\(node_id = 3921\) AND \(kind = 201\))|(\(kind = 201\) AND \(node_id = 3921\))\) LIMIT 1$/
511
588
  end
512
589
  end
513
590
 
514
591
  describe "Model#inspect" do
515
- before do
516
- @o = Sequel::Model.load(:x => 333)
517
- end
518
-
519
592
  specify "should include the class name and the values" do
520
- @o.inspect.should == '#<Sequel::Model @values={:x=>333}>'
593
+ Sequel::Model.load(:x => 333).inspect.should == '#<Sequel::Model @values={:x=>333}>'
521
594
  end
522
595
  end
523
596
 
@@ -526,16 +599,13 @@ describe "Model.db_schema" do
526
599
  @c = Class.new(Sequel::Model(:items)) do
527
600
  def self.columns; orig_columns; end
528
601
  end
529
- @dataset = Sequel::Dataset.new(nil).from(:items)
530
- @dataset.meta_def(:db){@db ||= Sequel::Database.new}
531
- def @dataset.naked; self; end
532
- def @dataset.columns; []; end
533
- def @dataset.def_mutation_method(*names); end
602
+ @db = Sequel.mock
603
+ @dataset = @db[:items]
534
604
  end
535
605
 
536
606
  specify "should use the database's schema_for_table and set the columns and dataset columns" do
537
607
  d = @dataset.db
538
- def d.schema(table, opts = {})
608
+ def @db.schema(table, opts = {})
539
609
  [[:x, {:type=>:integer}], [:y, {:type=>:string}]]
540
610
  end
541
611
  @c.dataset = @dataset
@@ -545,36 +615,20 @@ describe "Model.db_schema" do
545
615
  end
546
616
 
547
617
  specify "should not restrict the schema for datasets with a :select option" do
548
- ds = @dataset.select(:x, :y___z)
549
- d = ds.db
550
- def d.schema(table, opts = {})
618
+ def @c.columns; [:x, :z]; end
619
+ def @db.schema(table, opts = {})
551
620
  [[:x, {:type=>:integer}], [:y, {:type=>:string}]]
552
621
  end
553
- def @c.columns; [:x, :z]; end
554
- @c.dataset = ds
622
+ @c.dataset = @dataset.select(:x, :y___z)
555
623
  @c.db_schema.should == {:x=>{:type=>:integer}, :z=>{}, :y=>{:type=>:string}}
556
624
  end
557
625
 
558
- specify "should not use schema if the dataset uses multiple tables or custom sql" do
559
- ds = @dataset.join(:x, :id)
560
- d = ds.db
561
- e = false
562
- d.meta_def(:schema){|table, *opts| e = true}
563
- def @c.columns; [:x]; end
564
- @c.dataset = ds
565
- @c.db_schema.should == {:x=>{}}
566
- e.should == false
567
- end
568
-
569
626
  specify "should fallback to fetching records if schema raises an error" do
570
- ds = @dataset.join(:x, :id)
571
- d = ds.db
572
- def d.schema(table, opts={})
573
- raise StandardError
627
+ def @db.schema(table, opts={})
628
+ raise Sequel::Error
574
629
  end
575
- def @c.columns; [:x]; end
576
- @c.dataset = ds
577
- @c.db_schema.should == {:x=>{}}
630
+ @c.dataset = @dataset.join(:x, :id).columns(:id, :x)
631
+ @c.db_schema.should == {:x=>{}, :id=>{}}
578
632
  end
579
633
 
580
634
  specify "should automatically set a singular primary key based on the schema" do