ModalFields¶ ↑
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 end end
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.name, declaration.attributes[:class].class || Object declaration.replace!(:type=>:text).remove_attributes!(:class) end # Add specific support for date fields (_ui virtual attributes) date do |model, declaration| model.date_ui declaration.name end # 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 = declaration.name.to_s.split('_').last) prec = {'m'=>1, 'mm'=>0, 'cm'=>0, 'km'=>3}[units] || 0 magnitude_ui name, prec, units end end end # 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 ModalFields::FieldDeclaration.new(column.name.to_sym, type, [], attributes) end 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 end 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| declaration.replace!(:type=>:geometry).add!(:sql_type=>spatial_type.upcase.tr('_','')) end end end # 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]} end model.enum_id declaration.name, values declaration.replace! :type=>:integer, :name=>"#{declaration.name}_id" end class ModalFields::Declaration def enum(*values) values = values.first if values.size==1 && values.first.kind_of?(Hash) {:values=>values} end end end
Copyright¶ ↑
Copyright © 2011 Javier Goizueta. See LICENSE.txt for further details.