sequel 3.21.0 → 3.31.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 (225) hide show
  1. data/CHANGELOG +485 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +37 -19
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +249 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/dataset_filtering.rdoc +8 -0
  8. data/doc/mass_assignment.rdoc +54 -0
  9. data/doc/migration.rdoc +15 -538
  10. data/doc/model_hooks.rdoc +72 -27
  11. data/doc/opening_databases.rdoc +89 -50
  12. data/doc/prepared_statements.rdoc +40 -13
  13. data/doc/reflection.rdoc +8 -2
  14. data/doc/release_notes/3.22.0.txt +39 -0
  15. data/doc/release_notes/3.23.0.txt +172 -0
  16. data/doc/release_notes/3.24.0.txt +420 -0
  17. data/doc/release_notes/3.25.0.txt +88 -0
  18. data/doc/release_notes/3.26.0.txt +88 -0
  19. data/doc/release_notes/3.27.0.txt +82 -0
  20. data/doc/release_notes/3.28.0.txt +304 -0
  21. data/doc/release_notes/3.29.0.txt +459 -0
  22. data/doc/release_notes/3.30.0.txt +135 -0
  23. data/doc/release_notes/3.31.0.txt +146 -0
  24. data/doc/schema_modification.rdoc +547 -0
  25. data/doc/sharding.rdoc +7 -1
  26. data/doc/testing.rdoc +129 -0
  27. data/doc/transactions.rdoc +137 -0
  28. data/doc/virtual_rows.rdoc +2 -2
  29. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  30. data/lib/sequel/adapters/ado.rb +40 -18
  31. data/lib/sequel/adapters/amalgalite.rb +17 -9
  32. data/lib/sequel/adapters/db2.rb +175 -86
  33. data/lib/sequel/adapters/dbi.rb +15 -15
  34. data/lib/sequel/adapters/do/mysql.rb +5 -7
  35. data/lib/sequel/adapters/do/postgres.rb +0 -5
  36. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/do.rb +17 -36
  38. data/lib/sequel/adapters/firebird.rb +27 -208
  39. data/lib/sequel/adapters/ibmdb.rb +448 -0
  40. data/lib/sequel/adapters/informix.rb +6 -23
  41. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  42. data/lib/sequel/adapters/jdbc/db2.rb +45 -0
  43. data/lib/sequel/adapters/jdbc/derby.rb +247 -0
  44. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  45. data/lib/sequel/adapters/jdbc/h2.rb +56 -29
  46. data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
  47. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  48. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  49. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  50. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  51. data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
  52. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  53. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  54. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  55. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  56. data/lib/sequel/adapters/jdbc.rb +231 -78
  57. data/lib/sequel/adapters/mock.rb +335 -0
  58. data/lib/sequel/adapters/mysql.rb +82 -184
  59. data/lib/sequel/adapters/mysql2.rb +26 -25
  60. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  61. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  62. data/lib/sequel/adapters/odbc.rb +28 -9
  63. data/lib/sequel/adapters/openbase.rb +3 -5
  64. data/lib/sequel/adapters/oracle.rb +351 -53
  65. data/lib/sequel/adapters/postgres.rb +194 -33
  66. data/lib/sequel/adapters/shared/access.rb +32 -9
  67. data/lib/sequel/adapters/shared/db2.rb +321 -0
  68. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  69. data/lib/sequel/adapters/shared/informix.rb +53 -0
  70. data/lib/sequel/adapters/shared/mssql.rb +231 -106
  71. data/lib/sequel/adapters/shared/mysql.rb +181 -63
  72. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +231 -41
  74. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  75. data/lib/sequel/adapters/shared/progress.rb +4 -7
  76. data/lib/sequel/adapters/shared/sqlite.rb +201 -57
  77. data/lib/sequel/adapters/sqlite.rb +29 -21
  78. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  79. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  80. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  81. data/lib/sequel/adapters/swift.rb +5 -5
  82. data/lib/sequel/adapters/tinytds.rb +160 -19
  83. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  84. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  85. data/lib/sequel/ast_transformer.rb +190 -0
  86. data/lib/sequel/connection_pool/threaded.rb +3 -2
  87. data/lib/sequel/connection_pool.rb +1 -1
  88. data/lib/sequel/core.rb +46 -5
  89. data/lib/sequel/database/connecting.rb +5 -6
  90. data/lib/sequel/database/dataset.rb +4 -4
  91. data/lib/sequel/database/dataset_defaults.rb +59 -1
  92. data/lib/sequel/database/logging.rb +1 -1
  93. data/lib/sequel/database/misc.rb +104 -19
  94. data/lib/sequel/database/query.rb +153 -58
  95. data/lib/sequel/database/schema_generator.rb +17 -7
  96. data/lib/sequel/database/schema_methods.rb +59 -29
  97. data/lib/sequel/dataset/actions.rb +231 -76
  98. data/lib/sequel/dataset/features.rb +91 -8
  99. data/lib/sequel/dataset/graph.rb +38 -11
  100. data/lib/sequel/dataset/misc.rb +50 -23
  101. data/lib/sequel/dataset/mutation.rb +5 -6
  102. data/lib/sequel/dataset/prepared_statements.rb +48 -27
  103. data/lib/sequel/dataset/query.rb +236 -43
  104. data/lib/sequel/dataset/sql.rb +665 -253
  105. data/lib/sequel/dataset.rb +8 -0
  106. data/lib/sequel/exceptions.rb +4 -0
  107. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  108. data/lib/sequel/extensions/migration.rb +6 -4
  109. data/lib/sequel/extensions/named_timezones.rb +5 -0
  110. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  111. data/lib/sequel/extensions/to_dot.rb +95 -83
  112. data/lib/sequel/model/associations.rb +1020 -337
  113. data/lib/sequel/model/base.rb +414 -132
  114. data/lib/sequel/model/errors.rb +1 -1
  115. data/lib/sequel/model/exceptions.rb +5 -1
  116. data/lib/sequel/model.rb +15 -8
  117. data/lib/sequel/plugins/association_pks.rb +22 -4
  118. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  119. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  120. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  121. data/lib/sequel/plugins/force_encoding.rb +6 -6
  122. data/lib/sequel/plugins/identity_map.rb +114 -7
  123. data/lib/sequel/plugins/many_through_many.rb +65 -7
  124. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  125. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  126. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  127. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  128. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  129. data/lib/sequel/plugins/serialization.rb +88 -44
  130. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  131. data/lib/sequel/plugins/sharding.rb +21 -54
  132. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  133. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  134. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  135. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  136. data/lib/sequel/sql.rb +109 -52
  137. data/lib/sequel/timezones.rb +57 -38
  138. data/lib/sequel/version.rb +1 -1
  139. data/spec/adapters/db2_spec.rb +146 -0
  140. data/spec/adapters/mssql_spec.rb +111 -29
  141. data/spec/adapters/mysql_spec.rb +135 -123
  142. data/spec/adapters/oracle_spec.rb +49 -77
  143. data/spec/adapters/postgres_spec.rb +267 -78
  144. data/spec/adapters/spec_helper.rb +6 -5
  145. data/spec/adapters/sqlite_spec.rb +35 -21
  146. data/spec/core/connection_pool_spec.rb +71 -92
  147. data/spec/core/core_sql_spec.rb +21 -30
  148. data/spec/core/database_spec.rb +788 -512
  149. data/spec/core/dataset_spec.rb +1073 -1053
  150. data/spec/core/expression_filters_spec.rb +159 -42
  151. data/spec/core/mock_adapter_spec.rb +419 -0
  152. data/spec/core/object_graph_spec.rb +86 -114
  153. data/spec/core/schema_generator_spec.rb +3 -3
  154. data/spec/core/schema_spec.rb +298 -38
  155. data/spec/core/spec_helper.rb +6 -48
  156. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  157. data/spec/extensions/association_dependencies_spec.rb +2 -12
  158. data/spec/extensions/association_pks_spec.rb +66 -32
  159. data/spec/extensions/caching_spec.rb +23 -50
  160. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  161. data/spec/extensions/columns_introspection_spec.rb +91 -0
  162. data/spec/extensions/composition_spec.rb +18 -13
  163. data/spec/extensions/dataset_associations_spec.rb +199 -0
  164. data/spec/extensions/defaults_setter_spec.rb +64 -0
  165. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  166. data/spec/extensions/identity_map_spec.rb +114 -22
  167. data/spec/extensions/instance_filters_spec.rb +11 -21
  168. data/spec/extensions/instance_hooks_spec.rb +53 -0
  169. data/spec/extensions/json_serializer_spec.rb +4 -5
  170. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  171. data/spec/extensions/list_spec.rb +17 -39
  172. data/spec/extensions/many_through_many_spec.rb +283 -250
  173. data/spec/extensions/migration_spec.rb +35 -32
  174. data/spec/extensions/named_timezones_spec.rb +23 -3
  175. data/spec/extensions/nested_attributes_spec.rb +97 -88
  176. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  177. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  178. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  179. data/spec/extensions/prepared_statements_spec.rb +53 -0
  180. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  181. data/spec/extensions/pretty_table_spec.rb +1 -6
  182. data/spec/extensions/rcte_tree_spec.rb +41 -43
  183. data/spec/extensions/schema_dumper_spec.rb +5 -8
  184. data/spec/extensions/schema_spec.rb +13 -21
  185. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  186. data/spec/extensions/serialization_spec.rb +72 -43
  187. data/spec/extensions/sharding_spec.rb +66 -140
  188. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  189. data/spec/extensions/spec_helper.rb +13 -61
  190. data/spec/extensions/sql_expr_spec.rb +20 -60
  191. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  192. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  193. data/spec/extensions/timestamps_spec.rb +6 -6
  194. data/spec/extensions/to_dot_spec.rb +4 -7
  195. data/spec/extensions/touch_spec.rb +13 -14
  196. data/spec/extensions/tree_spec.rb +11 -26
  197. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  198. data/spec/extensions/update_primary_key_spec.rb +30 -24
  199. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  200. data/spec/extensions/validation_helpers_spec.rb +16 -35
  201. data/spec/extensions/xml_serializer_spec.rb +16 -4
  202. data/spec/integration/associations_test.rb +537 -21
  203. data/spec/integration/database_test.rb +4 -2
  204. data/spec/integration/dataset_test.rb +461 -65
  205. data/spec/integration/eager_loader_test.rb +19 -21
  206. data/spec/integration/model_test.rb +80 -1
  207. data/spec/integration/plugin_test.rb +304 -116
  208. data/spec/integration/prepared_statement_test.rb +212 -122
  209. data/spec/integration/schema_test.rb +161 -30
  210. data/spec/integration/spec_helper.rb +39 -30
  211. data/spec/integration/timezone_test.rb +38 -12
  212. data/spec/integration/transaction_test.rb +172 -5
  213. data/spec/integration/type_test.rb +17 -3
  214. data/spec/model/association_reflection_spec.rb +201 -7
  215. data/spec/model/associations_spec.rb +865 -662
  216. data/spec/model/base_spec.rb +275 -95
  217. data/spec/model/dataset_methods_spec.rb +7 -27
  218. data/spec/model/eager_loading_spec.rb +677 -676
  219. data/spec/model/hooks_spec.rb +309 -67
  220. data/spec/model/model_spec.rb +222 -168
  221. data/spec/model/plugins_spec.rb +24 -13
  222. data/spec/model/record_spec.rb +381 -218
  223. data/spec/model/spec_helper.rb +13 -71
  224. data/spec/model/validations_spec.rb +11 -0
  225. metadata +115 -73
@@ -48,9 +48,10 @@ describe "Composition plugin" do
48
48
  @c.composition :date, :composer=>proc{Date.new(year+1, month+2, day+3)}, :decomposer=>proc{[:year, :month, :day].each{|s| self.send("#{s}=", date.send(s) * 2)}}
49
49
  @o.date.should == Date.new(2, 4, 6)
50
50
  @o.save
51
- MODEL_DB.sqls.last.should include("year = 4")
52
- MODEL_DB.sqls.last.should include("month = 8")
53
- MODEL_DB.sqls.last.should include("day = 12")
51
+ sql = MODEL_DB.sqls.last
52
+ sql.should include("year = 4")
53
+ sql.should include("month = 8")
54
+ sql.should include("day = 12")
54
55
  end
55
56
 
56
57
  it "should allow call super in composition getter and setter method definition in class" do
@@ -139,8 +140,9 @@ describe "Composition plugin" do
139
140
  @o.date.month.should == 6
140
141
  @o.date = c.new(3, 4)
141
142
  @o.save
142
- MODEL_DB.sqls.last.should include("year = 6")
143
- MODEL_DB.sqls.last.should include("month = 12")
143
+ sql = MODEL_DB.sqls.last
144
+ sql.should include("year = 6")
145
+ sql.should include("month = 12")
144
146
  end
145
147
 
146
148
  it ":mapping option should work with an array of two pairs of symbols" do
@@ -160,8 +162,9 @@ describe "Composition plugin" do
160
162
  @o.date.m.should == 6
161
163
  @o.date = c.new(3, 4)
162
164
  @o.save
163
- MODEL_DB.sqls.last.should include("year = 6")
164
- MODEL_DB.sqls.last.should include("month = 12")
165
+ sql = MODEL_DB.sqls.last
166
+ sql.should include("year = 6")
167
+ sql.should include("month = 12")
165
168
  end
166
169
 
167
170
  it ":mapping option :composer should return nil if all values are nil" do
@@ -173,9 +176,10 @@ describe "Composition plugin" do
173
176
  @c.composition :date, :mapping=>[:year, :month, :day]
174
177
  @o.date = nil
175
178
  @o.save
176
- MODEL_DB.sqls.last.should include("year = NULL")
177
- MODEL_DB.sqls.last.should include("month = NULL")
178
- MODEL_DB.sqls.last.should include("day = NULL")
179
+ sql = MODEL_DB.sqls.last
180
+ sql.should include("year = NULL")
181
+ sql.should include("month = NULL")
182
+ sql.should include("day = NULL")
179
183
  end
180
184
 
181
185
  it "should work correctly with subclasses" do
@@ -184,8 +188,9 @@ describe "Composition plugin" do
184
188
  o = c.load(:id=>1, :year=>1, :month=>2, :day=>3)
185
189
  o.date.should == Date.new(1, 2, 3)
186
190
  o.save
187
- MODEL_DB.sqls.last.should include("year = 1")
188
- MODEL_DB.sqls.last.should include("month = 2")
189
- MODEL_DB.sqls.last.should include("day = 3")
191
+ sql = MODEL_DB.sqls.last
192
+ sql.should include("year = 1")
193
+ sql.should include("month = 2")
194
+ sql.should include("day = 3")
190
195
  end
191
196
  end
@@ -0,0 +1,199 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::DatasetAssociations" do
4
+ before do
5
+ @db = Sequel::Database.new
6
+ @Base = Class.new(Sequel::Model)
7
+ @Base.plugin :dataset_associations
8
+
9
+ @Artist = Class.new(@Base)
10
+ @Album = Class.new(@Base)
11
+ @Tag = Class.new(@Base)
12
+
13
+ @Artist.meta_def(:name){'Artist'}
14
+ @Album.meta_def(:name){'Album'}
15
+ @Tag.meta_def(:name){'Tag'}
16
+
17
+ @Artist.dataset = @db[:artists]
18
+ @Album.dataset = @db[:albums]
19
+ @Tag.dataset = @db[:tags]
20
+
21
+ @Artist.columns :id, :name
22
+ @Album.columns :id, :name, :artist_id
23
+ @Tag.columns :id, :name
24
+
25
+ @Artist.plugin :many_through_many
26
+ @Artist.one_to_many :albums, :class=>@Album
27
+ @Artist.one_to_one :first_album, :class=>@Album
28
+ @Album.many_to_one :artist, :class=>@Artist
29
+ @Album.many_to_many :tags, :class=>@Tag
30
+ @Tag.many_to_many :albums, :class=>@Album
31
+ @Artist.many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]], :class=>@Tag
32
+ end
33
+
34
+ it "should work for many_to_one associations" do
35
+ ds = @Album.artists
36
+ ds.should be_a_kind_of(Sequel::Dataset)
37
+ ds.model.should == @Artist
38
+ ds.sql.should == "SELECT * FROM artists WHERE (artists.id IN (SELECT albums.artist_id FROM albums))"
39
+ end
40
+
41
+ it "should work for one_to_many associations" do
42
+ ds = @Artist.albums
43
+ ds.should be_a_kind_of(Sequel::Dataset)
44
+ ds.model.should == @Album
45
+ ds.sql.should == "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists))"
46
+ end
47
+
48
+ it "should work for one_to_one associations" do
49
+ ds = @Artist.first_albums
50
+ ds.should be_a_kind_of(Sequel::Dataset)
51
+ ds.model.should == @Album
52
+ ds.sql.should == "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists))"
53
+ end
54
+
55
+ it "should work for many_to_many associations" do
56
+ ds = @Album.tags
57
+ ds.should be_a_kind_of(Sequel::Dataset)
58
+ ds.model.should == @Tag
59
+ ds.sql.should == "SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM albums INNER JOIN albums_tags ON (albums_tags.album_id = albums.id)))"
60
+ end
61
+
62
+ it "should work for many_through_many associations" do
63
+ ds = @Artist.tags
64
+ ds.should be_a_kind_of(Sequel::Dataset)
65
+ ds.model.should == @Tag
66
+ ds.sql.should == "SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM artists INNER JOIN albums ON (albums.artist_id = artists.id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id)))"
67
+ end
68
+
69
+ it "should have an associated method that takes an association symbol" do
70
+ ds = @Album.associated(:artist)
71
+ ds.should be_a_kind_of(Sequel::Dataset)
72
+ ds.model.should == @Artist
73
+ ds.sql.should == "SELECT * FROM artists WHERE (artists.id IN (SELECT albums.artist_id FROM albums))"
74
+ end
75
+
76
+ it "should raise an Error if an invalid association is given to associated" do
77
+ proc{@Album.associated(:foo)}.should raise_error(Sequel::Error)
78
+ end
79
+
80
+ it "should raise an Error if an unrecognized association type is used" do
81
+ @Album.association_reflection(:artist)[:type] = :foo
82
+ proc{@Album.artists}.should raise_error(Sequel::Error)
83
+ end
84
+
85
+ it "should work correctly when chaining" do
86
+ ds = @Artist.albums.tags
87
+ ds.should be_a_kind_of(Sequel::Dataset)
88
+ ds.model.should == @Tag
89
+ ds.sql.should == "SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM albums INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE (albums.artist_id IN (SELECT artists.id FROM artists))))"
90
+ end
91
+
92
+ it "should deal correctly with filters before the association method" do
93
+ @Artist.filter(:id=>1).albums.sql.should == "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE (id = 1)))"
94
+ end
95
+
96
+ it "should deal correctly with filters after the association method" do
97
+ @Artist.albums.filter(:id=>1).sql.should == "SELECT * FROM albums WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND (id = 1))"
98
+ end
99
+
100
+ it "should deal correctly with block on the association" do
101
+ @Artist.one_to_many :albums, :clone=>:albums do |ds| ds.filter(:id=>1..100) end
102
+ @Artist.albums.sql.should == "SELECT * FROM albums WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND (id >= 1) AND (id <= 100))"
103
+ end
104
+
105
+ it "should deal correctly with :conditions option on the association" do
106
+ @Artist.one_to_many :albums, :clone=>:albums, :conditions=>{:id=>1..100}
107
+ @Artist.albums.sql.should == "SELECT * FROM albums WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND (id >= 1) AND (id <= 100))"
108
+ end
109
+
110
+ it "should deal correctly with :distinct option on the association" do
111
+ @Artist.one_to_many :albums, :clone=>:albums, :distinct=>true
112
+ @Artist.albums.sql.should == "SELECT DISTINCT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists))"
113
+ end
114
+
115
+ it "should deal correctly with :eager option on the association" do
116
+ @Artist.one_to_many :albums, :clone=>:albums, :eager=>:tags
117
+ @Artist.albums.opts[:eager].should == {:tags=>nil}
118
+ end
119
+
120
+ it "should deal correctly with :eager_block option on the association, ignoring the association block" do
121
+ @Artist.one_to_many :albums, :clone=>:albums, :eager_block=>proc{|ds| ds.filter(:id=>1..100)} do |ds| ds.filter(:id=>2..200) end
122
+ @Artist.albums.sql.should == "SELECT * FROM albums WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND (id >= 1) AND (id <= 100))"
123
+ end
124
+
125
+ it "should deal correctly with :extend option on the association" do
126
+ @Artist.one_to_many :albums, :clone=>:albums, :extend=>Module.new{def foo(x) filter(:id=>x) end}
127
+ @Artist.albums.foo(1).sql.should == "SELECT * FROM albums WHERE ((albums.artist_id IN (SELECT artists.id FROM artists)) AND (id = 1))"
128
+ end
129
+
130
+ it "should deal correctly with :order option on the association" do
131
+ @Artist.one_to_many :albums, :clone=>:albums, :order=>:name
132
+ @Artist.albums.sql.should == "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists)) ORDER BY name"
133
+ end
134
+
135
+ it "should deal correctly with :select option on the association" do
136
+ @Artist.one_to_many :albums, :clone=>:albums, :select=>[:id, :name]
137
+ @Artist.albums.sql.should == "SELECT id, name FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists))"
138
+ end
139
+ end
140
+
141
+ describe "Sequel::Plugins::DatasetAssociations with composite keys" do
142
+ before do
143
+ @db = Sequel::Database.new
144
+ @Base = Class.new(Sequel::Model)
145
+ @Base.plugin :dataset_associations
146
+
147
+ @Artist = Class.new(@Base)
148
+ @Album = Class.new(@Base)
149
+ @Tag = Class.new(@Base)
150
+
151
+ @Artist.meta_def(:name){'Artist'}
152
+ @Album.meta_def(:name){'Album'}
153
+ @Tag.meta_def(:name){'Tag'}
154
+
155
+ @Artist.dataset = @db[:artists]
156
+ @Album.dataset = @db[:albums]
157
+ @Tag.dataset = @db[:tags]
158
+
159
+ @Artist.set_primary_key([:id1, :id2])
160
+ @Album.set_primary_key([:id1, :id2])
161
+ @Tag.set_primary_key([:id1, :id2])
162
+
163
+ @Artist.columns :id1, :id2, :name
164
+ @Album.columns :id1, :id2, :name, :artist_id1, :artist_id2
165
+ @Tag.columns :id1, :id2, :name
166
+
167
+ @Artist.plugin :many_through_many
168
+ @Artist.one_to_many :albums, :class=>@Album, :key=>[:artist_id1, :artist_id2]
169
+ @Artist.one_to_one :first_album, :class=>@Album, :key=>[:artist_id1, :artist_id2]
170
+ @Album.many_to_one :artist, :class=>@Artist, :key=>[:artist_id1, :artist_id2]
171
+ @Album.many_to_many :tags, :class=>@Tag, :left_key=>[:album_id1, :album_id2], :right_key=>[:tag_id1, :tag_id2]
172
+ @Tag.many_to_many :albums, :class=>@Album, :right_key=>[:album_id1, :album_id2], :left_key=>[:tag_id1, :tag_id2]
173
+ @Artist.many_through_many :tags, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]], :class=>@Tag
174
+ end
175
+
176
+ it "should work for many_to_one associations" do
177
+ @Album.artists.sql.should == "SELECT * FROM artists WHERE ((artists.id1, artists.id2) IN (SELECT albums.artist_id1, albums.artist_id2 FROM albums))"
178
+ end
179
+
180
+ it "should work for one_to_many associations" do
181
+ @Artist.albums.sql.should == "SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists))"
182
+ end
183
+
184
+ it "should work for one_to_one associations" do
185
+ @Artist.first_albums.sql.should == "SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists))"
186
+ end
187
+
188
+ it "should work for many_to_many associations" do
189
+ @Album.tags.sql.should == "SELECT tags.* FROM tags WHERE ((tags.id1, tags.id2) IN (SELECT albums_tags.tag_id1, albums_tags.tag_id2 FROM albums INNER JOIN albums_tags ON ((albums_tags.album_id1 = albums.id1) AND (albums_tags.album_id2 = albums.id2))))"
190
+ end
191
+
192
+ it "should work for many_through_many associations" do
193
+ @Artist.tags.sql.should == "SELECT tags.* FROM tags WHERE ((tags.id1, tags.id2) IN (SELECT albums_tags.tag_id1, albums_tags.tag_id2 FROM artists INNER JOIN albums ON ((albums.artist_id1 = artists.id1) AND (albums.artist_id2 = artists.id2)) INNER JOIN albums_tags ON ((albums_tags.album_id1 = albums.id1) AND (albums_tags.album_id2 = albums.id2))))"
194
+ end
195
+
196
+ it "should work correctly when chaining" do
197
+ @Artist.albums.tags.sql.should == "SELECT tags.* FROM tags WHERE ((tags.id1, tags.id2) IN (SELECT albums_tags.tag_id1, albums_tags.tag_id2 FROM albums INNER JOIN albums_tags ON ((albums_tags.album_id1 = albums.id1) AND (albums_tags.album_id2 = albums.id2)) WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists))))"
198
+ end
199
+ end
@@ -0,0 +1,64 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::DefaultsSetter" do
4
+ before do
5
+ @db = db = Sequel::Database.new
6
+ @c = c = Class.new(Sequel::Model(db[:foo]))
7
+ @c.instance_variable_set(:@db_schema, {:a=>{}})
8
+ @c.plugin :defaults_setter
9
+ @c.columns :a
10
+ @pr = proc{|x| db.meta_def(:schema){|*| [[:a, {:ruby_default => x}]]}; c.dataset = c.dataset; c}
11
+ end
12
+
13
+ it "should set default value upon initialization" do
14
+ @pr.call(2).new.values.should == {:a=>2}
15
+ end
16
+
17
+ it "should not mark the column as modified" do
18
+ @pr.call(2).new.changed_columns.should == []
19
+ end
20
+
21
+ it "should not set a default of nil" do
22
+ @pr.call(nil).new.values.should == {}
23
+ end
24
+
25
+ it "should not override a given value" do
26
+ @pr.call(2)
27
+ @c.new('a'=>3).values.should == {:a=>3}
28
+ @c.new('a'=>nil).values.should == {:a=>nil}
29
+ end
30
+
31
+ it "should work correctly when subclassing" do
32
+ Class.new(@pr.call(2)).new.values.should == {:a=>2}
33
+ end
34
+
35
+ it "should contain the default values in default_values" do
36
+ @pr.call(2).default_values.should == {:a=>2}
37
+ @pr.call(nil).default_values.should == {}
38
+ end
39
+
40
+ it "should allow modifications of default values" do
41
+ @pr.call(2)
42
+ @c.default_values[:a] = 3
43
+ @c.new.values.should == {:a => 3}
44
+ end
45
+
46
+ it "should allow proc default values" do
47
+ @pr.call(2)
48
+ @c.default_values[:a] = proc{3}
49
+ @c.new.values.should == {:a => 3}
50
+ end
51
+
52
+ it "should have procs that set default values set them to nil" do
53
+ @pr.call(2)
54
+ @c.default_values[:a] = proc{nil}
55
+ @c.new.values.should == {:a => nil}
56
+ end
57
+
58
+ it "should work correctly on a model without a dataset" do
59
+ @pr.call(2)
60
+ c = Class.new(Sequel::Model(@db[:bar]))
61
+ c.plugin :defaults_setter
62
+ c.default_values.should == {:a=>2}
63
+ end
64
+ end
@@ -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