= MetaProgramming
Various meta-programming methods and 'spells' for Ruby
== Resources
Install
- sudo gem install meta_programming
Use
- require 'meta_programming'
Concepts/Terminology
Example Code
== 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.
If you include metaprogramming in your projects, I suggest you limit the version
dependency to 0.2.x, something like
s.add_dependency('meta_programming', '>= 0.2.0', '< 0.3.0')
Any 0.2.x version will be backward compatible with any earlier 0.2.x version. A
bump to 0.3.0 cannot be guaranteed backward compatible.
I welcome comments and collaborators who
would like to develop a library for simplifying common patterns and limit
the pitfalls of Ruby meta-programming.
== Changelog
Removes object parameter from method blocks and lambda matchers in define_ghost_method, and
adds more descriptive error handling. define_ghost_method block and matchers are now
evaluated in the context of the object instance.
I've bumped the version to 0.2.0 because this changes the interface.
initial implementation
== 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 |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. As of 0.2.0, the
block and lambda matcher is evaluated in the context of the object.
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) {|sym, *args| ... }
define_ghost_method(:my_method) {|sym, *args| ... }
define_ghost_method(/^my_method$/) {|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. The method is
invoked for any lambda not evaluating to nil or false.
proc = lambda{|method_name| ...matching code... }
define_ghost_method(proc) {|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{|sym| sym =~ /^not_(.+?)$/ && self.class.method_defined?($1.to_sym) && $1.to_sym }
The lambda matcher is evaluated in the context of the object and can
call methods of the object as in the above example. The lambda matcher is also called
in the respond_to? method, so it is a good idea to avoid usind respond_to? in the lambda
matcher to avoid a stack overflow error. Instead call method_defined? on the class object.
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{|sym| sym =~ /^not_.+?$/ && sym }
==== Critique
2010.04.30
I'm not satisfied with the ghost method implementation.
2010.05.02
I like the fact that define_ghost_method blocks and lambda matchers are now evaluated
in the context of the object and that the object is no longer passed into the method block
and lambda matcher. The method now behaves similarly to its cousin, +define_method+.
== Testing
Passes for Ruby
== Dependencies
none