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
@@ -6,187 +6,185 @@ describe "Model hooks" do
6
6
  end
7
7
 
8
8
  specify "should be definable using a block" do
9
- $adds = []
9
+ adds = []
10
10
  c = Class.new(Sequel::Model)
11
11
  c.class_eval do
12
- before_save {$adds << 'hi'}
12
+ before_save{adds << 'hi'}
13
13
  end
14
14
 
15
15
  c.new.before_save
16
- $adds.should == ['hi']
16
+ adds.should == ['hi']
17
17
  end
18
18
 
19
19
  specify "should be definable using a method name" do
20
- $adds = []
20
+ adds = []
21
21
  c = Class.new(Sequel::Model)
22
22
  c.class_eval do
23
- def bye; $adds << 'bye'; end
23
+ define_method(:bye){adds << 'bye'}
24
24
  before_save :bye
25
25
  end
26
26
 
27
27
  c.new.before_save
28
- $adds.should == ['bye']
28
+ adds.should == ['bye']
29
29
  end
30
30
 
31
31
  specify "should be additive" do
32
- $adds = []
32
+ adds = []
33
33
  c = Class.new(Sequel::Model)
34
34
  c.class_eval do
35
- after_save {$adds << 'hyiyie'}
36
- after_save {$adds << 'byiyie'}
35
+ after_save{adds << 'hyiyie'}
36
+ after_save{adds << 'byiyie'}
37
37
  end
38
38
 
39
39
  c.new.after_save
40
- $adds.should == ['hyiyie', 'byiyie']
40
+ adds.should == ['hyiyie', 'byiyie']
41
41
  end
42
42
 
43
43
  specify "before hooks should run in reverse order" do
44
- $adds = []
44
+ adds = []
45
45
  c = Class.new(Sequel::Model)
46
46
  c.class_eval do
47
- before_save {$adds << 'hyiyie'}
48
- before_save {$adds << 'byiyie'}
47
+ before_save{adds << 'hyiyie'}
48
+ before_save{adds << 'byiyie'}
49
49
  end
50
50
 
51
51
  c.new.before_save
52
- $adds.should == ['byiyie', 'hyiyie']
52
+ adds.should == ['byiyie', 'hyiyie']
53
53
  end
54
54
 
55
55
  specify "should not be additive if the method or tag already exists" do
56
- $adds = []
56
+ adds = []
57
57
  c = Class.new(Sequel::Model)
58
58
  c.class_eval do
59
- def bye; $adds << 'bye'; end
59
+ define_method(:bye){adds << 'bye'}
60
60
  before_save :bye
61
61
  before_save :bye
62
62
  end
63
63
 
64
64
  c.new.before_save
65
- $adds.should == ['bye']
65
+ adds.should == ['bye']
66
66
 
67
- $adds = []
67
+ adds = []
68
68
  d = Class.new(Sequel::Model)
69
69
  d.class_eval do
70
- before_save(:bye){$adds << 'hyiyie'}
71
- before_save(:bye){$adds << 'byiyie'}
70
+ before_save(:bye){adds << 'hyiyie'}
71
+ before_save(:bye){adds << 'byiyie'}
72
72
  end
73
73
 
74
74
  d.new.before_save
75
- $adds.should == ['byiyie']
75
+ adds.should == ['byiyie']
76
76
 
77
- $adds = []
77
+ adds = []
78
78
  e = Class.new(Sequel::Model)
79
79
  e.class_eval do
80
- def bye; $adds << 'bye'; end
80
+ define_method(:bye){adds << 'bye'}
81
81
  before_save :bye
82
- before_save(:bye){$adds << 'byiyie'}
82
+ before_save(:bye){adds << 'byiyie'}
83
83
  end
84
84
 
85
85
  e.new.before_save
86
- $adds.should == ['byiyie']
86
+ adds.should == ['byiyie']
87
87
 
88
- $adds = []
88
+ adds = []
89
89
  e = Class.new(Sequel::Model)
90
90
  e.class_eval do
91
- def bye; $adds << 'bye'; end
92
- before_save(:bye){$adds << 'byiyie'}
91
+ define_method(:bye){adds << 'bye'}
92
+ before_save(:bye){adds << 'byiyie'}
93
93
  before_save :bye
94
94
  end
95
95
 
96
96
  e.new.before_save
97
- $adds.should == ['bye']
97
+ adds.should == ['bye']
98
98
  end
99
99
 
100
100
  specify "should be inheritable" do
101
- # pending
102
-
103
- $adds = []
101
+ adds = []
104
102
  a = Class.new(Sequel::Model)
105
103
  a.class_eval do
106
- after_save {$adds << '123'}
104
+ after_save{adds << '123'}
107
105
  end
108
106
 
109
107
  b = Class.new(a)
110
108
  b.class_eval do
111
- after_save {$adds << '456'}
112
- after_save {$adds << '789'}
109
+ after_save{adds << '456'}
110
+ after_save{adds << '789'}
113
111
  end
114
112
 
115
113
  b.new.after_save
116
- $adds.should == ['123', '456', '789']
114
+ adds.should == ['123', '456', '789']
117
115
  end
118
116
 
119
117
  specify "should be overridable in descendant classes" do
120
- $adds = []
118
+ adds = []
121
119
  a = Class.new(Sequel::Model)
122
120
  a.class_eval do
123
- before_save {$adds << '123'}
121
+ before_save{adds << '123'}
124
122
  end
125
123
 
126
124
  b = Class.new(a)
127
125
  b.class_eval do
128
- def before_save; $adds << '456'; end
126
+ define_method(:before_save){adds << '456'}
129
127
  end
130
128
 
131
129
  a.new.before_save
132
- $adds.should == ['123']
133
- $adds = []
130
+ adds.should == ['123']
131
+ adds = []
134
132
  b.new.before_save
135
- $adds.should == ['456']
133
+ adds.should == ['456']
136
134
  end
137
135
 
138
136
  specify "should stop processing if a before hook returns false" do
139
- $flag = true
140
- $adds = []
137
+ flag = true
138
+ adds = []
141
139
 
142
140
  a = Class.new(Sequel::Model)
143
141
  a.class_eval do
144
- before_save {$adds << 'cruel'; $flag}
145
- before_save {$adds << 'blah'; $flag}
142
+ before_save{adds << 'cruel'; flag}
143
+ before_save{adds << 'blah'; flag}
146
144
  end
147
145
 
148
146
  a.new.before_save
149
- $adds.should == ['blah', 'cruel']
147
+ adds.should == ['blah', 'cruel']
150
148
 
151
149
  # chain should not break on nil
152
- $adds = []
153
- $flag = nil
150
+ adds = []
151
+ flag = nil
154
152
  a.new.before_save
155
- $adds.should == ['blah', 'cruel']
153
+ adds.should == ['blah', 'cruel']
156
154
 
157
- $adds = []
158
- $flag = false
155
+ adds = []
156
+ flag = false
159
157
  a.new.before_save
160
- $adds.should == ['blah']
158
+ adds.should == ['blah']
161
159
 
162
160
  b = Class.new(a)
163
161
  b.class_eval do
164
- before_save {$adds << 'mau'}
162
+ before_save{adds << 'mau'}
165
163
  end
166
164
 
167
- $adds = []
165
+ adds = []
168
166
  b.new.before_save
169
- $adds.should == ['mau', 'blah']
167
+ adds.should == ['mau', 'blah']
170
168
  end
171
169
  end
172
170
 
173
171
  describe "Model#after_initialize" do
174
172
  specify "should be called after initialization" do
175
- $values1 = nil
176
- $reached_after_initialized = false
173
+ values1 = nil
174
+ reached_after_initialized = false
177
175
 
178
176
  a = Class.new(Sequel::Model)
179
177
  a.class_eval do
180
178
  columns :x, :y
181
179
  after_initialize do
182
- $values1 = @values.clone
183
- $reached_after_initialized = true
180
+ values1 = @values.clone
181
+ reached_after_initialized = true
184
182
  end
185
183
  end
186
184
 
187
185
  a.new(:x => 1, :y => 2)
188
- $values1.should == {:x => 1, :y => 2}
189
- $reached_after_initialized.should == true
186
+ values1.should == {:x => 1, :y => 2}
187
+ reached_after_initialized.should == true
190
188
  end
191
189
  end
192
190
 
@@ -206,11 +204,7 @@ describe "Model#before_create && Model#after_create" do
206
204
  specify "should be called around new record creation" do
207
205
  @c.before_create {MODEL_DB << "BLAH before"}
208
206
  @c.create(:x => 2)
209
- MODEL_DB.sqls.should == [
210
- 'BLAH before',
211
- 'INSERT INTO items (x) VALUES (2)',
212
- 'BLAH after'
213
- ]
207
+ MODEL_DB.sqls.should == ['BLAH before', 'INSERT INTO items (x) VALUES (2)', 'BLAH after']
214
208
  end
215
209
 
216
210
  specify ".create should cancel the save and raise an error if before_create returns false and raise_on_save_failure is true" do
@@ -242,11 +236,7 @@ describe "Model#before_update && Model#after_update" do
242
236
  @c.before_update {MODEL_DB << "BLAH before"}
243
237
  m = @c.load(:id => 2233, :x=>123)
244
238
  m.save
245
- MODEL_DB.sqls.should == [
246
- 'BLAH before',
247
- 'UPDATE items SET x = 123 WHERE (id = 2233)',
248
- 'BLAH after'
249
- ]
239
+ MODEL_DB.sqls.should == ['BLAH before', 'UPDATE items SET x = 123 WHERE (id = 2233)', 'BLAH after']
250
240
  end
251
241
 
252
242
  specify "#save should cancel the save and raise an error if before_update returns false and raise_on_save_failure is true" do
@@ -279,22 +269,14 @@ describe "Model#before_save && Model#after_save" do
279
269
  @c.before_save {MODEL_DB << "BLAH before"}
280
270
  m = @c.load(:id => 2233, :x=>123)
281
271
  m.save
282
- MODEL_DB.sqls.should == [
283
- 'BLAH before',
284
- 'UPDATE items SET x = 123 WHERE (id = 2233)',
285
- 'BLAH after'
286
- ]
272
+ MODEL_DB.sqls.should == ['BLAH before', 'UPDATE items SET x = 123 WHERE (id = 2233)', 'BLAH after']
287
273
  end
288
274
 
289
275
  specify "should be called around record creation" do
290
276
  @c.before_save {MODEL_DB << "BLAH before"}
291
277
  @c.no_primary_key
292
278
  @c.create(:x => 2)
293
- MODEL_DB.sqls.should == [
294
- 'BLAH before',
295
- 'INSERT INTO items (x) VALUES (2)',
296
- 'BLAH after'
297
- ]
279
+ MODEL_DB.sqls.should == ['BLAH before', 'INSERT INTO items (x) VALUES (2)', 'BLAH after']
298
280
  end
299
281
 
300
282
  specify "#save should cancel the save and raise an error if before_save returns false and raise_on_save_failure is true" do
@@ -319,10 +301,6 @@ describe "Model#before_destroy && Model#after_destroy" do
319
301
  @c = Class.new(Sequel::Model(:items))
320
302
  @c.class_eval do
321
303
  after_destroy {MODEL_DB << "BLAH after"}
322
-
323
- def delete
324
- MODEL_DB << "DELETE BLAH"
325
- end
326
304
  end
327
305
  end
328
306
 
@@ -330,11 +308,7 @@ describe "Model#before_destroy && Model#after_destroy" do
330
308
  @c.before_destroy {MODEL_DB << "BLAH before"}
331
309
  m = @c.load(:id => 2233)
332
310
  m.destroy
333
- MODEL_DB.sqls.should == [
334
- 'BLAH before',
335
- 'DELETE BLAH',
336
- 'BLAH after'
337
- ]
311
+ MODEL_DB.sqls.should == ['BLAH before', "DELETE FROM items WHERE (id = 2233)", 'BLAH after']
338
312
  end
339
313
 
340
314
  specify "#destroy should cancel the destroy and raise an error if before_destroy returns false and raise_on_save_failure is true" do
@@ -5,13 +5,14 @@ describe "Sequel::Plugins::IdentityMap" do
5
5
  class ::IdentityMapModel < Sequel::Model
6
6
  plugin :identity_map
7
7
  attr_accessor :foo
8
- ds = dataset
9
- def ds.fetch_rows(sql)
10
- c = @opts[:where].args.first
11
- c = c.column if c.is_a?(Sequel::SQL::QualifiedIdentifier)
12
- h = {c=>@opts[:where].args.last}
13
- execute(sql)
14
- yield h
8
+ dataset._fetch = proc do |sql|
9
+ sql =~ /WHERE \(?(\w+\.)?(\w+) = (\d)\)?/
10
+ {$2.to_sym=>$3.to_i}
11
+ end
12
+ def self.waw_identity_map(&block) # with and without
13
+ with_identity_map(&block)
14
+ db.reset
15
+ yield
15
16
  end
16
17
  end
17
18
  class ::IdentityMapAlbum < ::IdentityMapModel
@@ -105,9 +106,9 @@ describe "Sequel::Plugins::IdentityMap" do
105
106
  MODEL_DB.sqls.length.should == 1
106
107
  o.foo = 1
107
108
  @c[1].foo.should == o.foo
108
- MODEL_DB.sqls.length.should == 1
109
+ MODEL_DB.sqls.length.should == 0
109
110
  @c[2].foo.should_not == o.foo
110
- MODEL_DB.sqls.length.should == 2
111
+ MODEL_DB.sqls.length.should == 1
111
112
  end
112
113
  end
113
114
 
@@ -118,22 +119,22 @@ describe "Sequel::Plugins::IdentityMap" do
118
119
  MODEL_DB.sqls.length.should == 1
119
120
  o.foo = 1
120
121
  @c[1].should == o
121
- MODEL_DB.sqls.length.should == 1
122
+ MODEL_DB.sqls.length.should == 0
122
123
  o.destroy
123
- MODEL_DB.sqls.length.should == 2
124
+ MODEL_DB.sqls.length.should == 1
124
125
  @c[1].foo.should_not == o.foo
125
- MODEL_DB.sqls.length.should == 3
126
+ MODEL_DB.sqls.length.should == 1
126
127
 
127
128
  MODEL_DB.reset
128
129
  o = @c[2]
129
130
  MODEL_DB.sqls.length.should == 1
130
131
  o.foo = 1
131
132
  @c[2].should == o
132
- MODEL_DB.sqls.length.should == 1
133
+ MODEL_DB.sqls.length.should == 0
133
134
  o.delete
134
- MODEL_DB.sqls.length.should == 2
135
+ MODEL_DB.sqls.length.should == 1
135
136
  @c[2].foo.should_not == o.foo
136
- MODEL_DB.sqls.length.should == 3
137
+ MODEL_DB.sqls.length.should == 1
137
138
  end
138
139
  end
139
140
 
@@ -148,10 +149,10 @@ describe "Sequel::Plugins::IdentityMap" do
148
149
  MODEL_DB.sqls.length.should == 1
149
150
  o = @c1.load(:id=>1, :another_id=>2, :artist_id=>2)
150
151
  o.artist.should == a
151
- MODEL_DB.sqls.length.should == 1
152
+ MODEL_DB.sqls.length.should == 0
152
153
  o = @c1.load(:id=>3, :another_id=>3, :artist_id=>3)
153
154
  o.artist.should_not == a
154
- MODEL_DB.sqls.length.should == 2
155
+ MODEL_DB.sqls.length.should == 1
155
156
  end
156
157
  end
157
158
 
@@ -165,10 +166,10 @@ describe "Sequel::Plugins::IdentityMap" do
165
166
  MODEL_DB.sqls.length.should == 1
166
167
  o = @c1.load(:id=>2, :artist_id=>2)
167
168
  o.artist.should == a
168
- MODEL_DB.sqls.length.should == 1
169
+ MODEL_DB.sqls.length.should == 0
169
170
  o = @c1.load(:id=>3, :artist_id=>3)
170
171
  o.artist.should_not == a
171
- MODEL_DB.sqls.length.should == 2
172
+ MODEL_DB.sqls.length.should == 1
172
173
  end
173
174
  end
174
175
 
@@ -182,9 +183,9 @@ describe "Sequel::Plugins::IdentityMap" do
182
183
  a.should be_a_kind_of(@c1)
183
184
  MODEL_DB.sqls.length.should == 1
184
185
  o.reload
185
- MODEL_DB.sqls.length.should == 2
186
+ MODEL_DB.sqls.length.should == 1
186
187
  o.artist.should == a
187
- MODEL_DB.sqls.length.should == 3
188
+ MODEL_DB.sqls.length.should == 1
188
189
  end
189
190
  end
190
191
 
@@ -199,7 +200,7 @@ describe "Sequel::Plugins::IdentityMap" do
199
200
  MODEL_DB.sqls.length.should == 1
200
201
  o = @c1.load(:id=>2, :artist_id=>2)
201
202
  o.artist.should == a
202
- MODEL_DB.sqls.length.should == 2
203
+ MODEL_DB.sqls.length.should == 1
203
204
  end
204
205
  end
205
206
 
@@ -213,7 +214,98 @@ describe "Sequel::Plugins::IdentityMap" do
213
214
  MODEL_DB.sqls.length.should == 1
214
215
  o = @c1.load(:id=>2, :artist_id=>2)
215
216
  o.artist.should == a
217
+ MODEL_DB.sqls.length.should == 1
218
+ end
219
+ end
220
+
221
+ it "should not override custom :eager_loaders for many_to_many associations" do
222
+ @c1.columns :id
223
+ @c2.columns :id
224
+ c = @c2
225
+ @c1.many_to_many :artists, :class=>@c2, :left_key=>:album_id, :right_key=>:artist_id, :join_table=>:aa, :eager_loader=>(proc do |eo|
226
+ eo[:rows].each{|object| object.associations[:artists] = [c.load(:id=>object.id)]}
227
+ end)
228
+ @c1.dataset._fetch = [{:id=>1}, {:id=>2}, {:id=>3}]
229
+
230
+ @c.waw_identity_map do
231
+ MODEL_DB.sqls.length.should == 0
232
+ a = @c1.eager(:artists).all
233
+ MODEL_DB.sqls.length.should == 1
234
+ a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
235
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1)], [@c2.load(:id=>2)], [@c2.load(:id=>3)]]
236
+ MODEL_DB.sqls.length.should == 0
237
+ end
238
+ end
239
+
240
+ it "should work correctly when eagerly loading many_to_many associations" do
241
+ @c1.columns :id
242
+ @c2.columns :id
243
+ @c1.many_to_many :artists, :class=>@c2, :left_key=>:album_id, :right_key=>:artist_id, :join_table=>:aa
244
+ @c1.dataset._fetch = [{:id=>1}, {:id=>2}, {:id=>3}]
245
+ @c2.dataset._fetch = [{:id=>1, :x_foreign_key_x=>1}, {:id=>1, :x_foreign_key_x=>2}, {:id=>2, :x_foreign_key_x=>1}, {:id=>2, :x_foreign_key_x=>2}, {:id=>3, :x_foreign_key_x=>1}, {:id=>3, :x_foreign_key_x=>1}]
246
+
247
+ @c.waw_identity_map do
248
+ MODEL_DB.sqls.length.should == 0
249
+ a = @c1.eager(:artists).all
250
+ MODEL_DB.sqls.length.should == 2
251
+ a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
252
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
253
+ MODEL_DB.sqls.length.should == 0
254
+ end
255
+ end
256
+
257
+ it "should work correctly when eagerly loading many_to_many associations with composite keys" do
258
+ @c1.columns :id, :id2
259
+ @c2.columns :id
260
+ @c1.set_primary_key :id, :id2
261
+ @c1.many_to_many :artists, :class=>@c2, :left_key=>[:album_id1, :album_id2], :right_key=>:artist_id, :join_table=>:aa
262
+ @c1.dataset._fetch = [{:id=>1, :id2=>4}, {:id=>2, :id2=>5}, {:id=>3, :id2=>6}]
263
+ @c2.dataset._fetch = [ {:id=>1, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>1, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>2, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>2, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}]
264
+
265
+ @c.waw_identity_map do
266
+ MODEL_DB.sqls.length.should == 0
267
+ a = @c1.eager(:artists).all
268
+ MODEL_DB.sqls.length.should == 2
269
+ a.should == [@c1.load(:id=>1, :id2=>4), @c1.load(:id=>2, :id2=>5), @c1.load(:id=>3, :id2=>6)]
270
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
271
+ MODEL_DB.sqls.length.should == 0
272
+ end
273
+ end
274
+
275
+ it "should work correctly when eagerly loading many_through_many associations" do
276
+ @c1.columns :id
277
+ @c2.columns :id
278
+ @c1.plugin :many_through_many
279
+ @c1.many_through_many :artists, [[:aa, :album_id, :artist_id]], :class=>@c2
280
+ @c1.dataset._fetch = [{:id=>1}, {:id=>2}, {:id=>3}]
281
+ @c2.dataset._fetch = [{:id=>1, :x_foreign_key_x=>1}, {:id=>1, :x_foreign_key_x=>2}, {:id=>2, :x_foreign_key_x=>1}, {:id=>2, :x_foreign_key_x=>2}, {:id=>3, :x_foreign_key_x=>1}, {:id=>3, :x_foreign_key_x=>1}]
282
+
283
+ @c.waw_identity_map do
284
+ MODEL_DB.sqls.length.should == 0
285
+ a = @c1.eager(:artists).all
216
286
  MODEL_DB.sqls.length.should == 2
287
+ a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
288
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
289
+ MODEL_DB.sqls.length.should == 0
290
+ end
291
+ end
292
+
293
+ it "should work correctly when eagerly loading many_to_many associations with composite keys" do
294
+ @c1.columns :id, :id2
295
+ @c2.columns :id
296
+ @c1.set_primary_key :id, :id2
297
+ @c1.plugin :many_through_many
298
+ @c1.many_through_many :artists, [[:aa, [:album_id1, :album_id2], :artist_id]], :class=>@c2
299
+ @c1.dataset._fetch = [{:id=>1, :id2=>4}, {:id=>2, :id2=>5}, {:id=>3, :id2=>6}]
300
+ @c2.dataset._fetch = [ {:id=>1, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>1, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>2, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>2, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}]
301
+
302
+ @c.waw_identity_map do
303
+ MODEL_DB.sqls.length.should == 0
304
+ a = @c1.eager(:artists).all
305
+ MODEL_DB.sqls.length.should == 2
306
+ a.should == [@c1.load(:id=>1, :id2=>4), @c1.load(:id=>2, :id2=>5), @c1.load(:id=>3, :id2=>6)]
307
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
308
+ MODEL_DB.sqls.length.should == 0
217
309
  end
218
310
  end
219
311
  end
@@ -2,54 +2,44 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "instance_filters plugin" do
4
4
  before do
5
- @c = Class.new(Sequel::Model(:people)) do
6
- end
7
- @sql = sql = ''
8
- @v = v = [1]
5
+ @c = Class.new(Sequel::Model(:people))
9
6
  @c.dataset.quote_identifiers = false
10
- @c.dataset.meta_def(:update) do |opts|
11
- sql.replace(update_sql(opts))
12
- return v.first
13
- end
14
- @c.dataset.meta_def(:delete) do
15
- sql.replace(delete_sql)
16
- return v.first
17
- end
18
7
  @c.columns :id, :name, :num
19
8
  @c.plugin :instance_filters
20
9
  @p = @c.load(:id=>1, :name=>'John', :num=>1)
10
+ MODEL_DB.sqls
21
11
  end
22
12
 
23
13
  specify "should raise an error when updating a stale record" do
24
14
  @p.update(:name=>'Bob')
25
- @sql.should == "UPDATE people SET name = 'Bob' WHERE (id = 1)"
15
+ MODEL_DB.sqls.should == ["UPDATE people SET name = 'Bob' WHERE (id = 1)"]
26
16
  @p.instance_filter(:name=>'Jim')
27
- @v.replace([0])
17
+ @p.this.numrows = 0
28
18
  proc{@p.update(:name=>'Joe')}.should raise_error(Sequel::Plugins::InstanceFilters::Error)
29
- @sql.should == "UPDATE people SET name = 'Joe' WHERE ((id = 1) AND (name = 'Jim'))"
19
+ MODEL_DB.sqls.should == ["UPDATE people SET name = 'Joe' WHERE ((id = 1) AND (name = 'Jim'))"]
30
20
  end
31
21
 
32
22
  specify "should raise an error when destroying a stale record" do
33
23
  @p.destroy
34
- @sql.should == "DELETE FROM people WHERE (id = 1)"
24
+ MODEL_DB.sqls.should == ["DELETE FROM people WHERE (id = 1)"]
35
25
  @p.instance_filter(:name=>'Jim')
36
- @v.replace([0])
26
+ @p.this.numrows = 0
37
27
  proc{@p.destroy}.should raise_error(Sequel::Plugins::InstanceFilters::Error)
38
- @sql.should == "DELETE FROM people WHERE ((id = 1) AND (name = 'Jim'))"
28
+ MODEL_DB.sqls.should == ["DELETE FROM people WHERE ((id = 1) AND (name = 'Jim'))"]
39
29
  end
40
30
 
41
31
  specify "should apply all instance filters" do
42
32
  @p.instance_filter(:name=>'Jim')
43
33
  @p.instance_filter{num > 2}
44
34
  @p.update(:name=>'Bob')
45
- @sql.should == "UPDATE people SET name = 'Bob' WHERE ((id = 1) AND (name = 'Jim') AND (num > 2))"
35
+ MODEL_DB.sqls.should == ["UPDATE people SET name = 'Bob' WHERE ((id = 1) AND (name = 'Jim') AND (num > 2))"]
46
36
  end
47
37
 
48
38
  specify "should drop instance filters after updating" do
49
39
  @p.instance_filter(:name=>'Joe')
50
40
  @p.update(:name=>'Joe')
51
- @sql.should == "UPDATE people SET name = 'Joe' WHERE ((id = 1) AND (name = 'Joe'))"
41
+ MODEL_DB.sqls.should == ["UPDATE people SET name = 'Joe' WHERE ((id = 1) AND (name = 'Joe'))"]
52
42
  @p.update(:name=>'Bob')
53
- @sql.should == "UPDATE people SET name = 'Bob' WHERE (id = 1)"
43
+ MODEL_DB.sqls.should == ["UPDATE people SET name = 'Bob' WHERE (id = 1)"]
54
44
  end
55
45
  end
@@ -177,3 +177,56 @@ describe "InstanceHooks plugin" do
177
177
  @r.should == [2, 1, 4, 3]
178
178
  end
179
179
  end
180
+
181
+ describe "InstanceHooks plugin with transactions" do
182
+ before do
183
+ @db = Sequel.mock(:numrows=>1)
184
+ pr = proc{|x| r(x)}
185
+ @c = Class.new(Sequel::Model(@db[:items])) do
186
+ attr_accessor :rb
187
+ def after_save
188
+ db.execute('as')
189
+ raise Sequel::Rollback if rb
190
+ end
191
+ def after_destroy
192
+ db.execute('ad')
193
+ raise Sequel::Rollback if rb
194
+ end
195
+ end
196
+ @c.use_transactions = true
197
+ @c.plugin :instance_hooks
198
+ @o = @c.load({:id=>1})
199
+ @or = @c.load({:id=>1})
200
+ @or.rb = true
201
+ @r = []
202
+ @db.sqls
203
+ end
204
+
205
+ it "should support after_commit_hook" do
206
+ @o.after_commit_hook{@db.execute('ac1')}
207
+ @o.after_commit_hook{@db.execute('ac2')}
208
+ @o.save.should_not be_nil
209
+ @db.sqls.should == ['BEGIN', 'as', 'COMMIT', 'ac1', 'ac2']
210
+ end
211
+
212
+ it "should support after_rollback_hook" do
213
+ @or.after_rollback_hook{@db.execute('ar1')}
214
+ @or.after_rollback_hook{@db.execute('ar2')}
215
+ @or.save.should be_nil
216
+ @db.sqls.should == ['BEGIN', 'as', 'ROLLBACK', 'ar1', 'ar2']
217
+ end
218
+
219
+ it "should support after_commit_hook" do
220
+ @o.after_destroy_commit_hook{@db.execute('adc1')}
221
+ @o.after_destroy_commit_hook{@db.execute('adc2')}
222
+ @o.destroy.should_not be_nil
223
+ @db.sqls.should == ['BEGIN', "DELETE FROM items WHERE (id = 1)", 'ad', 'COMMIT', 'adc1', 'adc2']
224
+ end
225
+
226
+ it "should support after_rollback_hook" do
227
+ @or.after_destroy_rollback_hook{@db.execute('adr1')}
228
+ @or.after_destroy_rollback_hook{@db.execute('adr2')}
229
+ @or.destroy.should be_nil
230
+ @db.sqls.should == ['BEGIN', "DELETE FROM items WHERE (id = 1)", 'ad', 'ROLLBACK', 'adr1', 'adr2']
231
+ end
232
+ end
@@ -102,8 +102,8 @@ describe "Sequel::Plugins::JsonSerializer" do
102
102
  end
103
103
 
104
104
  it "should support a to_json class and dataset method" do
105
- album = @album
106
- Album.dataset.meta_def(:all){[album]}
105
+ Album.dataset._fetch = {:id=>1, :name=>'RF', :artist_id=>2}
106
+ Artist.dataset._fetch = {:id=>2, :name=>'YJM'}
107
107
  JSON.parse(Album.to_json).should == [@album]
108
108
  JSON.parse(Album.to_json(:include=>:artist)).map{|x| x.artist}.should == [@artist]
109
109
  JSON.parse(Album.dataset.to_json(:only=>:name)).should == [Album.load(:name=>@album.name)]
@@ -112,7 +112,7 @@ describe "Sequel::Plugins::JsonSerializer" do
112
112
  it "should have dataset to_json method work with naked datasets" do
113
113
  album = @album
114
114
  ds = Album.dataset.naked
115
- ds.meta_def(:all){[album.values]}
115
+ ds._fetch = {:id=>1, :name=>'RF', :artist_id=>2}
116
116
  JSON.parse(ds.to_json).should == [@album.values.inject({}){|h, (k, v)| h[k.to_s] = v; h}]
117
117
  end
118
118
 
@@ -137,8 +137,7 @@ describe "Sequel::Plugins::JsonSerializer" do
137
137
  end
138
138
 
139
139
  it "should handle the :root option to qualify a dataset of records" do
140
- album = @album
141
- Album.dataset.meta_def(:all){[album, album]}
140
+ Album.dataset._fetch = [{:id=>1, :name=>'RF'}, {:id=>1, :name=>'RF'}]
142
141
  Album.dataset.to_json(:root=>true, :only => :id).to_s.should == '{"albums":[{"album":{"id":1}},{"album":{"id":1}}]}'
143
142
  end
144
143