Skip to content

Commit ae79a78

Browse files
committed
Proof of concept on "strict" field option
2 parents f2bdc18 + cd933f8 commit ae79a78

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+757
-163
lines changed

docs/reference/fields.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,34 @@ Mongoid also defines the ``id`` field aliased to ``_id``. The ``id``
827827
alias can :ref:`be removed <unalias-id>` if desired (such as to integrate
828828
with systems that use the ``id`` field to store value different from ``_id``.
829829

830+
.. _uncastable-values:
831+
832+
Assigning Uncastable Values
833+
---------------------------
834+
835+
In Mongoid 8, Mongoid has standardized the treatment of the assignment of
836+
"uncastable" values. A value is considered "uncastable" when it cannot be
837+
coerced to the type of the field. For example:
838+
839+
.. code::
840+
841+
class User
842+
include Mongoid::Document
843+
844+
field :name, type: Integer
845+
end
846+
847+
User.new(name: [ "hello" ])
848+
849+
Assigning an array to a field of type Integer doesn't work since an array can't
850+
be coerced to an Integer. The assignment of uncastable values to a field will
851+
cause a ``nil`` to be written:
852+
853+
.. code::
854+
855+
User.new(name: [ "hello" ])
856+
# => #<User _id: 62b222d43282a47bf73e3264, name: nil>
857+
830858

831859
.. _customizing-field-behavior:
832860

@@ -978,6 +1006,12 @@ setter methods for fields of your custom type.
9781006
venue = Venue.new(location: point) # This uses the Point#mongoize instance method.
9791007
venue = Venue.new(location: [ 12, 24 ]) # This uses the Point.mongoize class method.
9801008

1009+
.. note::
1010+
1011+
The ``mongoize`` method should return ``nil`` on values that are uncastable to
1012+
your custom type. See the secion on :ref:`Uncastable Values <uncastable-values>`
1013+
for more details.
1014+
9811015
The class method ``demongoize`` does the inverse of ``mongoize``. It takes the raw object
9821016
from the MongoDB Ruby driver and converts it to an instance of your custom type.
9831017
In this case, the database driver returns an ``Array`` and we instantiate a ``Point`` from it.

docs/release-notes/mongoid-8.0.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,39 @@ changed in Mongoid 8.0:
5757
Please refer to :ref:`configuration option <configuration-options>` for
5858
the description and effects of each of these options.
5959

60+
Storing Uncastable Value
61+
------------------------
62+
63+
In Mongoid 8, Mongoid standardizes the storing of "uncastable values." On
64+
attempting to write an uncastable value, a ``nil`` is written instead. See the
65+
secion on :ref:`Uncastable Values <uncastable-values>` for more details.
66+
67+
Some ``mongoize`` methods were also changed to perform consistently with rails
68+
and the other mongoize methods. The following is a table of the changes in
69+
functionality:
70+
71+
+--------------+------------------------+------------------------+-------------------+
72+
| Field Type | Situation | Previous Functionality | New Functionality |
73+
+==============+========================+========================+===================+
74+
| Boolean | When a non-boolean | return ``false`` | return ``nil`` |
75+
| | string is assigned: | | |
76+
| | "bogus value" | | |
77+
+--------------+------------------------+------------------------+-------------------+
78+
| Symbol | When a value that does | return ``nil`` | return ``nil`` |
79+
| | not respond to | | |
80+
| | ``to_sym`` is | | |
81+
| | assigned: ``[]`` | | |
82+
+--------------+------------------------+------------------------+-------------------+
83+
| Array/Set | When a value that is | raise ``InvalidValue`` | return ``nil`` |
84+
| | not an array or set is | error | |
85+
| | assigned, and does NOT | | |
86+
| | respond to ``to_a``: 1 | | |
87+
+--------------+------------------------+------------------------+-------------------+
88+
| All Other | When an uncastable | undefined behavior, | return ``nil`` |
89+
| Types | value is assigned | occasionally raises | |
90+
| | | ``NoMethodError`` | |
91+
+--------------+------------------------+------------------------+-------------------+
92+
6093

6194
Order of Callback Invocation
6295
----------------------------

lib/config/locales/en.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -372,10 +372,6 @@ en:
372372
from the child side which association to go in."
373373
resolution: "Set the values from the parent, or redefine the association
374374
with only a single definition in the parent."
375-
invalid_value:
376-
message: "Value of type %{value_class} cannot be written to a field of type %{field_class}"
377-
summary: "Tried to set a value of type %{value_class} to a field of type %{field_class}"
378-
resolution: "Verify if the value to be set correspond to field definition"
379375
mixed_relations:
380376
message: "Referencing a(n) %{embedded} document from the %{root}
381377
document via a non-embedded association is not allowed since the

lib/mongoid/criteria/queryable/extensions/array.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,11 @@ module ClassMethods
133133
#
134134
# @return [ Object ] The evolved object.
135135
def evolve(object)
136-
if object.is_a?(::Array)
136+
case object
137+
when ::Array, ::Set
137138
object.map { |obj| obj.class.evolve(obj) }
138139
else
139-
object
140+
Mongoid::UncastableValue(object, 'Array')
140141
end
141142
end
142143
end

lib/mongoid/criteria/queryable/extensions/boolean.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ module ClassMethods
2020
# @return [ true, false ] The boolean value.
2121
def evolve(object)
2222
__evolve__(object) do |obj|
23-
obj.to_s =~ (/\A(true|t|yes|y|on|1|1.0)\z/i) ? true : false
23+
res = mongoize(object)
24+
res.nil? ? object : res
2425
end
2526
end
2627
end

lib/mongoid/errors.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
require "mongoid/errors/invalid_storage_options"
3838
require "mongoid/errors/invalid_storage_parent"
3939
require "mongoid/errors/invalid_time"
40-
require "mongoid/errors/invalid_value"
4140
require "mongoid/errors/inverse_not_found"
4241
require "mongoid/errors/mixed_relations"
4342
require "mongoid/errors/mixed_client_configuration"

lib/mongoid/extensions/array.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def multi_arged?
8787
# @example Mongoize the object.
8888
# object.mongoize
8989
#
90-
# @return [ Array ] The object.
90+
# @return [ Array | nil ] The object or nil.
9191
def mongoize
9292
::Array.mongoize(self)
9393
end
@@ -144,12 +144,14 @@ def __mongoize_fk__(association, object)
144144
#
145145
# @param [ Object ] object The object to mongoize.
146146
#
147-
# @return [ Array ] The object mongoized.
147+
# @return [ Array | nil ] The object mongoized or nil.
148148
def mongoize(object)
149-
if object.is_a?(::Array)
149+
return if object.nil?
150+
case object
151+
when ::Array, ::Set
150152
evolve(object).collect{ |obj| obj.class.mongoize(obj) }
151153
else
152-
evolve(object)
154+
Mongoid::UncastableValue(object, 'Array')
153155
end
154156
end
155157

lib/mongoid/extensions/big_decimal.rb

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,9 @@ def __to_inc__
2020
# @example Mongoize the object.
2121
# object.mongoize
2222
#
23-
# @return [ Object ] The object.
23+
# @return [ String | BSON::Decimal128 | nil ] The object or nil.
2424
def mongoize
25-
if Mongoid.map_big_decimal_to_decimal128
26-
BSON::Decimal128.new(self)
27-
else
28-
to_s
29-
end
25+
::BigDecimal.mongoize(self)
3026
end
3127

3228
# Is the BigDecimal a number?
@@ -67,21 +63,25 @@ def demongoize(object)
6763
# @param [ Object ] object The object to Mongoize
6864
#
6965
# @return [ String | BSON::Decimal128 | nil ] A String or Decimal128
70-
# representing the object or nil.
66+
# representing the object or nil. String if Mongoid.map_big_decimal_to_decimal128
67+
# is false, BSON::Decimal128 otherwise.
7168
def mongoize(object)
72-
unless object.nil?
69+
return if object.blank?
70+
if Mongoid.map_big_decimal_to_decimal128
7371
if object.is_a?(BSON::Decimal128)
7472
object
75-
elsif Mongoid.map_big_decimal_to_decimal128
76-
if object.is_a?(BigDecimal)
77-
BSON::Decimal128.new(object)
78-
elsif object.numeric?
79-
BSON::Decimal128.new(object.to_s)
80-
else
81-
object.mongoize
82-
end
73+
elsif object.is_a?(BigDecimal)
74+
BSON::Decimal128.new(object)
8375
elsif object.numeric?
76+
BSON::Decimal128.new(object.to_s)
77+
else
78+
Mongoid::UncastableValue(object, 'BigDecimal')
79+
end
80+
else
81+
if object.is_a?(BSON::Decimal128) || object.numeric?
8482
object.to_s
83+
else
84+
Mongoid::UncastableValue(object, 'BigDecimal')
8585
end
8686
end
8787
end

lib/mongoid/extensions/binary.rb

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module Binary
1010
# @example Mongoize the object.
1111
# object.mongoize
1212
#
13-
# @return [ Object ] The object.
13+
# @return [ BSON::Binary | nil ] The object.
1414
def mongoize
1515
BSON::Binary.mongoize(self)
1616
end
@@ -24,19 +24,13 @@ module ClassMethods
2424
#
2525
# @param [ Object ] object The object to Mongoize
2626
#
27-
# @return [ String | Symbol | BSON::Binary | nil ] A String or Binary
28-
# representing the object or nil.
27+
# @return [ BSON::Binary | nil ] A Binary representing the object or nil.
2928
def mongoize(object)
3029
return if object.nil?
31-
3230
case object
33-
when BSON::Binary
34-
object
35-
when String, Symbol
36-
BSON::Binary.new(object.to_s)
37-
else
38-
# TODO: MONGOID-5222 raise on the setting of feature flag.
39-
nil
31+
when BSON::Binary then object
32+
when String, Symbol then BSON::Binary.new(object.to_s)
33+
else Mongoid::UncastableValue(object, 'BSON::Binary')
4034
end
4135
end
4236
end

lib/mongoid/extensions/boolean.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,19 @@ class << self
88
# Turn the object from the ruby type we deal with to a Mongo friendly
99
# type.
1010
#
11-
# @example Mongoize the object.
11+
1212
# Boolean.mongoize("123.11")
1313
#
14-
# @return [ String ] The object mongoized.
14+
# @return [ true | false | nil ] The object mongoized or nil.
1515
def mongoize(object)
16-
evolve(object)
16+
return if object.nil?
17+
if object.to_s =~ (/\A(true|t|yes|y|on|1|1.0)\z/i)
18+
true
19+
elsif object.to_s =~ (/\A(false|f|no|n|off|0|0.0)\z/i)
20+
false
21+
else
22+
Mongoid::UncastableValue(object, 'Boolean')
23+
end
1724
end
1825
end
1926
end

0 commit comments

Comments
 (0)