module PatternMatching

Allows for crude pattern-matching like behavior. Very crude. Currently just a Symbol as a status tag paired with a value. Provides capitalized methods (bad form, perhaps?) to make them stand out: `#Pattern`, `#Result` and `#Match`.

Example usage:

def some_computation

Result(:ok, "awesome sauce")

end

case (result = some_computation) when Match(:ok)

puts "Things went okay: #{result.value}"

when Match(:error)

puts "Something went wrong: #{result.value}"

end

# => “Things went okay: awesome sauce”

Originally named `Result` but that would have a conflicting meaning with the typical use of the Result Monad in most languages/environments. So while this is not true pattern matching, it might one day grow into something of the sort, and so the name fits… in a limited way.

Constants

Any
Tail
Undefined

Additional singleton constants and wildcards

VERSION

Public Class Methods

build_custom_binding_helper(binding_helper) click to toggle source

The implementation of the METHODS heredoc should remain identical to the contents of the Match and Pattern methods of PatternMatching::MethodsWithBindingHelper except that here we are interpolating on our

# File lib/pattern_matching.rb, line 138
  def self.build_custom_binding_helper(binding_helper)
    methods_with_binding = ->() {
      eval(
<<-METHODS
def Match(*pattern)
  result = ::PatternMatching::CaseEqualityReversal.new(*pattern)
  (self.class)::#{binding_helper}._clear_bindings!(caller_locations(1,1)[0].label) unless result
  result
end

def Pattern(*pattern)
  (self.class)::#{binding_helper}._clear_bindings!(caller_locations(1,1)[0].label)
  ::PatternMatching::PatternMatch.new(*pattern)
end
METHODS
      )
    }
    @methods_with_custom_binding_helper = Module.new
    @methods_with_custom_binding_helper.class_exec(&methods_with_binding)
  end
build_custom_proc_helpers(send_helper, call_helper) click to toggle source

Implementations internal to the blocks of the define_method calls should remain identical to the implementations in the PatternMatching::ProcHelpers module, with the only change being that the produced module has different names for the methods behind the call and send helpers.

# File lib/pattern_matching.rb, line 119
def self.build_custom_proc_helpers(send_helper, call_helper)
  proc_helpers = ->() {
    define_method(send_helper) do |symbol|
      symbol.to_proc
    end

    define_method(call_helper) do |symbol|
      Proc.new { |obj| self.send(symbol, obj) }
    end
  }

  @custom_proc_helpers = Module.new
  @custom_proc_helpers.class_exec(&proc_helpers)
end
config() click to toggle source

Simple class-instance variable to hold configuration

# File lib/pattern_matching.rb, line 84
def self.config
  @config ||= ::PatternMatching::Configuration.default
end
configure(&block) click to toggle source

Available configuration options are:

  • use_proc_helpers: controls whether or not helpers for sending messages and calling a method in the local context are included with this module.

  • use_binding_helper: controls whether or not bindings are enabled (and thus) whether or not helpers are included.

  • send_helper: the method name used as the proc helper for “sending a message” to the object when matching.

  • call_helper: the method name used as the proc helper for “calling a method in the current context” with the object as an argument when matching.

  • binding_helper: the method name used as the binding set for each match.

# File lib/pattern_matching.rb, line 69
def self.configure(&block)
  block.call(config)

  unless config.default_proc_helpers?
    build_custom_proc_helpers(config.send_helper, config.call_helper)
  end

  if config.use_binding_helper && !config.default_binding_helper?
    build_custom_binding_helper(config.binding_helper)
    puts "Using a custom binding helper are we?"
  end
end
custom_proc_helpers() click to toggle source

Will only be set with a proper module if a call to ::configure results in there being proc helpers with non-default names. Is not intelligently assigned, nor should it be used, when called in any manner before a call to ::configure that specifies an alternative value to config.call_helper and config.binding_helper.

# File lib/pattern_matching.rb, line 101
def self.custom_proc_helpers
  @custom_proc_helpers
end
default_configuration!() click to toggle source

For of use in testing, because of how Ruby loading works when testing behavior that is thread-global (?) for the instance of this module.

# File lib/pattern_matching.rb, line 91
def self.default_configuration!
  @config = ::PatternMatching::Configuration.default
end
included(base) click to toggle source

Configure behavior based on existing configuration. This is a lot of noodley-looking nested conditional logic but that's kind of the point with lots of boolean-based configuration of behavior!

# File lib/pattern_matching.rb, line 31
def self.included(base)
  if PatternMatching.config.use_binding_helper
    base.const_set(PatternMatching.config.binding_helper, PatternMatching::BindingsSet.new)
    if PatternMatching.config.default_binding_helper?
      base.send(:include, PatternMatching::MethodsWithBindingHelper)
    else
      base.send(:include, PatternMatching.methods_with_custom_binding_helper)
    end
  else
    base.send(:include, PatternMatching::Methods)
  end

  if PatternMatching.config.use_proc_helpers
    if PatternMatching.config.default_proc_helpers?
      base.send(:include, PatternMatching::ProcHelpers)
    else
      base.send(:include, PatternMatching.custom_proc_helpers)
    end
  end
end
methods_with_custom_binding_helper() click to toggle source

Will only be set with a proper module if a call to ::configure results in a binding helper set to a name which is not the default. Is not intelligently assigned, nor should it be used, when called in any manner before a call to ::configure that specifies an alternative value to config.binding_helper

# File lib/pattern_matching.rb, line 110
def self.methods_with_custom_binding_helper
  @methods_with_custom_binding_helper
end

Public Instance Methods

match_item(from_self, from_other) click to toggle source

Handles matching for non-collection values, including the logic behind the wildcard Any. In the case of a collection, defers instead to match_enumerable.

# File lib/pattern_matching/pattern_match.rb, line 44
def match_item(from_self, from_other)
  if Any == from_other
    true
  elsif Enumerable === from_other && Enumerable === from_self
    match_enumerable(from_self, from_other)
  else
    from_other === from_self
  end
end