Class: Musa::Extension::SmartProcBinder::SmartProcBinder

Inherits:
Object
  • Object
show all
Defined in:
lib/musa-dsl/core-ext/smart-proc-binder.rb

Overview

Wrapper for Proc objects that provides intelligent parameter matching and binding.

This class introspects a Proc's parameter list and provides methods to:

  • Determine which parameters the Proc accepts
  • Filter provided arguments to match the Proc's signature
  • Call the Proc with properly matched arguments
  • Optionally rescue and handle exceptions

Parameter Types Handled

  • :req, :opt: Required and optional positional parameters
  • :rest: Splat parameter (*args)
  • :key, :keyreq: Optional and required keyword parameters
  • :keyrest: Double splat parameter (**kwargs)

Use Cases

  • DSL methods that need flexible parameter passing
  • Builder patterns with variable block signatures
  • Wrapper methods that forward arguments intelligently
  • Error handling for DSL block execution

Examples:

Basic usage

block = proc { |a, b, c:| [a, b, c] }
binder = SmartProcBinder.new(block)

binder.call(1, 2, 3, 4, c: 5, d: 6)
# => [1, 2, 5]
# Only passes parameters that match signature

With rescue handling

error_handler = proc { |e| puts "Error: #{e.message}" }
binder = SmartProcBinder.new(block, on_rescue: error_handler)

binder.call(invalid_args)  # Calls error_handler instead of raising

Checking parameter support

binder.key?(:pitch)  # => true/false
binder.has_key?(:velocity)  # => true/false

Instance Method Summary collapse

Constructor Details

#initialize(block, on_rescue: nil) ⇒ SmartProcBinder

Creates a new SmartProcBinder wrapping the given block.

Introspects the block's parameters and categorizes them for later matching.

Parameters:

  • block (Proc)

    the proc/block to wrap.

  • on_rescue (Proc, nil) (defaults to: nil)

    optional error handler called with exception if block execution fails.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 60

def initialize(block, on_rescue: nil)
  @block = block
  @on_rescue = on_rescue

  # Track keyword parameters by name
  @key_parameters = {}
  @has_key_rest = false

  # Track positional parameter count
  @value_parameters_count = 0
  @has_value_rest = false

  # Introspect block's parameter signature
  block.parameters.each do |parameter|
    @key_parameters[parameter[1]] = nil if parameter[0] == :key || parameter[0] == :keyreq
    @has_key_rest = true if parameter[0] == :keyrest

    @value_parameters_count += 1 if parameter[0] == :req || parameter[0] == :opt
    @has_value_rest = true if parameter[0] == :rest
  end
end

Instance Method Details

#_apply(value_parameters, key_parameters) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal implementation of argument filtering.

Logic:

  • Positional: takes first N values (or all if *rest present)
  • Keywords: includes only expected keys (or all if **rest present)
  • Pads positional with nils if needed


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
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 179

def _apply(value_parameters, key_parameters)
  value_parameters ||= []
  key_parameters ||= {}

  if @has_value_rest
    values_result = value_parameters.clone
  else
    values_result = value_parameters.first(@value_parameters_count)
    values_result += Array.new(@value_parameters_count - values_result.size)
  end

  hash_result = @key_parameters.clone

  @key_parameters.each_key do |parameter_name|
    hash_result[parameter_name] = key_parameters[parameter_name]
  end

  if @has_key_rest
    key_parameters.each do |key, value|
      hash_result[key] = value unless hash_result.key?(key)
    end
  end

  return values_result, hash_result
end

#_call(value_parameters, key_parameters = {}, block = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal call implementation with error handling.



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 112

def _call(value_parameters, key_parameters = {}, block = nil)
  if @on_rescue
    begin
      __call value_parameters, key_parameters, block
    rescue StandardError, ScriptError => e
      @on_rescue.call e
    end
  else
    __call value_parameters, key_parameters, block
  end
end

#apply(*value_parameters, **key_parameters) ⇒ Array<Array, Hash>

Filters arguments to match the Proc's signature.

Parameters:

  • value_parameters (Array)

    positional arguments to filter.

  • key_parameters (Hash)

    keyword arguments to filter.

Returns:

  • (Array<Array, Hash>)

    tuple of [filtered_positionals, filtered_keywords].



167
168
169
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 167

def apply(*value_parameters, **key_parameters)
  _apply(value_parameters, key_parameters)
end

#call(*value_parameters, **key_parameters, &block) ⇒ Object

Calls the wrapped Proc with smart parameter matching.

Parameters:

  • value_parameters (Array)

    positional arguments.

  • key_parameters (Hash)

    keyword arguments.

  • block (Proc, nil)

    block to pass to wrapped Proc.

Returns:

  • (Object)

    result of calling the wrapped Proc.



105
106
107
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 105

def call(*value_parameters, **key_parameters, &block)
  _call value_parameters, key_parameters, block
end

#inspectString Also known as: to_s

Returns a string representation of the SmartProcBinder for debugging.

Shows the wrapped Proc's parameter signature and internal state, making it easy to understand what parameters the binder expects and how it will match arguments.

Examples:

Inspecting binder state

block = proc { |a, b, c:, **rest| }
binder = SmartProcBinder.new(block)
puts binder.inspect
# => "SmartProcBinder: parameters = [[:req, :a], [:req, :b], [:key, :c], [:keyrest, :rest]]
#     key_parameters = {:c=>nil} has_rest = true"

Returns:

  • (String)

    formatted string showing parameters and configuration.



219
220
221
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 219

def inspect
  "SmartProcBinder: parameters = #{parameters} key_parameters = #{@key_parameters} has_rest = #{@has_key_rest}"
end

#key?(key) ⇒ Boolean Also known as: has_key?

Checks if the wrapped Proc accepts a specific keyword parameter.

Returns true if the Proc has a keyword parameter with the given name, or if it has a **kwargs rest parameter that accepts any keyword.

Examples:

proc { |a:, b:, **rest| }.key?(:a)       # => true
proc { |a:, b:, **rest| }.key?(:unknown) # => true (has **rest)
proc { |a:, b:| }.key?(:unknown)         # => false

Parameters:

  • key (Symbol)

    keyword parameter name to check.

Returns:

  • (Boolean)

    true if key is accepted, false otherwise.



155
156
157
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 155

def key?(key)
  @has_key_rest || @key_parameters.include?(key)
end

#parametersArray<Array>

Returns the parameter signature of the wrapped Proc.

Examples:

proc { |a, b, c:| }.parameters  # => [[:req, :a], [:req, :b], [:key, :c]]

Returns:

  • (Array<Array>)

    array of [type, name] pairs describing parameters.



94
95
96
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 94

def parameters
  @block.parameters
end

#procProc

Returns the wrapped Proc.

Returns:

  • (Proc)

    the original block.



85
86
87
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 85

def proc
  @block
end