sequel 3.47.0 → 3.48.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 (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +230 -0
  3. data/README.rdoc +31 -40
  4. data/Rakefile +1 -14
  5. data/doc/active_record.rdoc +29 -29
  6. data/doc/association_basics.rdoc +4 -13
  7. data/doc/cheat_sheet.rdoc +8 -6
  8. data/doc/code_order.rdoc +89 -0
  9. data/doc/core_extensions.rdoc +3 -3
  10. data/doc/dataset_basics.rdoc +7 -8
  11. data/doc/dataset_filtering.rdoc +7 -2
  12. data/doc/mass_assignment.rdoc +2 -3
  13. data/doc/migration.rdoc +8 -8
  14. data/doc/model_hooks.rdoc +11 -7
  15. data/doc/object_model.rdoc +2 -2
  16. data/doc/opening_databases.rdoc +5 -14
  17. data/doc/prepared_statements.rdoc +5 -9
  18. data/doc/querying.rdoc +23 -28
  19. data/doc/reflection.rdoc +11 -0
  20. data/doc/release_notes/3.48.0.txt +477 -0
  21. data/doc/schema_modification.rdoc +12 -5
  22. data/doc/security.rdoc +2 -2
  23. data/doc/sharding.rdoc +1 -2
  24. data/doc/sql.rdoc +10 -13
  25. data/doc/testing.rdoc +8 -4
  26. data/doc/transactions.rdoc +2 -2
  27. data/doc/validations.rdoc +40 -17
  28. data/doc/virtual_rows.rdoc +2 -2
  29. data/lib/sequel/adapters/ado.rb +25 -20
  30. data/lib/sequel/adapters/ado/access.rb +1 -0
  31. data/lib/sequel/adapters/ado/mssql.rb +1 -0
  32. data/lib/sequel/adapters/db2.rb +9 -7
  33. data/lib/sequel/adapters/dbi.rb +16 -16
  34. data/lib/sequel/adapters/do.rb +17 -18
  35. data/lib/sequel/adapters/do/mysql.rb +1 -0
  36. data/lib/sequel/adapters/do/postgres.rb +2 -0
  37. data/lib/sequel/adapters/do/sqlite.rb +1 -0
  38. data/lib/sequel/adapters/firebird.rb +5 -7
  39. data/lib/sequel/adapters/ibmdb.rb +23 -20
  40. data/lib/sequel/adapters/informix.rb +8 -2
  41. data/lib/sequel/adapters/jdbc.rb +39 -35
  42. data/lib/sequel/adapters/jdbc/as400.rb +1 -0
  43. data/lib/sequel/adapters/jdbc/cubrid.rb +1 -0
  44. data/lib/sequel/adapters/jdbc/db2.rb +1 -0
  45. data/lib/sequel/adapters/jdbc/derby.rb +1 -0
  46. data/lib/sequel/adapters/jdbc/firebird.rb +1 -0
  47. data/lib/sequel/adapters/jdbc/h2.rb +1 -0
  48. data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -0
  49. data/lib/sequel/adapters/jdbc/informix.rb +1 -0
  50. data/lib/sequel/adapters/jdbc/jtds.rb +1 -0
  51. data/lib/sequel/adapters/jdbc/mssql.rb +1 -0
  52. data/lib/sequel/adapters/jdbc/mysql.rb +1 -0
  53. data/lib/sequel/adapters/jdbc/oracle.rb +1 -0
  54. data/lib/sequel/adapters/jdbc/postgresql.rb +2 -0
  55. data/lib/sequel/adapters/jdbc/progress.rb +1 -0
  56. data/lib/sequel/adapters/jdbc/sqlite.rb +1 -0
  57. data/lib/sequel/adapters/jdbc/sqlserver.rb +1 -0
  58. data/lib/sequel/adapters/mock.rb +30 -31
  59. data/lib/sequel/adapters/mysql.rb +6 -7
  60. data/lib/sequel/adapters/mysql2.rb +5 -6
  61. data/lib/sequel/adapters/odbc.rb +22 -20
  62. data/lib/sequel/adapters/odbc/mssql.rb +1 -0
  63. data/lib/sequel/adapters/openbase.rb +4 -1
  64. data/lib/sequel/adapters/oracle.rb +10 -8
  65. data/lib/sequel/adapters/postgres.rb +12 -10
  66. data/lib/sequel/adapters/shared/access.rb +6 -0
  67. data/lib/sequel/adapters/shared/cubrid.rb +2 -0
  68. data/lib/sequel/adapters/shared/db2.rb +2 -0
  69. data/lib/sequel/adapters/shared/firebird.rb +2 -0
  70. data/lib/sequel/adapters/shared/informix.rb +2 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +14 -8
  72. data/lib/sequel/adapters/shared/mysql.rb +6 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +2 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +14 -4
  75. data/lib/sequel/adapters/shared/progress.rb +1 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +4 -3
  77. data/lib/sequel/adapters/sqlite.rb +6 -7
  78. data/lib/sequel/adapters/swift.rb +20 -21
  79. data/lib/sequel/adapters/swift/mysql.rb +1 -0
  80. data/lib/sequel/adapters/swift/postgres.rb +2 -0
  81. data/lib/sequel/adapters/swift/sqlite.rb +1 -0
  82. data/lib/sequel/adapters/tinytds.rb +5 -6
  83. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +68 -0
  84. data/lib/sequel/connection_pool.rb +1 -1
  85. data/lib/sequel/core.rb +57 -50
  86. data/lib/sequel/database/connecting.rb +9 -10
  87. data/lib/sequel/database/dataset.rb +11 -6
  88. data/lib/sequel/database/dataset_defaults.rb +61 -69
  89. data/lib/sequel/database/features.rb +21 -0
  90. data/lib/sequel/database/misc.rb +23 -3
  91. data/lib/sequel/database/query.rb +13 -7
  92. data/lib/sequel/database/schema_methods.rb +6 -6
  93. data/lib/sequel/database/transactions.rb +1 -0
  94. data/lib/sequel/dataset/actions.rb +51 -38
  95. data/lib/sequel/dataset/features.rb +1 -0
  96. data/lib/sequel/dataset/graph.rb +9 -33
  97. data/lib/sequel/dataset/misc.rb +30 -5
  98. data/lib/sequel/dataset/mutation.rb +2 -3
  99. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  100. data/lib/sequel/dataset/query.rb +91 -27
  101. data/lib/sequel/dataset/sql.rb +40 -6
  102. data/lib/sequel/deprecated.rb +74 -0
  103. data/lib/sequel/deprecated_core_extensions.rb +135 -0
  104. data/lib/sequel/extensions/columns_introspection.rb +1 -5
  105. data/lib/sequel/extensions/core_extensions.rb +10 -3
  106. data/lib/sequel/extensions/date_arithmetic.rb +1 -0
  107. data/lib/sequel/extensions/empty_array_ignore_nulls.rb +33 -0
  108. data/lib/sequel/extensions/filter_having.rb +58 -0
  109. data/lib/sequel/extensions/graph_each.rb +63 -0
  110. data/lib/sequel/extensions/hash_aliases.rb +44 -0
  111. data/lib/sequel/extensions/looser_typecasting.rb +14 -3
  112. data/lib/sequel/extensions/migration.rb +2 -3
  113. data/lib/sequel/extensions/named_timezones.rb +14 -1
  114. data/lib/sequel/extensions/null_dataset.rb +7 -1
  115. data/lib/sequel/extensions/pagination.rb +15 -5
  116. data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -0
  117. data/lib/sequel/extensions/pg_hstore_ops.rb +48 -14
  118. data/lib/sequel/extensions/pg_json.rb +7 -7
  119. data/lib/sequel/extensions/pg_range_ops.rb +8 -2
  120. data/lib/sequel/extensions/pg_statement_cache.rb +1 -0
  121. data/lib/sequel/extensions/pretty_table.rb +13 -4
  122. data/lib/sequel/extensions/query.rb +21 -4
  123. data/lib/sequel/extensions/ruby18_symbol_extensions.rb +22 -0
  124. data/lib/sequel/extensions/schema_caching.rb +10 -7
  125. data/lib/sequel/extensions/schema_dumper.rb +35 -48
  126. data/lib/sequel/extensions/select_remove.rb +13 -4
  127. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +117 -0
  128. data/lib/sequel/extensions/set_overrides.rb +43 -0
  129. data/lib/sequel/extensions/to_dot.rb +6 -0
  130. data/lib/sequel/model.rb +12 -6
  131. data/lib/sequel/model/associations.rb +80 -38
  132. data/lib/sequel/model/base.rb +137 -52
  133. data/lib/sequel/model/errors.rb +7 -2
  134. data/lib/sequel/plugins/active_model.rb +13 -0
  135. data/lib/sequel/plugins/after_initialize.rb +43 -0
  136. data/lib/sequel/plugins/association_proxies.rb +63 -7
  137. data/lib/sequel/plugins/auto_validations.rb +56 -16
  138. data/lib/sequel/plugins/blacklist_security.rb +63 -0
  139. data/lib/sequel/plugins/class_table_inheritance.rb +9 -0
  140. data/lib/sequel/plugins/constraint_validations.rb +50 -8
  141. data/lib/sequel/plugins/dataset_associations.rb +2 -0
  142. data/lib/sequel/plugins/hook_class_methods.rb +7 -1
  143. data/lib/sequel/plugins/identity_map.rb +4 -0
  144. data/lib/sequel/plugins/json_serializer.rb +32 -13
  145. data/lib/sequel/plugins/optimistic_locking.rb +1 -1
  146. data/lib/sequel/plugins/rcte_tree.rb +4 -4
  147. data/lib/sequel/plugins/scissors.rb +33 -0
  148. data/lib/sequel/plugins/serialization.rb +1 -1
  149. data/lib/sequel/plugins/single_table_inheritance.rb +6 -0
  150. data/lib/sequel/plugins/tree.rb +5 -1
  151. data/lib/sequel/plugins/validation_class_methods.rb +2 -1
  152. data/lib/sequel/plugins/validation_helpers.rb +15 -11
  153. data/lib/sequel/plugins/xml_serializer.rb +12 -3
  154. data/lib/sequel/sql.rb +12 -2
  155. data/lib/sequel/timezones.rb +1 -1
  156. data/lib/sequel/version.rb +1 -1
  157. data/lib/sequel_core.rb +1 -0
  158. data/lib/sequel_model.rb +1 -0
  159. data/spec/adapters/mssql_spec.rb +24 -57
  160. data/spec/adapters/postgres_spec.rb +27 -55
  161. data/spec/adapters/spec_helper.rb +1 -1
  162. data/spec/adapters/sqlite_spec.rb +1 -1
  163. data/spec/bin_spec.rb +251 -0
  164. data/spec/core/database_spec.rb +46 -32
  165. data/spec/core/dataset_spec.rb +233 -181
  166. data/spec/core/deprecated_spec.rb +78 -0
  167. data/spec/core/expression_filters_spec.rb +3 -4
  168. data/spec/core/mock_adapter_spec.rb +9 -9
  169. data/spec/core/object_graph_spec.rb +9 -19
  170. data/spec/core/schema_spec.rb +3 -1
  171. data/spec/core/spec_helper.rb +19 -0
  172. data/spec/core_extensions_spec.rb +80 -30
  173. data/spec/extensions/after_initialize_spec.rb +24 -0
  174. data/spec/extensions/association_proxies_spec.rb +37 -1
  175. data/spec/extensions/auto_validations_spec.rb +20 -4
  176. data/spec/extensions/blacklist_security_spec.rb +87 -0
  177. data/spec/extensions/boolean_readers_spec.rb +2 -1
  178. data/spec/extensions/class_table_inheritance_spec.rb +7 -0
  179. data/spec/extensions/columns_introspection_spec.rb +3 -3
  180. data/spec/extensions/constraint_validations_plugin_spec.rb +83 -5
  181. data/spec/extensions/core_refinements_spec.rb +7 -7
  182. data/spec/extensions/dataset_associations_spec.rb +2 -2
  183. data/spec/extensions/date_arithmetic_spec.rb +1 -1
  184. data/spec/extensions/defaults_setter_spec.rb +2 -1
  185. data/spec/extensions/empty_array_ignore_nulls_spec.rb +24 -0
  186. data/spec/extensions/filter_having_spec.rb +40 -0
  187. data/spec/extensions/graph_each_spec.rb +109 -0
  188. data/spec/extensions/hash_aliases_spec.rb +16 -0
  189. data/spec/extensions/hook_class_methods_spec.rb +2 -2
  190. data/spec/extensions/identity_map_spec.rb +3 -3
  191. data/spec/extensions/json_serializer_spec.rb +19 -19
  192. data/spec/extensions/lazy_attributes_spec.rb +1 -0
  193. data/spec/extensions/list_spec.rb +13 -13
  194. data/spec/extensions/looser_typecasting_spec.rb +10 -3
  195. data/spec/extensions/many_through_many_spec.rb +1 -1
  196. data/spec/extensions/migration_spec.rb +7 -7
  197. data/spec/extensions/named_timezones_spec.rb +6 -0
  198. data/spec/extensions/nested_attributes_spec.rb +2 -2
  199. data/spec/extensions/null_dataset_spec.rb +1 -1
  200. data/spec/extensions/pagination_spec.rb +2 -2
  201. data/spec/extensions/pg_hstore_ops_spec.rb +75 -0
  202. data/spec/extensions/pg_range_ops_spec.rb +4 -2
  203. data/spec/extensions/pg_row_plugin_spec.rb +1 -1
  204. data/spec/extensions/pretty_table_spec.rb +1 -1
  205. data/spec/extensions/query_literals_spec.rb +1 -1
  206. data/spec/extensions/query_spec.rb +3 -3
  207. data/spec/extensions/schema_caching_spec.rb +3 -3
  208. data/spec/extensions/schema_dumper_spec.rb +27 -2
  209. data/spec/extensions/schema_spec.rb +2 -2
  210. data/spec/extensions/scissors_spec.rb +26 -0
  211. data/spec/extensions/select_remove_spec.rb +1 -1
  212. data/spec/extensions/sequel_3_dataset_methods_spec.rb +102 -0
  213. data/spec/extensions/set_overrides_spec.rb +45 -0
  214. data/spec/extensions/single_table_inheritance_spec.rb +10 -0
  215. data/spec/extensions/spec_helper.rb +24 -1
  216. data/spec/extensions/static_cache_spec.rb +1 -1
  217. data/spec/extensions/string_stripper_spec.rb +2 -1
  218. data/spec/extensions/to_dot_spec.rb +1 -1
  219. data/spec/extensions/typecast_on_load_spec.rb +3 -2
  220. data/spec/extensions/update_primary_key_spec.rb +2 -2
  221. data/spec/extensions/validation_class_methods_spec.rb +19 -19
  222. data/spec/extensions/validation_helpers_spec.rb +30 -21
  223. data/spec/extensions/xml_serializer_spec.rb +5 -5
  224. data/spec/integration/associations_test.rb +10 -30
  225. data/spec/integration/dataset_test.rb +20 -24
  226. data/spec/integration/eager_loader_test.rb +5 -5
  227. data/spec/integration/model_test.rb +3 -3
  228. data/spec/integration/plugin_test.rb +7 -39
  229. data/spec/integration/schema_test.rb +4 -38
  230. data/spec/integration/spec_helper.rb +2 -1
  231. data/spec/model/association_reflection_spec.rb +70 -5
  232. data/spec/model/associations_spec.rb +11 -11
  233. data/spec/model/base_spec.rb +25 -8
  234. data/spec/model/class_dataset_methods_spec.rb +143 -0
  235. data/spec/model/dataset_methods_spec.rb +1 -1
  236. data/spec/model/eager_loading_spec.rb +25 -25
  237. data/spec/model/hooks_spec.rb +1 -1
  238. data/spec/model/model_spec.rb +22 -7
  239. data/spec/model/plugins_spec.rb +1 -6
  240. data/spec/model/record_spec.rb +37 -29
  241. data/spec/model/spec_helper.rb +23 -1
  242. data/spec/model/validations_spec.rb +15 -17
  243. metadata +32 -3
@@ -0,0 +1,24 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::AfterInitialize" do
4
+ before do
5
+ @db = Sequel.mock(:host=>'mysql', :numrows=>1)
6
+ @c = Class.new(Sequel::Model(@db[:test]))
7
+ @c.class_eval do
8
+ columns :id, :name
9
+ plugin :after_initialize
10
+ def after_initialize
11
+ self.name *= 2
12
+ self.id *= 3 if id
13
+ end
14
+ end
15
+ end
16
+
17
+ it "should have after_initialize hook be called for new objects" do
18
+ @c.new(:name=>'foo').values.should == {:name=>'foofoo'}
19
+ end
20
+
21
+ it "should have after_initialize hook be called for objects loaded from the database" do
22
+ @c.call(:id=>1, :name=>'foo').values.should == {:id=>3, :name=>'foofoo'}
23
+ end
24
+ end
@@ -6,7 +6,7 @@ describe "Sequel::Plugins::AssociationProxies" do
6
6
  end
7
7
  class ::Item < Sequel::Model
8
8
  plugin :association_proxies
9
- many_to_many :tags
9
+ many_to_many :tags, :extend=>Module.new{def size; count end}
10
10
  end
11
11
  @i = Item.load(:id=>1)
12
12
  @t = @i.tags
@@ -29,6 +29,33 @@ describe "Sequel::Plugins::AssociationProxies" do
29
29
  @i.associations.has_key?(:tags).should == false
30
30
  end
31
31
 
32
+ it "should accept block to plugin to specify which methods to proxy to dataset" do
33
+ Item.plugin :association_proxies do |opts|
34
+ opts[:method] == :where || opts[:arguments].length == 2 || opts[:block]
35
+ end
36
+ @i.associations.has_key?(:tags).should == false
37
+ @t.where(:a=>1).sql.should == "SELECT tags.* FROM tags INNER JOIN items_tags ON ((items_tags.tag_id = tags.id) AND (items_tags.item_id = 1)) WHERE (a = 1)"
38
+ @t.filter('a = ?', 1).sql.should == "SELECT tags.* FROM tags INNER JOIN items_tags ON ((items_tags.tag_id = tags.id) AND (items_tags.item_id = 1)) WHERE (a = 1)"
39
+ @t.filter{{:a=>1}}.sql.should == "SELECT tags.* FROM tags INNER JOIN items_tags ON ((items_tags.tag_id = tags.id) AND (items_tags.item_id = 1)) WHERE (a = 1)"
40
+
41
+ @i.associations.has_key?(:tags).should == false
42
+ Item.plugin :association_proxies do |opts|
43
+ proxy_arg = opts[:proxy_argument]
44
+ proxy_block = opts[:proxy_block]
45
+ cached = opts[:instance].associations[opts[:reflection][:name]]
46
+ is_size = opts[:method] == :size
47
+ is_size && !cached && !proxy_arg && !proxy_block
48
+ end
49
+ @t.size.should == 1
50
+ Item.db.sqls.should == ["SELECT count(*) AS count FROM tags INNER JOIN items_tags ON ((items_tags.tag_id = tags.id) AND (items_tags.item_id = 1)) LIMIT 1"]
51
+ @i.tags{|ds| ds}.size.should == 1
52
+ Item.db.sqls.should == ["SELECT tags.* FROM tags INNER JOIN items_tags ON ((items_tags.tag_id = tags.id) AND (items_tags.item_id = 1))"]
53
+ @i.tags(true).size.should == 1
54
+ Item.db.sqls.should == ["SELECT tags.* FROM tags INNER JOIN items_tags ON ((items_tags.tag_id = tags.id) AND (items_tags.item_id = 1))"]
55
+ @t.size.should == 1
56
+ Item.db.sqls.should == []
57
+ end
58
+
32
59
  it "should reload the cached association if sent an array method and the reload flag was given" do
33
60
  @t.select{|x| false}.should == []
34
61
  Item.db.sqls.length.should == 1
@@ -47,4 +74,13 @@ describe "Sequel::Plugins::AssociationProxies" do
47
74
  Tag.one_to_one :item
48
75
  proc{Tag.load(:id=>1, :item_id=>2).item.filter(:a=>1)}.should raise_error(NoMethodError)
49
76
  end
77
+
78
+ it "should work correctly in subclasses" do
79
+ i = Class.new(Item).load(:id=>1)
80
+ i.associations.has_key?(:tags).should == false
81
+ i.tags.select{|x| false}.should == []
82
+ i.associations.has_key?(:tags).should == true
83
+ i.tags.filter(:a=>1).sql.should == "SELECT tags.* FROM tags INNER JOIN items_tags ON ((items_tags.tag_id = tags.id) AND (items_tags.item_id = 1)) WHERE (a = 1)"
84
+ end
85
+
50
86
  end
@@ -7,17 +7,19 @@ describe "Sequel::Plugins::AutoValidations" do
7
7
  def db.schema(t, *)
8
8
  t = t.first_source if t.is_a?(Sequel::Dataset)
9
9
  return [] if t != :test
10
- [[:id, {:primary_key=>true, :type=>:integer}],
10
+ [[:id, {:primary_key=>true, :type=>:integer, :allow_null=>false}],
11
11
  [:name, {:primary_key=>false, :type=>:string, :allow_null=>false}],
12
12
  [:num, {:primary_key=>false, :type=>:integer, :allow_null=>true}],
13
- [:d, {:primary_key=>false, :type=>:date, :allow_null=>false}]]
13
+ [:d, {:primary_key=>false, :type=>:date, :allow_null=>false}],
14
+ [:nnd, {:primary_key=>false, :type=>:string, :allow_null=>false, :ruby_default=>'nnd'}]]
14
15
  end
16
+ def db.supports_index_parsing?() true end
15
17
  def db.indexes(t, *)
16
18
  return [] if t != :test
17
19
  {:a=>{:columns=>[:name, :num], :unique=>true}, :b=>{:columns=>[:num], :unique=>false}}
18
20
  end
19
21
  @c = Class.new(Sequel::Model(db[:test]))
20
- @c.send(:def_column_accessor, :id, :name, :num, :d)
22
+ @c.send(:def_column_accessor, :id, :name, :num, :d, :nnd)
21
23
  @c.raise_on_typecast_failure = false
22
24
  @c.plugin :auto_validations
23
25
  @m = @c.new
@@ -41,10 +43,24 @@ describe "Sequel::Plugins::AutoValidations" do
41
43
  @m.errors.should == {[:name, :num]=>["is already taken"]}
42
44
  end
43
45
 
46
+ it "should support :not_null=>:presence option" do
47
+ @c.plugin :auto_validations, :not_null=>:presence
48
+ @m.set(:d=>Date.today, :num=>'')
49
+ @m.valid?.should be_false
50
+ @m.errors.should == {:name=>["is not present"]}
51
+ end
52
+
53
+ it "should automatically validate explicit nil values for columns with not nil defaults" do
54
+ @m.set(:d=>Date.today, :name=>1, :nnd=>nil)
55
+ @m.id = nil
56
+ @m.valid?.should be_false
57
+ @m.errors.should == {:id=>["is not present"], :nnd=>["is not present"]}
58
+ end
59
+
44
60
  it "should allow skipping validations by type" do
45
61
  @c = Class.new(@c)
46
62
  @m = @c.new
47
- @c.skip_auto_validations(:presence)
63
+ @c.skip_auto_validations(:not_null)
48
64
  @m.valid?.should be_true
49
65
 
50
66
  @m.set(:d=>'/', :num=>'a', :name=>'1')
@@ -0,0 +1,87 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe Sequel::Model, "#(set|update)_except" do
4
+ before do
5
+ @c = Class.new(Sequel::Model(:items))
6
+ @c.class_eval do
7
+ plugin :blacklist_security
8
+ set_primary_key :id
9
+ columns :x, :y, :z, :id
10
+ set_restricted_columns :y
11
+ end
12
+ @c.strict_param_setting = false
13
+ @o1 = @c.new
14
+ MODEL_DB.reset
15
+ end
16
+
17
+ it "should raise errors if not all hash fields can be set and strict_param_setting is true" do
18
+ @c.strict_param_setting = true
19
+ proc{@c.new.set_except({:x => 1, :y => 2, :z=>3, :id=>4}, :x, :y)}.should raise_error(Sequel::Error)
20
+ proc{@c.new.set_except({:x => 1, :y => 2, :z=>3}, :x, :y)}.should raise_error(Sequel::Error)
21
+ (o = @c.new).set_except({:z => 3}, :x, :y)
22
+ o.values.should == {:z=>3}
23
+ end
24
+
25
+ it "#set_except should not set given attributes or the primary key" do
26
+ @o1.set_except({:x => 1, :y => 2, :z=>3, :id=>4}, [:y, :z])
27
+ @o1.values.should == {:x => 1}
28
+ @o1.set_except({:x => 4, :y => 2, :z=>3, :id=>4}, :y, :z)
29
+ @o1.values.should == {:x => 4}
30
+ end
31
+
32
+ it "#update_except should not update given attributes" do
33
+ @o1.update_except({:x => 1, :y => 2, :z=>3, :id=>4}, [:y, :z])
34
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
35
+ @c.new.update_except({:x => 1, :y => 2, :z=>3, :id=>4}, :y, :z)
36
+ MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (1)", "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
37
+ end
38
+ end
39
+
40
+ describe Sequel::Model, ".restricted_columns " do
41
+ before do
42
+ @c = Class.new(Sequel::Model(:blahblah))
43
+ @c.class_eval do
44
+ plugin :blacklist_security
45
+ columns :x, :y, :z
46
+ end
47
+ @c.strict_param_setting = false
48
+ @c.instance_variable_set(:@columns, [:x, :y, :z])
49
+ end
50
+
51
+ it "should set the restricted columns correctly" do
52
+ @c.restricted_columns.should == nil
53
+ @c.set_restricted_columns :x
54
+ @c.restricted_columns.should == [:x]
55
+ @c.set_restricted_columns :x, :y
56
+ @c.restricted_columns.should == [:x, :y]
57
+ end
58
+
59
+ it "should not set restricted columns by default" do
60
+ @c.set_restricted_columns :z
61
+ i = @c.new(:x => 1, :y => 2, :z => 3)
62
+ i.values.should == {:x => 1, :y => 2}
63
+ i.set(:x => 4, :y => 5, :z => 6)
64
+ i.values.should == {:x => 4, :y => 5}
65
+
66
+ @c.instance_dataset._fetch = @c.dataset._fetch = {:x => 7}
67
+ i = @c.new
68
+ i.update(:x => 7, :z => 9)
69
+ i.values.should == {:x => 7}
70
+ MODEL_DB.sqls.should == ["INSERT INTO blahblah (x) VALUES (7)", "SELECT * FROM blahblah WHERE (id = 10) LIMIT 1"]
71
+ end
72
+
73
+ it "should have allowed take precedence over restricted" do
74
+ @c.set_allowed_columns :x, :y
75
+ @c.set_restricted_columns :y, :z
76
+ i = @c.new(:x => 1, :y => 2, :z => 3)
77
+ i.values.should == {:x => 1, :y => 2}
78
+ i.set(:x => 4, :y => 5, :z => 6)
79
+ i.values.should == {:x => 4, :y => 5}
80
+
81
+ @c.instance_dataset._fetch = @c.dataset._fetch = {:y => 7}
82
+ i = @c.new
83
+ i.update(:y => 7, :z => 9)
84
+ i.values.should == {:y => 7}
85
+ MODEL_DB.sqls.should == ["INSERT INTO blahblah (y) VALUES (7)", "SELECT * FROM blahblah WHERE (id = 10) LIMIT 1"]
86
+ end
87
+ end
@@ -2,7 +2,8 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe Sequel::Model, "BooleanReaders plugin" do
4
4
  before do
5
- @db = Sequel::Database.new({})
5
+ @db = Sequel::Database.new
6
+ def @db.supports_schema_parsing?() true end
6
7
  def @db.schema(*args)
7
8
  [[:id, {}], [:z, {:type=>:integer, :db_type=>'tinyint(1)'}], [:b, {:type=>:boolean, :db_type=>'boolean'}]]
8
9
  end
@@ -3,6 +3,7 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
3
3
  describe "class_table_inheritance plugin" do
4
4
  before do
5
5
  @db = Sequel.mock(:autoid=>proc{|sql| 1})
6
+ def @db.supports_schema_parsing?() true end
6
7
  def @db.schema(table, opts={})
7
8
  {:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]],
8
9
  :managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}]],
@@ -54,6 +55,12 @@ describe "class_table_inheritance plugin" do
54
55
  Staff.simple_table.should == nil
55
56
  end
56
57
 
58
+ specify "should have working row_proc if using set_dataset in subclass to remove columns" do
59
+ Manager.set_dataset(Manager.dataset.select(*(Manager.columns - [:blah])))
60
+ Manager.dataset._fetch = {:id=>1, :kind=>'Executive'}
61
+ Manager[1].should == Executive.load(:id=>1, :kind=>'Executive')
62
+ end
63
+
57
64
  specify "should use a joined dataset in subclasses" do
58
65
  Employee.dataset.sql.should == 'SELECT * FROM employees'
59
66
  Manager.dataset.sql.should == 'SELECT * FROM employees INNER JOIN managers USING (id)'
@@ -8,8 +8,6 @@ describe "Sequel::Dataset.introspect_all_columns" do
8
8
  # Handle case where introspect_all_columns has already been called
9
9
  alias columns columns_without_introspection unless instance_methods(false).map{|x| x.to_s}.include?('columns')
10
10
  end
11
- Sequel::Dataset.introspect_all_columns
12
- @db.reset
13
11
  end
14
12
  after do
15
13
  class Sequel::Dataset
@@ -17,7 +15,9 @@ describe "Sequel::Dataset.introspect_all_columns" do
17
15
  end
18
16
  end
19
17
 
20
- specify "should turn on column introspection by default" do
18
+ qspecify "should turn on column introspection by default" do
19
+ Sequel::Dataset.introspect_all_columns
20
+ @db.reset
21
21
  @ds.select(:x).columns.should == [:x]
22
22
  @db.sqls.length.should == 0
23
23
  end
@@ -20,7 +20,7 @@ describe "Sequel::Plugins::ConstraintValidations" do
20
20
  set_fetch({})
21
21
  @ds = @db[:items]
22
22
  @ds.instance_variable_set(:@columns, [:name])
23
- @ds2 = @db.dup[:items2]
23
+ @ds2 = Sequel.mock[:items2]
24
24
  @ds2.instance_variable_set(:@columns, [:name])
25
25
  end
26
26
 
@@ -31,8 +31,9 @@ describe "Sequel::Plugins::ConstraintValidations" do
31
31
  it "should parse constraint validations when loading plugin" do
32
32
  @c = model_class
33
33
  @db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
34
- @db.constraint_validations.should == {'items'=>[[:validates_presence, :name]]}
34
+ @db.constraint_validations.should == {"items"=>[{:allow_nil=>nil, :constraint_name=>nil, :message=>nil, :validation_type=>"presence", :column=>"name", :argument=>nil, :table=>"items"}]}
35
35
  @c.constraint_validations.should == [[:validates_presence, :name]]
36
+ @c.constraint_validation_reflections.should == {:name=>[[:presence, {}]]}
36
37
  end
37
38
 
38
39
  it "should parse constraint validations with a custom constraint validations table" do
@@ -40,8 +41,9 @@ describe "Sequel::Plugins::ConstraintValidations" do
40
41
  @db.sqls
41
42
  c.plugin :constraint_validations, :constraint_validations_table=>:foo
42
43
  @db.sqls.should == ["SELECT * FROM foo"]
43
- @db.constraint_validations.should == {'items'=>[[:validates_presence, :name]]}
44
+ @db.constraint_validations.should == {"items"=>[{:allow_nil=>nil, :constraint_name=>nil, :message=>nil, :validation_type=>"presence", :column=>"name", :argument=>nil, :table=>"items"}]}
44
45
  c.constraint_validations.should == [[:validates_presence, :name]]
46
+ c.constraint_validation_reflections.should == {:name=>[[:presence, {}]]}
45
47
  end
46
48
 
47
49
  it "should populate constraint_validations when subclassing" do
@@ -52,6 +54,20 @@ describe "Sequel::Plugins::ConstraintValidations" do
52
54
  sc.set_dataset @ds
53
55
  @db.sqls.should == []
54
56
  sc.constraint_validations.should == [[:validates_presence, :name]]
57
+ sc.constraint_validation_reflections.should == {:name=>[[:presence, {}]]}
58
+ end
59
+
60
+ it "should handle plugin being loaded in subclass when superclass uses a custom constraint validations table" do
61
+ c = Class.new(Sequel::Model(@db))
62
+ c.plugin :constraint_validations, :constraint_validations_table=>:foo
63
+ @db.sqls.should == ["SELECT * FROM foo"]
64
+ sc = Class.new(c)
65
+ sc.plugin :constraint_validations
66
+ sc.constraint_validations_table.should == :foo
67
+ sc.set_dataset @ds
68
+ @db.sqls.should == []
69
+ sc.constraint_validations.should == [[:validates_presence, :name]]
70
+ sc.constraint_validation_reflections.should == {:name=>[[:presence, {}]]}
55
71
  end
56
72
 
57
73
  it "should populate constraint_validations when changing the model's dataset" do
@@ -64,114 +80,174 @@ describe "Sequel::Plugins::ConstraintValidations" do
64
80
  sc.set_dataset @ds
65
81
  @db.sqls.should == []
66
82
  sc.constraint_validations.should == [[:validates_presence, :name]]
83
+ sc.constraint_validation_reflections.should == {:name=>[[:presence, {}]]}
67
84
  end
68
85
 
69
86
  it "should reparse constraint validations when changing the model's database" do
70
87
  c = Class.new(Sequel::Model(@ds2))
71
88
  c.plugin :constraint_validations
72
- @db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
89
+ @ds2.db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
73
90
  sc = Class.new(c)
74
91
  sc.set_dataset @ds
75
92
  @db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
76
93
  sc.constraint_validations.should == [[:validates_presence, :name]]
94
+ sc.constraint_validation_reflections.should == {:name=>[[:presence, {}]]}
77
95
  end
78
96
 
79
97
  it "should reparse constraint validations when changing the model's database with a custom constraint validations table" do
80
98
  c = Class.new(Sequel::Model(@ds2))
81
99
  c.plugin :constraint_validations, :constraint_validations_table=>:foo
82
- @db.sqls.should == ["SELECT * FROM foo"]
100
+ @ds2.db.sqls.should == ["SELECT * FROM foo"]
83
101
  sc = Class.new(c)
84
102
  sc.set_dataset @ds
85
103
  @db.sqls.should == ["SELECT * FROM foo"]
86
104
  sc.constraint_validations.should == [[:validates_presence, :name]]
105
+ sc.constraint_validation_reflections.should == {:name=>[[:presence, {}]]}
87
106
  end
88
107
 
89
108
  it "should correctly retrieve :message option from constraint validations table" do
90
109
  model_class(:message=>'foo').constraint_validations.should == [[:validates_presence, :name, {:message=>'foo'}]]
110
+ @c.constraint_validation_reflections.should == {:name=>[[:presence, {:message=>'foo'}]]}
91
111
  end
92
112
 
93
113
  it "should correctly retrieve :allow_nil option from constraint validations table" do
94
114
  model_class(:allow_nil=>true).constraint_validations.should == [[:validates_presence, :name, {:allow_nil=>true}]]
115
+ @c.constraint_validation_reflections.should == {:name=>[[:presence, {:allow_nil=>true}]]}
95
116
  end
96
117
 
97
118
  it "should handle presence validation" do
98
119
  model_class(:validation_type=>'presence').constraint_validations.should == [[:validates_presence, :name]]
120
+ @c.constraint_validation_reflections.should == {:name=>[[:presence, {}]]}
99
121
  end
100
122
 
101
123
  it "should handle exact_length validation" do
102
124
  model_class(:validation_type=>'exact_length', :argument=>'5').constraint_validations.should == [[:validates_exact_length, 5, :name]]
125
+ @c.constraint_validation_reflections.should == {:name=>[[:exact_length, {:argument=>5}]]}
103
126
  end
104
127
 
105
128
  it "should handle min_length validation" do
106
129
  model_class(:validation_type=>'min_length', :argument=>'5').constraint_validations.should == [[:validates_min_length, 5, :name]]
130
+ @c.constraint_validation_reflections.should == {:name=>[[:min_length, {:argument=>5}]]}
107
131
  end
108
132
 
109
133
  it "should handle max_length validation" do
110
134
  model_class(:validation_type=>'max_length', :argument=>'5').constraint_validations.should == [[:validates_max_length, 5, :name]]
135
+ @c.constraint_validation_reflections.should == {:name=>[[:max_length, {:argument=>5}]]}
111
136
  end
112
137
 
113
138
  it "should handle length_range validation" do
114
139
  model_class(:validation_type=>'length_range', :argument=>'3..5').constraint_validations.should == [[:validates_length_range, 3..5, :name]]
140
+ @c.constraint_validation_reflections.should == {:name=>[[:length_range, {:argument=>3..5}]]}
115
141
  end
116
142
 
117
143
  it "should handle length_range validation with an exclusive end" do
118
144
  model_class(:validation_type=>'length_range', :argument=>'3...5').constraint_validations.should == [[:validates_length_range, 3...5, :name]]
145
+ @c.constraint_validation_reflections.should == {:name=>[[:length_range, {:argument=>3...5}]]}
119
146
  end
120
147
 
121
148
  it "should handle format validation" do
122
149
  model_class(:validation_type=>'format', :argument=>'^foo.*').constraint_validations.should == [[:validates_format, /^foo.*/, :name]]
150
+ @c.constraint_validation_reflections.should == {:name=>[[:format, {:argument=>/^foo.*/}]]}
123
151
  end
124
152
 
125
153
  it "should handle format validation with case insensitive format" do
126
154
  model_class(:validation_type=>'iformat', :argument=>'^foo.*').constraint_validations.should == [[:validates_format, /^foo.*/i, :name]]
155
+ @c.constraint_validation_reflections.should == {:name=>[[:format, {:argument=>/^foo.*/i}]]}
127
156
  end
128
157
 
129
158
  it "should handle includes validation with array of strings" do
130
159
  model_class(:validation_type=>'includes_str_array', :argument=>'a,b,c').constraint_validations.should == [[:validates_includes, %w'a b c', :name]]
160
+ @c.constraint_validation_reflections.should == {:name=>[[:includes, {:argument=>%w'a b c'}]]}
131
161
  end
132
162
 
133
163
  it "should handle includes validation with array of integers" do
134
164
  model_class(:validation_type=>'includes_int_array', :argument=>'1,2,3').constraint_validations.should == [[:validates_includes, [1, 2, 3], :name]]
165
+ @c.constraint_validation_reflections.should == {:name=>[[:includes, {:argument=>[1, 2, 3]}]]}
135
166
  end
136
167
 
137
168
  it "should handle includes validation with inclusive range of integers" do
138
169
  model_class(:validation_type=>'includes_int_range', :argument=>'3..5').constraint_validations.should == [[:validates_includes, 3..5, :name]]
170
+ @c.constraint_validation_reflections.should == {:name=>[[:includes, {:argument=>3..5}]]}
139
171
  end
140
172
 
141
173
  it "should handle includes validation with exclusive range of integers" do
142
174
  model_class(:validation_type=>'includes_int_range', :argument=>'3...5').constraint_validations.should == [[:validates_includes, 3...5, :name]]
175
+ @c.constraint_validation_reflections.should == {:name=>[[:includes, {:argument=>3...5}]]}
143
176
  end
144
177
 
145
178
  it "should handle like validation" do
146
179
  model_class(:validation_type=>'like', :argument=>'foo').constraint_validations.should == [[:validates_format, /\Afoo\z/, :name]]
180
+ @c.constraint_validation_reflections.should == {:name=>[[:format, {:argument=>/\Afoo\z/}]]}
147
181
  end
148
182
 
149
183
  it "should handle ilike validation" do
150
184
  model_class(:validation_type=>'ilike', :argument=>'foo').constraint_validations.should == [[:validates_format, /\Afoo\z/i, :name]]
185
+ @c.constraint_validation_reflections.should == {:name=>[[:format, {:argument=>/\Afoo\z/i}]]}
151
186
  end
152
187
 
153
188
  it "should handle like validation with % metacharacter" do
154
189
  model_class(:validation_type=>'like', :argument=>'%foo%').constraint_validations.should == [[:validates_format, /\A.*foo.*\z/, :name]]
190
+ @c.constraint_validation_reflections.should == {:name=>[[:format, {:argument=>/\A.*foo.*\z/}]]}
155
191
  end
156
192
 
157
193
  it "should handle like validation with %% metacharacter" do
158
194
  model_class(:validation_type=>'like', :argument=>'%%foo%%').constraint_validations.should == [[:validates_format, /\A%foo%\z/, :name]]
195
+ @c.constraint_validation_reflections.should == {:name=>[[:format, {:argument=>/\A%foo%\z/}]]}
159
196
  end
160
197
 
161
198
  it "should handle like validation with _ metacharacter" do
162
199
  model_class(:validation_type=>'like', :argument=>'f_o').constraint_validations.should == [[:validates_format, /\Af.o\z/, :name]]
200
+ @c.constraint_validation_reflections.should == {:name=>[[:format, {:argument=>/\Af.o\z/}]]}
163
201
  end
164
202
 
165
203
  it "should handle like validation with Regexp metacharacter" do
166
204
  model_class(:validation_type=>'like', :argument=>'\wfoo\d').constraint_validations.should == [[:validates_format, /\A\\wfoo\\d\z/, :name]]
205
+ @c.constraint_validation_reflections.should == {:name=>[[:format, {:argument=>/\A\\wfoo\\d\z/}]]}
167
206
  end
168
207
 
169
208
  it "should handle unique validation" do
170
209
  model_class(:validation_type=>'unique').constraint_validations.should == [[:validates_unique, [:name]]]
210
+ @c.constraint_validation_reflections.should == {:name=>[[:unique, {}]]}
171
211
  end
172
212
 
173
213
  it "should handle unique validation with multiple columns" do
174
214
  model_class(:validation_type=>'unique', :column=>'name,id').constraint_validations.should == [[:validates_unique, [:name, :id]]]
215
+ @c.constraint_validation_reflections.should == {[:name, :id]=>[[:unique, {}]]}
216
+ end
217
+
218
+ it "should handle :validation_options" do
219
+ c = model_class(:validation_type=>'unique', :column=>'name')
220
+ c.plugin :constraint_validations, :validation_options=>{:unique=>{:message=>'is bad'}}
221
+ c.constraint_validations.should == [[:validates_unique, [:name], {:message=>'is bad'}]]
222
+ c.constraint_validation_reflections.should == {:name=>[[:unique, {:message=>'is bad'}]]}
223
+ c.dataset._fetch = {:count=>1}
224
+ o = c.new(:name=>'a')
225
+ o.valid?.should == false
226
+ o.errors.full_messages.should == ['name is bad']
227
+ end
228
+
229
+ it "should handle :validation_options merging with constraint validation options" do
230
+ c = model_class(:validation_type=>'unique', :column=>'name', :allow_nil=>true)
231
+ c.plugin :constraint_validations, :validation_options=>{:unique=>{:message=>'is bad'}}
232
+ c.constraint_validations.should == [[:validates_unique, [:name], {:message=>'is bad', :allow_nil=>true}]]
233
+ c.constraint_validation_reflections.should == {:name=>[[:unique, {:message=>'is bad', :allow_nil=>true}]]}
234
+ c.dataset._fetch = {:count=>1}
235
+ o = c.new(:name=>'a')
236
+ o.valid?.should == false
237
+ o.errors.full_messages.should == ['name is bad']
238
+ end
239
+
240
+ it "should handle :validation_options merging with subclasses" do
241
+ c = model_class(:validation_type=>'unique', :column=>'name')
242
+ c.plugin :constraint_validations, :validation_options=>{:unique=>{:message=>'is bad', :allow_nil=>true}}
243
+ sc = Class.new(c)
244
+ sc.plugin :constraint_validations, :validation_options=>{:unique=>{:allow_missing=>true, :allow_nil=>false}}
245
+ sc.constraint_validations.should == [[:validates_unique, [:name], {:message=>'is bad', :allow_missing=>true, :allow_nil=>false}]]
246
+ sc.constraint_validation_reflections.should == {:name=>[[:unique, {:message=>'is bad', :allow_missing=>true, :allow_nil=>false}]]}
247
+ sc.dataset._fetch = {:count=>1}
248
+ o = sc.new(:name=>'a')
249
+ o.valid?.should == false
250
+ o.errors.full_messages.should == ['name is bad']
175
251
  end
176
252
 
177
253
  it "should used parsed constraint validations when validating" do
@@ -185,6 +261,7 @@ describe "Sequel::Plugins::ConstraintValidations" do
185
261
  c = Class.new(Sequel::Model(@db[Sequel.identifier(:sch__items)]))
186
262
  c.plugin :constraint_validations
187
263
  c.constraint_validations.should == [[:validates_presence, :name]]
264
+ c.constraint_validation_reflections.should == {:name=>[[:presence, {}]]}
188
265
  end
189
266
 
190
267
  it "should handle a table name specified as SQL::QualifiedIdentifier" do
@@ -192,5 +269,6 @@ describe "Sequel::Plugins::ConstraintValidations" do
192
269
  c = Class.new(Sequel::Model(@db[Sequel.qualify(:sch, :items)]))
193
270
  c.plugin :constraint_validations
194
271
  c.constraint_validations.should == [[:validates_presence, :name]]
272
+ c.constraint_validation_reflections.should == {:name=>[[:presence, {}]]}
195
273
  end
196
274
  end