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}" }
guarded = SmartProcBinder.new(proc { raise 'boom' }, on_rescue: error_handler)

guarded.call  # prints "Error: boom" 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


191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 191

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.



123
124
125
126
127
128
129
130
131
132
133
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 123

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].



179
180
181
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 179

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.



116
117
118
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 116

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.



231
232
233
# File 'lib/musa-dsl/core-ext/smart-proc-binder.rb', line 231

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:

Keywords, including a **rest that accepts any

# key? belongs to the binder, not to Proc.
SmartProcBinder.new(proc { |a:, b:, **rest| }).key?(:a)        # => true
SmartProcBinder.new(proc { |a:, b:, **rest| }).key?(:unknown)  # => true (has **rest)
SmartProcBinder.new(proc { |a:, b:| }).key?(:unknown)          # => false

Parameters:

  • key (Symbol)

    keyword parameter name to check.

Returns:

  • (Boolean)

    true if key is accepted, false otherwise.



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

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

#parametersArray<Array>

Note:

The positional types differ between a proc and a lambda -- :opt and :req respectively -- because a proc tolerates being called with the wrong number of arguments and a lambda does not. A keyword without a default is :keyreq in both.

Returns the parameter signature of the wrapped Proc.

Examples:

Reading a proc's parameters

SmartProcBinder.new(proc { |a, b, c:| }).parameters
# => [[:opt, :a], [:opt, :b], [:keyreq, :c]]

A lambda reports the same parameters as required

SmartProcBinder.new(lambda { |a, b, c:| }).parameters
# => [[:req, :a], [:req, :b], [:keyreq, :c]]

Returns:

  • (Array<Array>)

    array of [type, name] pairs describing parameters.



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

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