sequel 3.46.0 → 3.47.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.
- checksums.yaml +4 -4
- data/CHANGELOG +96 -0
- data/Rakefile +7 -1
- data/bin/sequel +6 -4
- data/doc/active_record.rdoc +1 -1
- data/doc/advanced_associations.rdoc +14 -35
- data/doc/association_basics.rdoc +66 -4
- data/doc/migration.rdoc +4 -0
- data/doc/opening_databases.rdoc +6 -0
- data/doc/postgresql.rdoc +302 -0
- data/doc/release_notes/3.47.0.txt +270 -0
- data/doc/security.rdoc +6 -0
- data/lib/sequel/adapters/ibmdb.rb +9 -9
- data/lib/sequel/adapters/jdbc.rb +22 -7
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
- data/lib/sequel/adapters/mock.rb +2 -0
- data/lib/sequel/adapters/postgres.rb +44 -13
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +2 -2
- data/lib/sequel/adapters/shared/postgres.rb +94 -55
- data/lib/sequel/adapters/shared/sqlite.rb +3 -1
- data/lib/sequel/adapters/sqlite.rb +2 -2
- data/lib/sequel/adapters/utils/pg_types.rb +1 -14
- data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +2 -2
- data/lib/sequel/database/features.rb +5 -0
- data/lib/sequel/database/misc.rb +47 -5
- data/lib/sequel/database/query.rb +2 -2
- data/lib/sequel/dataset/actions.rb +4 -2
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/query.rb +8 -6
- data/lib/sequel/dataset/sql.rb +8 -6
- data/lib/sequel/extensions/constraint_validations.rb +5 -2
- data/lib/sequel/extensions/migration.rb +10 -8
- data/lib/sequel/extensions/pagination.rb +3 -0
- data/lib/sequel/extensions/pg_array.rb +85 -25
- data/lib/sequel/extensions/pg_hstore.rb +8 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
- data/lib/sequel/extensions/pg_inet.rb +16 -13
- data/lib/sequel/extensions/pg_interval.rb +6 -2
- data/lib/sequel/extensions/pg_json.rb +18 -11
- data/lib/sequel/extensions/pg_range.rb +17 -2
- data/lib/sequel/extensions/pg_range_ops.rb +7 -5
- data/lib/sequel/extensions/pg_row.rb +29 -12
- data/lib/sequel/extensions/pretty_table.rb +3 -0
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/schema_caching.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -1
- data/lib/sequel/extensions/select_remove.rb +3 -0
- data/lib/sequel/model.rb +8 -2
- data/lib/sequel/model/associations.rb +39 -27
- data/lib/sequel/model/base.rb +99 -38
- data/lib/sequel/model/plugins.rb +25 -0
- data/lib/sequel/plugins/association_autoreloading.rb +27 -22
- data/lib/sequel/plugins/association_dependencies.rb +1 -7
- data/lib/sequel/plugins/auto_validations.rb +110 -0
- data/lib/sequel/plugins/boolean_readers.rb +1 -6
- data/lib/sequel/plugins/caching.rb +6 -13
- data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
- data/lib/sequel/plugins/composition.rb +14 -7
- data/lib/sequel/plugins/constraint_validations.rb +2 -13
- data/lib/sequel/plugins/defaults_setter.rb +1 -6
- data/lib/sequel/plugins/dirty.rb +8 -0
- data/lib/sequel/plugins/error_splitter.rb +54 -0
- data/lib/sequel/plugins/force_encoding.rb +1 -5
- data/lib/sequel/plugins/hook_class_methods.rb +1 -6
- data/lib/sequel/plugins/input_transformer.rb +79 -0
- data/lib/sequel/plugins/instance_filters.rb +7 -1
- data/lib/sequel/plugins/instance_hooks.rb +7 -1
- data/lib/sequel/plugins/json_serializer.rb +5 -10
- data/lib/sequel/plugins/lazy_attributes.rb +20 -7
- data/lib/sequel/plugins/list.rb +1 -6
- data/lib/sequel/plugins/many_through_many.rb +1 -2
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
- data/lib/sequel/plugins/optimistic_locking.rb +1 -5
- data/lib/sequel/plugins/pg_row.rb +4 -2
- data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
- data/lib/sequel/plugins/prepared_statements.rb +1 -5
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
- data/lib/sequel/plugins/rcte_tree.rb +2 -2
- data/lib/sequel/plugins/serialization.rb +11 -13
- data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/static_cache.rb +67 -19
- data/lib/sequel/plugins/string_stripper.rb +7 -27
- data/lib/sequel/plugins/subclasses.rb +3 -5
- data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
- data/lib/sequel/plugins/timestamps.rb +2 -7
- data/lib/sequel/plugins/touch.rb +5 -8
- data/lib/sequel/plugins/tree.rb +1 -6
- data/lib/sequel/plugins/typecast_on_load.rb +1 -5
- data/lib/sequel/plugins/update_primary_key.rb +26 -14
- data/lib/sequel/plugins/validation_class_methods.rb +31 -16
- data/lib/sequel/plugins/validation_helpers.rb +50 -26
- data/lib/sequel/plugins/xml_serializer.rb +3 -6
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +131 -15
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +16 -17
- data/spec/core/database_spec.rb +111 -40
- data/spec/core/dataset_spec.rb +65 -74
- data/spec/core/expression_filters_spec.rb +6 -5
- data/spec/core/object_graph_spec.rb +0 -1
- data/spec/core/schema_spec.rb +23 -23
- data/spec/core/spec_helper.rb +5 -1
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/auto_validations_spec.rb +90 -0
- data/spec/extensions/caching_spec.rb +6 -0
- data/spec/extensions/class_table_inheritance_spec.rb +8 -1
- data/spec/extensions/composition_spec.rb +12 -5
- data/spec/extensions/constraint_validations_spec.rb +4 -4
- data/spec/extensions/core_refinements_spec.rb +29 -79
- data/spec/extensions/dirty_spec.rb +14 -0
- data/spec/extensions/error_splitter_spec.rb +18 -0
- data/spec/extensions/identity_map_spec.rb +0 -1
- data/spec/extensions/input_transformer_spec.rb +54 -0
- data/spec/extensions/instance_filters_spec.rb +6 -0
- data/spec/extensions/instance_hooks_spec.rb +12 -1
- data/spec/extensions/json_serializer_spec.rb +0 -1
- data/spec/extensions/lazy_attributes_spec.rb +64 -55
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +3 -4
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
- data/spec/extensions/migration_spec.rb +16 -0
- data/spec/extensions/null_dataset_spec.rb +1 -1
- data/spec/extensions/pg_array_spec.rb +48 -1
- data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
- data/spec/extensions/pg_hstore_spec.rb +5 -0
- data/spec/extensions/pg_inet_spec.rb +5 -0
- data/spec/extensions/pg_interval_spec.rb +7 -3
- data/spec/extensions/pg_json_spec.rb +6 -1
- data/spec/extensions/pg_range_ops_spec.rb +4 -1
- data/spec/extensions/pg_range_spec.rb +5 -0
- data/spec/extensions/pg_row_plugin_spec.rb +13 -0
- data/spec/extensions/pg_row_spec.rb +28 -19
- data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
- data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
- data/spec/extensions/query_literals_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +2 -2
- data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
- data/spec/extensions/serialization_spec.rb +15 -1
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/single_table_inheritance_spec.rb +1 -1
- data/spec/extensions/static_cache_spec.rb +59 -9
- data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
- data/spec/extensions/update_primary_key_spec.rb +17 -1
- data/spec/extensions/validation_class_methods_spec.rb +25 -0
- data/spec/extensions/validation_helpers_spec.rb +59 -3
- data/spec/integration/associations_test.rb +5 -5
- data/spec/integration/eager_loader_test.rb +32 -63
- data/spec/integration/model_test.rb +2 -2
- data/spec/integration/plugin_test.rb +88 -56
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +1 -1
- data/spec/integration/timezone_test.rb +0 -1
- data/spec/integration/transaction_test.rb +0 -1
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +106 -84
- data/spec/model/base_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +8 -8
- data/spec/model/model_spec.rb +27 -9
- data/spec/model/plugins_spec.rb +71 -0
- data/spec/model/record_spec.rb +99 -13
- metadata +12 -2
|
@@ -45,17 +45,15 @@ module Sequel
|
|
|
45
45
|
Sequel.synchronize{_descendents}
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
Plugins.inherited_instance_variables(self, :@subclasses=>lambda{|v| []}, :@on_subclass=>nil)
|
|
49
|
+
|
|
48
50
|
# Add the subclass to this model's current subclasses,
|
|
49
51
|
# and initialize a new subclasses instance variable
|
|
50
52
|
# in the subclass.
|
|
51
53
|
def inherited(subclass)
|
|
52
54
|
super
|
|
53
55
|
Sequel.synchronize{subclasses << subclass}
|
|
54
|
-
|
|
55
|
-
if on_subclass
|
|
56
|
-
subclass.instance_variable_set(:@on_subclass, on_subclass)
|
|
57
|
-
on_subclass.call(subclass)
|
|
58
|
-
end
|
|
56
|
+
on_subclass.call(subclass) if on_subclass
|
|
59
57
|
end
|
|
60
58
|
|
|
61
59
|
private
|
|
@@ -42,9 +42,9 @@ module Sequel
|
|
|
42
42
|
# objects retrieved with the current object.
|
|
43
43
|
def load_associated_objects(opts, reload=false)
|
|
44
44
|
name = opts[:name]
|
|
45
|
-
if !associations.include?(name) && retrieved_by
|
|
45
|
+
if !associations.include?(name) && retrieved_by && !frozen?
|
|
46
46
|
begin
|
|
47
|
-
retrieved_by.send(:eager_load, retrieved_with, name=>{})
|
|
47
|
+
retrieved_by.send(:eager_load, retrieved_with.reject{|o| o.frozen?}, name=>{})
|
|
48
48
|
rescue Sequel::UndefinedAssociation
|
|
49
49
|
# This can happen if class table inheritance is used and the association
|
|
50
50
|
# is only defined in a subclass. This particular instance can use the
|
|
@@ -47,13 +47,8 @@ module Sequel
|
|
|
47
47
|
@create_timestamp_overwrite
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
super
|
|
53
|
-
[:@create_timestamp_field, :@update_timestamp_field, :@create_timestamp_overwrite, :@set_update_timestamp_on_create].each do |iv|
|
|
54
|
-
subclass.instance_variable_set(iv, instance_variable_get(iv))
|
|
55
|
-
end
|
|
56
|
-
end
|
|
50
|
+
Plugins.inherited_instance_variables(self, :@create_timestamp_field=>nil, :@update_timestamp_field=>nil,
|
|
51
|
+
:@create_timestamp_overwrite=>nil, :@set_update_timestamp_on_create=>nil)
|
|
57
52
|
|
|
58
53
|
# Whether to set the update timestamp to the create timestamp when creating
|
|
59
54
|
def set_update_timestamp_on_create?
|
data/lib/sequel/plugins/touch.rb
CHANGED
|
@@ -30,6 +30,10 @@ module Sequel
|
|
|
30
30
|
# The default column to update when touching
|
|
31
31
|
TOUCH_COLUMN_DEFAULT = :updated_at
|
|
32
32
|
|
|
33
|
+
def self.apply(model, opts={})
|
|
34
|
+
model.instance_variable_set(:@touched_associations, {})
|
|
35
|
+
end
|
|
36
|
+
|
|
33
37
|
# Set the touch_column and touched_associations variables for the model.
|
|
34
38
|
# Options:
|
|
35
39
|
# * :associations - The associations to touch when a model instance is
|
|
@@ -41,7 +45,6 @@ module Sequel
|
|
|
41
45
|
# * :column - The column to modify when touching a model instance.
|
|
42
46
|
def self.configure(model, opts={})
|
|
43
47
|
model.touch_column = opts[:column] || TOUCH_COLUMN_DEFAULT if opts[:column] || !model.touch_column
|
|
44
|
-
model.instance_variable_set(:@touched_associations, {})
|
|
45
48
|
model.touch_associations(opts[:associations]) if opts[:associations]
|
|
46
49
|
end
|
|
47
50
|
|
|
@@ -56,13 +59,7 @@ module Sequel
|
|
|
56
59
|
# are column name symbols.
|
|
57
60
|
attr_reader :touched_associations
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
# Also, create a copy of the touched_associations in the subclass.
|
|
61
|
-
def inherited(subclass)
|
|
62
|
-
super
|
|
63
|
-
subclass.touch_column = touch_column
|
|
64
|
-
subclass.instance_variable_set(:@touched_associations, touched_associations.dup)
|
|
65
|
-
end
|
|
62
|
+
Plugins.inherited_instance_variables(self, :@touched_associations=>:dup, :@touch_column=>nil)
|
|
66
63
|
|
|
67
64
|
# Add additional associations to be touched. See the :association option
|
|
68
65
|
# of the Sequel::Plugin::Touch.configure method for the format of the associations
|
data/lib/sequel/plugins/tree.rb
CHANGED
|
@@ -55,12 +55,7 @@ module Sequel
|
|
|
55
55
|
# parent of the leaf.
|
|
56
56
|
attr_accessor :parent_column
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
def inherited(subclass)
|
|
60
|
-
super
|
|
61
|
-
subclass.parent_column = parent_column
|
|
62
|
-
subclass.tree_order = tree_order
|
|
63
|
-
end
|
|
58
|
+
Plugins.inherited_instance_variables(self, :@parent_column=>nil, :@tree_order=>nil)
|
|
64
59
|
|
|
65
60
|
# Returns list of all root nodes (those with no parent nodes).
|
|
66
61
|
#
|
|
@@ -36,11 +36,7 @@ module Sequel
|
|
|
36
36
|
@typecast_on_load_columns.concat(columns)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
def inherited(subclass)
|
|
41
|
-
super
|
|
42
|
-
subclass.instance_variable_set(:@typecast_on_load_columns, typecast_on_load_columns.dup)
|
|
43
|
-
end
|
|
39
|
+
Plugins.inherited_instance_variables(self, :@typecast_on_load_columns=>:dup)
|
|
44
40
|
end
|
|
45
41
|
|
|
46
42
|
module InstanceMethods
|
|
@@ -20,27 +20,39 @@ module Sequel
|
|
|
20
20
|
# # Make the Album class support primary key updates
|
|
21
21
|
# Album.plugin :update_primary_key
|
|
22
22
|
module UpdatePrimaryKey
|
|
23
|
-
module ClassMethods
|
|
24
|
-
# Cache the pk_hash when loading records
|
|
25
|
-
def call(h)
|
|
26
|
-
r = super(h)
|
|
27
|
-
r.pk_hash
|
|
28
|
-
r
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
23
|
module InstanceMethods
|
|
33
|
-
# Clear the
|
|
34
|
-
# the pk_hash
|
|
24
|
+
# Clear the cached primary key.
|
|
35
25
|
def after_update
|
|
36
26
|
super
|
|
37
27
|
@pk_hash = nil
|
|
38
|
-
pk_hash
|
|
39
28
|
end
|
|
40
29
|
|
|
41
|
-
#
|
|
30
|
+
# Use the cached primary key if one is present.
|
|
42
31
|
def pk_hash
|
|
43
|
-
@pk_hash
|
|
32
|
+
@pk_hash || super
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# If the primary key column changes, clear related associations and cache
|
|
38
|
+
# the previous primary key values.
|
|
39
|
+
def change_column_value(column, value)
|
|
40
|
+
pk = primary_key
|
|
41
|
+
if (pk.is_a?(Array) ? pk.include?(column) : pk == column)
|
|
42
|
+
@pk_hash ||= pk_hash unless new?
|
|
43
|
+
clear_associations_using_primary_key
|
|
44
|
+
end
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Clear associations that are likely to be tied to the primary key.
|
|
49
|
+
# Note that this currently can clear additional options that don't reference
|
|
50
|
+
# the primary key (such as one_to_many columns referencing a column other than the
|
|
51
|
+
# primary key).
|
|
52
|
+
def clear_associations_using_primary_key
|
|
53
|
+
associations.keys.each do |k|
|
|
54
|
+
associations.delete(k) if model.association_reflection(k)[:type] != :many_to_one
|
|
55
|
+
end
|
|
44
56
|
end
|
|
45
57
|
end
|
|
46
58
|
end
|
|
@@ -21,7 +21,6 @@ module Sequel
|
|
|
21
21
|
# Setup the validations hash for the given model.
|
|
22
22
|
def self.apply(model)
|
|
23
23
|
model.class_eval do
|
|
24
|
-
@validation_mutex = Mutex.new
|
|
25
24
|
@validations = {}
|
|
26
25
|
@validation_reflections = {}
|
|
27
26
|
end
|
|
@@ -63,21 +62,15 @@ module Sequel
|
|
|
63
62
|
!validations.empty?
|
|
64
63
|
end
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
vr = @validation_reflections
|
|
69
|
-
subclass.class_eval do
|
|
70
|
-
@validation_mutex = Mutex.new
|
|
71
|
-
@validations = {}
|
|
72
|
-
h = {}
|
|
73
|
-
vr.each{|k,v| h[k] = v.dup}
|
|
74
|
-
@validation_reflections = h
|
|
75
|
-
end
|
|
76
|
-
super
|
|
77
|
-
end
|
|
78
|
-
|
|
65
|
+
Plugins.inherited_instance_variables(self, :@validations=>:hash_dup, :@validation_reflections=>:hash_dup)
|
|
66
|
+
|
|
79
67
|
# Instructs the model to skip validations defined in superclasses
|
|
80
68
|
def skip_superclass_validations
|
|
69
|
+
superclass.validations.each do |att, procs|
|
|
70
|
+
if ps = @validations[att]
|
|
71
|
+
@validations[att] -= procs
|
|
72
|
+
end
|
|
73
|
+
end
|
|
81
74
|
@skip_superclass_validations = true
|
|
82
75
|
end
|
|
83
76
|
|
|
@@ -107,7 +100,6 @@ module Sequel
|
|
|
107
100
|
|
|
108
101
|
# Validates the given instance.
|
|
109
102
|
def validate(o)
|
|
110
|
-
superclass.validate(o) if superclass.respond_to?(:validate) && !skip_superclass_validations?
|
|
111
103
|
validations.each do |att, procs|
|
|
112
104
|
v = case att
|
|
113
105
|
when Array
|
|
@@ -200,7 +192,7 @@ module Sequel
|
|
|
200
192
|
end
|
|
201
193
|
tag = opts[:tag]
|
|
202
194
|
atts.each do |a|
|
|
203
|
-
a_vals =
|
|
195
|
+
a_vals = Sequel.synchronize{validations[a] ||= []}
|
|
204
196
|
if tag && (old = a_vals.find{|x| x[0] == tag})
|
|
205
197
|
old[1] = blk
|
|
206
198
|
else
|
|
@@ -362,6 +354,28 @@ module Sequel
|
|
|
362
354
|
end
|
|
363
355
|
end
|
|
364
356
|
|
|
357
|
+
# Validates whether an attribute has the correct ruby type for the associated
|
|
358
|
+
# database type. This is generally useful in conjunction with
|
|
359
|
+
# raise_on_typecast_failure = false, to handle typecasting errors at validation
|
|
360
|
+
# time instead of at setter time.
|
|
361
|
+
#
|
|
362
|
+
# Possible Options:
|
|
363
|
+
# * :message - The message to use (default: 'is not a valid (integer|datetime|etc.)')
|
|
364
|
+
def validates_schema_type(*atts)
|
|
365
|
+
opts = {
|
|
366
|
+
:tag => :schema_type,
|
|
367
|
+
}.merge!(extract_options!(atts))
|
|
368
|
+
reflect_validation(:schema_type, opts, atts)
|
|
369
|
+
atts << opts
|
|
370
|
+
validates_each(*atts) do |o, a, v|
|
|
371
|
+
next if v.nil? || (klass = o.send(:schema_type_class, a)).nil?
|
|
372
|
+
if klass.is_a?(Array) ? !klass.any?{|kls| v.is_a?(kls)} : !v.is_a?(klass)
|
|
373
|
+
message = opts[:message] || "is not a valid #{Array(klass).join(" or ").downcase}"
|
|
374
|
+
o.errors.add(a, message)
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
365
379
|
# Validates only if the fields in the model (specified by atts) are
|
|
366
380
|
# unique in the database. Pass an array of fields instead of multiple
|
|
367
381
|
# fields to specify that the combination of fields must be unique,
|
|
@@ -446,6 +460,7 @@ module Sequel
|
|
|
446
460
|
# Validates the object.
|
|
447
461
|
def validate
|
|
448
462
|
model.validate(self)
|
|
463
|
+
super
|
|
449
464
|
end
|
|
450
465
|
end
|
|
451
466
|
end
|
|
@@ -6,35 +6,36 @@ module Sequel
|
|
|
6
6
|
# Sequel::Model.plugin :validation_helpers
|
|
7
7
|
# class Album < Sequel::Model
|
|
8
8
|
# def validate
|
|
9
|
+
# super
|
|
9
10
|
# validates_min_length 1, :num_tracks
|
|
10
11
|
# end
|
|
11
12
|
# end
|
|
12
13
|
#
|
|
13
|
-
# The validates_unique
|
|
14
|
-
# the API explained here:
|
|
14
|
+
# The validates_unique and validates_schema_types methods have a unique API, but the other
|
|
15
|
+
# validations have the API explained here:
|
|
15
16
|
#
|
|
16
17
|
# Arguments:
|
|
17
|
-
#
|
|
18
|
-
#
|
|
18
|
+
# atts :: Single attribute symbol or an array of attribute symbols specifying the
|
|
19
|
+
# attribute(s) to validate.
|
|
19
20
|
# Options:
|
|
20
|
-
#
|
|
21
|
-
#
|
|
21
|
+
# :allow_blank :: Whether to skip the validation if the value is blank. You should
|
|
22
|
+
# make sure all objects respond to blank if you use this option, which you can do by:
|
|
22
23
|
# Sequel.extension :blank
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
24
|
+
# :allow_missing :: Whether to skip the validation if the attribute isn't a key in the
|
|
25
|
+
# values hash. This is different from allow_nil, because Sequel only sends the attributes
|
|
26
|
+
# in the values when doing an insert or update. If the attribute is not present, Sequel
|
|
27
|
+
# doesn't specify it, so the database will use the table's default value. This is different
|
|
28
|
+
# from having an attribute in values with a value of nil, which Sequel will send as NULL.
|
|
29
|
+
# If your database table has a non NULL default, this may be a good option to use. You
|
|
30
|
+
# don't want to use allow_nil, because if the attribute is in values but has a value nil,
|
|
31
|
+
# Sequel will attempt to insert a NULL value into the database, instead of using the
|
|
32
|
+
# database's default.
|
|
33
|
+
# :allow_nil :: Whether to skip the validation if the value is nil.
|
|
34
|
+
# :message :: The message to use. Can be a string which is used directly, or a
|
|
35
|
+
# proc which is called. If the validation method takes a argument before the array of attributes,
|
|
36
|
+
# that argument is passed as an argument to the proc. The exception is the
|
|
37
|
+
# validates_not_string method, which doesn't take an argument, but passes
|
|
38
|
+
# the schema type symbol as the argument to the proc.
|
|
38
39
|
#
|
|
39
40
|
# The default validation options for all models can be modified by
|
|
40
41
|
# changing the values of the Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS hash. You
|
|
@@ -83,13 +84,14 @@ module Sequel
|
|
|
83
84
|
:length_range=>{:message=>lambda{|range| "is too short or too long"}},
|
|
84
85
|
:max_length=>{:message=>lambda{|max| "is longer than #{max} characters"}, :nil_message=>lambda{"is not present"}},
|
|
85
86
|
:min_length=>{:message=>lambda{|min| "is shorter than #{min} characters"}},
|
|
87
|
+
:not_null=>{:message=>lambda{"is not present"}},
|
|
86
88
|
:not_string=>{:message=>lambda{|type| type ? "is not a valid #{type}" : "is a string"}},
|
|
87
89
|
:numeric=>{:message=>lambda{"is not a number"}},
|
|
88
|
-
:type=>{:message=>lambda{|klass| "is not a #{klass}"}},
|
|
90
|
+
:type=>{:message=>lambda{|klass| klass.is_a?(Array) ? "is not a valid #{klass.join(" or ").downcase}" : "is not a valid #{klass.to_s.downcase}"}},
|
|
89
91
|
:presence=>{:message=>lambda{"is not present"}},
|
|
90
92
|
:unique=>{:message=>lambda{'is already taken'}}
|
|
91
93
|
}
|
|
92
|
-
|
|
94
|
+
|
|
93
95
|
module InstanceMethods
|
|
94
96
|
# Check that the attribute values are the given exact length.
|
|
95
97
|
def validates_exact_length(exact, atts, opts={})
|
|
@@ -136,6 +138,11 @@ module Sequel
|
|
|
136
138
|
validatable_attributes_for_type(:min_length, atts, opts){|a,v,m| validation_error_message(m, min) unless v && v.length >= min}
|
|
137
139
|
end
|
|
138
140
|
|
|
141
|
+
# Check attribute value(s) are not NULL/nil.
|
|
142
|
+
def validates_not_null(atts, opts={})
|
|
143
|
+
validatable_attributes_for_type(:not_null, atts, opts){|a,v,m| validation_error_message(m) if v.nil?}
|
|
144
|
+
end
|
|
145
|
+
|
|
139
146
|
# Check that the attribute value(s) is not a string. This is generally useful
|
|
140
147
|
# in conjunction with raise_on_typecast_failure = false, where you are
|
|
141
148
|
# passing in string values for non-string attributes (such as numbers and dates).
|
|
@@ -158,12 +165,28 @@ module Sequel
|
|
|
158
165
|
end
|
|
159
166
|
end
|
|
160
167
|
|
|
161
|
-
#
|
|
168
|
+
# Validates for all of the model columns (or just the given columns)
|
|
169
|
+
# that the column value is an instance of the expected class based on
|
|
170
|
+
# the column's schema type.
|
|
171
|
+
def validates_schema_types(atts=keys)
|
|
172
|
+
Array(atts).each do |k|
|
|
173
|
+
next unless type = schema_type_class(k)
|
|
174
|
+
validates_type(type, k)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Check if value is an instance of a class. If +klass+ is an array,
|
|
179
|
+
# the value must be an instance of one of the classes in the array.
|
|
162
180
|
def validates_type(klass, atts, opts={})
|
|
163
181
|
klass = klass.to_s.constantize if klass.is_a?(String) || klass.is_a?(Symbol)
|
|
164
|
-
validatable_attributes_for_type(:type, atts, opts)
|
|
182
|
+
validatable_attributes_for_type(:type, atts, opts) do |a,v,m|
|
|
183
|
+
next if v.nil?
|
|
184
|
+
if klass.is_a?(Array) ? !klass.any?{|kls| v.is_a?(kls)} : !v.is_a?(klass)
|
|
185
|
+
validation_error_message(m, klass)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
165
188
|
end
|
|
166
|
-
|
|
189
|
+
|
|
167
190
|
# Check attribute value(s) is not considered blank by the database, but allow false values.
|
|
168
191
|
def validates_presence(atts, opts={})
|
|
169
192
|
validatable_attributes_for_type(:presence, atts, opts){|a,v,m| validation_error_message(m) if model.db.send(:blank_object?, v) && v != false}
|
|
@@ -220,6 +243,7 @@ module Sequel
|
|
|
220
243
|
where = opts[:where]
|
|
221
244
|
atts.each do |a|
|
|
222
245
|
arr = Array(a)
|
|
246
|
+
next if arr.any?{|x| errors.on(x)}
|
|
223
247
|
next if opts[:only_if_modified] && !new? && !arr.any?{|x| changed_columns.include?(x)}
|
|
224
248
|
ds = if where
|
|
225
249
|
where.call(model.dataset, self, arr)
|
|
@@ -151,11 +151,6 @@ module Sequel
|
|
|
151
151
|
new.from_xml_node(parent, opts)
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
-
# Call the dataset +to_xml+ method.
|
|
155
|
-
def to_xml(opts={})
|
|
156
|
-
dataset.to_xml(opts)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
154
|
# Return an appropriate Nokogiri::XML::Builder instance
|
|
160
155
|
# used to create the XML. This should probably not be used
|
|
161
156
|
# directly by user code.
|
|
@@ -201,6 +196,8 @@ module Sequel
|
|
|
201
196
|
end
|
|
202
197
|
proc{|s| "#{pr[s]}_"}
|
|
203
198
|
end
|
|
199
|
+
|
|
200
|
+
Plugins.def_dataset_methods(self, :to_xml)
|
|
204
201
|
end
|
|
205
202
|
|
|
206
203
|
module InstanceMethods
|
|
@@ -282,7 +279,7 @@ module Sequel
|
|
|
282
279
|
parent.children.each do |node|
|
|
283
280
|
next if node.is_a?(Nokogiri::XML::Text)
|
|
284
281
|
k = name_proc[node.name]
|
|
285
|
-
if assocs_hash &&
|
|
282
|
+
if assocs_hash && assocs_hash[k]
|
|
286
283
|
assocs_present << [k.to_sym, node]
|
|
287
284
|
else
|
|
288
285
|
hash[k] = node.key?('nil') ? nil : node.children.first.to_s
|
data/lib/sequel/sql.rb
CHANGED
data/lib/sequel/version.rb
CHANGED
|
@@ -3,7 +3,7 @@ module Sequel
|
|
|
3
3
|
MAJOR = 3
|
|
4
4
|
# The minor version of Sequel. Bumped for every non-patch level
|
|
5
5
|
# release, generally around once a month.
|
|
6
|
-
MINOR =
|
|
6
|
+
MINOR = 47
|
|
7
7
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
|
8
8
|
# releases that fix regressions from previous versions.
|
|
9
9
|
TINY = 0
|
|
@@ -116,6 +116,25 @@ describe "A PostgreSQL database" do
|
|
|
116
116
|
end
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
+
describe "A PostgreSQL database with domain types" do
|
|
120
|
+
before(:all) do
|
|
121
|
+
@db = POSTGRES_DB
|
|
122
|
+
@db << "DROP DOMAIN IF EXISTS positive_number CASCADE"
|
|
123
|
+
@db << "CREATE DOMAIN positive_number AS numeric(10,2) CHECK (VALUE > 0)"
|
|
124
|
+
@db.create_table!(:testfk){positive_number :id, :primary_key=>true}
|
|
125
|
+
end
|
|
126
|
+
after(:all) do
|
|
127
|
+
@db.drop_table?(:testfk)
|
|
128
|
+
@db << "DROP DOMAIN positive_number"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
specify "should correctly parse the schema" do
|
|
132
|
+
sch = @db.schema(:testfk, :reload=>true)
|
|
133
|
+
sch.first.last.delete(:domain_oid).should be_a_kind_of(Integer)
|
|
134
|
+
sch.should == [[:id, {:type=>:decimal, :ruby_default=>nil, :db_type=>"numeric(10,2)", :default=>nil, :oid=>1700, :primary_key=>true, :allow_null=>false, :db_domain_type=>'positive_number'}]]
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
119
138
|
describe "A PostgreSQL dataset" do
|
|
120
139
|
before(:all) do
|
|
121
140
|
@db = POSTGRES_DB
|
|
@@ -482,6 +501,23 @@ describe "A PostgreSQL dataset with a timestamp field" do
|
|
|
482
501
|
@db[:test3].get(:time).should == 'infinity'
|
|
483
502
|
@db.convert_infinite_timestamps = :float
|
|
484
503
|
@db[:test3].get(:time).should == 1.0/0.0
|
|
504
|
+
@db.convert_infinite_timestamps = 'nil'
|
|
505
|
+
@db[:test3].get(:time).should == nil
|
|
506
|
+
@db.convert_infinite_timestamps = 'string'
|
|
507
|
+
@db[:test3].get(:time).should == 'infinity'
|
|
508
|
+
@db.convert_infinite_timestamps = 'float'
|
|
509
|
+
@db[:test3].get(:time).should == 1.0/0.0
|
|
510
|
+
@db.convert_infinite_timestamps = 't'
|
|
511
|
+
@db[:test3].get(:time).should == 1.0/0.0
|
|
512
|
+
if ((Time.parse('infinity'); nil) rescue true)
|
|
513
|
+
# Skip for loose time parsing (e.g. old rbx)
|
|
514
|
+
@db.convert_infinite_timestamps = 'f'
|
|
515
|
+
proc{@db[:test3].get(:time)}.should raise_error
|
|
516
|
+
@db.convert_infinite_timestamps = nil
|
|
517
|
+
proc{@db[:test3].get(:time)}.should raise_error
|
|
518
|
+
@db.convert_infinite_timestamps = false
|
|
519
|
+
proc{@db[:test3].get(:time)}.should raise_error
|
|
520
|
+
end
|
|
485
521
|
|
|
486
522
|
@d.update(:time=>Sequel.cast('-infinity', DateTime))
|
|
487
523
|
@db.convert_infinite_timestamps = :nil
|
|
@@ -1217,7 +1253,7 @@ describe "Postgres::Database functions, languages, schemas, and triggers" do
|
|
|
1217
1253
|
args = ['tf', 'SELECT 1', {:returns=>:integer}]
|
|
1218
1254
|
@d.send(:create_function_sql, *args).should =~ /\A\s*CREATE FUNCTION tf\(\)\s+RETURNS integer\s+LANGUAGE SQL\s+AS 'SELECT 1'\s*\z/
|
|
1219
1255
|
@d.create_function(*args)
|
|
1220
|
-
|
|
1256
|
+
@d['SELECT tf()'].all.should == [{:tf=>1}]
|
|
1221
1257
|
@d.send(:drop_function_sql, 'tf').should == 'DROP FUNCTION tf()'
|
|
1222
1258
|
@d.drop_function('tf')
|
|
1223
1259
|
proc{@d['SELECT tf()'].all}.should raise_error(Sequel::DatabaseError)
|
|
@@ -1229,7 +1265,7 @@ describe "Postgres::Database functions, languages, schemas, and triggers" do
|
|
|
1229
1265
|
@d.create_function(*args)
|
|
1230
1266
|
# Make sure replace works
|
|
1231
1267
|
@d.create_function(*args)
|
|
1232
|
-
|
|
1268
|
+
@d['SELECT tf(1, 2)'].all.should == [{:tf=>3}]
|
|
1233
1269
|
args = ['tf', {:if_exists=>true, :cascade=>true, :args=>[[:integer, :a], :integer]}]
|
|
1234
1270
|
@d.send(:drop_function_sql,*args).should == 'DROP FUNCTION IF EXISTS tf(a integer, integer) CASCADE'
|
|
1235
1271
|
@d.drop_function(*args)
|
|
@@ -1323,6 +1359,7 @@ if POSTGRES_DB.adapter_scheme == :postgres
|
|
|
1323
1359
|
before do
|
|
1324
1360
|
@db = POSTGRES_DB
|
|
1325
1361
|
Sequel::Postgres::PG_NAMED_TYPES[:interval] = lambda{|v| v.reverse}
|
|
1362
|
+
@db.extension :pg_array
|
|
1326
1363
|
@db.reset_conversion_procs
|
|
1327
1364
|
end
|
|
1328
1365
|
after do
|
|
@@ -1336,6 +1373,12 @@ if POSTGRES_DB.adapter_scheme == :postgres
|
|
|
1336
1373
|
@db[:foo].insert(Sequel.cast('21 days', :interval))
|
|
1337
1374
|
@db[:foo].get(:bar).should == 'syad 12'
|
|
1338
1375
|
end
|
|
1376
|
+
|
|
1377
|
+
specify "should handle array types of named types" do
|
|
1378
|
+
@db.create_table!(:foo){column :bar, 'interval[]'}
|
|
1379
|
+
@db[:foo].insert(Sequel.pg_array(['21 days'], :interval))
|
|
1380
|
+
@db[:foo].get(:bar).should == ['syad 12']
|
|
1381
|
+
end
|
|
1339
1382
|
end
|
|
1340
1383
|
end
|
|
1341
1384
|
|
|
@@ -1792,6 +1835,42 @@ describe 'PostgreSQL array handling' do
|
|
|
1792
1835
|
end
|
|
1793
1836
|
end
|
|
1794
1837
|
|
|
1838
|
+
specify 'insert and retrieve custom array types' do
|
|
1839
|
+
int2vector = Class.new do
|
|
1840
|
+
attr_reader :array
|
|
1841
|
+
def initialize(array)
|
|
1842
|
+
@array = array
|
|
1843
|
+
end
|
|
1844
|
+
def sql_literal_append(ds, sql)
|
|
1845
|
+
sql << "'#{array.join(' ')}'"
|
|
1846
|
+
end
|
|
1847
|
+
def ==(other)
|
|
1848
|
+
if other.is_a?(self.class)
|
|
1849
|
+
array == other.array
|
|
1850
|
+
else
|
|
1851
|
+
super
|
|
1852
|
+
end
|
|
1853
|
+
end
|
|
1854
|
+
end
|
|
1855
|
+
@db.register_array_type(:int2vector){|s| int2vector.new(s.split.map{|i| i.to_i})}
|
|
1856
|
+
@db.create_table!(:items) do
|
|
1857
|
+
column :b, 'int2vector[]'
|
|
1858
|
+
end
|
|
1859
|
+
@tp.call.should == [:int2vector_array]
|
|
1860
|
+
int2v = int2vector.new([1, 2])
|
|
1861
|
+
@ds.insert(Sequel.pg_array([int2v], :int2vector))
|
|
1862
|
+
@ds.count.should == 1
|
|
1863
|
+
rs = @ds.all
|
|
1864
|
+
if @native
|
|
1865
|
+
rs.should == [{:b=>[int2v]}]
|
|
1866
|
+
rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
|
|
1867
|
+
rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
|
|
1868
|
+
@ds.delete
|
|
1869
|
+
@ds.insert(rs.first)
|
|
1870
|
+
@ds.all.should == rs
|
|
1871
|
+
end
|
|
1872
|
+
end unless POSTGRES_DB.adapter_scheme == :jdbc
|
|
1873
|
+
|
|
1795
1874
|
specify 'use arrays in bound variables' do
|
|
1796
1875
|
@db.create_table!(:items) do
|
|
1797
1876
|
column :i, 'int4[]'
|
|
@@ -1929,7 +2008,7 @@ end
|
|
|
1929
2008
|
describe 'PostgreSQL hstore handling' do
|
|
1930
2009
|
before(:all) do
|
|
1931
2010
|
@db = POSTGRES_DB
|
|
1932
|
-
@db.extension :pg_hstore
|
|
2011
|
+
@db.extension :pg_array, :pg_hstore
|
|
1933
2012
|
@ds = @db[:items]
|
|
1934
2013
|
@h = {'a'=>'b', 'c'=>nil, 'd'=>'NULL', 'e'=>'\\\\" \\\' ,=>'}
|
|
1935
2014
|
@native = POSTGRES_DB.adapter_scheme == :postgres
|
|
@@ -1956,6 +2035,24 @@ describe 'PostgreSQL hstore handling' do
|
|
|
1956
2035
|
end
|
|
1957
2036
|
end
|
|
1958
2037
|
|
|
2038
|
+
specify 'insert and retrieve hstore[] values' do
|
|
2039
|
+
@db.create_table!(:items) do
|
|
2040
|
+
column :h, 'hstore[]'
|
|
2041
|
+
end
|
|
2042
|
+
@ds.insert(Sequel.pg_array([Sequel.hstore(@h)], :hstore))
|
|
2043
|
+
@ds.count.should == 1
|
|
2044
|
+
if @native
|
|
2045
|
+
rs = @ds.all
|
|
2046
|
+
v = rs.first[:h].first
|
|
2047
|
+
v.should_not be_a_kind_of(Hash)
|
|
2048
|
+
v.to_hash.should be_a_kind_of(Hash)
|
|
2049
|
+
v.to_hash.should == @h
|
|
2050
|
+
@ds.delete
|
|
2051
|
+
@ds.insert(rs.first)
|
|
2052
|
+
@ds.all.should == rs
|
|
2053
|
+
end
|
|
2054
|
+
end
|
|
2055
|
+
|
|
1959
2056
|
specify 'use hstore in bound variables' do
|
|
1960
2057
|
@db.create_table!(:items) do
|
|
1961
2058
|
column :i, :hstore
|
|
@@ -2136,6 +2233,7 @@ describe 'PostgreSQL hstore handling' do
|
|
|
2136
2233
|
@ds.from(:items___i).select(Sequel.hstore('t'=>'s').op.record_set(:i).as(:r)).from_self(:alias=>:s).select(Sequel.lit('(r).*')).from_self.select_map(:t).should == ['s']
|
|
2137
2234
|
|
|
2138
2235
|
@ds.from(Sequel.hstore('t'=>'s', 'a'=>'b').op.skeys.as(:s)).select_order_map(:s).should == %w'a t'
|
|
2236
|
+
@ds.from((Sequel.hstore('t'=>'s', 'a'=>'b').op - 'a').skeys.as(:s)).select_order_map(:s).should == %w't'
|
|
2139
2237
|
|
|
2140
2238
|
@ds.get(h1.slice(Sequel.pg_array(%w'a c')).keys.pg_array.length).should == 2
|
|
2141
2239
|
@ds.get(h1.slice(Sequel.pg_array(%w'd c')).keys.pg_array.length).should == 1
|
|
@@ -2300,7 +2398,7 @@ describe 'PostgreSQL inet/cidr types' do
|
|
|
2300
2398
|
@ds.count.should == 1
|
|
2301
2399
|
if @native
|
|
2302
2400
|
rs = @ds.all
|
|
2303
|
-
|
|
2401
|
+
rs.first[:j]
|
|
2304
2402
|
rs.first[:i].should == @ipv6
|
|
2305
2403
|
rs.first[:c].should == @ipv6nm
|
|
2306
2404
|
rs.first[:i].should be_a_kind_of(IPAddr)
|
|
@@ -2472,7 +2570,7 @@ describe 'PostgreSQL range types' do
|
|
|
2472
2570
|
c.plugin :pg_typecast_on_load, :i4, :i8, :n, :d, :t, :tz unless @native
|
|
2473
2571
|
v = c.create(@ra).values
|
|
2474
2572
|
v.delete(:id)
|
|
2475
|
-
v.each{|k,
|
|
2573
|
+
v.each{|k,v1| v1.should == @ra[k].to_a}
|
|
2476
2574
|
end
|
|
2477
2575
|
end
|
|
2478
2576
|
|
|
@@ -2500,15 +2598,19 @@ describe 'PostgreSQL range types' do
|
|
|
2500
2598
|
@db.get(Sequel.pg_range(1..5, :int4range).op.right_of(-1..0)).should be_true
|
|
2501
2599
|
@db.get(Sequel.pg_range(1..5, :int4range).op.right_of(-1..3)).should be_false
|
|
2502
2600
|
|
|
2503
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
|
2504
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
|
2505
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
|
2506
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
|
2601
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(6..10)).should be_true
|
|
2602
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(5..10)).should be_true
|
|
2603
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(-1..0)).should be_false
|
|
2604
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(-1..3)).should be_false
|
|
2605
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.ends_before(-1..7)).should be_true
|
|
2507
2606
|
|
|
2508
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
|
2509
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
|
2510
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
|
2511
|
-
@db.get(Sequel.pg_range(1..5, :int4range).op.
|
|
2607
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(6..10)).should be_false
|
|
2608
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(5..10)).should be_false
|
|
2609
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(3..10)).should be_false
|
|
2610
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-1..10)).should be_true
|
|
2611
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-1..0)).should be_true
|
|
2612
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-1..3)).should be_true
|
|
2613
|
+
@db.get(Sequel.pg_range(1..5, :int4range).op.starts_after(-5..-1)).should be_true
|
|
2512
2614
|
|
|
2513
2615
|
@db.get(Sequel.pg_range(1..5, :int4range).op.adjacent_to(6..10)).should be_true
|
|
2514
2616
|
@db.get(Sequel.pg_range(1...5, :int4range).op.adjacent_to(6..10)).should be_false
|
|
@@ -2637,7 +2739,7 @@ describe 'PostgreSQL interval types' do
|
|
|
2637
2739
|
v = c.create(:i=>'1 year 2 mons 25 days 05:06:07').i
|
|
2638
2740
|
v.is_a?(ActiveSupport::Duration).should be_true
|
|
2639
2741
|
v.should == ActiveSupport::Duration.new(31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]])
|
|
2640
|
-
v.parts.sort_by{|k,
|
|
2742
|
+
v.parts.sort_by{|k,_| k.to_s}.should == [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]].sort_by{|k,_| k.to_s}
|
|
2641
2743
|
end
|
|
2642
2744
|
end if (begin require 'active_support/duration'; require 'active_support/inflector'; require 'active_support/core_ext/string/inflections'; true; rescue LoadError; false end)
|
|
2643
2745
|
|
|
@@ -2697,6 +2799,21 @@ describe 'PostgreSQL row-valued/composite types' do
|
|
|
2697
2799
|
end
|
|
2698
2800
|
end
|
|
2699
2801
|
|
|
2802
|
+
specify 'insert and retrieve row types containing domains' do
|
|
2803
|
+
begin
|
|
2804
|
+
@db << "DROP DOMAIN IF EXISTS positive_integer CASCADE"
|
|
2805
|
+
@db << "CREATE DOMAIN positive_integer AS integer CHECK (VALUE > 0)"
|
|
2806
|
+
@db.create_table!(:domain_check) do
|
|
2807
|
+
positive_integer :id
|
|
2808
|
+
end
|
|
2809
|
+
@db.register_row_type(:domain_check)
|
|
2810
|
+
@db.get(@db.row_type(:domain_check, [1])).should == {:id=>1}
|
|
2811
|
+
ensure
|
|
2812
|
+
@db.drop_table(:domain_check)
|
|
2813
|
+
@db << "DROP DOMAIN positive_integer"
|
|
2814
|
+
end
|
|
2815
|
+
end if POSTGRES_DB.adapter_scheme == :postgres
|
|
2816
|
+
|
|
2700
2817
|
specify 'insert and retrieve arrays of row types' do
|
|
2701
2818
|
@ds = @db[:company]
|
|
2702
2819
|
@ds.insert(:id=>1, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])]))
|
|
@@ -2734,7 +2851,6 @@ describe 'PostgreSQL row-valued/composite types' do
|
|
|
2734
2851
|
@ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12345'])])]))[:id].should == 1
|
|
2735
2852
|
@ds.filter(:employees=>Sequel.cast(:$employees, 'person[]')).call(:first, :employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row(['123 Sesame St', 'Somewhere', '12356'])])])).should == nil
|
|
2736
2853
|
|
|
2737
|
-
|
|
2738
2854
|
@ds.delete
|
|
2739
2855
|
@ds.call(:insert, {:employees=>Sequel.pg_array([@db.row_type(:person, [1, Sequel.pg_row([nil, nil, nil])])])}, {:employees=>:$employees, :id=>1})
|
|
2740
2856
|
@ds.get(:employees).should == [{:address=>{:city=>nil, :zip=>nil, :street=>nil}, :id=>1}]
|