Module: Dry::Mutations::Extensions::Command

Includes:
Dry::Monads::Either::Mixin
Defined in:
lib/dry/mutations/extensions/command.rb

Overview

:nodoc:

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#validationObject (readonly)

Returns the value of attribute validation.



50
51
52
# File 'lib/dry/mutations/extensions/command.rb', line 50

def validation
  @validation
end

Class Method Details

.prepended(base) ⇒ Object



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

def self.prepended base
  fail ArgumentError, "Can not prepend #{self.class} to #{base.class}: base class must be a ::Mutations::Command descendant." unless base < ::Mutations::Command
  base.extend(DSL::Module) unless base.ancestors.include?(DSL::Module)
  base.extend(Module.new do
    def exceptions_as_errors(value)
      @exceptions_as_errors = value
    end

    def finalizers(outcome: nil, errors: nil)
      @finalizers = { outcome: outcome, errors: errors }
    end

    def call(*args)
      callable = to_proc.(*args)
      outcome = callable.()
    ensure
      ::Dry::Mutations::Utils.extend_outcome outcome, callable.host if callable.respond_to?(:host)
    end

    def to_proc
      ->(*args) { new(*args) }
    end

    if base.name && !::Kernel.methods.include?(base_name = base.name.split('::').last.to_sym)
      ::Kernel.class_eval <<-FACTORY, __FILE__, __LINE__ + 1
        def #{base_name}(*args)
          #{base}.call(*args)
        end
      FACTORY
    end
  end)

  base.singleton_class.prepend(Module.new do
    def respond_to_missing?(method_name, include_private = false)
      %i|exceptions_as_errors finalizers call to_proc|.include?(method_name) || super
    end
  end)

  define_method :host do
    base.to_s
  end
end

Instance Method Details

#add_error(key, kind, message = nil, dry_message = nil) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/dry/mutations/extensions/command.rb', line 129

def add_error(key, kind, message = nil, dry_message = nil)
  fail ArgumentError.new("Invalid kind #{kind}") unless kind.is_a?(Symbol)

  path = key.to_s.split('.')
  # ["#<struct Dry::Validation::Message
  #            predicate=:int?,
  #            path=[:maturity_set, :maturity_days_set, :days],
  #            text=\"must be an integer\",
  #            options={:args=>[], :rule=>:days, :each=>false}>"
  last = path.pop
  dry_message ||= ::Dry::Validation::Message.new(kind, last, message, rule: :♻)
  atom = Errors::ErrorAtom.new(last, kind, dry_message, message: message)

  (@errors ||= ::Mutations::ErrorHash.new).tap do |errs|
    path.inject(errs) do |cur_errors, part|
      cur_errors[part.to_s] ||= ::Mutations::ErrorHash.new
    end[last] = atom
  end # [key] = Errors::ErrorAtom.new(key, kind, dry_message, message: message)
end

#callObject



88
89
90
# File 'lib/dry/mutations/extensions/command.rb', line 88

def call
  run.either
end

#discard_empty!Object



106
107
108
109
110
111
# File 'lib/dry/mutations/extensions/command.rb', line 106

def discard_empty!
  discarded = schema.respond_to?(:discarded) ? schema.discarded : []
  schema.(
    ::Dry::Mutations::Utils.Hash(@raw_inputs.reject { |k, v| discarded.include?(k.to_sym) && vacant?(v) })
  )
end

#executeObject



121
122
123
124
125
126
127
# File 'lib/dry/mutations/extensions/command.rb', line 121

def execute
  super.tap { |outcome| finalizer(:outcome, outcome) }
rescue => e
  add_error(:♻, :runtime_exception, "#{e.class.name}: #{e.message}")
  finalizer(:errors, @errors)
  raise e unless exceptions_as_errors?
end

#initialize(*args) ⇒ Object



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
# File 'lib/dry/mutations/extensions/command.rb', line 52

def initialize(*args)
  @raw_inputs = defaults.merge(Utils.RawInputs(*args))
  @validation_result = discard_empty!
  @inputs = Utils.Hash @validation_result.output

  fix_accessors!

  # dry: {:name=>["size cannot be greater than 10"],
  #       :properties=>{:first_arg=>["must be a string", "is in invalid format"]},
  #       :second_arg=>{:second_sub_arg=>["must be one of: 42"]},
  #       :amount=>["must be one of: 42"]}}
  # mut: {:name=>#<Mutations::ErrorAtom:0x00000009534e50 @key=:name, @symbol=:max_length, @message=nil, @index=nil>,
  #       :properties=>{
  #           :second_arg=>{:second_sub_arg=>#<Mutations::ErrorAtom:0x000000095344a0 @key=:second_sub_arg, @symbol=:in, @message=nil, @index=nil>}
  #       :amount=>#<Mutations::ErrorAtom:0x00000009534068 @key=:amount, @symbol=:in, @message=nil, @index=nil>}

  @errors = Errors::ErrorAtom.patch_message_set(
    Errors::ErrorCompiler.new(schema).(@validation_result.to_ast.last)
  )

  # Run a custom validation method if supplied:
  validate unless has_errors?

  finalizer(:errors, @errors) if has_errors?
end

#messagesObject



149
150
151
# File 'lib/dry/mutations/extensions/command.rb', line 149

def messages
  @messages ||= yield_messages
end

#runObject

Functional helpers



82
83
84
85
86
# File 'lib/dry/mutations/extensions/command.rb', line 82

def run
  outcome = super
ensure
  ::Dry::Mutations::Utils.extend_outcome outcome, host
end

#vacant?(value) ⇒ Boolean

Legacy mutations support

Returns:

  • (Boolean)


96
97
98
99
100
101
102
103
104
# File 'lib/dry/mutations/extensions/command.rb', line 96

def vacant?(value)
  case value
  when NilClass then true
  when Integer, Float then false # FIXME: make sure!
  when ->(v) { v.respond_to? :empty? } then value.empty?
  when ->(v) { v.respond_to? :blank? } then value.blank?
  else false
  end
end

#validation_outcome(result = nil) ⇒ Object

Overrides



117
118
119
# File 'lib/dry/mutations/extensions/command.rb', line 117

def validation_outcome(result = nil)
  ::Dry::Mutations::Extensions::Outcome(super)
end