sequel 3.48.0 → 4.0.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 (267) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +114 -0
  3. data/Rakefile +10 -7
  4. data/doc/association_basics.rdoc +25 -23
  5. data/doc/code_order.rdoc +7 -0
  6. data/doc/core_extensions.rdoc +0 -10
  7. data/doc/object_model.rdoc +4 -1
  8. data/doc/querying.rdoc +3 -3
  9. data/doc/release_notes/4.0.0.txt +262 -0
  10. data/doc/security.rdoc +0 -28
  11. data/doc/testing.rdoc +8 -14
  12. data/lib/sequel/adapters/ado.rb +7 -11
  13. data/lib/sequel/adapters/ado/access.rb +8 -8
  14. data/lib/sequel/adapters/ado/mssql.rb +4 -4
  15. data/lib/sequel/adapters/amalgalite.rb +6 -6
  16. data/lib/sequel/adapters/cubrid.rb +7 -7
  17. data/lib/sequel/adapters/db2.rb +5 -9
  18. data/lib/sequel/adapters/dbi.rb +2 -6
  19. data/lib/sequel/adapters/do.rb +4 -4
  20. data/lib/sequel/adapters/firebird.rb +4 -4
  21. data/lib/sequel/adapters/ibmdb.rb +8 -8
  22. data/lib/sequel/adapters/informix.rb +2 -10
  23. data/lib/sequel/adapters/jdbc.rb +17 -17
  24. data/lib/sequel/adapters/jdbc/as400.rb +2 -2
  25. data/lib/sequel/adapters/jdbc/cubrid.rb +1 -1
  26. data/lib/sequel/adapters/jdbc/db2.rb +1 -1
  27. data/lib/sequel/adapters/jdbc/derby.rb +1 -1
  28. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  29. data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -1
  30. data/lib/sequel/adapters/jdbc/informix.rb +1 -1
  31. data/lib/sequel/adapters/jdbc/mssql.rb +2 -2
  32. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  33. data/lib/sequel/adapters/jdbc/oracle.rb +5 -1
  34. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -3
  35. data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
  36. data/lib/sequel/adapters/jdbc/transactions.rb +3 -3
  37. data/lib/sequel/adapters/mock.rb +7 -7
  38. data/lib/sequel/adapters/mysql.rb +3 -3
  39. data/lib/sequel/adapters/mysql2.rb +4 -4
  40. data/lib/sequel/adapters/odbc.rb +2 -6
  41. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  42. data/lib/sequel/adapters/openbase.rb +1 -5
  43. data/lib/sequel/adapters/oracle.rb +13 -17
  44. data/lib/sequel/adapters/postgres.rb +20 -25
  45. data/lib/sequel/adapters/shared/cubrid.rb +3 -3
  46. data/lib/sequel/adapters/shared/db2.rb +2 -2
  47. data/lib/sequel/adapters/shared/firebird.rb +7 -7
  48. data/lib/sequel/adapters/shared/mssql.rb +9 -9
  49. data/lib/sequel/adapters/shared/mysql.rb +29 -13
  50. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +7 -7
  51. data/lib/sequel/adapters/shared/oracle.rb +22 -13
  52. data/lib/sequel/adapters/shared/postgres.rb +61 -46
  53. data/lib/sequel/adapters/shared/sqlite.rb +9 -9
  54. data/lib/sequel/adapters/sqlite.rb +17 -11
  55. data/lib/sequel/adapters/swift.rb +3 -3
  56. data/lib/sequel/adapters/swift/mysql.rb +1 -1
  57. data/lib/sequel/adapters/swift/sqlite.rb +1 -1
  58. data/lib/sequel/adapters/tinytds.rb +8 -8
  59. data/lib/sequel/ast_transformer.rb +3 -1
  60. data/lib/sequel/connection_pool.rb +4 -2
  61. data/lib/sequel/connection_pool/sharded_single.rb +2 -2
  62. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -5
  63. data/lib/sequel/connection_pool/threaded.rb +7 -7
  64. data/lib/sequel/core.rb +4 -67
  65. data/lib/sequel/database.rb +1 -0
  66. data/lib/sequel/database/connecting.rb +2 -8
  67. data/lib/sequel/database/dataset.rb +2 -7
  68. data/lib/sequel/database/dataset_defaults.rb +0 -18
  69. data/lib/sequel/database/features.rb +4 -4
  70. data/lib/sequel/database/misc.rb +6 -8
  71. data/lib/sequel/database/query.rb +5 -61
  72. data/lib/sequel/database/schema_generator.rb +22 -20
  73. data/lib/sequel/database/schema_methods.rb +48 -20
  74. data/lib/sequel/database/transactions.rb +7 -17
  75. data/lib/sequel/dataset.rb +2 -0
  76. data/lib/sequel/dataset/actions.rb +23 -91
  77. data/lib/sequel/dataset/features.rb +1 -4
  78. data/lib/sequel/dataset/graph.rb +3 -47
  79. data/lib/sequel/dataset/misc.rb +4 -33
  80. data/lib/sequel/dataset/prepared_statements.rb +3 -1
  81. data/lib/sequel/dataset/query.rb +116 -240
  82. data/lib/sequel/dataset/sql.rb +19 -97
  83. data/lib/sequel/deprecated.rb +0 -16
  84. data/lib/sequel/exceptions.rb +0 -3
  85. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  86. data/lib/sequel/extensions/columns_introspection.rb +1 -12
  87. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  88. data/lib/sequel/extensions/core_extensions.rb +0 -9
  89. data/lib/sequel/extensions/date_arithmetic.rb +1 -2
  90. data/lib/sequel/extensions/graph_each.rb +11 -0
  91. data/lib/sequel/extensions/migration.rb +5 -5
  92. data/lib/sequel/extensions/null_dataset.rb +11 -13
  93. data/lib/sequel/extensions/pagination.rb +3 -6
  94. data/lib/sequel/extensions/pg_array.rb +6 -4
  95. data/lib/sequel/extensions/pg_array_ops.rb +35 -1
  96. data/lib/sequel/extensions/pg_json.rb +12 -2
  97. data/lib/sequel/extensions/pg_json_ops.rb +266 -0
  98. data/lib/sequel/extensions/pg_range.rb +2 -2
  99. data/lib/sequel/extensions/pg_range_ops.rb +0 -8
  100. data/lib/sequel/extensions/pg_row.rb +2 -2
  101. data/lib/sequel/extensions/pretty_table.rb +0 -4
  102. data/lib/sequel/extensions/query.rb +3 -8
  103. data/lib/sequel/extensions/schema_caching.rb +0 -7
  104. data/lib/sequel/extensions/schema_dumper.rb +10 -17
  105. data/lib/sequel/extensions/select_remove.rb +0 -4
  106. data/lib/sequel/extensions/set_overrides.rb +28 -0
  107. data/lib/sequel/extensions/to_dot.rb +6 -10
  108. data/lib/sequel/model.rb +6 -7
  109. data/lib/sequel/model/associations.rb +127 -182
  110. data/lib/sequel/model/base.rb +88 -211
  111. data/lib/sequel/model/errors.rb +0 -13
  112. data/lib/sequel/model/plugins.rb +2 -2
  113. data/lib/sequel/no_core_ext.rb +0 -1
  114. data/lib/sequel/plugins/after_initialize.rb +11 -17
  115. data/lib/sequel/plugins/association_autoreloading.rb +1 -47
  116. data/lib/sequel/plugins/association_dependencies.rb +2 -2
  117. data/lib/sequel/plugins/auto_validations.rb +2 -8
  118. data/lib/sequel/plugins/blacklist_security.rb +32 -2
  119. data/lib/sequel/plugins/caching.rb +1 -1
  120. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  121. data/lib/sequel/plugins/composition.rb +10 -8
  122. data/lib/sequel/plugins/constraint_validations.rb +2 -2
  123. data/lib/sequel/plugins/dataset_associations.rb +4 -0
  124. data/lib/sequel/plugins/defaults_setter.rb +8 -6
  125. data/lib/sequel/plugins/dirty.rb +6 -6
  126. data/lib/sequel/plugins/force_encoding.rb +13 -8
  127. data/lib/sequel/plugins/hook_class_methods.rb +1 -7
  128. data/lib/sequel/plugins/json_serializer.rb +13 -74
  129. data/lib/sequel/plugins/lazy_attributes.rb +2 -4
  130. data/lib/sequel/plugins/list.rb +1 -1
  131. data/lib/sequel/plugins/many_through_many.rb +4 -11
  132. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +1 -49
  133. data/lib/sequel/plugins/nested_attributes.rb +1 -1
  134. data/lib/sequel/plugins/optimistic_locking.rb +3 -5
  135. data/lib/sequel/plugins/pg_array_associations.rb +453 -0
  136. data/lib/sequel/plugins/pg_typecast_on_load.rb +23 -7
  137. data/lib/sequel/plugins/prepared_statements.rb +1 -1
  138. data/lib/sequel/plugins/prepared_statements_associations.rb +20 -14
  139. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -2
  140. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  141. data/lib/sequel/plugins/serialization.rb +5 -4
  142. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
  143. data/lib/sequel/plugins/sharding.rb +7 -1
  144. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  145. data/lib/sequel/plugins/timestamps.rb +1 -1
  146. data/lib/sequel/plugins/touch.rb +2 -2
  147. data/lib/sequel/plugins/tree.rb +1 -1
  148. data/lib/sequel/plugins/typecast_on_load.rb +19 -4
  149. data/lib/sequel/plugins/validation_class_methods.rb +0 -30
  150. data/lib/sequel/plugins/validation_helpers.rb +13 -31
  151. data/lib/sequel/plugins/xml_serializer.rb +18 -57
  152. data/lib/sequel/sql.rb +20 -22
  153. data/lib/sequel/version.rb +2 -2
  154. data/spec/adapters/db2_spec.rb +14 -23
  155. data/spec/adapters/firebird_spec.rb +25 -29
  156. data/spec/adapters/informix_spec.rb +11 -14
  157. data/spec/adapters/mssql_spec.rb +71 -77
  158. data/spec/adapters/mysql_spec.rb +165 -172
  159. data/spec/adapters/oracle_spec.rb +36 -39
  160. data/spec/adapters/postgres_spec.rb +175 -100
  161. data/spec/adapters/spec_helper.rb +13 -11
  162. data/spec/adapters/sqlite_spec.rb +36 -44
  163. data/spec/core/connection_pool_spec.rb +2 -1
  164. data/spec/core/database_spec.rb +55 -55
  165. data/spec/core/dataset_spec.rb +45 -249
  166. data/spec/core/deprecated_spec.rb +0 -8
  167. data/spec/core/expression_filters_spec.rb +23 -5
  168. data/spec/core/object_graph_spec.rb +4 -66
  169. data/spec/core/schema_spec.rb +35 -12
  170. data/spec/core/spec_helper.rb +3 -2
  171. data/spec/core_extensions_spec.rb +17 -19
  172. data/spec/extensions/arbitrary_servers_spec.rb +2 -3
  173. data/spec/extensions/association_dependencies_spec.rb +14 -14
  174. data/spec/extensions/auto_validations_spec.rb +7 -0
  175. data/spec/extensions/blacklist_security_spec.rb +5 -5
  176. data/spec/extensions/blank_spec.rb +2 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +2 -2
  178. data/spec/extensions/columns_introspection_spec.rb +2 -29
  179. data/spec/extensions/composition_spec.rb +10 -17
  180. data/spec/extensions/core_refinements_spec.rb +5 -1
  181. data/spec/extensions/dataset_associations_spec.rb +18 -0
  182. data/spec/extensions/date_arithmetic_spec.rb +2 -2
  183. data/spec/extensions/defaults_setter_spec.rb +9 -9
  184. data/spec/extensions/dirty_spec.rb +0 -5
  185. data/spec/extensions/eval_inspect_spec.rb +2 -0
  186. data/spec/extensions/force_encoding_spec.rb +2 -18
  187. data/spec/extensions/hash_aliases_spec.rb +8 -0
  188. data/spec/extensions/hook_class_methods_spec.rb +39 -58
  189. data/spec/extensions/inflector_spec.rb +2 -0
  190. data/spec/extensions/instance_filters_spec.rb +8 -8
  191. data/spec/extensions/json_serializer_spec.rb +1 -41
  192. data/spec/extensions/list_spec.rb +1 -1
  193. data/spec/extensions/many_through_many_spec.rb +106 -109
  194. data/spec/extensions/migration_spec.rb +2 -0
  195. data/spec/extensions/named_timezones_spec.rb +1 -0
  196. data/spec/extensions/pg_array_associations_spec.rb +603 -0
  197. data/spec/extensions/pg_array_ops_spec.rb +25 -0
  198. data/spec/extensions/pg_array_spec.rb +9 -1
  199. data/spec/extensions/pg_hstore_ops_spec.rb +13 -0
  200. data/spec/extensions/pg_hstore_spec.rb +1 -0
  201. data/spec/extensions/pg_json_ops_spec.rb +131 -0
  202. data/spec/extensions/pg_json_spec.rb +10 -4
  203. data/spec/extensions/pg_range_ops_spec.rb +2 -5
  204. data/spec/extensions/pg_range_spec.rb +6 -2
  205. data/spec/extensions/pg_row_ops_spec.rb +2 -0
  206. data/spec/extensions/prepared_statements_associations_spec.rb +26 -5
  207. data/spec/extensions/rcte_tree_spec.rb +15 -15
  208. data/spec/extensions/schema_dumper_spec.rb +0 -1
  209. data/spec/extensions/schema_spec.rb +9 -9
  210. data/spec/extensions/serialization_modification_detection_spec.rb +1 -1
  211. data/spec/extensions/serialization_spec.rb +18 -29
  212. data/spec/extensions/set_overrides_spec.rb +4 -0
  213. data/spec/extensions/{many_to_one_pk_lookup_spec.rb → shared_caching_spec.rb} +1 -4
  214. data/spec/extensions/single_table_inheritance_spec.rb +4 -4
  215. data/spec/extensions/spec_helper.rb +8 -9
  216. data/spec/extensions/sql_expr_spec.rb +2 -0
  217. data/spec/extensions/string_date_time_spec.rb +2 -0
  218. data/spec/extensions/string_stripper_spec.rb +2 -0
  219. data/spec/extensions/tactical_eager_loading_spec.rb +12 -12
  220. data/spec/extensions/thread_local_timezones_spec.rb +2 -0
  221. data/spec/extensions/timestamps_spec.rb +1 -1
  222. data/spec/extensions/to_dot_spec.rb +1 -1
  223. data/spec/extensions/touch_spec.rb +24 -24
  224. data/spec/extensions/tree_spec.rb +7 -7
  225. data/spec/extensions/typecast_on_load_spec.rb +8 -1
  226. data/spec/extensions/update_primary_key_spec.rb +10 -10
  227. data/spec/extensions/validation_class_methods_spec.rb +10 -39
  228. data/spec/extensions/validation_helpers_spec.rb +29 -47
  229. data/spec/extensions/xml_serializer_spec.rb +1 -23
  230. data/spec/integration/associations_test.rb +231 -40
  231. data/spec/integration/database_test.rb +1 -1
  232. data/spec/integration/dataset_test.rb +64 -64
  233. data/spec/integration/eager_loader_test.rb +28 -28
  234. data/spec/integration/migrator_test.rb +1 -1
  235. data/spec/integration/model_test.rb +2 -2
  236. data/spec/integration/plugin_test.rb +21 -21
  237. data/spec/integration/prepared_statement_test.rb +7 -7
  238. data/spec/integration/schema_test.rb +115 -110
  239. data/spec/integration/spec_helper.rb +17 -27
  240. data/spec/integration/timezone_test.rb +1 -1
  241. data/spec/integration/transaction_test.rb +10 -10
  242. data/spec/integration/type_test.rb +2 -2
  243. data/spec/model/association_reflection_spec.rb +2 -28
  244. data/spec/model/associations_spec.rb +239 -188
  245. data/spec/model/base_spec.rb +27 -68
  246. data/spec/model/dataset_methods_spec.rb +4 -4
  247. data/spec/model/eager_loading_spec.rb +160 -172
  248. data/spec/model/hooks_spec.rb +62 -79
  249. data/spec/model/model_spec.rb +36 -51
  250. data/spec/model/plugins_spec.rb +5 -19
  251. data/spec/model/record_spec.rb +125 -151
  252. data/spec/model/spec_helper.rb +8 -6
  253. data/spec/model/validations_spec.rb +4 -17
  254. data/spec/spec_config.rb +2 -10
  255. metadata +50 -56
  256. data/lib/sequel/deprecated_core_extensions.rb +0 -135
  257. data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -185
  258. data/lib/sequel/extensions/pg_statement_cache.rb +0 -318
  259. data/lib/sequel/plugins/identity_map.rb +0 -260
  260. data/lib/sequel_core.rb +0 -2
  261. data/lib/sequel_model.rb +0 -2
  262. data/spec/extensions/association_autoreloading_spec.rb +0 -102
  263. data/spec/extensions/identity_map_spec.rb +0 -337
  264. data/spec/extensions/pg_auto_parameterize_spec.rb +0 -70
  265. data/spec/extensions/pg_statement_cache_spec.rb +0 -208
  266. data/spec/rcov.opts +0 -8
  267. data/spec/spec_config.rb.example +0 -10
@@ -1,5 +1,7 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
2
2
 
3
+ Sequel.extension :migration
4
+
3
5
  describe "Migration.descendants" do
4
6
  before do
5
7
  Sequel::Migration.descendants.clear
@@ -5,6 +5,7 @@ begin
5
5
  rescue LoadError => e
6
6
  skip_warn "named_timezones_spec: can't load tzinfo (#{e.class}: #{e})"
7
7
  else
8
+ Sequel.extension :thread_local_timezones
8
9
  Sequel.extension :named_timezones
9
10
  Sequel.datetime_class = Time
10
11
 
@@ -0,0 +1,603 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe Sequel::Model, "pg_array_associations" do
4
+ before do
5
+ class ::Artist < Sequel::Model
6
+ attr_accessor :yyy
7
+ columns :id, :tag_ids
8
+ plugin :pg_array_associations
9
+ pg_array_to_many :tags
10
+ end
11
+ class ::Tag < Sequel::Model
12
+ columns :id
13
+ plugin :pg_array_associations
14
+ many_to_pg_array :artists
15
+ def id3
16
+ id*3
17
+ end
18
+ end
19
+ @c1 = Artist
20
+ @c2 = Tag
21
+ @c1.dataset._fetch = {:id=>1, :tag_ids=>Sequel.pg_array([1,2,3])}
22
+ @c2.dataset._fetch = {:id=>2}
23
+ @o1 = @c1.first
24
+ @o2 = @c2.first
25
+ @n1 = @c1.new
26
+ @n2 = @c2.new
27
+ DB.reset
28
+ end
29
+ after do
30
+ Object.send(:remove_const, :Artist)
31
+ Object.send(:remove_const, :Tag)
32
+ end
33
+
34
+ it "should populate :key_hash and :id_map option correctly for custom eager loaders" do
35
+ khs = []
36
+ pr = proc{|h| khs << [h[:key_hash], h[:id_map]]}
37
+ @c1.pg_array_to_many :tags, :clone=>:tags, :eager_loader=>pr
38
+ @c2.many_to_pg_array :artists, :clone=>:artists, :eager_loader=>pr
39
+ @c1.eager(:tags).all
40
+ @c2.eager(:artists).all
41
+ khs.should == [[{}, nil], [{:id=>{2=>[Tag.load(:id=>2)]}}, {2=>[Tag.load(:id=>2)]}]]
42
+ end
43
+
44
+ it "should not issue queries if the object cannot have associated objects" do
45
+ @n1.tags.should == []
46
+ @c1.load(:tag_ids=>[]).tags.should == []
47
+ @n2.artists.should == []
48
+ DB.sqls.should == []
49
+ end
50
+
51
+ it "should use correct SQL when loading associations lazily" do
52
+ @o1.tags.should == [@o2]
53
+ @o2.artists.should == [@o1]
54
+ DB.sqls.should == ["SELECT * FROM tags WHERE (tags.id IN (1, 2, 3))", "SELECT * FROM artists WHERE (artists.tag_ids @> ARRAY[2])"]
55
+ end
56
+
57
+ it "should accept :primary_key option for primary keys to use in current and associated table" do
58
+ @c1.pg_array_to_many :tags, :clone=>:tags, :primary_key=>Sequel./(:id, 3)
59
+ @c2.many_to_pg_array :artists, :clone=>:artists, :primary_key=>:id3
60
+ @o1.tags_dataset.sql.should == "SELECT * FROM tags WHERE ((tags.id / 3) IN (1, 2, 3))"
61
+ @o2.artists_dataset.sql.should == "SELECT * FROM artists WHERE (artists.tag_ids @> ARRAY[6])"
62
+ end
63
+
64
+ it "should allowing filtering by associations" do
65
+ @c1.filter(:tags=>@o2).sql.should == "SELECT * FROM artists WHERE (artists.tag_ids @> ARRAY[2])"
66
+ @c2.filter(:artists=>@o1).sql.should == "SELECT * FROM tags WHERE (tags.id IN (1, 2, 3))"
67
+ end
68
+
69
+ it "should allowing excluding by associations" do
70
+ @c1.exclude(:tags=>@o2).sql.should == "SELECT * FROM artists WHERE (NOT (artists.tag_ids @> ARRAY[2]) OR (artists.tag_ids IS NULL))"
71
+ @c2.exclude(:artists=>@o1).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (1, 2, 3)) OR (tags.id IS NULL))"
72
+ end
73
+
74
+ it "should allowing filtering by multiple associations" do
75
+ @c1.filter(:tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE (artists.tag_ids && ARRAY[1,2])"
76
+ @c2.filter(:artists=>[@c1.load(:tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:tag_ids=>Sequel.pg_array([4, 5]))]).sql.should == "SELECT * FROM tags WHERE (tags.id IN (3, 4, 5))"
77
+ end
78
+
79
+ it "should allowing excluding by multiple associations" do
80
+ @c1.exclude(:tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE (NOT (artists.tag_ids && ARRAY[1,2]) OR (artists.tag_ids IS NULL))"
81
+ @c2.exclude(:artists=>[@c1.load(:tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:tag_ids=>Sequel.pg_array([4, 5]))]).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (3, 4, 5)) OR (tags.id IS NULL))"
82
+ end
83
+
84
+ it "should allowing filtering/excluding associations with NULL or empty values" do
85
+ @c1.filter(:tags=>@c2.new).sql.should == 'SELECT * FROM artists WHERE \'f\''
86
+ @c1.exclude(:tags=>@c2.new).sql.should == 'SELECT * FROM artists WHERE \'t\''
87
+ @c2.filter(:artists=>@c1.new).sql.should == 'SELECT * FROM tags WHERE \'f\''
88
+ @c2.exclude(:artists=>@c1.new).sql.should == 'SELECT * FROM tags WHERE \'t\''
89
+
90
+ @c2.filter(:artists=>@c1.load(:tag_ids=>[])).sql.should == 'SELECT * FROM tags WHERE \'f\''
91
+ @c2.exclude(:artists=>@c1.load(:tag_ids=>[])).sql.should == 'SELECT * FROM tags WHERE \'t\''
92
+
93
+ @c1.filter(:tags=>[@c2.new, @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE (artists.tag_ids && ARRAY[2])"
94
+ @c2.filter(:artists=>[@c1.load(:tag_ids=>Sequel.pg_array([3, 4])), @c1.new]).sql.should == "SELECT * FROM tags WHERE (tags.id IN (3, 4))"
95
+ end
96
+
97
+ it "should allowing filtering by association datasets" do
98
+ @c1.filter(:tags=>@c2.where(:id=>1)).sql.should == "SELECT * FROM artists WHERE coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE (id = 1))), 'f')"
99
+ @c2.filter(:artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE (id = 1)))"
100
+ end
101
+
102
+ it "should allowing excluding by association datasets" do
103
+ @c1.exclude(:tags=>@c2.where(:id=>1)).sql.should == "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE (id = 1))), 'f') OR (artists.tag_ids IS NULL))"
104
+ @c2.exclude(:artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE (id = 1))) OR (tags.id IS NULL))"
105
+ end
106
+
107
+ it "filter by associations should respect key options" do
108
+ @c1.class_eval{def tag3_ids; tag_ids.map{|x| x*3} end}
109
+ @c1.pg_array_to_many :tags, :clone=>:tags, :primary_key=>Sequel.*(:id, 3), :primary_key_method=>:id3, :key=>:tag3_ids, :key_column=>Sequel.pg_array(:tag_ids)[1..2]
110
+ @c2.many_to_pg_array :artists, :clone=>:artists, :primary_key=>Sequel.*(:id, 3), :primary_key_method=>:id3, :key=>:tag3_ids, :key_column=>Sequel.pg_array(:tag_ids)[1..2]
111
+
112
+ @c1.filter(:tags=>@o2).sql.should == "SELECT * FROM artists WHERE (artists.tag_ids[1:2] @> ARRAY[6])"
113
+ @c2.filter(:artists=>@o1).sql.should == "SELECT * FROM tags WHERE ((tags.id * 3) IN (3, 6, 9))"
114
+ @c1.filter(:tags=>@c2.where(:id=>1)).sql.should == "SELECT * FROM artists WHERE coalesce((artists.tag_ids[1:2] && (SELECT array_agg((tags.id * 3)) FROM tags WHERE (id = 1))), 'f')"
115
+ @c2.filter(:artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE ((tags.id * 3) IN (SELECT unnest(artists.tag_ids[1:2]) FROM artists WHERE (id = 1)))"
116
+ end
117
+
118
+ it "should support a :key option" do
119
+ @c1.pg_array_to_many :tags, :clone=>:tags, :key=>:tag2_ids
120
+ @c2.many_to_pg_array :artists, :clone=>:artists, :key=>:tag2_ids
121
+ @c1.class_eval{def tag2_ids; tag_ids.map{|x| x * 2} end}
122
+ @o1.tags_dataset.sql.should == "SELECT * FROM tags WHERE (tags.id IN (2, 4, 6))"
123
+ @o2.artists_dataset.sql.should == "SELECT * FROM artists WHERE (artists.tag2_ids @> ARRAY[2])"
124
+ end
125
+
126
+ it "should support a :key_column option" do
127
+ @c2.many_to_pg_array :artists, :clone=>:artists, :key_column=>Sequel.pg_array(:tag_ids)[1..2], :key=>:tag2_ids
128
+ @o2.artists_dataset.sql.should == "SELECT * FROM artists WHERE (artists.tag_ids[1:2] @> ARRAY[2])"
129
+ end
130
+
131
+ it "should support a :primary_key option" do
132
+ @c1.pg_array_to_many :tags, :clone=>:tags, :primary_key=>:id2
133
+ @c2.many_to_pg_array :artists, :clone=>:artists, :primary_key=>:id2
134
+ @o1.tags_dataset.sql.should == "SELECT * FROM tags WHERE (tags.id2 IN (1, 2, 3))"
135
+ @c2.class_eval{def id2; id*2 end}
136
+ @o2.artists_dataset.sql.should == "SELECT * FROM artists WHERE (artists.tag_ids @> ARRAY[4])"
137
+ end
138
+
139
+ it "should support a :conditions option" do
140
+ @c1.pg_array_to_many :tags, :clone=>:tags, :conditions=>{:a=>1}
141
+ @c2.many_to_pg_array :artists, :clone=>:artists, :conditions=>{:a=>1}
142
+ @o1.tags_dataset.sql.should == "SELECT * FROM tags WHERE ((a = 1) AND (tags.id IN (1, 2, 3)))"
143
+ @o2.artists_dataset.sql.should == "SELECT * FROM artists WHERE ((a = 1) AND (artists.tag_ids @> ARRAY[2]))"
144
+ end
145
+
146
+ it "should support an :order option" do
147
+ @c1.pg_array_to_many :tags, :clone=>:tags, :order=>[:a, :b]
148
+ @c2.many_to_pg_array :artists, :clone=>:artists, :order=>[:a, :b]
149
+ @o1.tags_dataset.sql.should == "SELECT * FROM tags WHERE (tags.id IN (1, 2, 3)) ORDER BY a, b"
150
+ @o2.artists_dataset.sql.should == "SELECT * FROM artists WHERE (artists.tag_ids @> ARRAY[2]) ORDER BY a, b"
151
+ end
152
+
153
+ it "should support a select option" do
154
+ @c1.pg_array_to_many :tags, :clone=>:tags, :select=>[:a, :b]
155
+ @c2.many_to_pg_array :artists, :clone=>:artists, :select=>[:a, :b]
156
+ @c1.load(:tag_ids=>Sequel.pg_array([1,2,3])).tags_dataset.sql.should == "SELECT a, b FROM tags WHERE (tags.id IN (1, 2, 3))"
157
+ @c2.load(:id=>1).artists_dataset.sql.should == "SELECT a, b FROM artists WHERE (artists.tag_ids @> ARRAY[1])"
158
+ end
159
+
160
+ it "should accept a block" do
161
+ @c1.pg_array_to_many :tags, :clone=>:tags do |ds| ds.filter(:yyy=>@yyy) end
162
+ @c2.many_to_pg_array :artists, :clone=>:artists do |ds| ds.filter(:a=>1) end
163
+ @c1.new(:yyy=>6, :tag_ids=>Sequel.pg_array([1,2,3])).tags_dataset.sql.should == "SELECT * FROM tags WHERE ((tags.id IN (1, 2, 3)) AND (yyy = 6))"
164
+ @o2.artists_dataset.sql.should == "SELECT * FROM artists WHERE ((artists.tag_ids @> ARRAY[2]) AND (a = 1))"
165
+ end
166
+
167
+ it "should support a :dataset option that is used instead of the default" do
168
+ @c1.pg_array_to_many :tags, :clone=>:tags, :dataset=>proc{Tag.where(:id=>tag_ids.map{|x| x*2})}
169
+ @c2.many_to_pg_array :artists, :clone=>:artists, :dataset=>proc{Artist.where(Sequel.pg_array(Sequel.pg_array(:tag_ids)[1..2]).contains([id]))}
170
+ @o1.tags_dataset.sql.should == "SELECT * FROM tags WHERE (id IN (2, 4, 6))"
171
+ @o2.artists_dataset.sql.should == "SELECT * FROM artists WHERE (tag_ids[1:2] @> ARRAY[2])"
172
+ end
173
+
174
+ it "should support a :limit option" do
175
+ @c1.pg_array_to_many :tags, :clone=>:tags, :limit=>[2, 3]
176
+ @c2.many_to_pg_array :artists, :clone=>:artists, :limit=>[3, 2]
177
+ @o1.tags_dataset.sql.should == "SELECT * FROM tags WHERE (tags.id IN (1, 2, 3)) LIMIT 2 OFFSET 3"
178
+ @o2.artists_dataset.sql.should == "SELECT * FROM artists WHERE (artists.tag_ids @> ARRAY[2]) LIMIT 3 OFFSET 2"
179
+ end
180
+
181
+ it "should support a :uniq option that removes duplicates from the association" do
182
+ @c1.pg_array_to_many :tags, :clone=>:tags, :uniq=>true
183
+ @c2.many_to_pg_array :artists, :clone=>:artists, :uniq=>true
184
+ @c1.dataset._fetch = [{:id=>20}, {:id=>30}, {:id=>20}, {:id=>30}]
185
+ @c2.dataset._fetch = [{:id=>20}, {:id=>30}, {:id=>20}, {:id=>30}]
186
+ @o1.tags.should == [@c2.load(:id=>20), @c2.load(:id=>30)]
187
+ @o2.artists.should == [@c1.load(:id=>20), @c1.load(:id=>30)]
188
+ end
189
+
190
+ it "reflection associated_object_keys should return correct values" do
191
+ @c1.association_reflection(:tags).associated_object_keys.should == [:id]
192
+ @c2.association_reflection(:artists).associated_object_keys.should == [:tag_ids]
193
+ end
194
+
195
+ it "reflection remove_before_destroy? should return correct values" do
196
+ @c1.association_reflection(:tags).remove_before_destroy?.should be_true
197
+ @c2.association_reflection(:artists).remove_before_destroy?.should be_false
198
+ end
199
+
200
+ it "reflection reciprocal should be correct" do
201
+ @c1.association_reflection(:tags).reciprocal.should == :artists
202
+ @c2.association_reflection(:artists).reciprocal.should == :tags
203
+ end
204
+
205
+ it "should eagerly load correctly" do
206
+ a = @c1.eager(:tags).all
207
+ a.should == [@o1]
208
+ sqls = DB.sqls
209
+ sqls.pop.should =~ /SELECT \* FROM tags WHERE \(tags\.id IN \([123], [123], [123]\)\)/
210
+ sqls.should == ["SELECT * FROM artists"]
211
+ a.first.tags.should == [@o2]
212
+ DB.sqls.should == []
213
+
214
+ a = @c2.eager(:artists).all
215
+ a.should == [@o2]
216
+ DB.sqls.should == ['SELECT * FROM tags', "SELECT * FROM artists WHERE (artists.tag_ids && ARRAY[2])"]
217
+ a.first.artists.should == [@o1]
218
+ DB.sqls.should == []
219
+ end
220
+
221
+ it "should support using custom key options when eager loading associations" do
222
+ @c1.class_eval{def tag3_ids; tag_ids.map{|x| x*3} end}
223
+ @c1.pg_array_to_many :tags, :clone=>:tags, :primary_key=>Sequel.*(:id, 3), :primary_key_method=>:id3, :key=>:tag3_ids
224
+ @c2.many_to_pg_array :artists, :clone=>:artists, :primary_key=>:id3, :key=>:tag3_ids, :key_column=>Sequel.pg_array(:tag_ids)[1..2]
225
+
226
+ a = @c1.eager(:tags).all
227
+ a.should == [@o1]
228
+ sqls = DB.sqls
229
+ sqls.pop.should =~ /SELECT \* FROM tags WHERE \(\(tags\.id \* 3\) IN \([369], [369], [369]\)\)/
230
+ sqls.should == ["SELECT * FROM artists"]
231
+ a.first.tags.should == [@o2]
232
+ DB.sqls.should == []
233
+
234
+ a = @c2.eager(:artists).all
235
+ a.should == [@o2]
236
+ DB.sqls.should == ["SELECT * FROM tags", "SELECT * FROM artists WHERE (artists.tag_ids[1:2] && ARRAY[6])"]
237
+ a.first.artists.should == [@o1]
238
+ DB.sqls.should == []
239
+ end
240
+
241
+ it "should allow cascading of eager loading for associations of associated models" do
242
+ a = @c1.eager(:tags=>:artists).all
243
+ a.should == [@o1]
244
+ sqls = DB.sqls
245
+ sqls.slice!(1).should =~ /SELECT \* FROM tags WHERE \(tags\.id IN \([123], [123], [123]\)\)/
246
+ sqls.should == ['SELECT * FROM artists', "SELECT * FROM artists WHERE (artists.tag_ids && ARRAY[2])"]
247
+ a.first.tags.should == [@o2]
248
+ a.first.tags.first.artists.should == [@o1]
249
+ DB.sqls.should == []
250
+ end
251
+
252
+ it "should respect :eager when lazily loading an association" do
253
+ @c1.pg_array_to_many :tags2, :clone=>:tags, :eager=>:artists, :key=>:tag_ids
254
+ @c2.many_to_pg_array :artists2, :clone=>:artists, :eager=>:tags
255
+
256
+ @o1.tags2.should == [@o2]
257
+ DB.sqls.should == ["SELECT * FROM tags WHERE (tags.id IN (1, 2, 3))", "SELECT * FROM artists WHERE (artists.tag_ids && ARRAY[2])"]
258
+ @o1.tags2.first.artists.should == [@o1]
259
+ DB.sqls.should == []
260
+
261
+ @o2.artists2.should == [@o1]
262
+ sqls = DB.sqls
263
+ sqls.pop.should =~ /SELECT \* FROM tags WHERE \(tags\.id IN \([123], [123], [123]\)\)/
264
+ sqls.should == ["SELECT * FROM artists WHERE (artists.tag_ids @> ARRAY[2])"]
265
+ @o2.artists2.first.tags.should == [@o2]
266
+ DB.sqls.should == []
267
+ end
268
+
269
+ it "should cascade eagerly loading when the :eager_graph association option is used" do
270
+ @c1.pg_array_to_many :tags2, :clone=>:tags, :eager_graph=>:artists, :key=>:tag_ids
271
+ @c2.many_to_pg_array :artists2, :clone=>:artists, :eager_graph=>:tags
272
+
273
+ @c2.dataset._fetch = {:id=>2, :artists_id=>1, :tag_ids=>Sequel.pg_array([1,2,3])}
274
+ @c1.dataset._fetch = {:id=>1, :tags_id=>2, :tag_ids=>Sequel.pg_array([1,2,3])}
275
+
276
+ @o1.tags2.should == [@o2]
277
+ DB.sqls.first.should =~ /SELECT tags\.id, artists\.id AS artists_id, artists\.tag_ids FROM tags LEFT OUTER JOIN artists ON \(artists.tag_ids @> ARRAY\[tags.id\]\) WHERE \(tags\.id IN \([123], [123], [123]\)\)/
278
+ @o1.tags2.first.artists.should == [@o1]
279
+ DB.sqls.should == []
280
+
281
+ @o2.artists2.should == [@o1]
282
+ DB.sqls.should == ["SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON (artists.tag_ids @> ARRAY[tags.id]) WHERE (artists.tag_ids @> ARRAY[2])"]
283
+ @o2.artists2.first.tags.should == [@o2]
284
+ DB.sqls.should == []
285
+
286
+ @c2.dataset._fetch = {:id=>2, :artists_id=>1, :tag_ids=>Sequel.pg_array([1,2,3])}
287
+ @c1.dataset._fetch = {:id=>1, :tag_ids=>Sequel.pg_array([1,2,3])}
288
+
289
+ a = @c1.eager(:tags2).all
290
+ sqls = DB.sqls
291
+ sqls.pop.should =~ /SELECT tags\.id, artists\.id AS artists_id, artists\.tag_ids FROM tags LEFT OUTER JOIN artists ON \(artists.tag_ids @> ARRAY\[tags.id\]\) WHERE \(tags\.id IN \([123], [123], [123]\)\)/
292
+ sqls.should == ["SELECT * FROM artists"]
293
+ a.should == [@o1]
294
+ a.first.tags2.should == [@o2]
295
+ a.first.tags2.first.artists.should == [@o1]
296
+ DB.sqls.should == []
297
+
298
+ @c2.dataset._fetch = {:id=>2}
299
+ @c1.dataset._fetch = {:id=>1, :tags_id=>2, :tag_ids=>Sequel.pg_array([1,2,3])}
300
+
301
+ a = @c2.eager(:artists2).all
302
+ DB.sqls.should == ["SELECT * FROM tags", "SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON (artists.tag_ids @> ARRAY[tags.id]) WHERE (artists.tag_ids && ARRAY[2])"]
303
+ a.should == [@o2]
304
+ a.first.artists2.should == [@o1]
305
+ a.first.artists2.first.tags.should == [@o2]
306
+ DB.sqls.should == []
307
+ end
308
+
309
+ it "should respect the :limit option when eager loading" do
310
+ @c2.dataset._fetch = [{:id=>1},{:id=>2}, {:id=>3}]
311
+
312
+ @c1.pg_array_to_many :tags, :clone=>:tags, :limit=>2
313
+ a = @c1.eager(:tags).all
314
+ a.should == [@o1]
315
+ sqls = DB.sqls
316
+ sqls.pop.should =~ /SELECT \* FROM tags WHERE \(tags\.id IN \([123], [123], [123]\)\)/
317
+ sqls.should == ["SELECT * FROM artists"]
318
+ a.first.tags.should == [@c2.load(:id=>1), @c2.load(:id=>2)]
319
+ DB.sqls.should == []
320
+
321
+ @c1.pg_array_to_many :tags, :clone=>:tags, :limit=>[1, 1]
322
+ a = @c1.eager(:tags).all
323
+ a.should == [@o1]
324
+ sqls = DB.sqls
325
+ sqls.pop.should =~ /SELECT \* FROM tags WHERE \(tags\.id IN \([123], [123], [123]\)\)/
326
+ sqls.should == ["SELECT * FROM artists"]
327
+ a.first.tags.should == [@c2.load(:id=>2)]
328
+ DB.sqls.should == []
329
+
330
+ @c1.pg_array_to_many :tags, :clone=>:tags, :limit=>[nil, 1]
331
+ a = @c1.eager(:tags).all
332
+ a.should == [@o1]
333
+ sqls = DB.sqls
334
+ sqls.pop.should =~ /SELECT \* FROM tags WHERE \(tags\.id IN \([123], [123], [123]\)\)/
335
+ sqls.should == ["SELECT * FROM artists"]
336
+ a.first.tags.should == [@c2.load(:id=>2), @c2.load(:id=>3)]
337
+ DB.sqls.length.should == 0
338
+
339
+ @c2.dataset._fetch = [{:id=>2}]
340
+ @c1.dataset._fetch = [{:id=>5, :tag_ids=>Sequel.pg_array([1,2,3])},{:id=>6, :tag_ids=>Sequel.pg_array([2,3])}, {:id=>7, :tag_ids=>Sequel.pg_array([1,2])}]
341
+
342
+ @c2.many_to_pg_array :artists, :clone=>:artists, :limit=>2
343
+ a = @c2.eager(:artists).all
344
+ a.should == [@o2]
345
+ DB.sqls.should == ['SELECT * FROM tags', "SELECT * FROM artists WHERE (artists.tag_ids && ARRAY[2])"]
346
+ a.first.artists.should == [@c1.load(:id=>5, :tag_ids=>Sequel.pg_array([1,2,3])), @c1.load(:id=>6, :tag_ids=>Sequel.pg_array([2,3]))]
347
+ DB.sqls.should == []
348
+
349
+ @c2.many_to_pg_array :artists, :clone=>:artists, :limit=>[1, 1]
350
+ a = @c2.eager(:artists).all
351
+ a.should == [@o2]
352
+ DB.sqls.should == ['SELECT * FROM tags', "SELECT * FROM artists WHERE (artists.tag_ids && ARRAY[2])"]
353
+ a.first.artists.should == [@c1.load(:id=>6, :tag_ids=>Sequel.pg_array([2,3]))]
354
+ DB.sqls.should == []
355
+
356
+ @c2.many_to_pg_array :artists, :clone=>:artists, :limit=>[nil, 1]
357
+ a = @c2.eager(:artists).all
358
+ a.should == [@o2]
359
+ DB.sqls.should == ['SELECT * FROM tags', "SELECT * FROM artists WHERE (artists.tag_ids && ARRAY[2])"]
360
+ a.first.artists.should == [@c1.load(:id=>6, :tag_ids=>Sequel.pg_array([2,3])), @c1.load(:id=>7, :tag_ids=>Sequel.pg_array([1,2]))]
361
+ DB.sqls.should == []
362
+ end
363
+
364
+ it "should eagerly graph associations" do
365
+ @c2.dataset._fetch = {:id=>2, :artists_id=>1, :tag_ids=>Sequel.pg_array([1,2,3])}
366
+ @c1.dataset._fetch = {:id=>1, :tags_id=>2, :tag_ids=>Sequel.pg_array([1,2,3])}
367
+
368
+ a = @c1.eager_graph(:tags).all
369
+ DB.sqls.should == ["SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON (artists.tag_ids @> ARRAY[tags.id])"]
370
+ a.should == [@o1]
371
+ a.first.tags.should == [@o2]
372
+ DB.sqls.should == []
373
+
374
+ a = @c2.eager_graph(:artists).all
375
+ DB.sqls.should == ["SELECT tags.id, artists.id AS artists_id, artists.tag_ids FROM tags LEFT OUTER JOIN artists ON (artists.tag_ids @> ARRAY[tags.id])"]
376
+ a.should == [@o2]
377
+ a.first.artists.should == [@o1]
378
+ DB.sqls.should == []
379
+ end
380
+
381
+ it "should allow cascading of eager graphing for associations of associated models" do
382
+ @c2.dataset._fetch = {:id=>2, :artists_id=>1, :tag_ids=>Sequel.pg_array([1,2,3]), :tags_0_id=>2}
383
+ @c1.dataset._fetch = {:id=>1, :tags_id=>2, :tag_ids=>Sequel.pg_array([1,2,3]), :artists_0_id=>1, :artists_0_tag_ids=>Sequel.pg_array([1,2,3])}
384
+
385
+ a = @c1.eager_graph(:tags=>:artists).all
386
+ DB.sqls.should == ["SELECT artists.id, artists.tag_ids, tags.id AS tags_id, artists_0.id AS artists_0_id, artists_0.tag_ids AS artists_0_tag_ids FROM artists LEFT OUTER JOIN tags ON (artists.tag_ids @> ARRAY[tags.id]) LEFT OUTER JOIN artists AS artists_0 ON (artists_0.tag_ids @> ARRAY[tags.id])"]
387
+ a.should == [@o1]
388
+ a.first.tags.should == [@o2]
389
+ a.first.tags.first.artists.should == [@o1]
390
+ DB.sqls.should == []
391
+
392
+ a = @c2.eager_graph(:artists=>:tags).all
393
+ DB.sqls.should == ["SELECT tags.id, artists.id AS artists_id, artists.tag_ids, tags_0.id AS tags_0_id FROM tags LEFT OUTER JOIN artists ON (artists.tag_ids @> ARRAY[tags.id]) LEFT OUTER JOIN tags AS tags_0 ON (artists.tag_ids @> ARRAY[tags_0.id])"]
394
+ a.should == [@o2]
395
+ a.first.artists.should == [@o1]
396
+ a.first.artists.first.tags.should == [@o2]
397
+ DB.sqls.should == []
398
+ end
399
+
400
+ it "eager graphing should respect key options" do
401
+ @c1.class_eval{def tag3_ids; tag_ids.map{|x| x*3} end}
402
+ @c1.pg_array_to_many :tags, :clone=>:tags, :primary_key=>Sequel.*(:id, 3), :primary_key_method=>:id3, :key=>:tag3_ids, :key_column=>Sequel.pg_array(:tag_ids)[1..2]
403
+ @c2.many_to_pg_array :artists, :clone=>:artists, :primary_key=>:id3, :key=>:tag3_ids, :key_column=>Sequel.pg_array(:tag_ids)[1..2]
404
+
405
+ @c2.dataset._fetch = {:id=>2, :artists_id=>1, :tag_ids=>Sequel.pg_array([1,2,3]), :tags_0_id=>2}
406
+ @c1.dataset._fetch = {:id=>1, :tags_id=>2, :tag_ids=>Sequel.pg_array([1,2,3]), :artists_0_id=>1, :artists_0_tag_ids=>Sequel.pg_array([1,2,3])}
407
+
408
+ a = @c1.eager_graph(:tags).all
409
+ a.should == [@o1]
410
+ DB.sqls.should == ["SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON (artists.tag_ids[1:2] @> ARRAY[(tags.id * 3)])"]
411
+ a.first.tags.should == [@o2]
412
+ DB.sqls.should == []
413
+
414
+ a = @c2.eager_graph(:artists).all
415
+ a.should == [@o2]
416
+ DB.sqls.should == ["SELECT tags.id, artists.id AS artists_id, artists.tag_ids FROM tags LEFT OUTER JOIN artists ON (artists.tag_ids[1:2] @> ARRAY[tags.id3])"]
417
+ a.first.artists.should == [@o1]
418
+ DB.sqls.should == []
419
+ end
420
+
421
+ it "should respect the association's :graph_select option" do
422
+ @c1.pg_array_to_many :tags, :clone=>:tags, :graph_select=>:id2
423
+ @c2.many_to_pg_array :artists, :clone=>:artists, :graph_select=>:id
424
+
425
+ @c2.dataset._fetch = {:id=>2, :artists_id=>1}
426
+ @c1.dataset._fetch = {:id=>1, :id2=>2, :tag_ids=>Sequel.pg_array([1,2,3])}
427
+
428
+ a = @c1.eager_graph(:tags).all
429
+ DB.sqls.should == ["SELECT artists.id, artists.tag_ids, tags.id2 FROM artists LEFT OUTER JOIN tags ON (artists.tag_ids @> ARRAY[tags.id])"]
430
+ a.should == [@o1]
431
+ a.first.tags.should == [@c2.load(:id2=>2)]
432
+ DB.sqls.should == []
433
+
434
+ a = @c2.eager_graph(:artists).all
435
+ DB.sqls.should == ["SELECT tags.id, artists.id AS artists_id FROM tags LEFT OUTER JOIN artists ON (artists.tag_ids @> ARRAY[tags.id])"]
436
+ a.should == [@o2]
437
+ a.first.artists.should == [@c1.load(:id=>1)]
438
+ DB.sqls.should == []
439
+ end
440
+
441
+ it "should respect the association's :graph_join_type option" do
442
+ @c1.pg_array_to_many :tags, :clone=>:tags, :graph_join_type=>:inner
443
+ @c2.many_to_pg_array :artists, :clone=>:artists, :graph_join_type=>:inner
444
+ @c1.eager_graph(:tags).sql.should == "SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists INNER JOIN tags ON (artists.tag_ids @> ARRAY[tags.id])"
445
+ @c2.eager_graph(:artists).sql.should == "SELECT tags.id, artists.id AS artists_id, artists.tag_ids FROM tags INNER JOIN artists ON (artists.tag_ids @> ARRAY[tags.id])"
446
+ end
447
+
448
+ it "should respect the association's :conditions option" do
449
+ @c1.pg_array_to_many :tags, :clone=>:tags, :conditions=>{:a=>1}
450
+ @c2.many_to_pg_array :artists, :clone=>:artists, :conditions=>{:a=>1}
451
+ @c1.eager_graph(:tags).sql.should == "SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON ((tags.a = 1) AND (artists.tag_ids @> ARRAY[tags.id]))"
452
+ @c2.eager_graph(:artists).sql.should == "SELECT tags.id, artists.id AS artists_id, artists.tag_ids FROM tags LEFT OUTER JOIN artists ON ((artists.a = 1) AND (artists.tag_ids @> ARRAY[tags.id]))"
453
+ end
454
+
455
+ it "should respect the association's :graph_conditions option" do
456
+ @c1.pg_array_to_many :tags, :clone=>:tags, :graph_conditions=>{:a=>1}
457
+ @c2.many_to_pg_array :artists, :clone=>:artists, :graph_conditions=>{:a=>1}
458
+ @c1.eager_graph(:tags).sql.should == "SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON ((tags.a = 1) AND (artists.tag_ids @> ARRAY[tags.id]))"
459
+ @c2.eager_graph(:artists).sql.should == "SELECT tags.id, artists.id AS artists_id, artists.tag_ids FROM tags LEFT OUTER JOIN artists ON ((artists.a = 1) AND (artists.tag_ids @> ARRAY[tags.id]))"
460
+ end
461
+
462
+ it "should respect the association's :graph_block option" do
463
+ @c1.pg_array_to_many :tags, :clone=>:tags, :graph_block=>proc{|ja,lja,js| {Sequel.qualify(ja, :a)=>1}}
464
+ @c2.many_to_pg_array :artists, :clone=>:artists, :graph_block=>proc{|ja,lja,js| {Sequel.qualify(ja, :a)=>1}}
465
+ @c1.eager_graph(:tags).sql.should == "SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON ((tags.a = 1) AND (artists.tag_ids @> ARRAY[tags.id]))"
466
+ @c2.eager_graph(:artists).sql.should == "SELECT tags.id, artists.id AS artists_id, artists.tag_ids FROM tags LEFT OUTER JOIN artists ON ((artists.a = 1) AND (artists.tag_ids @> ARRAY[tags.id]))"
467
+ end
468
+
469
+ it "should respect the association's :graph_only_conditions option" do
470
+ @c1.pg_array_to_many :tags, :clone=>:tags, :graph_only_conditions=>{:a=>1}
471
+ @c2.many_to_pg_array :artists, :clone=>:artists, :graph_only_conditions=>{:a=>1}
472
+ @c1.eager_graph(:tags).sql.should == "SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON (tags.a = 1)"
473
+ @c2.eager_graph(:artists).sql.should == "SELECT tags.id, artists.id AS artists_id, artists.tag_ids FROM tags LEFT OUTER JOIN artists ON (artists.a = 1)"
474
+ end
475
+
476
+ it "should respect the association's :graph_only_conditions with :graph_block option" do
477
+ @c1.pg_array_to_many :tags, :clone=>:tags, :graph_only_conditions=>{:a=>1}, :graph_block=>proc{|ja,lja,js| {Sequel.qualify(lja, :b)=>1}}
478
+ @c2.many_to_pg_array :artists, :clone=>:artists, :graph_only_conditions=>{:a=>1}, :graph_block=>proc{|ja,lja,js| {Sequel.qualify(lja, :b)=>1}}
479
+ @c1.eager_graph(:tags).sql.should == "SELECT artists.id, artists.tag_ids, tags.id AS tags_id FROM artists LEFT OUTER JOIN tags ON ((tags.a = 1) AND (artists.b = 1))"
480
+ @c2.eager_graph(:artists).sql.should == "SELECT tags.id, artists.id AS artists_id, artists.tag_ids FROM tags LEFT OUTER JOIN artists ON ((artists.a = 1) AND (tags.b = 1))"
481
+ end
482
+
483
+ it "should define an add_ method for adding associated objects" do
484
+ @o1.add_tag(@c2.load(:id=>4))
485
+ @o1.tag_ids.should == [1,2,3,4]
486
+ DB.sqls.should == []
487
+ @o1.save_changes
488
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[1,2,3,4] WHERE (id = 1)"]
489
+
490
+ @o2.add_artist(@c1.load(:id=>1, :tag_ids=>Sequel.pg_array([4])))
491
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[4,2] WHERE (id = 1)"]
492
+ end
493
+
494
+ it "should define a remove_ method for removing associated objects" do
495
+ @o1.remove_tag(@o2)
496
+ @o1.tag_ids.should == [1,3]
497
+ DB.sqls.should == []
498
+ @o1.save_changes
499
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[1,3] WHERE (id = 1)"]
500
+
501
+ @o2.remove_artist(@c1.load(:id=>1, :tag_ids=>Sequel.pg_array([1,2,3,4])))
502
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[1,3,4] WHERE (id = 1)"]
503
+ end
504
+
505
+ it "should define a remove_all_ method for removing all associated objects" do
506
+ @o1.remove_all_tags
507
+ @o1.tag_ids.should == []
508
+ DB.sqls.should == []
509
+ @o1.save_changes
510
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[] WHERE (id = 1)"]
511
+
512
+ @o2.remove_all_artists
513
+ DB.sqls.should == ["UPDATE artists SET tag_ids = array_remove(tag_ids, 2) WHERE (tag_ids @> ARRAY[2])"]
514
+ end
515
+
516
+ it "should have pg_array_to_many association modification methods save if :save_after_modify option is used" do
517
+ @c1.pg_array_to_many :tags, :clone=>:tags, :save_after_modify=>true
518
+
519
+ @o1.add_tag(@c2.load(:id=>4))
520
+ @o1.tag_ids.should == [1,2,3,4]
521
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[1,2,3,4] WHERE (id = 1)"]
522
+
523
+ @o1.remove_tag(@o2)
524
+ @o1.tag_ids.should == [1,3,4]
525
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[1,3,4] WHERE (id = 1)"]
526
+
527
+ @o1.remove_all_tags
528
+ @o1.tag_ids.should == []
529
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[] WHERE (id = 1)"]
530
+ end
531
+
532
+ it "should have association modification methods deal with nil values" do
533
+ v = @c1.load(:id=>1)
534
+ v.add_tag(@c2.load(:id=>4))
535
+ v.tag_ids.should == [4]
536
+ DB.sqls.should == []
537
+ v.save_changes
538
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[4]::integer[] WHERE (id = 1)"]
539
+
540
+ @o2.add_artist(@c1.load(:id=>1))
541
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[2]::integer[] WHERE (id = 1)"]
542
+
543
+ v = @c1.load(:id=>1)
544
+ v.remove_tag(@c2.load(:id=>4))
545
+ v.tag_ids.should == nil
546
+ DB.sqls.should == []
547
+ v.save_changes
548
+ DB.sqls.should == []
549
+
550
+ @o2.remove_artist(@c1.load(:id=>1))
551
+ DB.sqls.should == []
552
+
553
+ v = @c1.load(:id=>1)
554
+ v.remove_all_tags
555
+ v.tag_ids.should == nil
556
+ DB.sqls.should == []
557
+ v.save_changes
558
+ DB.sqls.should == []
559
+ end
560
+
561
+ it "should have association modification methods deal with empty arrays values" do
562
+ v = @c1.load(:id=>1, :tag_ids=>Sequel.pg_array([]))
563
+ v.add_tag(@c2.load(:id=>4))
564
+ v.tag_ids.should == [4]
565
+ DB.sqls.should == []
566
+ v.save_changes
567
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[4] WHERE (id = 1)"]
568
+
569
+ @o2.add_artist(@c1.load(:id=>1, :tag_ids=>Sequel.pg_array([])))
570
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[2] WHERE (id = 1)"]
571
+
572
+ v = @c1.load(:id=>1, :tag_ids=>Sequel.pg_array([]))
573
+ v.remove_tag(@c2.load(:id=>4))
574
+ v.tag_ids.should == []
575
+ DB.sqls.should == []
576
+ v.save_changes
577
+ DB.sqls.should == []
578
+
579
+ @o2.remove_artist(@c1.load(:id=>1, :tag_ids=>Sequel.pg_array([])))
580
+ DB.sqls.should == []
581
+
582
+ v = @c1.load(:id=>1, :tag_ids=>Sequel.pg_array([]))
583
+ v.remove_all_tags
584
+ v.tag_ids.should == []
585
+ DB.sqls.should == []
586
+ v.save_changes
587
+ DB.sqls.should == []
588
+ end
589
+
590
+ it "should respect the :array_type option when manually creating arrays" do
591
+ @c1.pg_array_to_many :tags, :clone=>:tags, :array_type=>:int8
592
+ @c2.many_to_pg_array :artists, :clone=>:artists, :array_type=>:int8
593
+ v = @c1.load(:id=>1)
594
+ v.add_tag(@c2.load(:id=>4))
595
+ v.tag_ids.should == [4]
596
+ DB.sqls.should == []
597
+ v.save_changes
598
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[4]::int8[] WHERE (id = 1)"]
599
+
600
+ @o2.add_artist(@c1.load(:id=>1))
601
+ DB.sqls.should == ["UPDATE artists SET tag_ids = ARRAY[2]::int8[] WHERE (id = 1)"]
602
+ end
603
+ end