Module: Dry::Mutations::Utils

Defined in:
lib/dry/mutations/utils/procs.rb,
lib/dry/mutations/utils/generic.rb,
lib/dry/mutations/utils/dry-mutations.rb

Overview

:nodoc:

Constant Summary collapse

USE_HASHIE_MASH =

Lazy detector for Hashie::Mash

TODO: Make it possible to choose friendly hash implementation
Falsey?(ENV['PLAIN_HASHES'], explicit: false) && begin
  require 'hashie/mash'
  true
rescue LoadError => e
  $stderr.puts [
    '[DRY] Could not find Hashie::Mash.',
    'You probably want to install it / add it to your Gemfile.',
    "Error: [#{e.message}]."
  ].join($/)
end
FALSEY =
/\A#{Regexp.union(%w(0 skip false falsey no n)).source}\z/i
TRUTHY =
/\A#{Regexp.union(%w(1 use true truthy yes y)).source}\z/i
ITSELF =
->(h, k) { h[k] = k }
DRY_TO_MUTATIONS =
{
  min_size?:    :min_length,
  max_size?:    :max_length,
  format?:      :matches,
  inclusion?:   :in, # deprecated in Dry
  included_in?: :in,
  gteq?:        :min,
  lteq?:        :max
}.freeze
MUTATIONS_TO_DRY =
DRY_TO_MUTATIONS.invert.merge(default: :default?).freeze

Class Method Summary collapse

Class Method Details

.Camel(whatever) ⇒ Object



33
34
35
36
37
# File 'lib/dry/mutations/utils/generic.rb', line 33

def self.Camel(whatever)
  whatever.to_s.split('__').map do |s|
    s.gsub(/(?:\A|_)(?<letter>\w)/) { $~[:letter].upcase }
  end.join('::')
end

.Constant(whatever) ⇒ Object



39
40
41
# File 'lib/dry/mutations/utils/generic.rb', line 39

def self.Constant(whatever)
  ::Kernel.const_get(Camel(whatever))
end

.Falsey?(input, explicit: true) ⇒ Boolean

Returns:

  • (Boolean)


7
8
9
# File 'lib/dry/mutations/utils/generic.rb', line 7

def self.Falsey? input, explicit: true
  explicit ? input.to_s =~ FALSEY : input.to_s !~ TRUTHY
end

.Guards(*keys, **params) ⇒ Object

Fuzzy converts params between different implementaions



27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/dry/mutations/utils/dry-mutations.rb', line 27

def self.Guards *keys, **params
  return {} if params.empty? || params[:empty] # Mutations `empty?` guard takes precedence on all others

  keys = params.keys if keys.empty?
  keys.flatten! # allow array to be passed as the only parameter

  map = [DRY_TO_MUTATIONS, MUTATIONS_TO_DRY].detect do |h|
    (h.keys & keys).any?
  end || Hash.new(&ITSELF)

  map.values_at(*keys).zip(keys.map(&params.method(:[])))
     .to_h
     .tap { |h| h.delete(nil) }
end

.Hash(hash = {}) ⇒ Object

Converts a hash to a best available hash implementation

with stringified keys, since `Mutations` expect hash
keys to be strings.


46
47
48
49
50
51
52
53
54
55
# File 'lib/dry/mutations/utils/generic.rb', line 46

def self.Hash hash = {}
  case
  when USE_HASHIE_MASH
    Kernel.const_get('::Hashie::Mash').new(hash)
  when hash.respond_to?(:with_indifferent_access)
    hash.with_indifferent_access
  else
    hash.map { |k, v| [k.to_s, v] }.to_h
  end
end

.RawInputs(*args) ⇒ Object



15
16
17
18
19
20
21
22
23
24
# File 'lib/dry/mutations/utils/dry-mutations.rb', line 15

def self.RawInputs *args
  args.inject(Utils.Hash({})) do |h, arg|
    h.merge! case arg
             when Hash then arg
             when ::Dry::Monads::Either::Right then arg.value
             when ::Dry::Monads::Either::Left then fail ArgumentError.new("Can’t accept Left value: #{args.inspect}.")
             else fail ArgumentError.new("All arguments must be hashes. Given: #{args.inspect}.") unless arg.is_a?(Hash)
             end
  end
end

.smart_send(receiver, *args, **params) ⇒ Object



69
70
71
# File 'lib/dry/mutations/utils/dry-mutations.rb', line 69

def self.smart_send receiver, *args, **params
  params.empty? ? receiver.__send__(*args) : receiver.__send__(*args, **params)
end

.Snake(whatever, short: false, symbolize: false) ⇒ Object



15
16
17
18
19
20
21
# File 'lib/dry/mutations/utils/generic.rb', line 15

def self.Snake(whatever, short: false, symbolize: false)
  result = whatever.to_s.split('::').map do |e|
    e.gsub(/(?<=[^\W_])(?=[A-Z])/, '_').downcase
  end
  result = short ? result.last : result.join('__')
  symbolize ? result.to_sym : result
end

.SnakeSafe(whatever, existing = [], update_existing: true, short: false, symbolize: false) ⇒ Object



23
24
25
26
27
28
29
30
31
# File 'lib/dry/mutations/utils/generic.rb', line 23

def self.SnakeSafe(whatever, existing = [], update_existing: true, short: false, symbolize: false)
  result = Snake(whatever, short: short)
  str = loop do
    break result unless existing.include? result
    suffix = result[/(?<=_)\d+(?=\z)/]
    suffix.nil? ? result << '_1' : result[-suffix.length..-1] = (suffix.to_i + 1).to_s
  end.tap { |r| existing << r if update_existing }
  symbolize ? str.to_sym : str
end

.Truthy?(input, explicit: true) ⇒ Boolean

Returns:

  • (Boolean)


11
12
13
# File 'lib/dry/mutations/utils/generic.rb', line 11

def self.Truthy? input, explicit: true
  explicit ? input.to_s =~ TRUTHY : input.to_s !~ FALSEY
end

.Type(type, **params) ⇒ Object



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
# File 'lib/dry/mutations/utils/dry-mutations.rb', line 42

def self.Type type, **params
  case type.to_s
  when 'string'
    if Falsey?(params[:strip])
      :str?
    else
      # TODO: this silently coerces everything to be a string
      #       when `param[:strip]` is specified. This is likely OK, though.
      ::Dry::Types::Constructor.new(
        ::Dry::Types['strict.string'], fn: ->(v) { v.to_s.strip }
      )
    end
  when 'date'
    ::Dry::Types::Constructor.new(
      ::Dry::Types['strict.date'], fn: ->(v) { v.is_a?(Date) ? v : (Date.parse(v.to_s) rescue v) }
    )
  when 'integer'
    :int?
    # FIXME: Why ints are not coercible?!
    #::Dry::Types::Constructor.new(
    #  ::Dry::Types['coercible.int'], fn: ->(v) { v.is_a?(Integer) ? v : (v.to_i rescue v) }
    # )
  when 'boolean' then :bool?
  else :"#{type}?"
  end
end

.Λ(input, **params) ⇒ Object



4
5
6
7
8
9
10
11
# File 'lib/dry/mutations/utils/procs.rb', line 4

def self.Λ input, **params
  case
  when params[:method] then input.method(params.delete[:method].to_sym).to_proc
  when input.respond_to?(:to_proc) then input.to_proc
  when input.respond_to?(:call) then input.method(:call).to_proc
  else fail ArgumentError, "The executor given can not be executed (forgot to specify :method param?)"
  end
end