MetaProgramming

Various meta-programming methods and ‘spells’ for Ruby

Resources

Install

  • sudo gem install meta_programming

Use

  • require ‘meta_programming’

Concepts/Terminology

Beta Status

I consider this Gem in personal beta and suspect it will be in this state for a while. If you use it, expect changes that may break your code until the lexigraph has a chance to mature.

I welcome comments and collaborators who would like to develop a library for simplifying common patterns and limit the pitfalls of Ruby meta-programming.

Issues

1.9 vs 1.8

There are considerable differences with the way that Ruby 1.9 and 1.8 handle dynamic methods, procs and lambdas. For instance, Ruby 1.8.x cannot handle default parameter values in the block parameter list

call_some_method(*args) do |param1, param2=nil| #oops for Ruby 1.8
  ...
end

This raises support issues for this library. Should the library fully support both versions of Ruby, cripple 1.8 at the expense of 1.9, or kiss the past good-bye and only support version 1.9? Of course, the latter is clearer and easier, and may be the best path to pursue.

Description

Current Methods

  • eigenclass (or metaclass)

  • safe_alias_method_chain

  • define_chained_method

  • define_method_missing_chain

  • define_ghost_method

  • blank_slate

Usage

define_ghost_method

define_ghost_method creates a ‘ghost’ method for an undefined method that corresponds to the ‘matcher’ parameter.

define_ghost_method(matcher) do |object, method_name_symbol, *args|
  ...method body...
end

A block must be present and it takes (optionally) the receiving object, the method name (as a symbol with exception of a lambda matcher) and the method arguments.

The matcher may be any of a symbol, string, regular expression or Proc/lambda.

String, symbols and regular expression matchers

define_ghost_method('my_method) {|obj, sym, *args| ... }
define_ghost_method(':my_method) {|obj, sym, *args| ... }
define_ghost_method(/^my_method$/) {|obj, sym, *args| ... }

String and symbol matchers will only process the method if its name matches the string or symbol exactly. Regular expressions provide greater flexibility and care must be taken to define scrutinizing regular expression matchers. It is a good practice to use the /^ and $/ matchers to limit the scope.

In all cases, the method name is passed in the second parameter as a symbol to the method block. This is not the case with lambda matchers.

Proc/lambda matchers

Lambda matchers provide an opportunity to greatly scrutinize the method call based on the parameters passed to the method block (object, method_name, *args). The method is invoked for any lambda not evaluating to nil or false.

proc = lambda{|object, method_name, *args| ...matching code... }
define_ghost_method(proc) {|obj, proc_result, *args| ... }

Proc and lambda matchers differ from the other matcher in that the second parameter passes the result of the Proc/lambda matcher to the method block. This feature lets the matcher pre-process the name of the method passed to the body.

proc = lambda{|obj, sym, *args| sym =~ /^not_(.+\?)$/ && $1.to_sym }

The extra flexibility comes at greater responsibility. If you only want to pass the method name symbol to the method block, don’t forget to return it from the lambda.

proc = lambda{|obj, sym, *args| sym =~ /^not_.+\?$/ && sym }

Additionally, the ‘return’ keyword has different effects between Procs and lambdas. I suggest not explicitly using the ‘return’ keyword in your method blocks. It’s a Ruby thing. define_ghost_method will remind you if a LocalJumpError is raised.

Critique

I’m not completely satisfied with the design of the implementation.

Dependencies

none