sequel 3.47.0 → 3.48.0

Sign up to get free protection for your applications and to get access to all the features.
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