Datamappify
Compose, decouple and manage domain logic and data persistence separately. Works particularly great for composing form objects!
Install / Use
/learn @fredwu/DatamappifyREADME
Datamappify is no longer being maintained. It started off with a noble goal, unfortunately due to it being on the critical path of our project, we have decided not to continue developing it given the lack of development time from me.
Feel free to read the README and browse the code, I still believe in the solutions for this particular domain.
For a more active albeit still young project, check out Lotus::Model.
Datamappify

Compose, decouple and manage domain logic and data persistence separately. Works particularly great for composing form objects!
Overview
The typical Rails (and ActiveRecord) way of building applications is great for small to medium sized projects, but when projects grow larger and more complex, your models too become larger and more complex - it is not uncommon to have god classes such as a User model.
Datamappify tries to solve two common problems in web applications:
- The coupling between domain logic and data persistence.
- The coupling between forms and models.
Datamappify is loosely based on the Repository Pattern and Entity Aggregation, and is built on top of Virtus and existing ORMs (ActiveRecord and Sequel, etc).
There are three main design goals:
- To utilise the powerfulness of existing ORMs so that using Datamappify doesn't interrupt too much of your current workflow. For example, Devise would still work if you use it with a
UserAccountActiveRecord model that is attached to aUserentity managed by Datamappify. - To have a flexible entity model that works great with dealing with form data. For example, SimpleForm would still work with nested attributes from different ORM models if you map entity attributes smartly in your repositories managed by Datamappify.
- To have a set of data providers to encapsulate the handling of how the data is persisted. This is especially useful for dealing with external data sources such as a web service. For example, by calling
UserRepository.save(user), certain attributes of the user entity are now persisted on a remote web service. Better yet, dirty tracking and lazy loading are supported out of the box!
Datamappify consists of three components:
- Entity contains models behaviour, think an ActiveRecord model with the persistence specifics removed.
- Repository is responsible for data retrieval and persistence, e.g.
find,saveanddestroy, etc. - Data as the name suggests, holds your model data. It contains ORM objects (e.g. ActiveRecord models).
Below is a high level and somewhat simplified overview of Datamappify's architecture.

Note: Datamappify is NOT affiliated with the Datamapper project.
Built-in ORMs for Persistence
You may implement your own data provider and criteria, but Datamappify comes with build-in support for the following ORMS:
- ActiveRecord
- Sequel
Requirements
- ruby 2.0+
- ActiveModel 4.0+
Installation
Add this line to your application's Gemfile:
gem 'datamappify'
Usage
Entity
Entity uses Virtus DSL for defining attributes and ActiveModel::Validations DSL for validations.
The cool thing about Virtus is that all your attributes get coercion for free!
Below is an example of a User entity, with inline comments on how some of the DSLs work.
class User
include Datamappify::Entity
attribute :first_name, String
attribute :last_name, String
attribute :age, Integer
attribute :passport, String
attribute :driver_license, String
attribute :health_care, String
# Nested entity composition - composing the entity with attributes and validations from other entities
#
# class Job
# include Datamappify::Entity
#
# attributes :title, String
# validates :title, :presence => true
# end
#
# class User
# # ...
# attributes_from Job
# end
#
# essentially equals:
#
# class User
# # ...
# attributes :title, String
# validates :title, :presence => true
# end
attributes_from Job
# optionally you may prefix the attributes, so that:
#
# class Hobby
# include Datamappify::Entity
#
# attributes :name, String
# validates :name, :presence => true
# end
#
# class User
# # ...
# attributes_from Hobby, :prefix_with => :hobby
# end
#
# becomes:
#
# class User
# # ...
# attributes :hobby_name, String
# validates :hobby_name, :presence => true
# end
attributes_from Hobby, :prefix_with => :hobby
# Entity reference
#
# `references` is a convenient method for:
#
# attribute :account_id, Integer
# attr_accessor :account
#
# and it assigns `account_id` the correct value:
#
# user.account = account #=> user.account_id = account.id
references :account
validates :first_name, :presence => true,
:length => { :minimum => 2 }
validates :passport, :presence => true,
:length => { :minimum => 8 }
def full_name
"#{first_name} #{last_name}"
end
end
Entity inheritance
Inheritance is supported for entities, for example:
class AdminUser < User
attribute :level, Integer
end
class GuestUser < User
attribute :expiry, DateTime
end
Lazy loading
Datamappify supports attribute lazy loading via the Lazy module.
class User
include Datamappify::Entity
include Datamappify::Lazy
end
When an entity is lazy loaded, only attributes from the primary source (e.g. User entity's primary source would be ActiveRecord::User as specified in the corresponding repository) will be loaded. Other attributes will only be loaded once they are called. This is especially useful if some of your data sources are external web services.
Repository
Repository maps entity attributes to DB columns - better yet, you can even map attributes to different ORMs!
Below is an example of a repository for the User entity, you can have more than one repositories for the same entity.
class UserRepository
include Datamappify::Repository
# specify the entity class
for_entity User
# specify the default data provider for unmapped attributes
# optionally you may use `Datamappify.config` to config this globally
default_provider :ActiveRecord
# specify any attributes that need to be mapped
#
# for attributes mapped from a different source class, a foreign key on the source class is required
#
# for example:
# - 'last_name' is mapped to the 'User' ActiveRecord class and its 'surname' attribute
# - 'driver_license' is mapped to the 'UserDriverLicense' ActiveRecord class and its 'number' attribute
# - 'passport' is mapped to the 'UserPassport' Sequel class and its 'number' attribute
# - attributes not specified here are mapped automatically to 'User' with provider 'ActiveRecord'
map_attribute :last_name, :to => 'User#surname'
map_attribute :driver_license, :to => 'UserDriverLicense#number'
map_attribute :passport, :to => 'UserPassport#number', :provider => :Sequel
map_attribute :health_care, :to => 'UserHealthCare#number', :provider => :Sequel
# alternatively, you may group attribute mappings if they share certain options:
group :provider => :Sequel do
map_attribute :passport, :to => 'UserPassport#number'
map_attribute :health_care, :to => 'UserHealthCare#number'
end
# attributes can also be reverse mapped by specifying the `via` option
#
# for example, the below attribute will look for `hobby_id` on the user object,
# and map `hobby_name` from the `name` attribute of `ActiveRecord::Hobby`
#
# this is useful for mapping form fields (similar to ActiveRecord's nested attributes)
map_attribute :hobby_name, :to => 'Hobby#name', :via => :hobby_id
# by default, Datamappify maps attributes using an inferred reference (foreign) key,
# for example, the first mapping below will look for the `user_id` key in `Bio`,
# the second mapping below will look for the `person_id` key in `Bio` instead
map_attribute :bio, :to => 'Bio#body'
map_attribute :bio, :to => 'Bio#body', :reference_key => :person_id
end
Repository inheritance
Inheritance is supported for repositories when your data structure is based on STI (Single Table Inheritance), for example:
class AdminUserRepository < UserRepository
for_entity AdminUser
end
class GuestUserRepository < UserRepository
for_entity GuestUser
map_attribute :expiry, :to => 'User#expiry_date'
end
In the above example, both repositories deal with the ActiveRecord::User data model.
Override mapped data models
Datamappify repository by default creates the underlying data model classes for you. For example:
map_attribute :driver_license, :to => 'UserData::DriverLicense#number'
In the above example, a `Datamppify::Data::Record::Activ
Related Skills
node-connect
344.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
96.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
344.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
344.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
