Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Model annotator with Ruby (Hobo-like) syntax and hooks (Rails plugin)
Failed to load latest commit information.
lib Fix problem when classes are defined in model subdirectories
test New configuration parameters for what models to check/update
.document Initial commit to modalfields.
.gitignore Add tests for the update operation.
Gemfile New configuration parameters for what models to check/update
LICENSE.txt Initial commit to modalfields.
README.rdoc Automatic update of field declarations after migrations is now optional
Rakefile Remove rcov and change Bundler version requirement.
VERSION Version bump to 1.5.7



This is a Rails Plugin to maintain schema information in the models' definitions. It is a hybrid between HoboFields and model annotators.

It works like other annotators, by adding documentation to the model classes from the DB schema. But the annotations are syntactic Ruby as in HoboFields rather than comments:

class User < ActiveRecord::Base
  fields do
    name :string
    birthdate :date

Apart from looking prettier to my eyes, this allows triggering special functionality from the field declarations (such as specifying validations).

Fields that are foreign_keys of belongs_to associations are not annotated; it is assumed that belongs_to and other associations follow the fields block declaration, so the information is readily available.

Primary keys named are also not annotated (unless the ModalFields.show_primary_keys property is changed)

Custom type fields and hooks can be define in files (e.g. fields.rb) in config/initializers/

Rake Tasks

There's a couple of Rake tasks:

  • fields:update is what's called after a migration; it updates the fields blocks in the model class definitions.

  • fields:check shows the difference between the declared fields and the DB schema (what would be modified by fields:update)

Under Rails 2, you need to add this to your Rakefile to make the tasks available:

require 'modalfields/tasks'

Use Scenarios

There are two basic alternative strategies:

Define schema modifications with migrations first

To help with this strategy, the rake fields:update task can be made to be automatically called after each migration.

To activate this functionality, this must be added to your Rakefile:

require 'modalfields/migrate_tasks'

To alter the DB schema migrations are prepared; when the migrations are executed the field declarations are automatically updated. Further customization of the field declarations can be done before committing the changes.

Comments and validation, etc. specifications modified manually are preserved, at least if the field block syntax is kept as generated (one line per field, one line for the block start and end…)

Maintain schema via field declarations

If the preferred strategy is to define changes in the field declarations, rake fields:migration can be used to help write the necessary migration.

Some customization examples:

ModalFields.hook do

  # Declare serialized fields as
  #  field_name :serialized, :class=>Array
  # another option would be: (using the generic hook)
  #  field_name :text, :serialize=>Array
  serialized do |model, declaration|
    model.serialize, declaration.attributes[:class].class || Object

  # Add specific support for date fields (_ui virtual attributes)
  date do |model, declaration|

  # Add specific support for date and datetime and detect fields with units
  all_fields do |model, declaration|
    date_ui name if [:date, :datetime].include?(declaration.type)
    if ModalSupport::Units.valid_units?(units ='_').last)
      prec = {'m'=>1, 'mm'=>0, 'cm'=>0, 'km'=>3}[units] || 0
      magnitude_ui name, prec, units


# Spatial Adapter columns: require specific column to declaration conversion and field types

ModalFields.column_to_field_declaration do |column|
  type = column.type.to_sym
  type = column.geometry_type if type==:geometry
  attributes = {}
  attrs = ModalFields.definitions[type]
  attrs.keys.each do |attr|
    v = column.send(attr)
    attributes[attr] = v unless attrs[attr]==v
  end, type, [], attributes)

ModalFields.define do
  point               :srid=>nil, :with_z=>false, :with_m=>false, :sql_type=>'POINT'
  line_string         :srid=>nil, :with_z=>false, :with_m=>false, :sql_type=>'LINESTRING'
  polygon             :srid=>nil, :with_z=>false, :with_m=>false, :sql_type=>'POLYGON'
  geometry_collection :srid=>nil, :with_z=>false, :with_m=>false, :sql_type=>'GEOMETRYCOLLECTION'
  multi_point         :srid=>nil, :with_z=>false, :with_m=>false, :sql_type=>'MULTIPOINT'
  multi_line_string   :srid=>nil, :with_z=>false, :with_m=>false, :sql_type=>'MULTILINESTRING'
  multi_polygon       :srid=>nil, :with_z=>false, :with_m=>false, :sql_type=>'MULTIPOLYGON'
  geometry            :srid=>nil, :with_z=>false, :with_m=>false, :sql_type=>nil

ModalFields.hook do
  %w{point line_string polygon geometry_collection multi_point multi_line_string multi_polygon}.each do |spatial_type|
    field_type spatial_type.to_sym do |model, declaration|

# Enumerated field with symbolic constants associated (and translated literals) using the enum_id plugin
# Use:
#   enum :name, :values=>{id1=>:first_symbol, id2=>:second_symbol, ...}
# Or: (ids are sequential values starting in 1)
#  enum :name, :values=>[:first_symbol, :second_symbol, ...]
ModalFields.hook do
  enum do |model, declaration|
    values = declaration.attributes[:values]
    if values.kind_of?(Array)
      values = (1..values.size).map_hash{|i| values[i-1]}
    model.enum_id, values
    declaration.replace! :type=>:integer, :name=>"#{}_id"

  class ModalFields::Declaration
    def enum(*values)
      values = values.first if values.size==1 && values.first.kind_of?(Hash)


Copyright © 2011 Javier Goizueta. See LICENSE.txt for further details.

Something went wrong with that request. Please try again.