Class: Strict::Methods::VerifiableMethod

Inherits:
Object
  • Object
show all
Defined in:
lib/strict/methods/verifiable_method.rb

Overview

rubocop:disable Metrics/ClassLength

Defined Under Namespace

Classes: UnknownParameterError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(method:, configuration:, instance:, owner: nil, name: nil) ⇒ VerifiableMethod

Returns a new instance of VerifiableMethod.



45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/strict/methods/verifiable_method.rb', line 45

def initialize(method:, configuration:, instance:, owner: nil, name: nil)
  @method = method
  @parameters = configuration.parameters
  @parameters_index = parameters.to_h { |parameter| [parameter.name, parameter] }
  @returns = configuration.returns
  @instance = instance
  compile_invocation(invocation_parameters_for(method))
  return if method

  @owner = owner
  @name = name
end

Instance Attribute Details

#parametersObject (readonly)

Returns the value of attribute parameters.



43
44
45
# File 'lib/strict/methods/verifiable_method.rb', line 43

def parameters
  @parameters
end

#returnsObject (readonly)

Returns the value of attribute returns.



43
44
45
# File 'lib/strict/methods/verifiable_method.rb', line 43

def returns
  @returns
end

Class Method Details

.for_interface(owner:, name:, configuration:) ⇒ Object



15
16
17
18
19
20
21
22
23
# File 'lib/strict/methods/verifiable_method.rb', line 15

def for_interface(owner:, name:, configuration:)
  new(
    method: nil,
    owner: owner,
    name: name,
    configuration: configuration,
    instance: true
  )
end

.from_method(method:, configuration:, instance:) ⇒ Object



7
8
9
10
11
12
13
# File 'lib/strict/methods/verifiable_method.rb', line 7

def from_method(method:, configuration:, instance:)
  new(
    method: method,
    configuration: configuration,
    instance: instance
  )
end

Instance Method Details

#nameObject



67
68
69
# File 'lib/strict/methods/verifiable_method.rb', line 67

def name
  method ? method.name : @name
end

#to_sObject



71
72
73
# File 'lib/strict/methods/verifiable_method.rb', line 71

def to_s
  "#{owner}#{separator}#{name}"
end

#verify_definition!Object



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/strict/methods/verifiable_method.rb', line 75

def verify_definition!
  expected_parameters = Set.new(parameters.map(&:name))
  defined_parameters = Set.new(parameter_bindings.map(&:name))
  return if expected_parameters == defined_parameters

  missing_parameters = expected_parameters - defined_parameters
  additional_parameters = defined_parameters - expected_parameters
  raise Strict::MethodDefinitionError.new(
    verifiable_method: self,
    missing_parameters: missing_parameters,
    additional_parameters: additional_parameters
  )
end

#verify_parameters!(args, kwargs, configuration) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/BlockLength, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/strict/methods/verifiable_method.rb', line 90

def verify_parameters!(args, kwargs, configuration)
  invalid_parameters = nil
  missing_parameters = nil
  violations = nil
  verified_args = nil
  verified_kwargs = nil
  positional_argument_count = args.length
  positional_index = 0

  parameter_bindings.each do |binding|
    parameter = binding.parameter || parameter_named!(binding.name)
    positional_start = positional_index
    original_value = case binding.kind
                     when :positional
                       if positional_index < positional_argument_count
                         positional_index += 1
                         args.fetch(positional_start)
                       else
                         NOT_PROVIDED
                       end
                     when :optional_positional
                       if positional_argument_count - positional_index > binding.required_after
                         positional_index += 1
                         args.fetch(positional_start)
                       else
                         NOT_PROVIDED
                       end
                     when :rest
                       count = positional_argument_count - positional_index - binding.required_after
                       count = 0 if count.negative?
                       positional_index += count
                       if positional_start.zero? && count == positional_argument_count
                         args
                       else
                         args.slice(positional_start, count)
                       end
                     when :keyword
                       kwargs.key?(binding.name) ? kwargs.fetch(binding.name) : NOT_PROVIDED
                     when :keyrest
                       keyrest_value(kwargs)
                     end

    if original_value.equal?(NOT_PROVIDED) && parameter.optional?
      value = parameter.default_generator.call
      changed = true
    elsif original_value.equal?(NOT_PROVIDED)
      missing_parameters ||= []
      missing_parameters << parameter.name
      violations ||= []
      violations << Validation.missing(parameter.validator, path: [parameter.name])
      next
    else
      value = original_value
      changed = false
    end

    value = parameter.coerce(value)
    changed ||= !value.equal?(original_value)
    parameter_violations = parameter.violations(value, configuration)
    if parameter_violations.empty?
      case binding.kind
      when :positional, :optional_positional
        if verified_args
          verified_args << value
        elsif changed
          verified_args = args.take(positional_start)
          verified_args << value
        end
      when :rest
        if verified_args
          verified_args.concat(value)
        elsif rest_changed?(args, positional_start, positional_index, value, parameter.coercer)
          verified_args = args.take(positional_start)
          verified_args.concat(value)
        end
      when :keyword
        if changed
          verified_kwargs ||= kwargs.dup
          verified_kwargs[binding.name] = value
        end
      when :keyrest
        if keyrest_changed?(kwargs, value, parameter.coercer)
          verified_kwargs = merge_keyrest(verified_kwargs, kwargs, value)
        end
      end
    else
      invalid_parameters ||= {}
      invalid_parameters[parameter] = value
      violations ||= []
      violations.concat(Validation.prepend_path(parameter_violations, parameter.name))
    end
  end

  if positional_index == positional_argument_count && no_additional_keywords?(kwargs) &&
     invalid_parameters.nil? && missing_parameters.nil?
    return if verified_args.nil? && verified_kwargs.nil?

    return [verified_args || args, verified_kwargs || kwargs]
  end

  remaining_args = args.slice(positional_index, positional_argument_count - positional_index) || []
  remaining_kwargs = remaining_kwargs_from(kwargs)
  violations ||= []
  remaining_args.each_with_index do |argument, offset|
    violations << Validation.unexpected(argument, path: [positional_index + offset])
  end
  remaining_kwargs.each do |name, value|
    violations << Validation.unexpected(value, path: [name])
  end
  raise Strict::MethodCallError.new(
    verifiable_method: self,
    remaining_args: remaining_args,
    remaining_kwargs: remaining_kwargs,
    invalid_parameters: invalid_parameters,
    missing_parameters: missing_parameters,
    violations: violations
  )
end

#verify_returns!(value, configuration) ⇒ Object

rubocop:enable Metrics/AbcSize, Metrics/BlockLength, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity



210
211
212
213
214
215
# File 'lib/strict/methods/verifiable_method.rb', line 210

def verify_returns!(value, configuration)
  violations = returns.violations(value, configuration)
  return if violations.empty?

  raise Strict::MethodReturnError.new(verifiable_method: self, value: value, violations: violations)
end