Skip to content
adzap edited this page Sep 17, 2010 · 8 revisions

ORM/ODM Support

The plugin is built on the ActiveModel validation system. This means it can allow multiple ORMs which support this system to use the plugin. This was not possible in previous versions.

To add full and proper support to an ORM, a couple of method hooks need to be defined.

Hooks

Attribute Method Generation

The first hook is a for the attribute method generation performed by the ORM. To properly validate a date/time attribute, it’s raw value must be known. This is because an attribute value of nil may result from parsing an invalid value or the attribute is just nil. The plugin needs to be able to tell the difference so that it can add an error when invalid value has been parsed, but not when it is legitimately nil.

ActiveRecord, and perhaps other ORMs, does not store the raw value for timezone enabled attributes e.g. datetimes. To get around this, the plugin creates a write method for each validated attribute which stores the raw value and then calls super. In previous ActiveRecord versions this process was more complex because you could not just call super to resume the normal write method behaviour for the attribute.

In order to properly create this method, it must be done after the ORM has generated all the regular attribute methods. This method generation process can be triggered in various ways but each ORM seems to handle it differently. This is why a custom shim for each ORM must be created to hook into this process.

Here is the hook method for ActiveRecord:

def self.define_attribute_methods
  super
  define_timeliness_methods(true)
end

The super will allow ActiveRecord to do it’s normal method generation and then the plugin creates it methods for each validated attribute. The ‘true’ value passed to ‘define_timeliness_methods’ is indicate that the _before_type_cast methods should also be created for each validated attribute. This before_type_cast convention originally added to ActiveRecord, but may exist in other ORMs.

Timezone Handling

ActiveRecord uses the sophisticated timezone handling provided by ActiveSupport. The timezone handling is added for all datetime and timestamp columns by default unless explicitly excluded. To be able to accurately handle timezones for each attribute, the plugin must be able to query whether an attribute is timezone aware. ActiveRecord has a specific technique of doing this and will likely differ in other ORMs if supported at all.

Here is how the hook works for ActiveRecord:

def self.timeliness_attribute_timezone_aware?(attr_name)
  create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
end

An attribute name is passed to the plugin method timeliness_attribute_timezone_aware? and you are left to define how to discover the timezone awareness of that attribute as supported by the ORM.

Example

Here is the full shim for ActiveRecord. You can see the two required modules for inclusion at the top.

class ActiveRecord::Base
  include ValidatesTimeliness::HelperMethods
  include ValidatesTimeliness::AttributeMethods

  def self.define_attribute_methods
    super
    # Define write method and before_type_cast method
    define_timeliness_methods(true)
  end

  def self.timeliness_attribute_timezone_aware?(attr_name)
    create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
  end
end

Custom Shims

If you create a custom shim for an ORM, please let me know and it is likely I can add it to the plugin to make things easier.

Clone this wiki locally