Last updated:
0 purchases
mapster
English
| Русский
Mapster #
Mapster is an object mapping library.
How to use
Map functions
Pros & Cons
Pros
Cons
Other features
Redefine Mapper
Work with injectable
How to use #
Extend one of Mapper classes. There are 9 types: OneSourceMapper, TwoSourcesMapper, ...
, NineSourcesMapper. Extend OneSourceMapper, if you need to map 1 object to another.
Extend TwoSourcesMapper, if you need to map 2 objects to another, and so on up to 9 source
objects.
class UserToUserResponseMapper extends OneSourceMapper<User, UserResponse> {
UserToUserResponseMapper(super.input);
@override
UserResponse map() {
return UserResponse(
id: source.id,
fullName: '${source.firstName} ${source.lastName}',
);
}
}
copied to clipboard
Create Mapster instance.
void main() {
final mapster = Mapster();
}
copied to clipboard
Register all your Mappers in created instance of Mapster. Use MapperMeta's static
methods: one, two, ... , nine depend on Mapper type.
void main() {
final mapster = Mapster();
mapster.register(MapperMeta.one(UserToUserResponseMapper.new));
}
copied to clipboard
And use Mapster!
void main() {
final mapster = Mapster();
mapster.register(MapperMeta.one(UserToUserResponseMapper.new));
const user = User(
id: 1,
firstName: 'Harry',
lastName: 'Potter',
);
final userResponse = mapster.map1(user, To<UserResponse>());
}
copied to clipboard
Note that you should pass To<YourResultType>() as last parameter. This way you specify what type
should Mapster return.
Also, you can create private getters for sources. It is more comfortable.
class UserUserPostToLikedPostNotification
extends ThreeSourcesMapper<User, User, Post, LikedPostNotification> {
UserUserPostToLikedPostNotification(super.input);
@override
LikedPostNotification map() {
return LikedPostNotification(
postID: _post.id,
authorID: _user1.id,
likeUserID: _user2.id,
postText: _post.text,
authorName: '${_user1.firstName} ${_user1.lastName}',
likeUserName: '${_user2.firstName} ${_user2.lastName}',
);
}
User get _user1 => source1;
User get _user2 => source2;
Post get _post => source3;
}
copied to clipboard
Map functions #
Mapster has 9 map methods: map1, map2, ... , map9. All of them get source objects and
then To<YourResultType>().
You can pass source objects to Mapster's map methods IN ANY ORDER. You do not need to check
the order of input objects in signature of certain Mapper every time. Mapster is smart enough to
find a proper Mapper.
class UserPostToPostResponse extends TwoSourcesMapper<User, Post, PostResponse> {
UserPostToPostResponse(super.input);
@override
PostResponse map() {
return PostResponse(
id: source2.id,
text: source2.text,
userID: source1.id,
userName: '${source1.firstName} ${source1.lastName}',
);
}
}
void main() {
final mapster = Mapster();
mapster.register(MapperMeta.two(UserPostToPostResponse.new));
const user = User(
id: 1,
firstName: 'Harry',
lastName: 'Potter',
);
const post = Post(
id: 1,
text: "The philosopher's stone",
);
// You can swap source objects, the result will be the same.
final postResponse1 = mapster.map2(user, post, To<PostResponse>());
final postResponse2 = mapster.map2(post, user, To<PostResponse>());
}
copied to clipboard
Beware of Mappers with multiple source objects of the same type. Under the hood, Mapster
matches all input objects in order they are passed. For example, let's look at this code:
class UserUserPostToLikedPostNotification
extends ThreeSourcesMapper<User, User, Post, LikedPostNotification> {
UserUserPostToLikedPostNotification(super.input);
@override
LikedPostNotification map() {
return LikedPostNotification(
postID: source3.id,
authorID: source1.id,
likeUserID: source2.id,
postText: source3.text,
authorName: '${source1.firstName} ${source1.lastName}',
likeUserName: '${source2.firstName} ${source2.lastName}',
);
}
}
void main() {
final mapster = Mapster();
mapster.register(MapperMeta.three(UserUserPostToLikedPostNotification.new));
const user = User(
id: 1,
firstName: 'Harry',
lastName: 'Potter',
);
const post = Post(
id: 1,
text: "The philosopher's stone",
);
const likeUser = User(
id: 2,
firstName: 'Ronald',
lastName: 'Weasley',
);
// You can swap source objects, but if you swap multiple objects of the same type,
// the result WILL change.
// Mapster does its maximum. But Mapster is not able to define the right order
// for multiple objects of the same type.
// So, you should avoid creating Mappers with multiple objects of the same type.
final notification1 = mapster.map3(
user,
likeUser,
post,
To<LikedPostNotification>(),
);
final notification2 = mapster.map3(
likeUser,
user,
post,
To<LikedPostNotification>(),
);
}
copied to clipboard
Also you can not create a Mapper with nullable input or output types. Instead you can create
a DTO. For example:
class ToUserInfoResponseDTO {
const ToUserInfoResponseDTO({
required this.id,
required this.firstName,
this.lastName,
this.phone,
});
final int id;
final String firstName;
final String? lastName;
final String? phone;
}
class UserInfoToUserInfoResponseMapper
extends OneSourceMapper<ToUserInfoResponseDTO, UserInfoResponse> {
UserInfoToUserInfoResponseMapper(super.input);
@override
UserInfoResponse map() {
var fullName = source.firstName;
final lastName = source.lastName;
if (lastName != null) {
fullName += ' $lastName';
}
return UserInfoResponse(
id: source.id,
fullName: fullName,
phone: source.phone,
);
}
}
void main() {
final mapster = Mapster();
/// If you need to pass `null` create special DTO for it.
final dto = ToUserInfoResponseDTO(
id: 1,
firstName: 'Harry',
lastName: null,
phone: null,
);
mapster.register(MapperMeta.one(UserInfoToUserInfoResponseMapper.new));
print(mapster.map1(dto, To<UserInfoResponse>()));
}
copied to clipboard
Pros & Cons #
Pros #
Do not need to specify types in <> during using register and map functions of Mapster
Do not need to worry about the order of parameters
Analyzer correctly determines a return type of map functions
Mapster has O(1) time complexity of searching for a proper Mapper
Mapster has O(n) time complexity (where n is an amount of parameters) of ordering arguments
before passing them to a Mapper
Mapster has no dependency
Do not need to inject your classes/functions with large amount of mappers anymore. Just inject
with Mapster
Do not need to know a specific Mapper to map
Ability to specify Mappers in a one place
Ability to redefine Mappers
Cons #
not found yet🙂
Other features #
Redefine Mapper #
You can redefine Mapper by calling register again, like that:
void main() {
final mapster = Mapster();
const user = User(
id: 1,
firstName: 'Harry',
lastName: 'Potter',
);
// Register Mapper with input type: User, and output type: UserResponse.
mapster.register(MapperMeta.one(UserToUserResponseMapper.new));
final userResponse1 = mapster.map1(user, To<UserResponse>());
// Register another Mapper with the same types:
// input type: User, and output type: UserResponse.
mapster.register(MapperMeta.one(AnotherUserToUserResponseMapper.new));
final userResponse2 = mapster.map1(user, To<UserResponse>());
}
copied to clipboard
Mapster stores Mappers based on its' source types and result type. If new Mapper has the same
set of input types (an order of input types does NOT matter) and the same output type as the
old Mapper, then Mapster replaces old one with a new one.
void main() {
final mapster = Mapster();
const user = User(
id: 1,
firstName: 'Harry',
lastName: 'Potter',
);
// Register Mapper with input type: User, and output type: UserResponse.
mapster.register(MapperMeta.one(UserToUserResponseMapper.new));
final userResponse1 = mapster.map1(user, To<UserResponse>());
// Register another Mapper with swapped result and input types:
// input type: UserResponse, and output type: User.
mapster.register(MapperMeta.one(UserResponseToUserMapper.new));
// Because input types set of the 1st Mapper contains
// different types than input types set of the 2nd Mapper,
// these two mappers considered as different.
// Also we can say: because output type of the 1st Mapper not
// equals to output type of the 2nd Mapper, these two
// mappers considered as different.
final user2 = mapster.map1(userResponse1, To<User>());
}
copied to clipboard
Work with injectable #
If you use injectable package, you can register Mapster
and Mappers like that:
@module
abstract class MapsterModule {
@singleton
Mapster get mapster => Mapster();
}
@singleton
class MapsterRegistrar {
const MapsterRegistrar(this._mapster);
final Mapster _mapster;
@postConstruct
void register() {
_mapster..register(
MapperMeta.one(UserToUserResponseMapper.new),
)..register(
MapperMeta.three(UserUserPostToLikedPostNotification.new),
);
}
}
copied to clipboard
We won't use MapsterRegistrar class. But it's useful to us, because @singletons can
have @postConstruct method. So, this way, we can register Mapster and all our Mappers
in get_it.
If you use feature-first or layer-first approach in you project, you can declare
multiple MapsterRegistrar in multiple places with the same name, but do NOT try to
get MapsterRegister from get_it, if you creates multiple MasterRegistrar with the same name,
because it can cause a problem. Remember, that we do not need to get MapsterRegistrar, we creates
it only to use @postConstruct.
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.