# Decide how to define a method at runtime
class C
end
C.class_eval do
define_method :my_method do
'a dynamic method'
end
end
obj = C.new
obj.my_method # => "a dynamic method"
# powered by BasicObject#method_missing and BasicObject#respond_to_missing?
class Hello
PEOPLE = %w[Abe Brian Chia].freeze
def method_missing(name, *args, &block)
person = name.to_s.capitalize
# super if not one of the methods/names in const PEOPLE
super unless PEOPLE.include? person
"Hello #{person}!"
end
def respond_to_missing?(method, include_private = false)
person = method.to_s.capitalize
# super if not one of the methods/names in const PEOPLE
PEOPLE.include?(person) || super
end
end
hello = Hello.new
hello.brian # => "Hello Brian!"
hello.dave # NoMethodError: undefined method `dave' for #<Hello:...>
hello.respond_to? :brian # => true
hello.respond_to? :dave # => false
# A skinny class with a minimal number of methods by inheriting directly
# from Basic Object
#
# Alternatives
# Module#undef_method removes any method, including inherited ones
# Module#remove_method removes method from receiver, not inherited ones
#
# inherits from Object by default
class C
def method_missing(_name, *_args)
'a Ghost Method'
end
end
# inherits from BasicObject explicitly to set a "blank slate"
# BasicObject.instance_methods # =>
# [:!, :==, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]
class D < BasicObject
def method_missing(_name, *_args)
'a Ghost Method'
end
end
obj = C.new
obj.to_s # => "#<C:0x007fbd9b025958>"
blank_slate = D.new # => "a Ghost Method"
blank_slate.to_s # => "a Ghost Method"
# define a method in module Kernel to make the method available to *all* objects
module Kernel
def a_method
'a kernel method'
end
end
a_method # => "a kernel method"
# Execute a block to access information in an object's context
class C
def initialize
@x = 'a private instance variable'
end
end
obj = C.new
obj.instance_eval { @x } # => "a private instance variable"
# Store a piece of code and its context in a proc or lambda
# for evaluation later
class C
def store(&block)
@my_code_capsule = block
end
def execute
@my_code_capsule.call
end
end
obj = C.new
obj.store { $X = 1 }
$X = 0
obj.execute
$X # => 1
# 1. Procs are objects, blocks are not
# 2. At most one block can appear in an argument list
# 3. Lambdas check the number of arguments, while procs do not
# 4. Lambdas and procs treat the 'return' keyword differently
# Converting a block to a proc
# Ruby provides standard library class Proc, to turn a block into an object
# Different ways to create a proc
# inc = Proc.new { |x| x + 1 } # same as preferred...
inc = proc { |x| x + 1 }
inc.class # => Proc
inc.call(1) # => 2
# dec = lambda { |x| x - 1 } # same as preferred with "stabby lambda"...
dec = ->(x) { x - 1 }
dec.class # => Proc
dec.call(1) # => 0
# Define a method on a single object
obj = 'abc'
class << obj
def my_singleton_method
'x'
end
end
obj.my_singleton_method
# Another example
str = 'any string'
def str.title?
# self.upcase == self # self.upcase's self is redundant, so...
upcase == self
end
str.title? # => false
str.methods.grep(/title?/) # => [:title?]
str.singleton_methods # => [:title?]
# Use a class method in a class definition
class C; end
class << C
def my_macro(arg)
"my_macro(#{arg} called"
end
end
class C
my_macro :x # => "my_macro(x) called"
end
# Simple
class Book
def title
# ...
end
def self.deprecate(old_method, new_method)
define_method(old_method) do |*args, &block|
warn "Warning: #{old_method}() is deprecated. Use #{new_method}()."
send(new_method, *args, &block)
end
end
deprecate :get_title, :title
end
# Use Object#extend as a shortcut to
# include a module in a receiver's singleton class
#
# this will NOT work...
# module MyModule
# def self.my_class_method; end
# end
#
# class MyClass
# include MyModule
# end
#
class C; end
module M
# becomes a class method when included as an instance method
# on the singleton class of class C
def my_method
'a class method'
end
end
class << C
include M
end
# or use "extend" to do the same...
class C
extend M
end
C.my_method # => 'a class method'
module N
def my_method
'a singleton method'
end
end
obj = Object.new
class << obj
include N
end
# or use "extend" to do the same...
obj.extend N
obj.my_method # => 'a singleton method'
# Call the previous, aliased version of a method from a redifined method.
class String
# rubocop:disable Alias
alias_method :old_reverse, :reverse
# rubocop:enable Alias
def reverse
"x#{old_reverse}x"
end
end
'abc'.reverse # => "xcbax"
# Call an unrefined method from its refinement
module StringRefinement
refine String do
def reverse
"x#{super}x"
end
end
end
using StringRefinement
'abc'.reverse # => "xcbax"
# Call a method from its prepended override
module M
def reverse
"x#{super}x"
end
end
String.class_eval do
prepend M
end
'abc'.reverse # => "xcbax"
Code snippets from or inspired by Metaprogramming Ruby 2