Hi, I’m wondering what is the best approach to this problem.
I have an input Hash, and as a result, I want to have a new key, that is a function of several other keys and values.
Question: How I could achieve this with dry-transformer?
input = { foo: 'b', bar: 'd' }
combine(input)
# => { foo: 'b', bar: 'd', zoo: 'bd' }
The function used to combine a sub hash can be anything, like:
def combine(hash)
res = ''"
res = hash[:foo] if hash[:foo] == 'b'
res += hash[:foo] if hash[:zoo] == 'd'
hash.merge(zoo: res)
end
My Ideas
I got some ideas, but only half-working.
Idea 1.
module Functions
extend Dry::Transformer::Registry
def self.set_zoo(hash)
res = ''"
res = hash[:foo] if hash[:foo] == 'b'
res += hash[:zoo] if hash[:zoo] == 'd'
hash.merge(zoo: res)
end
end
class ZooTransformation < Dry::Transformer::Pipe
import Dry::Transformer::HashTransformations
import Functions
define! do
deep_symbolize_keys
set_zoo
end
end
It is working, but not very convenient, as for each attribute calculation, I’d need separate method, but also I’d need to always merge hash, even if in case of calculating the value makes more sense to just return this value.
Idea 2
I thought then about adding a new transformation function, that would be general to all above.
module Functions
extend Dry::Transformer::Registry
# combines subhash of listed keys using given function, and saves result under the specified new key.
# @param [Hash] source Hash
# @param keys [Array] - list of keys to consider
# @param to [Symbol] - name of key used to save the result under
# @param with [Proc] - function used to transform value
# @return [Hash] Transformed hash, merged with given key and calculated value pair.
#
def self.combine(hash, keys:, to:, with:)
subhash = hash.select { |k| keys.include?(k) }
res = with[&subhash]
hash.merge(to => res)
end
end
class ZooTransformation < Dry::Transformer::Pipe
import Dry::Transformer::HashTransformations
import Functions
define! do
deep_symbolize_keys
combine(
keys: [:foo, :bar],
to: :zoo,
with: ZooCalculator.new.method(:call)
)
end
end
class ZooCalculator
def call(foo:, bar:)
res = ''"
res = foo if foo == 'b'
res += zoo if zoo == 'd'
res
end
end
This way I can keep my calculations injectable, I have no restrictions, on where to put the code, and I can keep return values intact while keeping the arguments list readable and explicit.
I wonder what are other ideas on how to do this.