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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
require 'enumerable/lazy' unless Enumerable.method_defined?(:lazy)
require 'delegate'
module Mustermann
# Class for defining and running simple Hash transformations.
#
# @example
# caster = Mustermann::Caster.new
# caster.register(:foo) { |value| { bar: value.upcase } }
# caster.cast(foo: "hello", baz: "world") # => { bar: "HELLO", baz: "world" }
#
# @see Mustermann::Expander#cast
#
# @!visibility private
class Caster < DelegateClass(Array)
# @param (see #register)
# @!visibility private
def initialize(*types, &block)
super([])
register(*types, &block)
end
# @param [Array<Symbol, Regexp, #cast, #===>] types identifier for cast type (some need block)
# @!visibility private
def register(*types, &block)
types << Any.new(&block) if types.empty?
types.each { |type| self << caster_for(type, &block) }
end
# @param [Symbol, Regexp, #cast, #===] type identifier for cast type (some need block)
# @return [#cast] specific cast operation
# @!visibility private
def caster_for(type, &block)
case type
when Symbol, Regexp then Key.new(type, &block)
else type.respond_to?(:cast) ? type : Value.new(type, &block)
end
end
# Transforms a Hash.
# @param [Hash] hash pre-transform Hash
# @return [Hash] post-transform Hash
# @!visibility private
def cast(hash)
merge = {}
hash.delete_if do |key, value|
next unless casted = lazy.map { |e| e.cast(key, value) }.detect { |e| e }
casted = { key => casted } unless casted.respond_to? :to_hash
merge.update(casted.to_hash)
end
hash.update(merge)
end
# Specific cast for remove nil values.
# @!visibility private
module Nil
# @see Mustermann::Caster#cast
# @!visibility private
def self.cast(key, value)
{} if value.nil?
end
end
# Class for block based casts that are triggered for every key/value pair.
# @!visibility private
class Any
# @!visibility private
def initialize(&block)
@block = block
end
# @see Mustermann::Caster#cast
# @!visibility private
def cast(key, value)
case @block.arity
when 0 then @block.call
when 1 then @block.call(value)
else @block.call(key, value)
end
end
end
# Class for block based casts that are triggered for key/value pairs with a matching value.
# @!visibility private
class Value < Any
# @param [#===] type used for matching values
# @!visibility private
def initialize(type, &block)
@type = type
super(&block)
end
# @see Mustermann::Caster#cast
# @!visibility private
def cast(key, value)
super if @type === value
end
end
# Class for block based casts that are triggered for key/value pairs with a matching key.
# @!visibility private
class Key < Any
# @param [#===] type used for matching keys
# @!visibility private
def initialize(type, &block)
@type = type
super(&block)
end
# @see Mustermann::Caster#cast
# @!visibility private
def cast(key, value)
super if @type === key
end
end
end
end
|