Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Integralist/a29212a8eb10bc8154b7 to your computer and use it in GitHub Desktop.
Save Integralist/a29212a8eb10bc8154b7 to your computer and use it in GitHub Desktop.
Ruby Meta Programming
  • Dynamic Dispatch
  • Dynamic Method
  • Ghost Methods
  • Dynamic Proxies
  • Blank Slate
  • Kernel Method
  • Flattening the Scope (aka Nested Lexical Scopes)
  • Context Probe
  • Class Eval (not really a 'spell' more just a demonstration of its usage)
  • Class Macros
  • Around Alias
  • Hook Methods
  • Class Extension Mixin
  • Module Namespace Interpolation
# Dynamic Dispatch
# Allows us to send messages to even private methods
# object.send(message, *arguments)
1.send(:+, 2) # => 3
# Dynamic Method
# Allows us to dynamically create methods
# define_method :method_name { block that becomes method body }
class Foo
define_method :bar do
puts "This is a dynamic method"
end # => "This is a dynamic method"
# Dynamic Method
# Alternative example
class Bar
# we have to define this method on `self` (see below comment)
def self.create_method(method)
define_method "my_#{method}" do
puts "Dynamic method called 'my_#{method}'"
# these methods are executed within the definition of the Bar class
create_method :foo
create_method :bar
create_method :baz
end # => "Dynamic method called 'my_foo'" # => "Dynamic method called 'my_bar'" # => "Dynamic method called 'my_baz'"
# Dynamic Method
# Parse another class for data
class AnotherClass
def get_foo_stuff; end
def get_bar_stuff; end
def get_baz_stuff; end
class Baz
def initialize(a_class)
a_class.methods.grep(/^get_(.*)_stuff$/) { Baz.create_method $1 }
def self.create_method(method)
define_method "my_#{method}" do
puts "Dynamic method called 'my_#{method}'"
another_class = # => "Dynamic method called 'my_foo'" # => "Dynamic method called 'my_bar'" # => "Dynamic method called 'my_baz'"
class Foo
def initialize(bar)
self.class.send(:define_method, bar) { p "hello #{bar}!" }
foo ="world") # => "hello world!"
# Ghost Methods
# Utilises `method_missing`
class Hai
def method_missing(method, *args)
puts "You called: #{method}(#{args.join(', ')})"
puts "You also passed a block" if block_given?
end # => You called: yolo() "a", 123, :c # => You called: yolo(a, 123, c), :b, :c) { puts "a block" } # => You called: yolo(a, b, c)
# => You also passed a block
# Dynamic Proxies
# Catching "Ghost Methods" and forwarding them onto another method
# Whilst possibly adding logic around the call.
# For example,
# You can provide imaginary methods by utilising `method_missing` to parse
# the incoming message (e.g. `get_name`, `get_age`) and to delegate off to
# another method such as `get(:data_type)` where `:data_type` is `:name` or `:age`.
def method_missing(message, *args, &block)
return get($1.to_sym, *args, &block) if message.to_s =~ /^get_(.*)/
super # if we don't find a match then we'll call the top level `BasicObject#method_missing`
# If (after analysis) you discover a performance issue with using `method_missing`
# you can utilise the "Dynamic Method" technique to create a real method after
# the message has been received by `method_missing` the first time.
# Blank Slate
# Prevents issues when using "Dynamic Proxies"
# e.g. user calls a method that exists higher up the inheritance chain
# so your `method_missing` doesn't fire because the method does exist.
# To work around this issue, make sure your class starts with a "Blank Slate"
# So you remove any methods you don't want to appear at all in the inheritance chain
# by using `undef_method` (there is also `remove_method` which doesn't remove the named
# method from the inheritance chain but just the current class, but that doesn't help us
# fix the "Dynamic Proxy" scenario so we use `undef_method` instead).
# For "Dynamic Proxy" we use the parent `method_missing` so we keep that,
# we also might use `respond_to?` so we keep that (although you can remove it if you don't).
# Also the `__` in the below regex pattern is to prevent Ruby from displaying a warning
# about removing 'reserved' methods such as `__id__` and `__send__`
class ImBlank
instance_methods.each do |m|
undef_method m unless m.to_s =~ /^__|method_missing|respond_to?/
# rest of your code (such as your "Dynamic Proxy" implementation)
# Kernel Method
# Add a method that gives the illusion it's a language keyword
# But really it's just added to the `Kernel` module which all other objects inherit from.
# At the top level of a Ruby program `self` is == `main`.
# `self.class` == `Object` and the `Kernel` sits above it in the hierarchy.
# You can see this by running the following code:
class Foo; end
Foo.ancestors # => [Foo, Object, Kernel, BasicObject]
# So we can see we can add what looks to be a language provided feature like so:
module Kernel
def foobar
puts "I'm not a language keyword, I'm just a fake"
# Now from any where in our program we can call
foobar # => I'm not a language keyword, I'm just a fake
# Flattening the Scope (aka Nested Lexical Scopes)
# Where you change the code in such a way that it's easier for you to pass variables through "Scope Gates".
# A scope gate is any block, where normally when you enter its scope the variables outside of it become unreachable.
# This happens in: Class definitions, Module definitions, Method definitions
# I'm not sure what the real life examples are of this, but if you ever wonder why some code does the following,
# then maybe it was that they wanted to flatten the scope so they could more easily pass around variables.
# I guess it's better to do it this way than to define a global variable?
# In the following code we want to access `my_var` from inside the method (inner scope gate) that's
# inside the class (outer scope gate).
my_var = "abc"
class OuterScopeGate
puts my_var
def inner_scope_gate
puts my_var
# We fix this by flattening the code into method calls (method *calls* aren't scope gates)
# So we turn the class keyword into a method call using ``
# We also turn the method inside the class from a keyword into a method call using `define_method`
my_var = "abc"
MyClass = do
puts "Here is 'my_var' inside my class definition: #{my_var}"
define_method :my_method do
puts "Here is 'my_var' inside my class instance method: #{my_var}"
end # => Here is 'my_var' inside my class definition: abc # => Here is 'my_var' inside my class instance method: abc
# Context Probe
# Execute a code block in the context of another object using `instance_eval`
class Foo
def initialize
@z = 1
foo =
foo.instance_eval do
puts self # => #<Foo:0x7d15e891>
puts @z # => 1
new_value = 2
foo.instance_eval { @z = new_value }
foo.instance_eval { puts @z } # => 2
# There is also `instance_exec` which works the same way but allows passing arguments to the block
class Foo
def initialize
@x, @y = 1, 2
end { |arg| (@x + @y) * arg } # => 9
# Evaluate a block in the context of a class
# Similar to re-opening a class but more flexible in that it
# works on any variable that references a class, where as re-opening
# a class requires defining a constant.
# Classic class re-opening style
class String
def m; puts "hello!" end
# Class eval style
# The extra code is used to make the example a bit more re-usable/abstracted
def add_method_to_class(the_class)
the_class.class_eval do
def m; puts "hello!" end
add_method_to_class String
"abc".m # => hello!
# Class Macros are just regular class methods that are only used in a class definition
# e.g. not used from a new instance of the class (only at the time the class is defined)
# Below is an example of a Class Macro that alerts users of a publically available class
# that the methods they've been using are now deprecated and they should use the renamed version.
# It uses "Dynamic Method" to help performance by creating the old methods again and delegating off
# to the new methods (rather than using `method_missing` which can be quite slow as it has to spend
# time looking up the inheritance chain)
class Foo
def get_a; puts "I'm an A" end
def get_b; puts "I'm an B" end
def get_c; puts "I'm an C" end
# Defining our Class Macro
def self.deprecate(old_method, new_method)
define_method(old_method) do |*args, &block|
puts "Warning: #{old_method} is deprecated! Use #{new_method} instead"
send(new_method, *args, &block) # `self` is assumed when calling `send`
# Using our Class Macro
deprecate :a, :get_a
deprecate :b, :get_b
deprecate :c, :get_c
# Around Alias uses the `alias` keyword to store a copy of the original method under a new name,
# allowing you to redefine the original method name and to delegate off to the previous method implementation
class String
alias :orig_length :length
def length
"Length of string '#{self}' is: #{orig_length}"
#=> "Length of string 'abc' is: 3"
# Hook Methods are provided by the Ruby language and let you know about certain events
# such as when a class inherits from another class or when a method has been added to an object.
class String
  def self.inherited(subclass)
    puts "#{self} was inherited by #{subclass}"
class MyString < String; end # => String was inherited by MyString

There are quite a few hooks which I've listed below.

Method-related hooks


Class & Module Hooks


Marshalling Hooks


Coercion Hooks

# Class Extension Mixin allows you to both `include` and `extend` a class
module MyMixin
def self.included(base) # Hook Method
def a
puts "I'm A (an instance method)"
module ClassMethods # "ClassMethods" is a recognised naming pattern
def x
puts "I'm X (a class method)"
class Foo
include MyMixin
Foo.x # => I'm X (a class method) # => I'm A (an instance method)
type = "baz"
Foo::Bar.const_get(type.capitalize).new # => new instance of Foo::Bar::Baz
Copy link

"Dynamic dispatch" doesn't mean what you think it does.

Copy link

Ruby 2.0 also introduces prepend hook method that behaves similar to include method, but inserts your module before your class in its inheritance tree. I explained it in my "Ruby Metaprogramming" video course in more details.

BTW, I also applied $50 OFF coupon to the link above as a give back to GitHub community ;)

Copy link

cesartalves commented Sep 11, 2019

This is great!

I was doing a list of my own when I stumbled on this doing a web search. Very useful.
Here's my humble little repo:
I used RSpec to generated a doc-like structure, so I could reference it whenever I was in doubt about anything.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment