大部分 domain 的 mapper 皆为 key 的转换,而在此范例中使用的外部资源为 ActiveRecord
,他大部分的界面支援 Ruby 的原生物件 hash
,因此可以如下扩充 base class ,让 mapper 里专注在处理 entity hash key 对 schema hash key 的 mapping。
class Mapper < Boxenn::Repositories::RecordMapper
def build(hash)
result = batch_mapping(value: hash, mapper: map)
result = custom_mapping(input: hash, result: result) if respond_to?(:custom_mapping, :include_private)
result
end
def batch_mapping(value:, mapper:)
case value
when Array
value.map do |item|
mapping(hash: item, mapper: mapper)
end
when Hash
mapping(hash: value, mapper: mapper)
end
end
def mapping(hash:, mapper:)
result = {}
hash.each do |key, value|
next if !mapper.key?(key)
if mapper[key].is_a?(Array)
result[mapper[key].first] = batch_mapping(value: value, mapper: mapper[key][1])
else
result[mapper[key]] = value.is_a?(Hash) ? value.compact : value
end
end
result
end
end
# User
# input (entity 的 hash)
input = {
id_number: 'A123456789',
profile: {
name: '王小明',
gender: :male,
birthday: '1998/01/01',
email: '[email protected]',
},
}
# output (schema 的 hash)
output = {
id_number: 'A123456789',
user_profile: {
name: '王小明',
gender: :male,
birthdate: '1998/01/01',
email: '[email protected]',
age: 23,
}
}
# mapper
class UserMapper < Mapper
# 继承此 Mapper 可以定义两个 method, map 和 custom_mapping
# custom_mapping 只有在需要改值或做逻辑处理时才需要定义
private
# map 是用来将 entity hash 转为 db schema hash
# 如果遇到 nested 的 hash 请使用 array,第一个值为 key, 後面则为对应 hash 里的 key 配对
def map
{
id_number: :id_number,
profile: [
:user_profile,
{
name: :name,
gender: :gender,
birthday: :birthdate,
email: :email,
},
]
}
end
# custom_mapping 需传入两个参数, input 和 result
# input 为 entity 转成的 hash
# result 为已经转为 db schema key 的 hash
# 最後回传的是再特制处理过的 db schema key 的 hash
def custom_mapping(input:, result:)
result[:user_profile][:age] = age(input[:profile][:birthday])
result
end
def age(birthdate)
today = Time.zone.today
gap = today.year - birthdate.year
gap = gap - 1 if (
birthdate.month > today.month or
(birthdate.month >= today.month and birthdate.day > today.day)
)
gap
end
end
mapper = UserMapper.new
mapper.build(input) # => output
下一篇会介绍包装外部资源的 Source Wrapper。
Azure machine learning: Pipeline for data- 建立工作流程来...
Go provide pointer similar to C and C++. Go use &a...
tags: OC 30 day NSObject 是什麽? 是Foundation 框架中的类,在这...
昨天介绍了component,也知道要使用component要先注册才能使用,而今天我们要介绍的是p...
SQL && NoSQL SQL Structured Query Language...