dm-core 1.1.0 → 1.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +13 -11
- data/README.rdoc +1 -1
- data/Rakefile +1 -2
- data/VERSION +1 -1
- data/dm-core.gemspec +30 -176
- data/lib/dm-core.rb +32 -67
- data/lib/dm-core/adapters/abstract_adapter.rb +1 -2
- data/lib/dm-core/associations/many_to_many.rb +11 -5
- data/lib/dm-core/associations/many_to_one.rb +17 -2
- data/lib/dm-core/associations/one_to_many.rb +16 -0
- data/lib/dm-core/backwards.rb +13 -0
- data/lib/dm-core/collection.rb +1 -1
- data/lib/dm-core/model.rb +99 -41
- data/lib/dm-core/model/property.rb +24 -27
- data/lib/dm-core/model/relationship.rb +22 -28
- data/lib/dm-core/property.rb +37 -50
- data/lib/dm-core/property/boolean.rb +6 -10
- data/lib/dm-core/property/date.rb +0 -2
- data/lib/dm-core/property/date_time.rb +0 -2
- data/lib/dm-core/property/decimal.rb +5 -1
- data/lib/dm-core/property/discriminator.rb +24 -26
- data/lib/dm-core/property/float.rb +5 -1
- data/lib/dm-core/property/numeric.rb +6 -9
- data/lib/dm-core/property/string.rb +2 -1
- data/lib/dm-core/property/time.rb +0 -2
- data/lib/dm-core/property/typecast/time.rb +7 -2
- data/lib/dm-core/property_set.rb +1 -3
- data/lib/dm-core/query.rb +3 -10
- data/lib/dm-core/query/conditions/comparison.rb +5 -1
- data/lib/dm-core/query/conditions/operation.rb +1 -1
- data/lib/dm-core/relationship_set.rb +0 -2
- data/lib/dm-core/resource.rb +27 -28
- data/lib/dm-core/resource/{state.rb → persistence_state.rb} +2 -2
- data/lib/dm-core/resource/{state → persistence_state}/clean.rb +4 -4
- data/lib/dm-core/resource/{state → persistence_state}/deleted.rb +2 -2
- data/lib/dm-core/resource/{state → persistence_state}/dirty.rb +2 -2
- data/lib/dm-core/resource/{state → persistence_state}/immutable.rb +3 -3
- data/lib/dm-core/resource/{state → persistence_state}/persisted.rb +3 -3
- data/lib/dm-core/resource/{state → persistence_state}/transient.rb +3 -3
- data/lib/dm-core/spec/lib/adapter_helpers.rb +2 -5
- data/lib/dm-core/spec/setup.rb +3 -2
- data/lib/dm-core/spec/shared/public/property_spec.rb +8 -0
- data/lib/dm-core/spec/shared/resource_spec.rb +14 -0
- data/lib/dm-core/spec/shared/semipublic/property_spec.rb +1 -1
- data/lib/dm-core/support/descendant_set.rb +0 -2
- data/lib/dm-core/support/ext/array.rb +0 -19
- data/lib/dm-core/support/ext/blank.rb +1 -0
- data/lib/dm-core/support/hook.rb +0 -3
- data/lib/dm-core/support/naming_conventions.rb +6 -0
- data/lib/dm-core/support/ordered_set.rb +0 -2
- data/lib/dm-core/support/subject_set.rb +0 -2
- data/lib/dm-core/version.rb +1 -1
- data/spec/public/associations/many_to_many_spec.rb +0 -1
- data/spec/public/model/property_spec.rb +55 -9
- data/spec/public/model/relationship_spec.rb +24 -2
- data/spec/public/model_spec.rb +32 -0
- data/spec/public/property/binary_spec.rb +14 -6
- data/spec/public/property/boolean_spec.rb +14 -6
- data/spec/public/property/class_spec.rb +14 -6
- data/spec/public/property/date_spec.rb +14 -6
- data/spec/public/property/date_time_spec.rb +14 -6
- data/spec/public/property/decimal_spec.rb +10 -2
- data/spec/public/property/discriminator_spec.rb +15 -1
- data/spec/public/property/float_spec.rb +14 -6
- data/spec/public/property/integer_spec.rb +14 -6
- data/spec/public/property/object_spec.rb +8 -0
- data/spec/public/property/serial_spec.rb +14 -6
- data/spec/public/property/string_spec.rb +14 -6
- data/spec/public/property/text_spec.rb +14 -6
- data/spec/public/property/time_spec.rb +14 -6
- data/spec/public/resource_spec.rb +58 -0
- data/spec/public/shared/finder_shared_spec.rb +8 -4
- data/spec/semipublic/associations/many_to_many_spec.rb +2 -0
- data/spec/semipublic/associations/many_to_one_spec.rb +2 -0
- data/spec/semipublic/associations/one_to_many_spec.rb +2 -0
- data/spec/semipublic/associations/one_to_one_spec.rb +2 -0
- data/spec/semipublic/property/binary_spec.rb +5 -5
- data/spec/semipublic/property/boolean_spec.rb +5 -5
- data/spec/semipublic/property/class_spec.rb +5 -5
- data/spec/semipublic/property/date_spec.rb +5 -5
- data/spec/semipublic/property/date_time_spec.rb +5 -5
- data/spec/semipublic/property/decimal_spec.rb +2 -2
- data/spec/semipublic/property/discriminator_spec.rb +5 -5
- data/spec/semipublic/property/float_spec.rb +5 -5
- data/spec/semipublic/property/integer_spec.rb +5 -5
- data/spec/semipublic/property/lookup_spec.rb +3 -3
- data/spec/semipublic/property/serial_spec.rb +5 -5
- data/spec/semipublic/property/string_spec.rb +5 -5
- data/spec/semipublic/property/text_spec.rb +5 -5
- data/spec/semipublic/property/time_spec.rb +5 -5
- data/spec/semipublic/query/conditions/comparison_spec.rb +44 -4
- data/spec/semipublic/query_spec.rb +2 -11
- data/spec/semipublic/resource/state/clean_spec.rb +6 -6
- data/spec/semipublic/resource/state/deleted_spec.rb +4 -4
- data/spec/semipublic/resource/state/dirty_spec.rb +8 -8
- data/spec/semipublic/resource/state/immutable_spec.rb +6 -6
- data/spec/semipublic/resource/state/transient_spec.rb +5 -5
- data/spec/semipublic/resource/state_spec.rb +15 -15
- data/spec/semipublic/shared/resource_shared_spec.rb +7 -1
- data/spec/semipublic/shared/resource_state_shared_spec.rb +8 -8
- data/spec/unit/array_spec.rb +0 -14
- data/spec/unit/blank_spec.rb +11 -0
- metadata +70 -188
- data/lib/dm-core/support/inflector.rb +0 -3
@@ -1,15 +1,12 @@
|
|
1
1
|
# TODO: update Model#respond_to? to return true if method_method missing
|
2
2
|
# would handle the message
|
3
3
|
|
4
|
-
require 'dm-core/relationship_set'
|
5
|
-
|
6
4
|
module DataMapper
|
7
5
|
module Model
|
8
6
|
module Relationship
|
9
7
|
Model.append_extensions self
|
10
8
|
|
11
9
|
include DataMapper::Assertions
|
12
|
-
extend Chainable
|
13
10
|
|
14
11
|
# Initializes relationships hash for extended model
|
15
12
|
# class.
|
@@ -23,21 +20,19 @@ module DataMapper
|
|
23
20
|
model.instance_variable_set(:@relationships, {})
|
24
21
|
end
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
model.instance_variable_set(:@relationships, {})
|
33
|
-
|
34
|
-
@relationships.each do |repository_name, relationships|
|
35
|
-
model_relationships = model.relationships(repository_name)
|
36
|
-
relationships.each { |relationship| model_relationships << relationship }
|
37
|
-
end
|
23
|
+
# When DataMapper model is inherited, relationships
|
24
|
+
# of parent are duplicated and copied to subclass model
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
def inherited(model)
|
28
|
+
model.instance_variable_set(:@relationships, {})
|
38
29
|
|
39
|
-
|
30
|
+
@relationships.each do |repository_name, relationships|
|
31
|
+
model_relationships = model.relationships(repository_name)
|
32
|
+
relationships.each { |relationship| model_relationships << relationship }
|
40
33
|
end
|
34
|
+
|
35
|
+
super
|
41
36
|
end
|
42
37
|
|
43
38
|
# Returns copy of relationships set in given repository.
|
@@ -194,7 +189,7 @@ module DataMapper
|
|
194
189
|
relationship
|
195
190
|
end
|
196
191
|
|
197
|
-
|
192
|
+
private
|
198
193
|
|
199
194
|
# Extract the model from an Array of arguments
|
200
195
|
#
|
@@ -342,7 +337,7 @@ module DataMapper
|
|
342
337
|
# of 1:1, the underlying collection is hidden in a
|
343
338
|
# private ivar, and the resource is in a known ivar
|
344
339
|
|
345
|
-
|
340
|
+
persistence_state.get(relationships[#{name.inspect}], query)
|
346
341
|
end
|
347
342
|
RUBY
|
348
343
|
end
|
@@ -362,22 +357,21 @@ module DataMapper
|
|
362
357
|
#{writer_visibility}
|
363
358
|
def #{writer_name}(target)
|
364
359
|
relationship = relationships[#{name.inspect}]
|
365
|
-
self.
|
366
|
-
|
360
|
+
self.persistence_state = persistence_state.set(relationship, target)
|
361
|
+
persistence_state.get(relationship)
|
367
362
|
end
|
368
363
|
RUBY
|
369
364
|
end
|
370
365
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
return Query::Path.new([ relationship ])
|
376
|
-
end
|
377
|
-
|
378
|
-
super
|
366
|
+
# @api public
|
367
|
+
def method_missing(method, *args, &block)
|
368
|
+
if relationship = relationships(repository_name)[method]
|
369
|
+
return Query::Path.new([ relationship ])
|
379
370
|
end
|
371
|
+
|
372
|
+
super
|
380
373
|
end
|
374
|
+
|
381
375
|
end # module Relationship
|
382
376
|
end # module Model
|
383
377
|
end # module DataMapper
|
data/lib/dm-core/property.rb
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'dm-core/resource'
|
2
|
-
require 'dm-core/query'
|
3
|
-
|
4
1
|
module DataMapper
|
5
2
|
# = Properties
|
6
3
|
# Properties for a model are not derived from a database structure, but
|
@@ -83,11 +80,13 @@ module DataMapper
|
|
83
80
|
# property :title, String
|
84
81
|
#
|
85
82
|
# def title=(new_title)
|
86
|
-
# raise ArgumentError if new_title != '
|
87
|
-
#
|
83
|
+
# raise ArgumentError if new_title != 'Lee is l337'
|
84
|
+
# super(new_title)
|
88
85
|
# end
|
89
86
|
# end
|
90
87
|
#
|
88
|
+
# Calling super ensures that any validators defined for the property are kept active.
|
89
|
+
#
|
91
90
|
# == Lazy Loading
|
92
91
|
# By default, some properties are not loaded when an object is fetched in
|
93
92
|
# DataMapper. These lazily loaded properties are fetched on demand when their
|
@@ -107,7 +106,7 @@ module DataMapper
|
|
107
106
|
#
|
108
107
|
# If you want to over-ride the lazy loading on any field you can set it to a
|
109
108
|
# context or false to disable it with the :lazy option. Contexts allow
|
110
|
-
#
|
109
|
+
# multiple lazy properties to be loaded at one time. If you set :lazy to
|
111
110
|
# true, it is placed in the :default context
|
112
111
|
#
|
113
112
|
# class Post
|
@@ -179,44 +178,40 @@ module DataMapper
|
|
179
178
|
#
|
180
179
|
# == Inferred Validations
|
181
180
|
# If you require the dm-validations plugin, auto-validations will
|
182
|
-
# automatically be mixed-in in to your model classes:
|
183
|
-
#
|
184
|
-
# specific column restrictions.
|
181
|
+
# automatically be mixed-in in to your model classes: validation rules that
|
182
|
+
# are inferred when properties are declared with specific column restrictions.
|
185
183
|
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
189
|
-
# property :title, String, :length => 250
|
190
|
-
# # => infers 'validates_length :title,
|
191
|
-
# :minimum => 0, :maximum => 250'
|
184
|
+
# class Post
|
185
|
+
# include DataMapper::Resource
|
192
186
|
#
|
193
|
-
#
|
194
|
-
#
|
187
|
+
# property :title, String, :length => 250, :min => 0, :max => 250
|
188
|
+
# # => infers 'validates_length :title'
|
195
189
|
#
|
196
|
-
#
|
197
|
-
#
|
190
|
+
# property :title, String, :required => true
|
191
|
+
# # => infers 'validates_present :title'
|
198
192
|
#
|
199
|
-
#
|
200
|
-
#
|
201
|
-
# # 'validates_present'
|
202
|
-
# # better: property :title, String, :length => 1..255
|
193
|
+
# property :email, String, :format => :email_address
|
194
|
+
# # => infers 'validates_format :email, :with => :email_address'
|
203
195
|
#
|
204
|
-
#
|
196
|
+
# property :title, String, :length => 255, :required => true
|
197
|
+
# # => infers both 'validates_length' as well as 'validates_present'
|
198
|
+
# # better: property :title, String, :length => 1..255
|
199
|
+
# end
|
205
200
|
#
|
206
201
|
# This functionality is available with the dm-validations gem. For more information
|
207
202
|
# about validations, check the documentation for dm-validations.
|
208
203
|
#
|
209
204
|
# == Default Values
|
210
|
-
# To set a default for a property, use the <tt>:default</tt> key.
|
205
|
+
# To set a default for a property, use the <tt>:default</tt> key. The
|
211
206
|
# property will be set to the value associated with that key the first time
|
212
207
|
# it is accessed, or when the resource is saved if it hasn't been set with
|
213
|
-
# another value already.
|
208
|
+
# another value already. This value can be a static value, such as 'hello'
|
214
209
|
# but it can also be a proc that will be evaluated when the property is read
|
215
|
-
# before its value has been set.
|
216
|
-
# proc.
|
210
|
+
# before its value has been set. The property is set to the return of the
|
211
|
+
# proc. The proc is passed two values, the resource the property is being set
|
217
212
|
# for and the property itself.
|
218
213
|
#
|
219
|
-
# property :display_name, String, :default => { |resource, property| resource.login }
|
214
|
+
# property :display_name, String, :default => lambda { |resource, property| resource.login }
|
220
215
|
#
|
221
216
|
# Word of warning. Don't try to read the value of the property you're setting
|
222
217
|
# the default for in the proc. An infinite loop will ensue.
|
@@ -270,9 +265,6 @@ module DataMapper
|
|
270
265
|
# Only makes sense for float type properties. Must be > 0.
|
271
266
|
# Default is nil for Float type and 10 for BigDecimal
|
272
267
|
#
|
273
|
-
# All other keys you pass to +property+ method are stored and available
|
274
|
-
# as options[:extra_keys].
|
275
|
-
#
|
276
268
|
# == Overriding default Property options
|
277
269
|
#
|
278
270
|
# There is the ability to reconfigure a Property and it's subclasses by explicitly
|
@@ -291,7 +283,7 @@ module DataMapper
|
|
291
283
|
# DataMapper::Property.auto_validation(false)
|
292
284
|
#
|
293
285
|
# # set all mutator methods to be private by default
|
294
|
-
# DataMapper::Property.writer(
|
286
|
+
# DataMapper::Property.writer(:private)
|
295
287
|
#
|
296
288
|
# Please note that this has no effect when a subclass has explicitly
|
297
289
|
# defined it's own option. For example, setting the String length to
|
@@ -311,8 +303,7 @@ module DataMapper
|
|
311
303
|
module PassThroughLoadDump
|
312
304
|
# @api semipublic
|
313
305
|
def load(value)
|
314
|
-
|
315
|
-
typecast(value)
|
306
|
+
typecast(value) unless value.nil?
|
316
307
|
end
|
317
308
|
|
318
309
|
# Stub instance method for dumping
|
@@ -329,10 +320,9 @@ module DataMapper
|
|
329
320
|
|
330
321
|
include DataMapper::Assertions
|
331
322
|
include Subject
|
332
|
-
extend Chainable
|
333
323
|
extend Equalizer
|
334
324
|
|
335
|
-
equalize :model, :name
|
325
|
+
equalize :model, :name, :options
|
336
326
|
|
337
327
|
PRIMITIVES = [
|
338
328
|
TrueClass,
|
@@ -357,7 +347,10 @@ module DataMapper
|
|
357
347
|
VISIBILITY_OPTIONS = [ :public, :protected, :private ].to_set.freeze
|
358
348
|
|
359
349
|
# Invalid property names
|
360
|
-
INVALID_NAMES = (Resource.instance_methods +
|
350
|
+
INVALID_NAMES = (Resource.instance_methods +
|
351
|
+
Resource.private_instance_methods +
|
352
|
+
Query::OPTIONS.to_a
|
353
|
+
).map { |name| name.to_s }
|
361
354
|
|
362
355
|
attr_reader :primitive, :model, :name, :instance_variable_name,
|
363
356
|
:reader_visibility, :writer_visibility, :options,
|
@@ -462,9 +455,8 @@ module DataMapper
|
|
462
455
|
# @api public
|
463
456
|
def options
|
464
457
|
options = {}
|
465
|
-
accepted_options.each do |
|
466
|
-
|
467
|
-
options[method] = value unless value.nil?
|
458
|
+
accepted_options.each do |name|
|
459
|
+
options[name] = send(name) if instance_variable_defined?("@#{name}")
|
468
460
|
end
|
469
461
|
options
|
470
462
|
end
|
@@ -737,20 +729,15 @@ module DataMapper
|
|
737
729
|
value.kind_of?(primitive)
|
738
730
|
end
|
739
731
|
|
740
|
-
|
741
|
-
def self.new(model, name, options = {})
|
742
|
-
super
|
743
|
-
end
|
744
|
-
end
|
745
|
-
|
746
|
-
protected
|
732
|
+
protected
|
747
733
|
|
748
734
|
# @api semipublic
|
749
735
|
def initialize(model, name, options = {})
|
750
736
|
options = options.to_hash.dup
|
751
737
|
|
752
|
-
if INVALID_NAMES.include?(name.to_s)
|
753
|
-
raise ArgumentError,
|
738
|
+
if INVALID_NAMES.include?(name.to_s) || (kind_of?(Boolean) && INVALID_NAMES.include?("#{name}?"))
|
739
|
+
raise ArgumentError,
|
740
|
+
"+name+ was #{name.inspect}, which cannot be used as a property name since it collides with an existing method or a query option"
|
754
741
|
end
|
755
742
|
|
756
743
|
assert_valid_options(options)
|
@@ -5,6 +5,11 @@ module DataMapper
|
|
5
5
|
|
6
6
|
primitive ::TrueClass
|
7
7
|
|
8
|
+
TRUE_VALUES = [ 1, '1', 't', 'T', 'true', 'TRUE' ].freeze
|
9
|
+
FALSE_VALUES = [ 0, '0', 'f', 'F', 'false', 'FALSE' ].freeze
|
10
|
+
BOOLEAN_MAP = Hash[
|
11
|
+
TRUE_VALUES.product([ true ]) + FALSE_VALUES.product([ false ]) ].freeze
|
12
|
+
|
8
13
|
def primitive?(value)
|
9
14
|
value == true || value == false
|
10
15
|
end
|
@@ -19,16 +24,7 @@ module DataMapper
|
|
19
24
|
#
|
20
25
|
# @api private
|
21
26
|
def typecast_to_primitive(value)
|
22
|
-
|
23
|
-
return true if value == 1
|
24
|
-
return false if value == 0
|
25
|
-
elsif value.respond_to?(:to_str)
|
26
|
-
string_value = value.to_str.downcase
|
27
|
-
return true if %w[ true 1 t ].include?(string_value)
|
28
|
-
return false if %w[ false 0 f ].include?(string_value)
|
29
|
-
end
|
30
|
-
|
31
|
-
value
|
27
|
+
BOOLEAN_MAP.fetch(value, value)
|
32
28
|
end
|
33
29
|
end # class Boolean
|
34
30
|
end # class Property
|
@@ -8,40 +8,38 @@ module DataMapper
|
|
8
8
|
|
9
9
|
# @api private
|
10
10
|
def bind
|
11
|
-
model.
|
12
|
-
|
13
|
-
model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
14
|
-
extend Chainable
|
11
|
+
model.extend Model unless model < Model
|
12
|
+
end
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
module Model
|
15
|
+
def inherited(model)
|
16
|
+
super # setup self.descendants
|
17
|
+
set_discriminator_scope_for(model)
|
18
|
+
end
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
def new(*args, &block)
|
21
|
+
if args.size == 1 && args.first.kind_of?(Hash)
|
22
|
+
discriminator = properties(repository_name).discriminator
|
25
23
|
|
26
|
-
|
27
|
-
|
24
|
+
if discriminator_value = args.first[discriminator.name]
|
25
|
+
model = discriminator.typecast_to_primitive(discriminator_value)
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
end
|
27
|
+
if model.kind_of?(Model) && !model.equal?(self)
|
28
|
+
return model.new(*args, &block)
|
33
29
|
end
|
34
|
-
|
35
|
-
super
|
36
30
|
end
|
31
|
+
end
|
37
32
|
|
38
|
-
|
33
|
+
super
|
34
|
+
end
|
39
35
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
private
|
37
|
+
|
38
|
+
def set_discriminator_scope_for(model)
|
39
|
+
discriminator = self.properties.discriminator
|
40
|
+
default_scope = model.default_scope(discriminator.repository_name)
|
41
|
+
default_scope.update(discriminator.name => model.descendants.dup << model)
|
42
|
+
end
|
45
43
|
end
|
46
44
|
end # class Discriminator
|
47
45
|
end # module Property
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'dm-core/property/typecast/numeric'
|
2
|
-
|
3
1
|
module DataMapper
|
4
2
|
class Property
|
5
3
|
class Numeric < Object
|
@@ -9,9 +7,8 @@ module DataMapper
|
|
9
7
|
accept_options :precision, :scale, :min, :max
|
10
8
|
attr_reader :precision, :scale, :min, :max
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
DEFAULT_NUMERIC_MAX = 2**31-1
|
10
|
+
DEFAULT_NUMERIC_MIN = 0
|
11
|
+
DEFAULT_NUMERIC_MAX = 2**31-1
|
15
12
|
|
16
13
|
protected
|
17
14
|
|
@@ -19,8 +16,8 @@ module DataMapper
|
|
19
16
|
super
|
20
17
|
|
21
18
|
if @primitive == BigDecimal || @primitive == ::Float
|
22
|
-
@precision = @options.fetch(:precision
|
23
|
-
@scale = @options.fetch(:scale
|
19
|
+
@precision = @options.fetch(:precision)
|
20
|
+
@scale = @options.fetch(:scale)
|
24
21
|
|
25
22
|
unless @precision > 0
|
26
23
|
raise ArgumentError, "precision must be greater than 0, but was #{@precision.inspect}"
|
@@ -28,8 +25,8 @@ module DataMapper
|
|
28
25
|
end
|
29
26
|
|
30
27
|
if @options.key?(:min) || @options.key?(:max)
|
31
|
-
@min = @options.fetch(:min, DEFAULT_NUMERIC_MIN)
|
32
|
-
@max = @options.fetch(:max, DEFAULT_NUMERIC_MAX)
|
28
|
+
@min = @options.fetch(:min, self.class::DEFAULT_NUMERIC_MIN)
|
29
|
+
@max = @options.fetch(:max, self.class::DEFAULT_NUMERIC_MAX)
|
33
30
|
|
34
31
|
if @max < DEFAULT_NUMERIC_MIN && !@options.key?(:min)
|
35
32
|
raise ArgumentError, "min should be specified when the max is less than #{DEFAULT_NUMERIC_MIN}"
|