Skip to content

Commit

Permalink
Merge branch 'master' into pr-5049
Browse files Browse the repository at this point in the history
* master:
  MONGOID-5151 Respect aliased fields in pluck/distinct by having Document.database_field_name recursively consider embedded docs (mongodb#5047)
  RUBY-2675 test coverage on the Mongoid side (mongodb#5030)
  MONGOID-4592 Add examples of using read_attribute and write_attribute to implement custom field behavior (mongodb#5082)
  MONGOId-5185 Remove tests for _id serialization (mongodb#5081)
  MONGOID-5103 Implement eq symbol operator (mongodb#5076)
  Fix MONGOID-5006 Link default auth source documentation to driver instead of incorrectly claiming "admin" is always the default (mongodb#5048)
  Create security policy following github's template (mongodb#5070)
  • Loading branch information
p committed Sep 22, 2021
2 parents 5918abd + d7076b6 commit c909e39
Show file tree
Hide file tree
Showing 17 changed files with 568 additions and 125 deletions.
19 changes: 19 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Security Policy

## Supported Versions

Use this section to tell people about which versions of your project are
currently being supported with security updates.

| Version | Supported |
| ------- | ------------------ |
| 7.3 | :white_check_mark: |
| 7.2 | :white_check_mark: |
| 7.1 | :white_check_mark: |
| 7.0 | :white_check_mark: |
| < 7.0 | :x: |

## Reporting a Vulnerability

Please [follow the instructions here](https://docs.mongodb.com/manual/tutorial/create-a-vulnerability-report/)
to report a vulnerability.
14 changes: 10 additions & 4 deletions docs/reference/configuration.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ can be configured.
- myhost3.mydomain.com:27017
options:
# These options are Ruby driver options, documented in
# https://docs.mongodb.com/ruby-driver/current/tutorials/ruby-driver-create-client/
# https://docs.mongodb.com/ruby-driver/current/reference/create-client/

# Change the default write concern. (default = { w: 1 })
write:
Expand Down Expand Up @@ -168,7 +168,13 @@ can be configured.
# on 2.4 and 2.6 is :plain)
auth_mech: :scram

# The database or source to authenticate the user against. (default: admin)
# Specify the auth source, i.e. the database or other source which
# contains the user's login credentials. Allowed values for auth source
# depend on the authentication mechanism, as explained in the server documentation:
# https://docs.mongodb.com/manual/reference/connection-string/#mongodb-urioption-urioption.authSource
# If no auth source is specified, the default auth source as
# determined by the driver will be used. Please refer to:
# https://docs.mongodb.com/ruby-driver/current/reference/authentication/#auth-source
auth_source: admin

# Force the driver to connect in a specific way instead of auto-
Expand Down Expand Up @@ -291,7 +297,7 @@ can be configured.
use_utc: false

The Ruby driver options may be found in
`the driver documentation <https://docs.mongodb.com/ruby-driver/current/tutorials/ruby-driver-create-client/>`_.
`the driver documentation <https://docs.mongodb.com/ruby-driver/current/reference/create-client/>`_.

ERb Preprocessing
=================
Expand Down Expand Up @@ -596,7 +602,7 @@ be executed sequentially during socket creation.
in an application.

For more information about TLS context hooks, including best practices for
assigning and removing them, see `the Ruby driver documentation <https://docs.mongodb.com/ruby-driver/current/tutorials/ruby-driver-create-client/#modifying-sslcontext>`_.
assigning and removing them, see `the Ruby driver documentation <https://docs.mongodb.com/ruby-driver/current/reference/create-client/#modifying-sslcontext>`_.

Usage with Forking Servers
==========================
Expand Down
252 changes: 162 additions & 90 deletions docs/reference/fields.txt
Original file line number Diff line number Diff line change
Expand Up @@ -545,103 +545,14 @@ This is useful for storing different values in ``id`` and ``_id`` fields:
# => #<Band _id: 5fc1c3f42c97a6590684046c, id: "42">


Custom Fields
-------------

You can define custom types in Mongoid and determine how they are serialized and deserialized.
You simply need to provide three methods on it for Mongoid to call to convert your object to
and from MongoDB friendly values.

.. code-block:: ruby

class Profile
include Mongoid::Document
field :location, type: Point
end

class Point

attr_reader :x, :y

def initialize(x, y)
@x, @y = x, y
end

# Converts an object of this instance into a database friendly value.
def mongoize
[ x, y ]
end

class << self

# Get the object as it was stored in the database, and instantiate
# this custom class from it.
def demongoize(object)
Point.new(object[0], object[1])
end

# Takes any possible object and converts it to how it would be
# stored in the database.
def mongoize(object)
case object
when Point then object.mongoize
when Hash then Point.new(object[:x], object[:y]).mongoize
else object
end
end

# Converts the object that was supplied to a criteria and converts it
# into a database friendly form.
def evolve(object)
case object
when Point then object.mongoize
else object
end
end
end
end

The instance method ``mongoize`` takes an instance of your object, and converts it
into how it will be stored in the database. In our example above, we want to store our
point object as an array in the form ``[ x, y ]``.

The class method ``demongoize`` takes an object as how it was stored in the database,
and is responsible for instantiating an object of your custom type. In this case, we
take an array and instantiate a ``Point`` from it.

The class method ``mongoize`` takes an object that you would use to set on your model
from your application code, and create the object as it would be stored in the database.
This is for cases where you are not passing your model instances of your custom type in the setter:

.. code-block:: ruby

point = Point.new(12, 24)
venue = Venue.new(location: point) # This uses the mongoize instance method.
venue = Venue.new(location: [ 12, 24 ]) # This uses the mongoize class method.

The class method ``evolve`` takes an object, and determines how it is to be transformed
for use in criteria. For example we may want to write a query like so:

.. code-block:: ruby

point = Point.new(12, 24)
Venue.where(location: point)

Note that when accessing custom fields from the document, you will get a new instance
of that object with each call to the getter. This is because Mongoid is generating a new
object from the raw attributes on each access.

We need the point object in the criteria to be transformed to a Mongo friendly value when
it is not as well, ``evolve`` is the method that takes care of this. We check if the passed
in object is a ``Point`` first, in case we also want to be able to pass in ordinary arrays instead.

Reserved Names
--------------

Attempting to define a field on a document that conflicts with a reserved
method name in Mongoid will raise an error. The list of reserved names can
be obtained by invoking the ``Mongoid.destructive_fields`` method.


Field Redefinition
------------------

Expand Down Expand Up @@ -729,6 +640,167 @@ alias can :ref:`be removed <unalias-id>` if desired (such as to integrate
with systems that use the ``id`` field to store value different from ``_id``.


Customizing Field Behavior
==========================

Mongoid offers several options for customizing the behavior of fields.


Custom Getters And Setters
--------------------------

You can define custom getters and setters for fields to modify the values
when they are being accessed or written. The getters and setters use the
same name as the field. Use ``read_attribute`` and ``write_attribute``
methods inside the getters and setters to operate on the raw attribute
values.

For example, Mongoid provides the ``:default`` field option to write a
default value into the field. If you wish to have a field default value
in your application but do not wish to persist it, you can override the
getter as follows:

.. code-block:: ruby

class DistanceMeasurement
include Mongoid::Document

field :value, type: Float
field :unit, type: String

def unit
read_attribute(:unit) || "m"
end

def to_s
"#{value} #{unit}"
end
end

measurement = DistanceMeasurement.new(value: 2)
measurement.to_s
# => "2.0 m"
measurement.attributes
# => {"_id"=>BSON::ObjectId('613fa0b0a15d5d61502f3447'), "value"=>2.0}

To give another example, a field which converts empty strings to nil values
may be implemented as follows:

.. code-block:: ruby

class DistanceMeasurement
include Mongoid::Document

field :value, type: Float
field :unit, type: String

def unit=(value)
if value.blank?
value = nil
end
write_attribute(:unit, value)
end
end

measurement = DistanceMeasurement.new(value: 2, unit: "")
measurement.attributes
# => {"_id"=>BSON::ObjectId('613fa15aa15d5d617216104c'), "value"=>2.0, "unit"=>nil}


Custom Field Types
------------------

You can define custom types in Mongoid and determine how they are serialized
and deserialized. You simply need to provide three methods on it for Mongoid
to call to convert your object to and from MongoDB friendly values.

.. code-block:: ruby

class Profile
include Mongoid::Document
field :location, type: Point
end

class Point

attr_reader :x, :y

def initialize(x, y)
@x, @y = x, y
end

# Converts an object of this instance into a database friendly value.
def mongoize
[ x, y ]
end

class << self

# Get the object as it was stored in the database, and instantiate
# this custom class from it.
def demongoize(object)
Point.new(object[0], object[1])
end

# Takes any possible object and converts it to how it would be
# stored in the database.
def mongoize(object)
case object
when Point then object.mongoize
when Hash then Point.new(object[:x], object[:y]).mongoize
else object
end
end

# Converts the object that was supplied to a criteria and converts it
# into a database friendly form.
def evolve(object)
case object
when Point then object.mongoize
else object
end
end
end
end

The instance method ``mongoize`` takes an instance of your object, and
converts it into how it will be stored in the database. In our example above,
we want to store our point object as an array in the form ``[ x, y ]``.

The class method ``demongoize`` takes an object as how it was stored in the
database, and is responsible for instantiating an object of your custom type.
In this case, we take an array and instantiate a ``Point`` from it.

The class method ``mongoize`` takes an object that you would use to set on
your model from your application code, and create the object as it would be
stored in the database. This is for cases where you are not passing your
model instances of your custom type in the setter:

.. code-block:: ruby

point = Point.new(12, 24)
venue = Venue.new(location: point) # This uses the mongoize instance method.
venue = Venue.new(location: [ 12, 24 ]) # This uses the mongoize class method.

The class method ``evolve`` takes an object, and determines how it is to be
transformed for use in criteria. For example we may want to write a query
like so:

.. code-block:: ruby

point = Point.new(12, 24)
Venue.where(location: point)

Note that when accessing custom fields from the document, you will get a
new instance of that object with each call to the getter. This is because
Mongoid is generating a new object from the raw attributes on each access.

We need the point object in the criteria to be transformed to a
MongoDB-friendly value when it is not as well, ``evolve`` is the method
that takes care of this. We check if the passed in object is a ``Point``
first, in case we also want to be able to pass in ordinary arrays instead.


.. _dynamic-fields:

Dynamic Fields
Expand Down
Loading

0 comments on commit c909e39

Please sign in to comment.