Class: Foobara::Value::Processor::Casting

Inherits:
Selection show all
Defined in:
foobara-0.0.110/projects/value/src/processor/casting.rb

Overview

TODO: at least move this up to Types though that doesn’t solve the issue

Defined Under Namespace

Classes: CannotCastError

Instance Attribute Summary collapse

Attributes inherited from Selection

#enforce_unique

Attributes inherited from Multi

#prioritize, #processors

Attributes inherited from Foobara::Value::Processor

#created_in_namespace, #declaration_data, #parent_declaration_data

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Selection

#always_applicable?, #process_value, #processor_for, #processor_for!

Methods inherited from Multi

#applicable?, #error_classes, #possible_errors, #processor_names, #register

Methods inherited from Foobara::Value::Processor

#always_applicable?, #applicable?, #attribute_name, default_declaration_data, #dup_processor, error_class, #error_path, #foobara_manifest, #inspect, instance, #method_missing, #name, new_with_agnostic_args, #possible_errors, #priority, #process_outcome, #process_outcome!, #process_value, #process_value!, processor_name, requires_parent_declaration_data?, #respond_to_missing?, #runner, symbol

Methods included from IsManifestable

#foobara_domain, #foobara_manifest, #foobara_organization, #scoped_clear_caches

Methods included from Concern

foobara_class_methods_module_for, foobara_concern?, included

Constructor Details

#initialize(casters:, target_classes: nil) ⇒ Casting

Returns a new instance of Casting.



42
43
44
45
46
47
48
49
50
51
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 42

def initialize(*, casters:, target_classes: nil)
  self.target_classes = Util.array(target_classes)

  processors = [
    *does_not_need_cast_processor,
    *casters
  ]

  super(*, processors:)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Foobara::Value::Processor

Instance Attribute Details

#target_classesObject

Returns the value of attribute target_classes.



40
41
42
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 40

def target_classes
  @target_classes
end

Class Method Details

.error_classesObject



31
32
33
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 31

def error_classes
  [CannotCastError]
end

.foobara_manifestObject



25
26
27
28
29
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 25

def foobara_manifest
  # :nocov:
  super.merge(processor_type: :casting)
  # :nocov:
end

.requires_declaration_data?Boolean

Returns:

  • (Boolean)


35
36
37
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 35

def requires_declaration_data?
  true
end

Instance Method Details

#applies_messageObject



98
99
100
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 98

def applies_message
  Util.to_or_sentence(processors.map(&:applies_message).flatten)
end

#build_error(*args, **opts) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 109

def build_error(*args, **opts)
  error_class = opts[:error_class]

  if error_class == NoApplicableProcessorError
    build_error(*args)
  elsif error_class == MoreThanOneApplicableProcessorError
    # :nocov:
    raise "Matched too many casters for #{args.inspect} with #{opts.inspect}"
    # :nocov:
  else
    super
  end
end

#can_cast?(value) ⇒ Boolean

Returns:

  • (Boolean)


57
58
59
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 57

def can_cast?(value)
  processors.any? { |processor| processor.applicable?(value) }
end

#cast_toObject



123
124
125
126
127
128
129
130
131
132
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 123

def cast_to
  # TODO: isn't there a way to declare declaration_data_type so we don't have to validate here??
  unless declaration_data.key?(:cast_to)
    # :nocov:
    raise "Missing cast_to"
    # :nocov:
  end

  declaration_data[:cast_to]
end

#does_not_need_cast_processorObject



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 61

def does_not_need_cast_processor
  return @does_not_need_cast_processor if defined?(@does_not_need_cast_processor)

  errorified_name = target_classes.map do |c|
    if c.name
      c.name
    elsif c.respond_to?(:foobara_name)
      c.foobara_name
    else
      # TODO: test this code path
      # :nocov:
      "Anon"
      # :nocov:
    end
  end.map { |name| name.split("::").last }.sort.join("Or")

  class_name = "NoCastNeededIfIsA#{errorified_name}"

  @does_not_need_cast_processor = if target_classes && !target_classes.empty?
                                    Caster.subclass(
                                      name: class_name,
                                      applicable?: ->(value) {
                                        target_classes.any? { |target_class| value.is_a?(target_class) }
                                      },
                                      applies_message: "be a #{target_classes.map(&:name).join(" or ")}",
                                      cast: ->(value) { value }
                                    ).instance
                                  end
end

#error_context(value) ⇒ Object



102
103
104
105
106
107
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 102

def error_context(value)
  {
    cast_to:,
    value:
  }
end

#error_message(value) ⇒ Object



91
92
93
94
95
96
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 91

def error_message(value)
  type = declaration_data[:cast_to][:type].to_s
  article = type =~ /^[aeiouy]/i ? "an" : "a"

  "Cannot cast #{value.inspect} to #{article} #{type}. Expected it to #{applies_message}"
end

#needs_cast?(value) ⇒ Boolean

Returns:

  • (Boolean)


53
54
55
# File 'foobara-0.0.110/projects/value/src/processor/casting.rb', line 53

def needs_cast?(value)
  !does_not_need_cast_processor.applicable?(value)
end