Skip to content

Commit 4717986

Browse files
committed
- Prefer defining field type value as a Symbol rather than a Class. (Symbol is already supported today.)
- Deprecate using field type as a Class. - Add ability to define custom field types using a mini DSL (Mongoid::Fields.configure)
1 parent da48cea commit 4717986

File tree

8 files changed

+297
-85
lines changed

8 files changed

+297
-85
lines changed

docs/reference/fields.txt

Lines changed: 71 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -55,34 +55,40 @@ on a person by using the ``field`` macro.
5555

5656
class Person
5757
include Mongoid::Document
58-
field :name, type: String
59-
field :date_of_birth, type: Date
60-
field :weight, type: Float
58+
field :name, type: :string
59+
field :date_of_birth, type: :date
60+
field :weight, type: :float
6161
end
6262

6363
Below is a list of valid types for fields.
6464

65-
- ``Array``
66-
- ``BigDecimal``
67-
- ``Boolean``
68-
- ``Date``
69-
- ``DateTime``
70-
- ``Float``
71-
- ``Hash``
72-
- ``Integer``
73-
- ``BSON::ObjectId``
74-
- ``BSON::Binary``
75-
- ``Range``
76-
- ``Regexp``
77-
- ``Set``
78-
- ``String``
79-
- ``StringifiedSymbol``
80-
- ``Symbol``
81-
- ``Time``
82-
- ``TimeWithZone``
65+
- ``:array``
66+
- ``:big_decimal``
67+
- ``:boolean``
68+
- ``:date``
69+
- ``:date_time``
70+
- ``:decimal128`` (uses ``BSON::Decimal128``)
71+
- ``:float``
72+
- ``:hash``
73+
- ``:integer``
74+
- ``:object_id`` (uses ``BSON::ObjectID``)
75+
- ``:binary`` (uses ``BSON::Binary``)
76+
- ``:range``
77+
- ``:regexp``
78+
- ``:set``
79+
- ``:string``
80+
- ``:stringified_symbol`` (see below)
81+
- ``:symbol``
82+
- ``:time``
83+
- ``:time_with_zone``
8384

8485
To define custom field types, refer to :ref:`Custom Field Types <custom-field-types>` below.
8586

87+
As of Mongoid 7.5, ``field :type`` should be specified as a ``Symbol``.
88+
Specifying as a ``Class`` is deprecated and will be no longer supported in a
89+
future major version of Mongoid. Unrecognized field type symbols will result
90+
in an `InvalidFieldType` error when the model class is loaded.
91+
8692

8793
.. _omitting-field-type-definition:
8894

@@ -117,16 +123,16 @@ Types that are not supported as dynamic attributes since they cannot be cast are
117123

118124
.. _field-type-stringified-symbol:
119125

120-
Field Type: StringifiedSymbol
121-
-----------------------------
126+
Field Type :stringified_symbol
127+
------------------------------
122128

123-
The ``StringifiedSymbol`` field type is the recommended field type for storing
124-
values that should be exposed as symbols to Ruby applications. When using the ``Symbol`` field type,
129+
The ``:stringified_symbol`` field type is the recommended field type for storing
130+
values that should be exposed as symbols to Ruby applications. When using the ``:symbol`` field type,
125131
Mongoid defaults to storing values as BSON symbols. For more information on the
126132
BSON symbol type, see :ref:`here <field-type-symbol>`.
127133
However, the BSON symbol type is deprecated and is difficult to work with in programming languages
128-
without native symbol types, so the ``StringifiedSymbol`` type allows the use of symbols
129-
while ensuring interoperability with other drivers. The ``StringifiedSymbol`` type stores all data
134+
without native symbol types, so the ``:stringified_symbol`` type allows the use of symbols
135+
while ensuring interoperability with other drivers. The ``:stringified_symbol`` type stores all data
130136
on the database as strings, while exposing values to the application as symbols.
131137

132138
An example usage is shown below:
@@ -171,15 +177,15 @@ migration from fields that currently store either strings or BSON symbols in the
171177

172178
.. _field-type-symbol:
173179

174-
Field Type: Symbol
180+
Field Type :symbol
175181
------------------
176182

177-
New applications should use the :ref:`StringifiedSymbol field type <field-type-stringified-symbol>`
178-
to store Ruby symbols in the database. The ``StringifiedSymbol`` field type
183+
New applications should use the :ref:`:stringified_symbol field type <field-type-stringified-symbol>`
184+
to store Ruby symbols in the database. The ``:stringified_symbol`` field type
179185
provides maximum compatibility with other applications and programming languages
180186
and has the same behavior in all circumstances.
181187

182-
Mongoid also provides the deprecated ``Symbol`` field type for serializing
188+
Mongoid also provides the deprecated ``:symbol`` field type for serializing
183189
Ruby symbols to BSON symbols. Because the BSON specification deprecated the
184190
BSON symbol type, the `bson` gem will serialize Ruby symbols into BSON strings
185191
when used on its own. However, in order to maintain backwards compatibility
@@ -202,10 +208,10 @@ snippet in your project:
202208

203209
.. _field-type-hash:
204210

205-
Field Type: Hash
211+
Field Type :hash
206212
----------------
207213

208-
When using a field of type Hash, be wary of adhering to the
214+
When using a field of type ``:hash``, be wary of adhering to the
209215
`legal key names for mongoDB <http://docs.mongodb.org/manual/reference/limits/#naming-restrictions>`_,
210216
or else the values will not store properly.
211217

@@ -214,7 +220,7 @@ or else the values will not store properly.
214220
class Person
215221
include Mongoid::Document
216222
field :first_name
217-
field :url, type: Hash
223+
field :url, type: :hash
218224

219225
# will update the fields properly and save the values
220226
def set_vals
@@ -234,21 +240,21 @@ or else the values will not store properly.
234240

235241
.. _field-type-time:
236242

237-
Field Type: Time
243+
Field Type :time
238244
----------------
239245

240-
``Time`` fields store values as ``Time`` instances in the :ref:`configured
246+
``:time`` fields store values as ``Time`` instances in the :ref:`configured
241247
time zone <time-zones>`.
242248

243249
``Date`` and ``DateTime`` instances are converted to ``Time`` instances upon
244-
assignment to a ``Time`` field:
250+
assignment to a ``:time`` field:
245251

246252
.. code-block:: ruby
247253

248254
class Voter
249255
include Mongoid::Document
250256

251-
field :registered_at, type: Time
257+
field :registered_at, type: :time
252258
end
253259

254260
Voter.new(registered_at: Date.today)
@@ -260,10 +266,10 @@ local time, because the application was not configured to use UTC times.
260266

261267
.. _field-type-date:
262268

263-
Field Type: Date
269+
Field Type :date
264270
----------------
265271

266-
Mongoid allows assignment of values of several types to ``Date`` fields:
272+
Mongoid allows assignment of values of several types to ``:date`` fields:
267273

268274
- ``Date`` - the provided date is stored as is.
269275
- ``Time``, ``DateTime``, ``ActiveSupport::TimeWithZone`` - the date component
@@ -281,20 +287,20 @@ As a date & time to date conversion is lossy (it discards the time component),
281287
especially if an application operates with times in different time zones it is
282288
recommended to explicitly convert ``String``, ``Time`` and ``DateTime``
283289
objects to ``Date`` objects before assigning the values to fields of type
284-
``Date``.
290+
``:date``.
285291

286292

287293
.. _field-type-date-time:
288294

289-
Field Type: DateTime
295+
Field Type :date_time
290296
---------------------
291297

292298
MongoDB stores all times as UTC timestamps. When assigning a value to a
293-
``DateTime`` field, or when querying a ``DateTime`` field, Mongoid
299+
``:date_time`` field, or when querying a ``:date_time`` field, Mongoid
294300
converts the passed in value to a UTC ``Time`` before sending it to the
295301
MongoDB server.
296302

297-
``Time``, ``ActiveSupport::TimeWithZone`` and ``DateTime`` objects embed
303+
``Time``, ``ActiveSupport::TimeWithZone``, and ``DateTime`` objects embed
298304
time zone information, and the value persisted is the specified moment in
299305
time, in UTC. When the value is retrieved, the time zone in which it is
300306
returned is defined by the :ref:`configured time zone settings <time-zones>`.
@@ -303,7 +309,7 @@ returned is defined by the :ref:`configured time zone settings <time-zones>`.
303309

304310
class Ticket
305311
include Mongoid::Document
306-
field :opened_at, type: DateTime
312+
field :opened_at, type: :date_time
307313
end
308314

309315
Mongoid.use_activesupport_time_zone = true
@@ -333,7 +339,7 @@ doing so, the integers/floats are assumed to be Unix timestamps (in UTC):
333339
ticket.opened_at
334340
# => Fri, 14 Dec 2018 16:12:54 +0000
335341

336-
If a string is used as a ``DateTime`` field value, the behavior depends on
342+
If a ``String`` is used as a ``:date_time`` field value, the behavior depends on
337343
whether the string includes a time zone. If no time zone is specified,
338344
the :ref:`default Mongoid time zone <time-zones>` is used:
339345

@@ -355,7 +361,7 @@ If a time zone is specified, it is respected:
355361

356362
.. _field-type-regexp:
357363

358-
Field Type: Regexp
364+
Field Type :regexp
359365
------------------
360366

361367
MongoDB supports storing regular expressions in documents, and querying using
@@ -366,7 +372,7 @@ fork of `Oniguruma regular expression engine <https://github.com/kkos/oniguruma>
366372
The two regular expression implementations generally provide equivalent
367373
functionality but have several important syntax differences.
368374

369-
When a field is declared to be of type Regexp, Mongoid converts Ruby regular
375+
When a field is declared to be of type ``:regexp``, Mongoid converts Ruby regular
370376
expressions to BSON regular expressions and stores the result in MongoDB.
371377
Retrieving the field from the database produces a ``BSON::Regexp::Raw``
372378
instance:
@@ -376,7 +382,7 @@ instance:
376382
class Token
377383
include Mongoid::Document
378384

379-
field :pattern, type: Regexp
385+
field :pattern, type: :regexp
380386
end
381387

382388
token = Token.create!(pattern: /hello.world/m)
@@ -747,9 +753,20 @@ can use in our model class as follows:
747753

748754
class Profile
749755
include Mongoid::Document
750-
field :location, type: Point
756+
field :location, type: :point
757+
end
758+
759+
First, declare the new field type mapping in an initializer:
760+
761+
.. code-block:: ruby
762+
763+
# in /config/initializers/mongoid_custom_fields.rb
764+
765+
Mongoid::Fields.configure do
766+
type :point, Point
751767
end
752768

769+
753770
Then make a Ruby class to represent the type. This class must define methods
754771
used for MongoDB serialization and deserialization as follows:
755772

@@ -845,8 +862,10 @@ specifiying its handler function as a block:
845862

846863
# in /config/initializers/mongoid_custom_fields.rb
847864

848-
Mongoid::Fields.option :required do |model, field, value|
849-
model.validates_presence_of field if value
865+
Mongoid::Fields.configure do
866+
option :required do |model, field, value|
867+
model.validates_presence_of field if value
868+
end
850869
end
851870

852871
Then, use it your model class:

docs/release-notes/mongoid-7.5.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,40 @@ Mongoid 7.4 output:
159159

160160
Notice that in 7.4 ``attribute_was(:age)`` returns the old attribute value,
161161
while in 7.5 ``attribute_was(:age)`` returns the new value.
162+
163+
164+
Setting Field Type as a Class is Deprecated
165+
-------------------------------------------
166+
167+
Mongoid has historically supported defining the ``field :type`` option
168+
as either a Symbol or a Class. As of Mongoid 7.5, using a Class is deprecated
169+
and Symbol is preferred. Support for ``field :type`` as a Class will be
170+
removed in a future major version of Mongoid.
171+
172+
.. code-block:: ruby
173+
174+
class Person
175+
include Mongoid::Document
176+
177+
# Deprecated; will log an warning message.
178+
field :first_name, type: String
179+
180+
# Good
181+
field :last_name, type: :string
182+
end
183+
184+
185+
Support for Defining Custom Field Type Values
186+
---------------------------------------------
187+
188+
Mongoid 7.5 adds the ability to define custom ``field :type`` Symbol values as follows:
189+
190+
.. code-block:: ruby
191+
192+
# in /config/initializers/mongoid_custom_fields.rb
193+
194+
Mongoid::Fields.configure do
195+
type :point, Point
196+
end
197+
198+
Refer to the :ref:`docs <http://docs.mongodb.org/manual/reference/fields/#custom-field-types>` for details.

lib/config/locales/en.yml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,33 @@ en:
172172
resolution: "When defining the field :%{name} on '%{klass}', please provide
173173
valid options for the field. These are currently: %{valid}. If you
174174
meant to define a custom field option, please do so first as follows:\n\n
175-
\_\_Mongoid::Fields.option :%{option} do |model, field, value|\n
176-
\_\_\_\_# Your logic here...\n
175+
\_\_Mongoid::Fields.configure do\n
176+
\_\_\_\_option :%{option} do |model, field, value|\n
177+
\_\_\_\_\_\_# Your logic here...\n
178+
\_\_\_\_end\n
177179
\_\_end\n
178180
\_\_class %{klass}\n
179181
\_\_\_\_include Mongoid::Document\n
180182
\_\_\_\_field :%{name}, %{option}: true\n
181183
\_\_end\n\n
182184
Refer to:
183185
https://docs.mongodb.com/mongoid/current/reference/fields/#custom-field-options"
186+
invalid_field_type:
187+
message: "Invalid field type :%{type} for field :%{field} on model '%{klass}'."
188+
summary: "Model '%{klass}' defines a field :%{field} with an unknown :type value
189+
:%{type}. This value is neither present in Mongoid's default type mapping,
190+
nor defined in a custom field type mapping."
191+
resolution: "Please provide a valid :type value for the field. If you
192+
meant to define a custom field type, please do so first as follows:\n\n
193+
\_\_Mongoid::Fields.configure do\n
194+
\_\_\_\_type :%{type}, YourTypeClass
195+
\_\_end\n
196+
\_\_class %{klass}\n
197+
\_\_\_\_include Mongoid::Document\n
198+
\_\_\_\_field :%{field}, type: :%{type}\n
199+
\_\_end\n\n
200+
Refer to:
201+
https://docs.mongodb.com/mongoid/current/reference/fields/#custom-field-types"
184202
invalid_includes:
185203
message: "Invalid includes directive: %{klass}.includes(%{args})"
186204
summary: "Eager loading in Mongoid only supports providing arguments

lib/mongoid/errors.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
require "mongoid/errors/invalid_dependent_strategy"
1616
require "mongoid/errors/invalid_field"
1717
require "mongoid/errors/invalid_field_option"
18+
require "mongoid/errors/invalid_field_type"
1819
require "mongoid/errors/invalid_find"
1920
require "mongoid/errors/invalid_includes"
2021
require "mongoid/errors/invalid_index"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# frozen_string_literal: true
2+
3+
module Mongoid
4+
module Errors
5+
6+
# This error is raised when trying to define a field using a :type option value
7+
# that is not present in the field type mapping.
8+
class InvalidFieldType < MongoidError
9+
10+
# Create the new error.
11+
#
12+
# @example Instantiate the error.
13+
# InvalidFieldType.new('Person', 'first_name', 'stringgy')
14+
#
15+
# @param [ String ] klass The model class.
16+
# @param [ String ] field The field on which the invalid type is used.
17+
# @param [ String ] type The value of the field :type option.
18+
def initialize(klass, field, type)
19+
super(
20+
compose_message('invalid_field_type', { klass: klass, field: field, type: type })
21+
)
22+
end
23+
end
24+
end
25+
end

0 commit comments

Comments
 (0)