dry-transformer 0.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 +7 -0
- data/.codeclimate.yml +12 -0
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +10 -0
- data/.github/ISSUE_TEMPLATE/---bug-report.md +30 -0
- data/.github/ISSUE_TEMPLATE/---feature-request.md +18 -0
- data/.github/workflows/custom_ci.yml +66 -0
- data/.github/workflows/docsite.yml +34 -0
- data/.github/workflows/sync_configs.yml +34 -0
- data/.gitignore +16 -0
- data/.rspec +4 -0
- data/.rubocop.yml +95 -0
- data/CHANGELOG.md +3 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +19 -0
- data/LICENSE +20 -0
- data/README.md +29 -0
- data/Rakefile +6 -0
- data/docsite/source/built-in-transformations.html.md +47 -0
- data/docsite/source/index.html.md +15 -0
- data/docsite/source/transformation-objects.html.md +32 -0
- data/docsite/source/using-standalone-functions.html.md +82 -0
- data/dry-transformer.gemspec +22 -0
- data/lib/dry-transformer.rb +3 -0
- data/lib/dry/transformer.rb +23 -0
- data/lib/dry/transformer/all.rb +11 -0
- data/lib/dry/transformer/array.rb +183 -0
- data/lib/dry/transformer/array/combine.rb +65 -0
- data/lib/dry/transformer/class.rb +56 -0
- data/lib/dry/transformer/coercions.rb +196 -0
- data/lib/dry/transformer/compiler.rb +47 -0
- data/lib/dry/transformer/composite.rb +54 -0
- data/lib/dry/transformer/conditional.rb +76 -0
- data/lib/dry/transformer/constants.rb +7 -0
- data/lib/dry/transformer/error.rb +16 -0
- data/lib/dry/transformer/function.rb +109 -0
- data/lib/dry/transformer/hash.rb +453 -0
- data/lib/dry/transformer/pipe.rb +75 -0
- data/lib/dry/transformer/pipe/class_interface.rb +115 -0
- data/lib/dry/transformer/pipe/dsl.rb +58 -0
- data/lib/dry/transformer/proc.rb +46 -0
- data/lib/dry/transformer/recursion.rb +121 -0
- data/lib/dry/transformer/registry.rb +150 -0
- data/lib/dry/transformer/store.rb +128 -0
- data/lib/dry/transformer/version.rb +7 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/unit/array/combine_spec.rb +224 -0
- data/spec/unit/array_transformations_spec.rb +233 -0
- data/spec/unit/class_transformations_spec.rb +50 -0
- data/spec/unit/coercions_spec.rb +132 -0
- data/spec/unit/conditional_spec.rb +48 -0
- data/spec/unit/function_not_found_error_spec.rb +12 -0
- data/spec/unit/function_spec.rb +193 -0
- data/spec/unit/hash_transformations_spec.rb +490 -0
- data/spec/unit/proc_transformations_spec.rb +20 -0
- data/spec/unit/recursion_spec.rb +145 -0
- data/spec/unit/registry_spec.rb +202 -0
- data/spec/unit/store_spec.rb +198 -0
- data/spec/unit/transformer/class_interface_spec.rb +350 -0
- data/spec/unit/transformer/dsl_spec.rb +15 -0
- data/spec/unit/transformer/instance_methods_spec.rb +25 -0
- metadata +119 -0
| @@ -0,0 +1,56 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Dry
         | 
| 4 | 
            +
              module Transformer
         | 
| 5 | 
            +
                # Transformation functions for Classes
         | 
| 6 | 
            +
                #
         | 
| 7 | 
            +
                # @example
         | 
| 8 | 
            +
                #   require 'dry/transformer/class'
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                #   include Dry::Transformer::Helper
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                #   fn = t(:constructor_inject, Struct)
         | 
| 13 | 
            +
                #
         | 
| 14 | 
            +
                #   fn['User', :name, :age]
         | 
| 15 | 
            +
                #   # => Struct::User
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # @api public
         | 
| 18 | 
            +
                module ClassTransformations
         | 
| 19 | 
            +
                  extend Registry
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  # Inject given arguments into the constructor of the class
         | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # @example
         | 
| 24 | 
            +
                  #   Transproct(:constructor_inject, Struct)['User', :name, :age]
         | 
| 25 | 
            +
                  #   # => Struct::User
         | 
| 26 | 
            +
                  #
         | 
| 27 | 
            +
                  # @param [*Mixed] A list of arguments to inject
         | 
| 28 | 
            +
                  #
         | 
| 29 | 
            +
                  # @return [Object] An instance of the given klass
         | 
| 30 | 
            +
                  #
         | 
| 31 | 
            +
                  # @api public
         | 
| 32 | 
            +
                  def self.constructor_inject(*args, klass)
         | 
| 33 | 
            +
                    klass.new(*args)
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  # Set instance variables from the hash argument (key/value pairs) on the object
         | 
| 37 | 
            +
                  #
         | 
| 38 | 
            +
                  # @example
         | 
| 39 | 
            +
                  #   Dry::Transformer(:set_ivars, Object)[name: 'Jane', age: 25]
         | 
| 40 | 
            +
                  #   # => #<Object:0x007f411d06a210 @name="Jane", @age=25>
         | 
| 41 | 
            +
                  #
         | 
| 42 | 
            +
                  # @param [Object]
         | 
| 43 | 
            +
                  #
         | 
| 44 | 
            +
                  # @return [Object]
         | 
| 45 | 
            +
                  #
         | 
| 46 | 
            +
                  # @api public
         | 
| 47 | 
            +
                  def self.set_ivars(ivar_hash, klass)
         | 
| 48 | 
            +
                    object = klass.allocate
         | 
| 49 | 
            +
                    ivar_hash.each do |ivar_name, ivar_value|
         | 
| 50 | 
            +
                      object.instance_variable_set("@#{ivar_name}", ivar_value)
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                    object
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
            end
         | 
| @@ -0,0 +1,196 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'date'
         | 
| 4 | 
            +
            require 'time'
         | 
| 5 | 
            +
            require 'bigdecimal'
         | 
| 6 | 
            +
            require 'bigdecimal/util'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            module Dry
         | 
| 9 | 
            +
              module Transformer
         | 
| 10 | 
            +
                # Coercion functions for common types
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                # @api public
         | 
| 13 | 
            +
                module Coercions
         | 
| 14 | 
            +
                  extend Registry
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  TRUE_VALUES = [true, 1, '1', 'on', 't', 'true', 'y', 'yes'].freeze
         | 
| 17 | 
            +
                  FALSE_VALUES = [false, 0, '0', 'off', 'f', 'false', 'n', 'no', nil].freeze
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  BOOLEAN_MAP = Hash[
         | 
| 20 | 
            +
                    TRUE_VALUES.product([true]) + FALSE_VALUES.product([false])
         | 
| 21 | 
            +
                  ].freeze
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  # Does nothing and returns a value
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  # @example
         | 
| 26 | 
            +
                  #   fn = Coercions[:identity]
         | 
| 27 | 
            +
                  #   fn[:foo] # => :foo
         | 
| 28 | 
            +
                  #
         | 
| 29 | 
            +
                  # @param [Object] value
         | 
| 30 | 
            +
                  #
         | 
| 31 | 
            +
                  # @return [Object]
         | 
| 32 | 
            +
                  #
         | 
| 33 | 
            +
                  # @api public
         | 
| 34 | 
            +
                  def self.identity(value = nil)
         | 
| 35 | 
            +
                    value
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  # Coerce value into a string
         | 
| 39 | 
            +
                  #
         | 
| 40 | 
            +
                  # @example
         | 
| 41 | 
            +
                  #   Dry::Transformer(:to_string)[1]
         | 
| 42 | 
            +
                  #   # => "1"
         | 
| 43 | 
            +
                  #
         | 
| 44 | 
            +
                  # @param [Object] value The input value
         | 
| 45 | 
            +
                  #
         | 
| 46 | 
            +
                  # @return [String]
         | 
| 47 | 
            +
                  #
         | 
| 48 | 
            +
                  # @api public
         | 
| 49 | 
            +
                  def self.to_string(value)
         | 
| 50 | 
            +
                    value.to_s
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  # Coerce value into a symbol
         | 
| 54 | 
            +
                  #
         | 
| 55 | 
            +
                  # @example
         | 
| 56 | 
            +
                  #   Dry::Transformer(:to_symbol)['foo']
         | 
| 57 | 
            +
                  #   # => :foo
         | 
| 58 | 
            +
                  #
         | 
| 59 | 
            +
                  # @param [#to_s] value The input value
         | 
| 60 | 
            +
                  #
         | 
| 61 | 
            +
                  # @return [Symbol]
         | 
| 62 | 
            +
                  #
         | 
| 63 | 
            +
                  # @api public
         | 
| 64 | 
            +
                  def self.to_symbol(value)
         | 
| 65 | 
            +
                    value.to_s.to_sym
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  # Coerce value into a integer
         | 
| 69 | 
            +
                  #
         | 
| 70 | 
            +
                  # @example
         | 
| 71 | 
            +
                  #   Dry::Transformer(:to_integer)['1']
         | 
| 72 | 
            +
                  #   # => 1
         | 
| 73 | 
            +
                  #
         | 
| 74 | 
            +
                  # @param [Object] value The input value
         | 
| 75 | 
            +
                  #
         | 
| 76 | 
            +
                  # @return [Integer]
         | 
| 77 | 
            +
                  #
         | 
| 78 | 
            +
                  # @api public
         | 
| 79 | 
            +
                  def self.to_integer(value)
         | 
| 80 | 
            +
                    value.to_i
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  # Coerce value into a float
         | 
| 84 | 
            +
                  #
         | 
| 85 | 
            +
                  # @example
         | 
| 86 | 
            +
                  #   Dry::Transformer(:to_float)['1.2']
         | 
| 87 | 
            +
                  #   # => 1.2
         | 
| 88 | 
            +
                  #
         | 
| 89 | 
            +
                  # @param [Object] value The input value
         | 
| 90 | 
            +
                  #
         | 
| 91 | 
            +
                  # @return [Float]
         | 
| 92 | 
            +
                  #
         | 
| 93 | 
            +
                  # @api public
         | 
| 94 | 
            +
                  def self.to_float(value)
         | 
| 95 | 
            +
                    value.to_f
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  # Coerce value into a decimal
         | 
| 99 | 
            +
                  #
         | 
| 100 | 
            +
                  # @example
         | 
| 101 | 
            +
                  #   Dry::Transformer(:to_decimal)[1.2]
         | 
| 102 | 
            +
                  #   # => #<BigDecimal:7fca32acea50,'0.12E1',18(36)>
         | 
| 103 | 
            +
                  #
         | 
| 104 | 
            +
                  # @param [Object] value The input value
         | 
| 105 | 
            +
                  #
         | 
| 106 | 
            +
                  # @return [Decimal]
         | 
| 107 | 
            +
                  #
         | 
| 108 | 
            +
                  # @api public
         | 
| 109 | 
            +
                  def self.to_decimal(value)
         | 
| 110 | 
            +
                    value.to_d
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                  # Coerce value into a boolean
         | 
| 114 | 
            +
                  #
         | 
| 115 | 
            +
                  # @example
         | 
| 116 | 
            +
                  #   Dry::Transformer(:to_boolean)['true']
         | 
| 117 | 
            +
                  #   # => true
         | 
| 118 | 
            +
                  #   Dry::Transformer(:to_boolean)['f']
         | 
| 119 | 
            +
                  #   # => false
         | 
| 120 | 
            +
                  #
         | 
| 121 | 
            +
                  # @param [Object] value The input value
         | 
| 122 | 
            +
                  #
         | 
| 123 | 
            +
                  # @return [TrueClass,FalseClass]
         | 
| 124 | 
            +
                  #
         | 
| 125 | 
            +
                  # @api public
         | 
| 126 | 
            +
                  def self.to_boolean(value)
         | 
| 127 | 
            +
                    BOOLEAN_MAP.fetch(value)
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  # Coerce value into a date
         | 
| 131 | 
            +
                  #
         | 
| 132 | 
            +
                  # @example
         | 
| 133 | 
            +
                  #   Dry::Transformer(:to_date)['2015-04-14']
         | 
| 134 | 
            +
                  #   # => #<Date: 2015-04-14 ((2457127j,0s,0n),+0s,2299161j)>
         | 
| 135 | 
            +
                  #
         | 
| 136 | 
            +
                  # @param [Object] value The input value
         | 
| 137 | 
            +
                  #
         | 
| 138 | 
            +
                  # @return [Date]
         | 
| 139 | 
            +
                  #
         | 
| 140 | 
            +
                  # @api public
         | 
| 141 | 
            +
                  def self.to_date(value)
         | 
| 142 | 
            +
                    Date.parse(value)
         | 
| 143 | 
            +
                  end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                  # Coerce value into a time
         | 
| 146 | 
            +
                  #
         | 
| 147 | 
            +
                  # @example
         | 
| 148 | 
            +
                  #   Dry::Transformer(:to_time)['2015-04-14 12:01:45']
         | 
| 149 | 
            +
                  #   # => 2015-04-14 12:01:45 +0200
         | 
| 150 | 
            +
                  #
         | 
| 151 | 
            +
                  # @param [Object] value The input value
         | 
| 152 | 
            +
                  #
         | 
| 153 | 
            +
                  # @return [Time]
         | 
| 154 | 
            +
                  #
         | 
| 155 | 
            +
                  # @api public
         | 
| 156 | 
            +
                  def self.to_time(value)
         | 
| 157 | 
            +
                    Time.parse(value)
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                  # Coerce value into a datetime
         | 
| 161 | 
            +
                  #
         | 
| 162 | 
            +
                  # @example
         | 
| 163 | 
            +
                  #   Dry::Transformer(:to_datetime)['2015-04-14 12:01:45']
         | 
| 164 | 
            +
                  #   # => #<DateTime: 2015-04-14T12:01:45+00:00 ((2457127j,43305s,0n),+0s,2299161j)>
         | 
| 165 | 
            +
                  #
         | 
| 166 | 
            +
                  # @param [Object] value The input value
         | 
| 167 | 
            +
                  #
         | 
| 168 | 
            +
                  # @return [DateTime]
         | 
| 169 | 
            +
                  #
         | 
| 170 | 
            +
                  # @api public
         | 
| 171 | 
            +
                  def self.to_datetime(value)
         | 
| 172 | 
            +
                    DateTime.parse(value)
         | 
| 173 | 
            +
                  end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                  # Coerce value into an array containing tuples only
         | 
| 176 | 
            +
                  #
         | 
| 177 | 
            +
                  # If the source is not an array, or doesn't contain a tuple, returns
         | 
| 178 | 
            +
                  # an array with one empty tuple
         | 
| 179 | 
            +
                  #
         | 
| 180 | 
            +
                  # @example
         | 
| 181 | 
            +
                  #   Dry::Transformer(:to_tuples)[:foo]                  # => [{}]
         | 
| 182 | 
            +
                  #   Dry::Transformer(:to_tuples)[[]]                    # => [{}]
         | 
| 183 | 
            +
                  #   Dry::Transformer(:to_tuples)[[{ foo: :FOO, :bar }]] # => [{ foo: :FOO }]
         | 
| 184 | 
            +
                  #
         | 
| 185 | 
            +
                  # @param [Object] value
         | 
| 186 | 
            +
                  #
         | 
| 187 | 
            +
                  # @return [Array<Hash>]
         | 
| 188 | 
            +
                  #
         | 
| 189 | 
            +
                  def self.to_tuples(value)
         | 
| 190 | 
            +
                    array = value.is_a?(Array) ? Array[*value] : [{}]
         | 
| 191 | 
            +
                    array.select! { |item| item.is_a?(Hash) }
         | 
| 192 | 
            +
                    array.any? ? array : [{}]
         | 
| 193 | 
            +
                  end
         | 
| 194 | 
            +
                end
         | 
| 195 | 
            +
              end
         | 
| 196 | 
            +
            end
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Dry
         | 
| 4 | 
            +
              module Transformer
         | 
| 5 | 
            +
                # @api private
         | 
| 6 | 
            +
                class Compiler
         | 
| 7 | 
            +
                  InvalidFunctionNameError = Class.new(StandardError)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  attr_reader :registry, :transformer
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def initialize(registry, transformer = nil)
         | 
| 12 | 
            +
                    @registry = registry
         | 
| 13 | 
            +
                    @transformer = transformer
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def call(ast)
         | 
| 17 | 
            +
                    ast.map(&method(:visit)).reduce(:>>)
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def visit(node)
         | 
| 21 | 
            +
                    id, *rest = node
         | 
| 22 | 
            +
                    public_send(:"visit_#{id}", *rest)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def visit_fn(node)
         | 
| 26 | 
            +
                    name, rest = node
         | 
| 27 | 
            +
                    args = rest.map { |arg| visit(arg) }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    if registry.contain?(name)
         | 
| 30 | 
            +
                      registry[name, *args]
         | 
| 31 | 
            +
                    elsif transformer.respond_to?(name)
         | 
| 32 | 
            +
                      Function.new(transformer.method(name), name: name, args: args)
         | 
| 33 | 
            +
                    else
         | 
| 34 | 
            +
                      raise InvalidFunctionNameError, "function name +#{name}+ is not valid"
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def visit_arg(arg)
         | 
| 39 | 
            +
                    arg
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def visit_t(node)
         | 
| 43 | 
            +
                    call(node)
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Dry
         | 
| 4 | 
            +
              module Transformer
         | 
| 5 | 
            +
                # Composition of two functions
         | 
| 6 | 
            +
                #
         | 
| 7 | 
            +
                # @api private
         | 
| 8 | 
            +
                class Composite
         | 
| 9 | 
            +
                  # @return [Proc]
         | 
| 10 | 
            +
                  #
         | 
| 11 | 
            +
                  # @api private
         | 
| 12 | 
            +
                  attr_reader :left
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  # @return [Proc]
         | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  # @api private
         | 
| 17 | 
            +
                  attr_reader :right
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  # @api private
         | 
| 20 | 
            +
                  def initialize(left, right)
         | 
| 21 | 
            +
                    @left = left
         | 
| 22 | 
            +
                    @right = right
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  # Call right side with the result from the left side
         | 
| 26 | 
            +
                  #
         | 
| 27 | 
            +
                  # @param [Object] value The input value
         | 
| 28 | 
            +
                  #
         | 
| 29 | 
            +
                  # @return [Object]
         | 
| 30 | 
            +
                  #
         | 
| 31 | 
            +
                  # @api public
         | 
| 32 | 
            +
                  def call(value)
         | 
| 33 | 
            +
                    right.call(left.call(value))
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                  alias_method :[], :call
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  # @see Function#compose
         | 
| 38 | 
            +
                  #
         | 
| 39 | 
            +
                  # @api public
         | 
| 40 | 
            +
                  def compose(other)
         | 
| 41 | 
            +
                    self.class.new(self, other)
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                  alias_method :+, :compose
         | 
| 44 | 
            +
                  alias_method :>>, :compose
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  # @see Function#to_ast
         | 
| 47 | 
            +
                  #
         | 
| 48 | 
            +
                  # @api public
         | 
| 49 | 
            +
                  def to_ast
         | 
| 50 | 
            +
                    left.to_ast << right.to_ast
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
            end
         | 
| @@ -0,0 +1,76 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Dry
         | 
| 4 | 
            +
              module Transformer
         | 
| 5 | 
            +
                # Conditional transformation functions
         | 
| 6 | 
            +
                #
         | 
| 7 | 
            +
                # @example
         | 
| 8 | 
            +
                #   require 'dry/transformer/conditional'
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                #   include Dry::Transformer::Helper
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                #   fn = t(:guard, -> s { s.is_a?(::String) }, -> s { s.to_sym })
         | 
| 13 | 
            +
                #
         | 
| 14 | 
            +
                #   [fn[2], fn['Jane']]
         | 
| 15 | 
            +
                #   # => [2, :Jane]
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # @api public
         | 
| 18 | 
            +
                module Conditional
         | 
| 19 | 
            +
                  extend Registry
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  # Negates the result of transformation
         | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # @example
         | 
| 24 | 
            +
                  #   fn = Conditional[:not, -> value { value.is_a? ::String }]
         | 
| 25 | 
            +
                  #   fn[:foo]  # => true
         | 
| 26 | 
            +
                  #   fn["foo"] # => false
         | 
| 27 | 
            +
                  #
         | 
| 28 | 
            +
                  # @param [Object] value
         | 
| 29 | 
            +
                  # @param [Proc] fn
         | 
| 30 | 
            +
                  #
         | 
| 31 | 
            +
                  # @return [Boolean]
         | 
| 32 | 
            +
                  #
         | 
| 33 | 
            +
                  # @api public
         | 
| 34 | 
            +
                  def self.not(value, fn)
         | 
| 35 | 
            +
                    !fn[value]
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  # Apply the transformation function to subject if the predicate returns true, or return un-modified
         | 
| 39 | 
            +
                  #
         | 
| 40 | 
            +
                  # @example
         | 
| 41 | 
            +
                  #   [2, 'Jane'].map do |subject|
         | 
| 42 | 
            +
                  #     Dry::Transformer(:guard, -> s { s.is_a?(::String) }, -> s { s.to_sym })[subject]
         | 
| 43 | 
            +
                  #   end
         | 
| 44 | 
            +
                  #   # => [2, :Jane]
         | 
| 45 | 
            +
                  #
         | 
| 46 | 
            +
                  # @param [Mixed]
         | 
| 47 | 
            +
                  #
         | 
| 48 | 
            +
                  # @return [Mixed]
         | 
| 49 | 
            +
                  #
         | 
| 50 | 
            +
                  # @api public
         | 
| 51 | 
            +
                  def self.guard(value, predicate, fn)
         | 
| 52 | 
            +
                    predicate[value] ? fn[value] : value
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  # Calls a function when type-check passes
         | 
| 56 | 
            +
                  #
         | 
| 57 | 
            +
                  # @example
         | 
| 58 | 
            +
                  #   fn = Dry::Transformer(:is, Array, -> arr { arr.map(&:upcase) })
         | 
| 59 | 
            +
                  #   fn.call(['a', 'b', 'c']) # => ['A', 'B', 'C']
         | 
| 60 | 
            +
                  #
         | 
| 61 | 
            +
                  #   fn = Dry::Transformer(:is, Array, -> arr { arr.map(&:upcase) })
         | 
| 62 | 
            +
                  #   fn.call('foo') # => "foo"
         | 
| 63 | 
            +
                  #
         | 
| 64 | 
            +
                  # @param [Object]
         | 
| 65 | 
            +
                  # @param [Class]
         | 
| 66 | 
            +
                  # @param [Proc]
         | 
| 67 | 
            +
                  #
         | 
| 68 | 
            +
                  # @return [Object]
         | 
| 69 | 
            +
                  #
         | 
| 70 | 
            +
                  # @api public
         | 
| 71 | 
            +
                  def self.is(value, type, fn)
         | 
| 72 | 
            +
                    guard(value, -> v { v.is_a?(type) }, fn)
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
            end
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Dry
         | 
| 4 | 
            +
              module Transformer
         | 
| 5 | 
            +
                Error = Class.new(StandardError)
         | 
| 6 | 
            +
                FunctionAlreadyRegisteredError = Class.new(Error)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                class FunctionNotFoundError < Error
         | 
| 9 | 
            +
                  def initialize(function, source = nil)
         | 
| 10 | 
            +
                    return super "No registered function #{source}[:#{function}]" if source
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    super "No globally registered function for #{function}"
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         |