NAME
Data::Transform - performs rule-based data transformations of arbitrary structures
SYNOPSIS
use Data::Transform;
my $d = Data::Transform->std();
$d->add_transformers(qw(
Data::Transform::Type::DateTime::Duration
Data::Transform::HashKeys::CamelCase
), Data::Transform::Type->new(
type => 'Activity::Run'.
handler => sub ($data) {
{
start => $data->start_time, # DateTime
time => $data->time, # DateTime::Duration
distance => $data->distance, # number
pace => $data->pace, # DateTime::Duration
}
}
));
my $list = [
{ user_id => 3, run => Activity::Run->new(...) },
{ user_id => 4, ride => Activity::Ride->new(...) },
];
$d->transform($list); # [
# {
# userID => 3
# run => {
# start => "2023-05-15T074:11:14",
# time => "PT30M5S",
# distance => "5",
# pace => "PT9M30S",
# }
# },
# {
# userID => 4,
# ride => "Activity::Ride=HASH(0x2bbd7d16f640)",
# },
# ]
DESCRIPTION
Data::Transform
allows you to write reusable rules ('transformers') to modify parts (or all) of a data structure. There are many possible applications of this, but it was primarily written to handle converting object graphs of ORM objects into a structure that could be converted to JSON and delivered as an API endpoint response. One of the challenges of such a system is being able to reuse code because many different controllers could need to convert the an object type to the same structure, but then other controllers might need to convert that same type to a different structure.
A number of transformer roles and classes are included with this distribution:
Data::Transform::Node the root role which all transformers must implement
Data::Transform::Default a low priority transformer that only applies when no other transformers do
Data::Transform::Default::ToString a transformer that stringifies any value that is not otherwise transformed
Data::Transform::Type a transformer that matches against one or more data types
Data::Transform::Type::DateTime transforms DateTime objects to ISO8601 format.
Data::Transform::Type::DateTime::Duration transforms DateTime::Duration objects to ISO8601 (duration!) format
Data::Transform::Type::DBIx transforms DBIx::Class::Row instances into hashrefs of colname->value pairs. Does not recurse across relationships
Data::Transform::Type::DBIx::Recursive transforms DBIx::Class::Row instances into hashrefs of colname->value pairs, recursing down to_one-type relationships
Data::Transform::Value a transformer that matches against data values (exactly, by regex, or by coderef callback)
Data::Transform::Position a compound transformer that specifies one or more locations within the data structure to apply to, in addition to whatever other criteria its transformer specifies
Data::Transform::Tree a transformer that is applied to the entire data structure after all node transformations have been completed
Data::Transform::HashKeys::CamelCase a transformer that converts all hash keys in the data structure to lowerCamelCase
Data::Transform::HashKeys::SnakeCase a transformer that converts all hash keys in the data structure to snake_case
Data::Transform::HashKeys::CapitalizedIDSuffix a transformer that converts "Id" at the end of hash keys (as results from lowerCamelCase conversion) to "ID"
CONSTRUCTORS
Data::Transform->new()
Constructs a new default instance that pre-adds Data::Transform::Default::ToString to stringify values that are not otherwise transformed by user-provided transformers. Preserves (does not transform to empty string) undefined values.
Data::Transform->bare()
Returns a "bare-bones" instance that has no builtin data transformers.
Data::Transform->dbix()
Adds Data::Transform::DBIx::Recursive to to handle DBIx::Class
result rows
METHODS
add_transformers( @list )
Registers one or more data transformers with the Data::Transform
instance.
$t->add_transformers(Data::Transform::Type->new(
type => 'DateTime',
handler => sub ($data) {
$data->strftime('%F')
}
));
Each element of @list
must implement the Data::Transform::Node role, though these can either be strings containing class names or object instances.
Data::Transform
will automatically load class names passed in this list and construct an object instance from that class. This will fail if the class's new
constructor does not exist or has required parameters.
$t->add_transformers(qw(Data::Transform::Type::DateTime Data::Transform::Type::DBIx));
ArrayRefs passed in this list will be expanded and their contents will be treated the same as any item passed directly to this method.
my $default = Data::Transform::Type::Default->new(
handler => sub ($data) {
"[$data]"
}
);
my $bundle = [q(Data::Transform::Type::DateTime), $default];
$t->add_transformers($bundle);
When transforming data, only one transformer will be applied to each data element, prioritizing the most-specific types of matches. Among transformers that have equal match types, those added later have priority over those added earlier.
add_transformer_at( $position => $transformer )
add_transformer_at
is a convenience method for creating and adding a positional transformer (one that applies to a specific data-path within the given structure) in a single step.
See Data::Transform::Position for more on positional transformers.
transform( $data )
Transforms the data according to the transformers added to the instance and returns it. The data structure passed to the method is unmodified.
AUTHOR
Mark Tyrrell <mtyrrell@cpan.org>
LICENSE
Copyright (c) 2023 Mark Tyrrell
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.