remap 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/remap/base.rb +229 -75
- data/lib/remap/compiler.rb +127 -37
- data/lib/remap/constructor/argument.rb +20 -6
- data/lib/remap/constructor/keyword.rb +20 -4
- data/lib/remap/constructor/none.rb +3 -4
- data/lib/remap/constructor.rb +12 -5
- data/lib/remap/contract.rb +27 -0
- data/lib/remap/extensions/enumerable.rb +48 -0
- data/lib/remap/extensions/hash.rb +13 -0
- data/lib/remap/extensions/object.rb +37 -0
- data/lib/remap/failure.rb +25 -15
- data/lib/remap/iteration/array.rb +20 -11
- data/lib/remap/iteration/hash.rb +21 -13
- data/lib/remap/iteration/other.rb +7 -7
- data/lib/remap/iteration.rb +8 -2
- data/lib/remap/mapper/and.rb +29 -7
- data/lib/remap/mapper/binary.rb +3 -6
- data/lib/remap/mapper/or.rb +29 -6
- data/lib/remap/mapper/support/operations.rb +40 -0
- data/lib/remap/mapper/xor.rb +29 -7
- data/lib/remap/mapper.rb +1 -48
- data/lib/remap/notice/traced.rb +19 -0
- data/lib/remap/notice/untraced.rb +11 -0
- data/lib/remap/notice.rb +34 -0
- data/lib/remap/operation.rb +26 -13
- data/lib/remap/path/input.rb +37 -0
- data/lib/remap/path/output.rb +26 -0
- data/lib/remap/path.rb +22 -0
- data/lib/remap/path_error.rb +13 -0
- data/lib/remap/proxy.rb +18 -0
- data/lib/remap/rule/each.rb +25 -24
- data/lib/remap/rule/embed.rb +33 -28
- data/lib/remap/rule/map/optional.rb +42 -0
- data/lib/remap/rule/map/required.rb +35 -0
- data/lib/remap/rule/map.rb +176 -55
- data/lib/remap/rule/set.rb +23 -33
- data/lib/remap/rule/support/collection/empty.rb +7 -7
- data/lib/remap/rule/support/collection/filled.rb +21 -8
- data/lib/remap/rule/support/collection.rb +11 -3
- data/lib/remap/rule/support/enum.rb +44 -21
- data/lib/remap/rule/void.rb +17 -18
- data/lib/remap/rule/wrap.rb +25 -17
- data/lib/remap/rule.rb +8 -1
- data/lib/remap/selector/all.rb +29 -7
- data/lib/remap/selector/index.rb +24 -16
- data/lib/remap/selector/key.rb +31 -16
- data/lib/remap/selector.rb +17 -0
- data/lib/remap/state/extension.rb +182 -208
- data/lib/remap/state/schema.rb +1 -1
- data/lib/remap/state.rb +30 -4
- data/lib/remap/static/fixed.rb +14 -3
- data/lib/remap/static/option.rb +21 -6
- data/lib/remap/static.rb +13 -0
- data/lib/remap/struct.rb +1 -0
- data/lib/remap/types.rb +13 -28
- data/lib/remap.rb +15 -19
- metadata +91 -89
- data/lib/remap/result.rb +0 -11
- data/lib/remap/rule/support/path.rb +0 -45
- data/lib/remap/state/types.rb +0 -11
- data/lib/remap/success.rb +0 -29
- data/lib/remap/version.rb +0 -5
    
        data/lib/remap/rule/map.rb
    CHANGED
    
    | @@ -2,62 +2,72 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Remap
         | 
| 4 4 | 
             
              class Rule
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
             | 
| 5 | 
            +
                using Extensions::Enumerable
         | 
| 6 | 
            +
                using State::Extension
         | 
| 7 7 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 8 | 
            +
                # Maps an input path to an output path
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # @example Map { name: "Ford" } to { person: { name: "Ford" } }
         | 
| 11 | 
            +
                #   class Mapper < Remap::Base
         | 
| 12 | 
            +
                #     define do
         | 
| 13 | 
            +
                #       map :name, to: [:person, :name]
         | 
| 14 | 
            +
                #     end
         | 
| 15 | 
            +
                #   end
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                #   Mapper.call({ name: "Ford" }) # => { person: { name: "Ford" } }
         | 
| 18 | 
            +
                class Map < Abstract
         | 
| 19 | 
            +
                  class Path < Struct
         | 
| 20 | 
            +
                    Output = Remap::Path::Output
         | 
| 21 | 
            +
                    Input = Remap::Path::Input
         | 
| 10 22 |  | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
                   | 
| 14 | 
            -
                  # 2. For each yielded value
         | 
| 15 | 
            -
                  #   2.1. Map value using {#rule}
         | 
| 16 | 
            -
                  #   2.2. Map value using {#fn}
         | 
| 23 | 
            +
                    attribute :output, Output.default { Output.call(EMPTY_ARRAY) }
         | 
| 24 | 
            +
                    attribute :input, Input.default { Input.call(EMPTY_ARRAY) }
         | 
| 25 | 
            +
                  end
         | 
| 17 26 |  | 
| 18 | 
            -
                  # @ | 
| 19 | 
            -
                   | 
| 20 | 
            -
             | 
| 21 | 
            -
                  # | 
| 22 | 
            -
                   | 
| 23 | 
            -
             | 
| 24 | 
            -
                  # | 
| 25 | 
            -
                   | 
| 26 | 
            -
             | 
| 27 | 
            +
                  # @return [Hash]
         | 
| 28 | 
            +
                  attribute? :path, Path.default { Path.call(EMPTY_HASH) }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  # @return [Rule]
         | 
| 31 | 
            +
                  attribute :rule, Rule.default { Void.call(EMPTY_HASH) }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  # @return [Array<String>]
         | 
| 34 | 
            +
                  attribute? :backtrace, Types::Backtrace, default: EMPTY_ARRAY
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  order :Optional, :Required
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  # Represents a required or optional mapping rule
         | 
| 27 39 | 
             
                  #
         | 
| 28 | 
            -
                  # | 
| 40 | 
            +
                  # @param state [State]
         | 
| 29 41 | 
             
                  #
         | 
| 30 | 
            -
                  # @ | 
| 31 | 
            -
                  # @param state [State] Current state
         | 
| 42 | 
            +
                  # @return [State]
         | 
| 32 43 | 
             
                  #
         | 
| 33 | 
            -
                  # @ | 
| 34 | 
            -
                  def call(state)
         | 
| 35 | 
            -
                     | 
| 36 | 
            -
                       | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
                       | 
| 44 | 
            +
                  # @abstract
         | 
| 45 | 
            +
                  def call(state, &error)
         | 
| 46 | 
            +
                    unless error
         | 
| 47 | 
            +
                      raise ArgumentError, "Map#call(state, &error) requires error handler block"
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    notice = catch :fatal do
         | 
| 51 | 
            +
                      return path.input.call(state) do |inner_state|
         | 
| 52 | 
            +
                        rule.call(inner_state) do |failure|
         | 
| 53 | 
            +
                          return error[failure]
         | 
| 54 | 
            +
                        end.then(&callback)
         | 
| 55 | 
            +
                      end.then(&path.output)
         | 
| 41 56 | 
             
                    end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    raise notice.traced(backtrace).exception
         | 
| 42 59 | 
             
                  end
         | 
| 43 60 |  | 
| 44 | 
            -
                  #  | 
| 45 | 
            -
                  #
         | 
| 46 | 
            -
                  # @example Add 5 to mapped value
         | 
| 47 | 
            -
                  #  class Mapper < Remap
         | 
| 48 | 
            -
                  #    define do
         | 
| 49 | 
            -
                  #      map.adjust do |value|
         | 
| 50 | 
            -
                  #        value + 5
         | 
| 51 | 
            -
                  #      end
         | 
| 52 | 
            -
                  #    end
         | 
| 53 | 
            -
                  #  end
         | 
| 54 | 
            -
                  #
         | 
| 55 | 
            -
                  #  Mapper.call(10) # => 15
         | 
| 61 | 
            +
                  # A post-processor method
         | 
| 56 62 | 
             
                  #
         | 
| 57 | 
            -
                  # @ | 
| 58 | 
            -
                  #  | 
| 63 | 
            +
                  # @example Upcase mapped value
         | 
| 64 | 
            +
                  #   state = Remap::State.call("Hello World")
         | 
| 65 | 
            +
                  #   map = Remap::Rule::Map.call({})
         | 
| 66 | 
            +
                  #   upcase = map.adjust(&:upcase)
         | 
| 67 | 
            +
                  #   error = -> failure { raise failure.exception }
         | 
| 68 | 
            +
                  #   upcase.call(state, &error).fetch(:value) # => "HELLO WORLD"
         | 
| 59 69 | 
             
                  #
         | 
| 60 | 
            -
                  # @return [ | 
| 70 | 
            +
                  # @return [Map]
         | 
| 61 71 | 
             
                  def adjust(&block)
         | 
| 62 72 | 
             
                    add do |state|
         | 
| 63 73 | 
             
                      state.execute(&block)
         | 
| @@ -65,45 +75,156 @@ module Remap | |
| 65 75 | 
             
                  end
         | 
| 66 76 | 
             
                  alias then adjust
         | 
| 67 77 |  | 
| 78 | 
            +
                  # A pending rule
         | 
| 79 | 
            +
                  #
         | 
| 80 | 
            +
                  # @param reason [String]
         | 
| 81 | 
            +
                  #
         | 
| 82 | 
            +
                  # @example Pending mapping
         | 
| 83 | 
            +
                  #   state = Remap::State.call(:value)
         | 
| 84 | 
            +
                  #   map = Remap::Rule::Map.call({})
         | 
| 85 | 
            +
                  #   pending = map.pending("this is pending")
         | 
| 86 | 
            +
                  #   error = -> failure { raise failure.exception }
         | 
| 87 | 
            +
                  #   pending.call(state, &error).key?(:value) # => false
         | 
| 88 | 
            +
                  #
         | 
| 89 | 
            +
                  # @return [Map]
         | 
| 68 90 | 
             
                  def pending(reason = "Pending mapping")
         | 
| 69 91 | 
             
                    add do |state|
         | 
| 70 | 
            -
                      state. | 
| 92 | 
            +
                      state.notice!(reason)
         | 
| 71 93 | 
             
                    end
         | 
| 72 94 | 
             
                  end
         | 
| 73 95 |  | 
| 96 | 
            +
                  # An enumeration processor
         | 
| 97 | 
            +
                  #
         | 
| 98 | 
            +
                  # @example A mapped enum
         | 
| 99 | 
            +
                  #   enum = Remap::Rule::Map.call({}).enum do
         | 
| 100 | 
            +
                  #     value "A", "B"
         | 
| 101 | 
            +
                  #     otherwise "C"
         | 
| 102 | 
            +
                  #   end
         | 
| 103 | 
            +
                  #
         | 
| 104 | 
            +
                  #   error = -> failure { raise failure.exception }
         | 
| 105 | 
            +
                  #
         | 
| 106 | 
            +
                  #   a = Remap::State.call("A")
         | 
| 107 | 
            +
                  #   enum.call(a, &error).fetch(:value) # => "A"
         | 
| 108 | 
            +
                  #
         | 
| 109 | 
            +
                  #   b = Remap::State.call("B")
         | 
| 110 | 
            +
                  #   enum.call(b, &error).fetch(:value) # => "B"
         | 
| 111 | 
            +
                  #
         | 
| 112 | 
            +
                  #   c = Remap::State.call("C")
         | 
| 113 | 
            +
                  #   enum.call(c, &error).fetch(:value) # => "C"
         | 
| 114 | 
            +
                  #
         | 
| 115 | 
            +
                  #   d = Remap::State.call("D")
         | 
| 116 | 
            +
                  #   enum.call(d, &error).fetch(:value) # => "C"
         | 
| 117 | 
            +
                  #
         | 
| 118 | 
            +
                  # @return [Map]
         | 
| 74 119 | 
             
                  def enum(&block)
         | 
| 75 | 
            -
                    add do | | 
| 76 | 
            -
                       | 
| 77 | 
            -
                        Enum.call(&block).get(id | 
| 120 | 
            +
                    add do |outer_state|
         | 
| 121 | 
            +
                      outer_state.fmap do |id, state|
         | 
| 122 | 
            +
                        Enum.call(&block).get(id) do
         | 
| 123 | 
            +
                          state.ignore!("Enum value %p (%s) not defined", id, id.class)
         | 
| 124 | 
            +
                        end
         | 
| 78 125 | 
             
                      end
         | 
| 79 126 | 
             
                    end
         | 
| 80 127 | 
             
                  end
         | 
| 81 128 |  | 
| 129 | 
            +
                  # Keeps map, only if block is true
         | 
| 130 | 
            +
                  #
         | 
| 131 | 
            +
                  # @example Keep if value contains "A"
         | 
| 132 | 
            +
                  #   map = Remap::Rule::Map.call({}).if do
         | 
| 133 | 
            +
                  #     value.include?("A")
         | 
| 134 | 
            +
                  #   end
         | 
| 135 | 
            +
                  #
         | 
| 136 | 
            +
                  #   error = -> failure { raise failure.exception }
         | 
| 137 | 
            +
                  #
         | 
| 138 | 
            +
                  #   a = Remap::State.call("A")
         | 
| 139 | 
            +
                  #   map.call(a, &error).fetch(:value) # => "A"
         | 
| 140 | 
            +
                  #
         | 
| 141 | 
            +
                  #   b = Remap::State.call("BA")
         | 
| 142 | 
            +
                  #   map.call(b, &error).fetch(:value) # => "BA"
         | 
| 143 | 
            +
                  #
         | 
| 144 | 
            +
                  #   c = Remap::State.call("C")
         | 
| 145 | 
            +
                  #   map.call(c, &error).key?(:value) # => false
         | 
| 146 | 
            +
                  #
         | 
| 147 | 
            +
                  # @return [Map]
         | 
| 82 148 | 
             
                  def if(&block)
         | 
| 83 | 
            -
                    add do | | 
| 84 | 
            -
                       | 
| 85 | 
            -
                        bool ?  | 
| 149 | 
            +
                    add do |outer_state|
         | 
| 150 | 
            +
                      outer_state.execute(&block).fmap do |bool, state|
         | 
| 151 | 
            +
                        bool ? outer_state.value : state.notice!("#if returned false")
         | 
| 86 152 | 
             
                      end
         | 
| 87 153 | 
             
                    end
         | 
| 88 154 | 
             
                  end
         | 
| 89 155 |  | 
| 156 | 
            +
                  # Keeps map, only if block is false
         | 
| 157 | 
            +
                  #
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                  # @example Keep unless value contains "A"
         | 
| 160 | 
            +
                  #   map = Remap::Rule::Map.call({}).if_not do
         | 
| 161 | 
            +
                  #     value.include?("A")
         | 
| 162 | 
            +
                  #   end
         | 
| 163 | 
            +
                  #
         | 
| 164 | 
            +
                  #   error = -> failure { raise failure.exception }
         | 
| 165 | 
            +
                  #
         | 
| 166 | 
            +
                  #   a = Remap::State.call("A")
         | 
| 167 | 
            +
                  #   map.call(a, &error).key?(:value) # => false
         | 
| 168 | 
            +
                  #
         | 
| 169 | 
            +
                  #   b = Remap::State.call("BA")
         | 
| 170 | 
            +
                  #   map.call(b, &error).key?(:value) # => false
         | 
| 171 | 
            +
                  #
         | 
| 172 | 
            +
                  #   c = Remap::State.call("C")
         | 
| 173 | 
            +
                  #   map.call(c, &error).fetch(:value) # => "C"
         | 
| 174 | 
            +
                  #
         | 
| 175 | 
            +
                  # @return [Map]
         | 
| 90 176 | 
             
                  def if_not(&block)
         | 
| 91 | 
            -
                    add do | | 
| 92 | 
            -
                       | 
| 93 | 
            -
                        bool ?  | 
| 177 | 
            +
                    add do |outer_state|
         | 
| 178 | 
            +
                      outer_state.execute(&block).fmap do |bool, state|
         | 
| 179 | 
            +
                        bool ? state.notice!("#if_not returned false") : outer_state.value
         | 
| 94 180 | 
             
                      end
         | 
| 95 181 | 
             
                    end
         | 
| 96 182 | 
             
                  end
         | 
| 97 183 |  | 
| 98 184 | 
             
                  private
         | 
| 99 185 |  | 
| 186 | 
            +
                  # @return [self]
         | 
| 100 187 | 
             
                  def add(&block)
         | 
| 101 188 | 
             
                    tap { fn << block }
         | 
| 102 189 | 
             
                  end
         | 
| 103 190 |  | 
| 191 | 
            +
                  # @return [Array<Proc>]
         | 
| 104 192 | 
             
                  def fn
         | 
| 105 193 | 
             
                    @fn ||= []
         | 
| 106 194 | 
             
                  end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                  # @return [Proc]
         | 
| 197 | 
            +
                  def callback
         | 
| 198 | 
            +
                    -> state do
         | 
| 199 | 
            +
                      fn.reduce(state) do |inner, fn|
         | 
| 200 | 
            +
                        fn[inner]
         | 
| 201 | 
            +
                      end
         | 
| 202 | 
            +
                    end
         | 
| 203 | 
            +
                  end
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                  # Catches :fatal and raises {Notice::Error}
         | 
| 206 | 
            +
                  #
         | 
| 207 | 
            +
                  # @param state [State]
         | 
| 208 | 
            +
                  # @param id (:fatal) [:fatal, :notice, :ignore]
         | 
| 209 | 
            +
                  #
         | 
| 210 | 
            +
                  # raise [Notice::Error]
         | 
| 211 | 
            +
                  def fatal(state, id: :fatal, &block)
         | 
| 212 | 
            +
                    raise catch(id, &block).traced(backtrace).exception
         | 
| 213 | 
            +
                  end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                  # Catches :notice exceptions and repackages them as a state
         | 
| 216 | 
            +
                  #
         | 
| 217 | 
            +
                  # @param state [State]
         | 
| 218 | 
            +
                  #
         | 
| 219 | 
            +
                  # @return [State]
         | 
| 220 | 
            +
                  def notice(state, &block)
         | 
| 221 | 
            +
                    state.set(notice: catch(:notice, &block).traced(backtrace)).except(:value)
         | 
| 222 | 
            +
                  end
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                  # @abstract
         | 
| 225 | 
            +
                  def ignore(...)
         | 
| 226 | 
            +
                    raise NotImplementedError, "#{self.class}#ignore"
         | 
| 227 | 
            +
                  end
         | 
| 107 228 | 
             
                end
         | 
| 108 229 | 
             
              end
         | 
| 109 230 | 
             
            end
         | 
    
        data/lib/remap/rule/set.rb
    CHANGED
    
    | @@ -2,41 +2,31 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Remap
         | 
| 4 4 | 
             
              class Rule
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
                  using State::Extension
         | 
| 5 | 
            +
                using State::Extension
         | 
| 7 6 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 7 | 
            +
                # Set path to a static value
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # @example Set path [:a, :b] to value "C"
         | 
| 10 | 
            +
                #   value = Remap::Static::Fixed.new(value: "a value")
         | 
| 11 | 
            +
                #   set = Remap::Rule::Set.new(value: value, path: [:a, :b])
         | 
| 12 | 
            +
                #   state = Remap::State.call("ANY VALUE")
         | 
| 13 | 
            +
                #   set.call(state).fetch(:value) # => { a: { b: "a value" } }
         | 
| 14 | 
            +
                #
         | 
| 15 | 
            +
                # @example Set path [:a, :b] to option :c
         | 
| 16 | 
            +
                #   value = Remap::Static::Option.new(name: :c)
         | 
| 17 | 
            +
                #   set = Remap::Rule::Set.new(value: value, path: [:a, :b])
         | 
| 18 | 
            +
                #   state = Remap::State.call("ANY VALUE", options: { c: "C" })
         | 
| 19 | 
            +
                #   set.call(state).fetch(:value) # => { a: { b: "C" } }
         | 
| 20 | 
            +
                class Set < Concrete
         | 
| 21 | 
            +
                  # @return [Static]
         | 
| 22 | 
            +
                  attribute :value, Static, alias: :rule
         | 
| 10 23 |  | 
| 11 | 
            -
                  #  | 
| 12 | 
            -
                   | 
| 13 | 
            -
             | 
| 14 | 
            -
                  #
         | 
| 15 | 
            -
                   | 
| 16 | 
            -
             | 
| 17 | 
            -
                  #     option :name
         | 
| 18 | 
            -
                  #
         | 
| 19 | 
            -
                  #     define do
         | 
| 20 | 
            -
                  #       set [:person, :name], to: option(:name)
         | 
| 21 | 
            -
                  #     end
         | 
| 22 | 
            -
                  #   end
         | 
| 23 | 
            -
                  #
         | 
| 24 | 
            -
                  #   Mapper.call(input, name: "John") # => { person: { name: "John" } }
         | 
| 25 | 
            -
                  #
         | 
| 26 | 
            -
                  # @example Given a value
         | 
| 27 | 
            -
                  #   class Mapper < Remap::Base
         | 
| 28 | 
            -
                  #     define do
         | 
| 29 | 
            -
                  #       set [:api_key], to: value("ABC-123")
         | 
| 30 | 
            -
                  #     end
         | 
| 31 | 
            -
                  #   end
         | 
| 32 | 
            -
                  #
         | 
| 33 | 
            -
                  #   Mapper.call(input) # => { api_key: "ABC-123" }
         | 
| 34 | 
            -
                  #
         | 
| 35 | 
            -
                  # @return [State]
         | 
| 36 | 
            -
                  def call(state)
         | 
| 37 | 
            -
                    path.call(state) do
         | 
| 38 | 
            -
                      value.call(state)
         | 
| 39 | 
            -
                    end
         | 
| 24 | 
            +
                  # @return [Path::Output]
         | 
| 25 | 
            +
                  attribute :path, Path::Output
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  # @see Rule#call
         | 
| 28 | 
            +
                  def call(...)
         | 
| 29 | 
            +
                    rule.call(...).then(&path)
         | 
| 40 30 | 
             
                  end
         | 
| 41 31 | 
             
                end
         | 
| 42 32 | 
             
              end
         | 
| @@ -3,19 +3,19 @@ | |
| 3 3 | 
             
            module Remap
         | 
| 4 4 | 
             
              class Rule
         | 
| 5 5 | 
             
                class Collection
         | 
| 6 | 
            -
                   | 
| 7 | 
            -
                    using State::Extension
         | 
| 6 | 
            +
                  using State::Extension
         | 
| 8 7 |  | 
| 9 | 
            -
             | 
| 8 | 
            +
                  # Represents an empty rule block
         | 
| 9 | 
            +
                  class Empty < Unit
         | 
| 10 | 
            +
                    attribute? :rules, Value(EMPTY_ARRAY), default: EMPTY_ARRAY
         | 
| 10 11 |  | 
| 11 12 | 
             
                    # Represents an empty define block, without any rules
         | 
| 12 13 | 
             
                    #
         | 
| 13 | 
            -
                    # @param  | 
| 14 | 
            -
                    # @param state [State]
         | 
| 14 | 
            +
                    # @param state [State<T>]
         | 
| 15 15 | 
             
                    #
         | 
| 16 | 
            -
                    # @return [ | 
| 16 | 
            +
                    # @return [State<T>]
         | 
| 17 17 | 
             
                    def call(state)
         | 
| 18 | 
            -
                      state. | 
| 18 | 
            +
                      state.notice!("No rules, empty block")
         | 
| 19 19 | 
             
                    end
         | 
| 20 20 | 
             
                  end
         | 
| 21 21 | 
             
                end
         | 
| @@ -3,23 +3,36 @@ | |
| 3 3 | 
             
            module Remap
         | 
| 4 4 | 
             
              class Rule
         | 
| 5 5 | 
             
                class Collection
         | 
| 6 | 
            -
                   | 
| 7 | 
            -
                    using State::Extension
         | 
| 6 | 
            +
                  using State::Extension
         | 
| 8 7 |  | 
| 8 | 
            +
                  # Represents a non-empty rule block
         | 
| 9 | 
            +
                  #
         | 
| 10 | 
            +
                  # @example A collection containing a single rule
         | 
| 11 | 
            +
                  #   state = Remap::State.call("A")
         | 
| 12 | 
            +
                  #   void = Remap::Rule::Void.call({})
         | 
| 13 | 
            +
                  #   rule = Remap::Rule::Collection.call([void])
         | 
| 14 | 
            +
                  #   error = -> failure { raise failure.exception }
         | 
| 15 | 
            +
                  #   rule.call(state, &error).fetch(:value) # => "A"
         | 
| 16 | 
            +
                  class Filled < Unit
         | 
| 17 | 
            +
                    # @return [Array<Rule>]
         | 
| 9 18 | 
             
                    attribute :rules, [Types.Interface(:call)], min_size: 1
         | 
| 10 19 |  | 
| 11 20 | 
             
                    # Represents a non-empty define block with one or more rules
         | 
| 12 | 
            -
                    # Calls every {#rules} with  | 
| 21 | 
            +
                    # Calls every {#rules} with state and merges the output
         | 
| 13 22 | 
             
                    #
         | 
| 14 23 | 
             
                    # @param state [State]
         | 
| 15 24 | 
             
                    #
         | 
| 16 25 | 
             
                    # @return [State]
         | 
| 17 | 
            -
                    def call(state)
         | 
| 18 | 
            -
                       | 
| 19 | 
            -
                         | 
| 20 | 
            -
                      end.map(&:value).reduce do |acc, inner_state|
         | 
| 21 | 
            -
                        acc.merged(inner_state)
         | 
| 26 | 
            +
                    def call(state, &error)
         | 
| 27 | 
            +
                      unless error
         | 
| 28 | 
            +
                        raise ArgumentError, "Collection::Filled#call(state, &error) requires a block"
         | 
| 22 29 | 
             
                      end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      rules.map do |rule|
         | 
| 32 | 
            +
                        rule.call(state) do |failure|
         | 
| 33 | 
            +
                          return error[failure]
         | 
| 34 | 
            +
                        end
         | 
| 35 | 
            +
                      end.reduce(&:combine)
         | 
| 23 36 | 
             
                    end
         | 
| 24 37 | 
             
                  end
         | 
| 25 38 | 
             
                end
         | 
| @@ -2,9 +2,17 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Remap
         | 
| 4 4 | 
             
              class Rule
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 5 | 
            +
                # Represents a block defined by a rule
         | 
| 6 | 
            +
                class Collection < Abstract
         | 
| 7 | 
            +
                  attribute :rules, Array
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  # @param state [State]
         | 
| 10 | 
            +
                  #
         | 
| 11 | 
            +
                  # @return [State]
         | 
| 12 | 
            +
                  #
         | 
| 13 | 
            +
                  # @abstract
         | 
| 14 | 
            +
                  def call(state)
         | 
| 15 | 
            +
                    raise NotImplementedError, "#{self.class}#call not implemented"
         | 
| 8 16 | 
             
                  end
         | 
| 9 17 | 
             
                end
         | 
| 10 18 | 
             
              end
         | 
| @@ -2,17 +2,32 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Remap
         | 
| 4 4 | 
             
              class Rule
         | 
| 5 | 
            -
                class Enum
         | 
| 6 | 
            -
                  include Dry::Core::Constants
         | 
| 5 | 
            +
                class Enum < Proxy
         | 
| 7 6 | 
             
                  include Dry::Monads[:maybe]
         | 
| 8 7 |  | 
| 9 | 
            -
                   | 
| 10 | 
            -
             | 
| 8 | 
            +
                  # @return [Hash]
         | 
| 11 9 | 
             
                  option :mappings, default: -> { Hash.new { default } }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  # @return [Maybe]
         | 
| 12 12 | 
             
                  option :default, default: -> { None() }
         | 
| 13 13 |  | 
| 14 14 | 
             
                  alias execute instance_eval
         | 
| 15 15 |  | 
| 16 | 
            +
                  # Builds an enumeration using the block as context
         | 
| 17 | 
            +
                  #
         | 
| 18 | 
            +
                  # @example
         | 
| 19 | 
            +
                  #   enum = Remap::Rule::Enum.call do
         | 
| 20 | 
            +
                  #     from "B", to: "C"
         | 
| 21 | 
            +
                  #     value "A"
         | 
| 22 | 
            +
                  #     otherwise "D"
         | 
| 23 | 
            +
                  #   end
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  #   enum.get("A") # => "A"
         | 
| 26 | 
            +
                  #   enum.get("B") # => "C"
         | 
| 27 | 
            +
                  #   enum.get("C") # => "C"
         | 
| 28 | 
            +
                  #   enum.get("MISSING") # => "D"
         | 
| 29 | 
            +
                  #
         | 
| 30 | 
            +
                  # @return [Any]
         | 
| 16 31 | 
             
                  def self.call(&block)
         | 
| 17 32 | 
             
                    unless block
         | 
| 18 33 | 
             
                      raise ArgumentError, "no block given"
         | 
| @@ -21,40 +36,48 @@ module Remap | |
| 21 36 | 
             
                    new.tap { _1.execute(&block) }
         | 
| 22 37 | 
             
                  end
         | 
| 23 38 |  | 
| 24 | 
            -
                   | 
| 25 | 
            -
             | 
| 26 | 
            -
                   | 
| 27 | 
            -
             | 
| 39 | 
            +
                  # Translates key into a value using predefined mappings
         | 
| 40 | 
            +
                  #
         | 
| 41 | 
            +
                  # @param key [#hash]
         | 
| 42 | 
            +
                  #
         | 
| 43 | 
            +
                  # @yield [String]
         | 
| 44 | 
            +
                  #   If the key is not found & no default value is set
         | 
| 45 | 
            +
                  #
         | 
| 46 | 
            +
                  # @return [Any]
         | 
| 28 47 | 
             
                  def get(key, &error)
         | 
| 29 48 | 
             
                    unless error
         | 
| 30 49 | 
             
                      return get(key) { raise Error, _1 }
         | 
| 31 50 | 
             
                    end
         | 
| 32 51 |  | 
| 33 | 
            -
                     | 
| 52 | 
            +
                    self[key].bind { return _1 }.or do
         | 
| 34 53 | 
             
                      error["Enum key [#{key}] not found among [#{mappings.keys.inspect}]"]
         | 
| 35 54 | 
             
                    end
         | 
| 36 55 | 
             
                  end
         | 
| 37 56 | 
             
                  alias call get
         | 
| 38 57 |  | 
| 39 | 
            -
                  #  | 
| 40 | 
            -
                   | 
| 41 | 
            -
             | 
| 58 | 
            +
                  # @return [Maybe]
         | 
| 59 | 
            +
                  def [](key)
         | 
| 60 | 
            +
                    mappings[key]
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  # @return [void]
         | 
| 42 64 | 
             
                  def from(*keys, to:)
         | 
| 65 | 
            +
                    value = Some(to)
         | 
| 66 | 
            +
             | 
| 43 67 | 
             
                    keys.each do |key|
         | 
| 44 | 
            -
                      mappings[key] =  | 
| 68 | 
            +
                      mappings[key] = value
         | 
| 69 | 
            +
                      mappings[to] = value
         | 
| 45 70 | 
             
                    end
         | 
| 46 71 | 
             
                  end
         | 
| 47 72 |  | 
| 48 | 
            -
                  #  | 
| 49 | 
            -
                   | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
                     | 
| 73 | 
            +
                  # @return [void]
         | 
| 74 | 
            +
                  def value(*ids)
         | 
| 75 | 
            +
                    ids.each do |id|
         | 
| 76 | 
            +
                      from(id, to: id)
         | 
| 77 | 
            +
                    end
         | 
| 53 78 | 
             
                  end
         | 
| 54 79 |  | 
| 55 | 
            -
                  #  | 
| 56 | 
            -
                  #
         | 
| 57 | 
            -
                  # @return [Void]
         | 
| 80 | 
            +
                  # @return [void]
         | 
| 58 81 | 
             
                  def otherwise(value)
         | 
| 59 82 | 
             
                    mappings.default = Some(value)
         | 
| 60 83 | 
             
                  end
         | 
    
        data/lib/remap/rule/void.rb
    CHANGED
    
    | @@ -2,27 +2,26 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Remap
         | 
| 4 4 | 
             
              class Rule
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
                  using State::Extension
         | 
| 5 | 
            +
                using State::Extension
         | 
| 7 6 |  | 
| 8 | 
            -
             | 
| 7 | 
            +
                # Represents a mapping without block
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # @example Maps "A" to "A"
         | 
| 10 | 
            +
                #   class Mapper < Remap::Base
         | 
| 11 | 
            +
                #     define do
         | 
| 12 | 
            +
                #       map
         | 
| 13 | 
            +
                #     end
         | 
| 14 | 
            +
                #   end
         | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                #  Mapper.call("A") # => "A"
         | 
| 17 | 
            +
                class Void < Concrete
         | 
| 18 | 
            +
                  # @param state [State<T>]
         | 
| 9 19 | 
             
                  #
         | 
| 10 | 
            -
                  # @ | 
| 11 | 
            -
                  #
         | 
| 12 | 
            -
                  # @example An empty rule
         | 
| 13 | 
            -
                  #   class Mapper < Remap::Base
         | 
| 14 | 
            -
                  #     define do
         | 
| 15 | 
            -
                  #       map do
         | 
| 16 | 
            -
                  #         # Empty ...
         | 
| 17 | 
            -
                  #       end
         | 
| 18 | 
            -
                  #     end
         | 
| 19 | 
            -
                  #   end
         | 
| 20 | 
            -
                  #
         | 
| 21 | 
            -
                  #   Mapper.call(input) # => input
         | 
| 22 | 
            -
                  #
         | 
| 23 | 
            -
                  # @return [State]
         | 
| 20 | 
            +
                  # @return [State<T>]
         | 
| 24 21 | 
             
                  def call(state)
         | 
| 25 | 
            -
                    state.bind  | 
| 22 | 
            +
                    state.bind do |value, inner_state|
         | 
| 23 | 
            +
                      inner_state.set(value)
         | 
| 24 | 
            +
                    end
         | 
| 26 25 | 
             
                  end
         | 
| 27 26 | 
             
                end
         | 
| 28 27 | 
             
              end
         | 
    
        data/lib/remap/rule/wrap.rb
    CHANGED
    
    | @@ -1,29 +1,37 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require "active_support/core_ext/array/wrap"
         | 
| 4 | 
            -
             | 
| 5 3 | 
             
            module Remap
         | 
| 6 4 | 
             
              class Rule
         | 
| 7 | 
            -
                 | 
| 8 | 
            -
                  using State::Extension
         | 
| 5 | 
            +
                using State::Extension
         | 
| 9 6 |  | 
| 7 | 
            +
                # Wraps rule in a type
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # @example Maps { name: "Ford" } to { cars: ["Ford"] }
         | 
| 10 | 
            +
                #   class Mapper < Remap::Base
         | 
| 11 | 
            +
                #     define do
         | 
| 12 | 
            +
                #       to :cars do
         | 
| 13 | 
            +
                #         wrap(:array) do
         | 
| 14 | 
            +
                #           map :name
         | 
| 15 | 
            +
                #         end
         | 
| 16 | 
            +
                #       end
         | 
| 17 | 
            +
                #     end
         | 
| 18 | 
            +
                #   end
         | 
| 19 | 
            +
                #
         | 
| 20 | 
            +
                #   Mapper.call({ name: "Ford" }) # => { cars: ["Ford"] }
         | 
| 21 | 
            +
                class Wrap < Concrete
         | 
| 22 | 
            +
                  # @return [:array]
         | 
| 10 23 | 
             
                  attribute :type, Value(:array)
         | 
| 11 | 
            -
             | 
| 24 | 
            +
             | 
| 25 | 
            +
                  # @return [Rule]
         | 
| 26 | 
            +
                  attribute :rule, Types::Rule
         | 
| 12 27 |  | 
| 13 28 | 
             
                  # Wraps the output from {#rule} in a {#type}
         | 
| 14 29 | 
             
                  #
         | 
| 15 | 
            -
                  # @ | 
| 16 | 
            -
                   | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
                  #       map :car
         | 
| 21 | 
            -
                  #     end
         | 
| 22 | 
            -
                  #   end
         | 
| 23 | 
            -
                  #
         | 
| 24 | 
            -
                  # @return [State]
         | 
| 25 | 
            -
                  def call(state)
         | 
| 26 | 
            -
                    rule.call(state).fmap { Array.wrap(_1) }
         | 
| 30 | 
            +
                  # @see Rule#call
         | 
| 31 | 
            +
                  def call(...)
         | 
| 32 | 
            +
                    rule.call(...).fmap do |value|
         | 
| 33 | 
            +
                      Array.wrap(value)
         | 
| 34 | 
            +
                    end
         | 
| 27 35 | 
             
                  end
         | 
| 28 36 | 
             
                end
         | 
| 29 37 | 
             
              end
         | 
    
        data/lib/remap/rule.rb
    CHANGED
    
    | @@ -1,8 +1,15 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            module Remap
         | 
| 4 | 
            -
              class Rule < Dry:: | 
| 4 | 
            +
              class Rule < Dry::Interface
         | 
| 5 5 | 
             
                defines :requirement
         | 
| 6 6 | 
             
                requirement Types::Any
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                # @param state [State]
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # @abstract
         | 
| 11 | 
            +
                def call(state)
         | 
| 12 | 
            +
                  raise NotImplementedError, "#{self.class}#call not implemented"
         | 
| 13 | 
            +
                end
         | 
| 7 14 | 
             
              end
         | 
| 8 15 | 
             
            end
         |