File: caster.rb

package info (click to toggle)
ruby-mustermann19 0.4.3%2Bgit20160621-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 756 kB
  • ctags: 445
  • sloc: ruby: 7,197; makefile: 3
file content (117 lines) | stat: -rw-r--r-- 3,318 bytes parent folder | download
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