Module: Braintrust::Internal::Callable::KeywordFilter

Defined in:
lib/braintrust/internal/callable.rb

Overview

Filters keyword arguments so callers can pass a superset of kwargs and the receiver only gets the ones it declared. This avoids Ruby 3.2+ ArgumentError for unknown keywords without requiring ** on every definition.

When prepended on a class, intercepts #call and slices kwargs to match the declared parameters before forwarding. Methods with **keyrest receive all kwargs unfiltered.

Examples:

class Greeter
  prepend Internal::Callable::KeywordFilter
  def call(name:)
    "hello #{name}"
  end
end
Greeter.new.call(name: "world", extra: "ignored")  # => "hello world"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.filter(params, kwargs) ⇒ Hash

Filter kwargs to only the keyword params declared by the given parameters list. Returns kwargs unchanged if parameters include **keyrest.

Parameters:

  • params (Array<Array>)

    parameter list from Proc#parameters or Method#parameters

  • kwargs (Hash)

    keyword arguments to filter

Returns:

  • (Hash)

    filtered keyword arguments



29
30
31
32
33
34
35
36
# File 'lib/braintrust/internal/callable.rb', line 29

def self.filter(params, kwargs)
  return kwargs if has_keyword_splat?(params)

  declared_keys = params
    .select { |type, _| type == :keyreq || type == :key }
    .map(&:last)
  kwargs.slice(*declared_keys)
end

.has_any_keywords?(params) ⇒ Boolean

Whether params include any keyword parameters (key, keyreq, or keyrest).

Parameters:

  • params (Array<Array>)

    parameter list

Returns:

  • (Boolean)


60
61
62
# File 'lib/braintrust/internal/callable.rb', line 60

def self.has_any_keywords?(params)
  params.any? { |type, _| type == :keyreq || type == :key || type == :keyrest }
end

.has_keyword_splat?(params) ⇒ Boolean

Whether params include ** (keyword splat / keyrest).

Parameters:

  • params (Array<Array>)

    parameter list

Returns:

  • (Boolean)


52
53
54
# File 'lib/braintrust/internal/callable.rb', line 52

def self.has_keyword_splat?(params)
  params.any? { |type, _| type == :keyrest }
end

.wrap_block(block) ⇒ Proc

Wrap a Proc to filter kwargs to only its declared keyword params. Returns the block unchanged if it accepts **keyrest.

Parameters:

  • block (Proc)

    the block to wrap

Returns:

  • (Proc)

    a wrapper that filters kwargs, or the original block



43
44
45
46
# File 'lib/braintrust/internal/callable.rb', line 43

def self.wrap_block(block)
  return block if has_keyword_splat?(block.parameters)
  ->(**kw) { block.call(**filter(block.parameters, kw)) }
end

Instance Method Details

#call(**kwargs) ⇒ Object

When prepended, filters kwargs before the next #call in the ancestor chain. If the instance defines #call_parameters, uses those. Otherwise introspects super_method.

Parameters:

  • kwargs (Hash)

    keyword arguments

Returns:

  • (Object)

    result of the filtered #call



70
71
72
73
74
75
76
77
78
79
# File 'lib/braintrust/internal/callable.rb', line 70

def call(**kwargs)
  params = if respond_to?(:call_parameters)
    call_parameters
  else
    impl = method(:call).super_method
    return super unless impl
    impl.parameters
  end
  super(**KeywordFilter.filter(params, kwargs))
end