Working with Legacy Schemas
DataMapper has quite a few features and plugins which are useful for working with legacy schemas. We’re going to introduce the feature available in the core first, before moving on to plugins. Note that whilst the title is “Working with Legacy Schemas”, really this applies to any situation where there is no control over the ‘table’ in the data-store. These features could just as easily be used to modify the fields returned by a RESTful webservice adapter, for example.
Small Tweaks
If the number of modifications are small—just one table or a few
properties—it is probably easiest to modify the properties and table names
directly. This can be accomplished using the :field
option for properties,
:child_key
(or :target
) for relationships, and manipulation of
storage_names[]
for models. In all the following examples, the use of the
:legacy
repository name assumes that it is some secondary repository that
should behave in the special manner. If it is the main database the application
will be interacting with, :default
makes a much more sensible choice.
Note that for the below snippet to work, you need to have have the
:legacy
repository set up properly.
1
2
3
4
5
6
7
8
9
10
11
12
class Post
include DataMapper::Resource
# set the storage name for the :legacy repository
storage_names[:legacy] = 'tblPost'
# use the datastore's 'pid' field for the id property.
property :id, Serial, :field => 'pid'
# use a property called 'uid' as the child key (the foreign key)
belongs_to :user, :child_key => [ :uid ]
end
Changing Behaviour
With one or two models, it is quite possible to tweak properties and models
using :field
and storage_names
. When there is a whole repository to rename,
naming conventions are an alternative. These apply to all the tables in the
repository. Naming conventions should be applied before the model is used as
the table name gets frozen when it is first used. DataMapper comes with a
number of naming conventions and custom ones can be defined:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# the DataMapper model
class Example::PostModel
end
# this is the default
DataMapper.repository(:legacy).adapter.resource_naming_convention =
DataMapper::NamingConventions::Resource::UnderscoredAndPluralized
Example::PostModel.storage_name(:legacy)
# => example_post_models
# underscored
DataMapper.repository(:legacy).adapter.resource_naming_convention =
DataMapper::NamingConventions::Resource::Underscored
Example::PostModel.storage_name(:legacy)
# => example/post_models
# without the module name
DataMapper.repository(:legacy).adapter.resource_naming_convention =
DataMapper::NamingConventions::Resource::UnderscoredAndPluralizedWithoutModule
Example::PostModel.storage_name(:legacy)
# => post_models
# custom conventions can be defined using procs, or any module which
# responds to #call. They are passed the name of the model, as a string.
module ResourceNamingConvention
def self.call(model_name)
'tbl' + DataMapper::Inflector.classify(model_name)
end
end
DataMapper.repository(:legacy).adapter.resource_naming_convention =
ResourceNamingConvention
Example::PostModel.storage_name(:legacy)
# => 'tblExample::PostModel'
For field names, use the field_naming_convention
menthod. Field naming
conventions work in a similar manner, except the #call
function is passed the
property name.