ruby-metaprogramming

Master Ruby's powerful metaprogramming capabilities to write code that writes code. Ruby's dynamic nature makes it exceptionally good at metaprogramming.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "ruby-metaprogramming" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-ruby-metaprogramming

Ruby Metaprogramming

Master Ruby's powerful metaprogramming capabilities to write code that writes code. Ruby's dynamic nature makes it exceptionally good at metaprogramming.

Dynamic Method Definition

define_method

class Person [:name, :age, :email].each do |attribute| define_method(attribute) do instance_variable_get("@#{attribute}") end

define_method("#{attribute}=") do |value|
  instance_variable_set("@#{attribute}", value)
end

end end

person = Person.new person.name = "Alice" puts person.name # "Alice"

class_eval and instance_eval

class_eval - Evaluates code in context of a class

class MyClass end

MyClass.class_eval do def hello "Hello from class_eval" end end

puts MyClass.new.hello

instance_eval - Evaluates code in context of an instance

obj = Object.new obj.instance_eval do def greet "Hello from instance_eval" end end

puts obj.greet

module_eval

module MyModule end

MyModule.module_eval do def self.info "Module metaprogramming" end end

puts MyModule.info

Method Missing

Basic method_missing

class DynamicFinder def initialize(data) @data = data end

def method_missing(method_name, *args) if method_name.to_s.start_with?("find_by_") attribute = method_name.to_s.sub("find_by_", "") @data.find { |item| item[attribute.to_sym] == args.first } else super end end

def respond_to_missing?(method_name, include_private = false) method_name.to_s.start_with?("find_by_") || super end end

users = [ { name: "Alice", age: 30 }, { name: "Bob", age: 25 } ]

finder = DynamicFinder.new(users) puts finder.find_by_name("Alice") # {:name=>"Alice", :age=>30}

Const Missing

class DynamicConstants def self.const_missing(const_name) puts "Constant #{const_name} not found, creating it..." const_set(const_name, "Dynamic value for #{const_name}") end end

puts DynamicConstants::SOMETHING # "Dynamic value for SOMETHING"

send and public_send

class Calculator def add(x, y) x + y end

private

def secret_method "This is private" end end

calc = Calculator.new

send can call any method (including private)

puts calc.send(:add, 3, 4) # 7 puts calc.send(:secret_method) # "This is private"

public_send only calls public methods

puts calc.public_send(:add, 3, 4) # 7

calc.public_send(:secret_method) # NoMethodError

Class Macros

class ActiveModel def self.attr_with_history(attribute) define_method(attribute) do instance_variable_get("@#{attribute}") end

define_method("#{attribute}=") do |value|
  history = instance_variable_get("@#{attribute}_history") || []
  history << value
  instance_variable_set("@#{attribute}_history", history)
  instance_variable_set("@#{attribute}", value)
end

define_method("#{attribute}_history") do
  instance_variable_get("@#{attribute}_history") || []
end

end end

class Person < ActiveModel attr_with_history :name end

person = Person.new person.name = "Alice" person.name = "Alicia" puts person.name_history.inspect # ["Alice", "Alicia"]

Singleton Methods

obj = "hello"

Define method on single instance

def obj.shout self.upcase + "!!!" end

puts obj.shout # "HELLO!!!"

Using define_singleton_method

obj.define_singleton_method(:whisper) do self.downcase + "..." end

puts obj.whisper # "hello..."

Eigenclass (Singleton Class)

class Person def self.species "Homo sapiens" end end

Accessing eigenclass

eigenclass = class << Person self end

puts eigenclass # #<Class:Person>

Adding class methods via eigenclass

class Person class << self def count @@count ||= 0 end

def increment_count
  @@count ||= 0
  @@count += 1
end

end end

Person.increment_count puts Person.count # 1

Reflection and Introspection

Object Introspection

class MyClass def public_method; end protected def protected_method; end private def private_method; end end

obj = MyClass.new

List methods

puts obj.methods.include?(:public_method) puts obj.private_methods.include?(:private_method) puts obj.protected_methods.include?(:protected_method)

Check method existence

puts obj.respond_to?(:public_method) # true puts obj.respond_to?(:private_method) # false puts obj.respond_to?(:private_method, true) # true (include private)

Get method object

method = obj.method(:public_method) puts method.class # Method

Class Introspection

class Parent def parent_method; end end

class Child < Parent def child_method; end end

Inheritance chain

puts Child.ancestors # [Child, Parent, Object, Kernel, BasicObject]

Instance methods

puts Child.instance_methods(false) # Only Child's methods

Class variables and instance variables

class Person @@count = 0 def initialize(name) @name = name end end

puts Person.class_variables # [:@@count] person = Person.new("Alice") puts person.instance_variables # [:@name]

Hook Methods

Inheritance Hooks

class BaseClass def self.inherited(subclass) puts "#{subclass} inherited from #{self}" subclass.instance_variable_set(:@inherited_at, Time.now) end end

class ChildClass < BaseClass end

Output: ChildClass inherited from BaseClass

Method Hooks

module Monitored def self.included(base) base.extend(ClassMethods) end

module ClassMethods def method_added(method_name) puts "Method #{method_name} was added to #{self}" end

def method_removed(method_name)
  puts "Method #{method_name} was removed from #{self}"
end

end end

class MyClass include Monitored

def my_method end

Output: Method my_method was added to MyClass

end

included and extended

module MyModule def self.included(base) puts "#{self} included in #{base}" base.extend(ClassMethods) end

def self.extended(base) puts "#{self} extended by #{base}" end

module ClassMethods def class_method "I'm a class method" end end

def instance_method "I'm an instance method" end end

class MyClass include MyModule # Adds instance_method as instance method end

class AnotherClass extend MyModule # Adds instance_method as class method end

DSL Creation

class RouteBuilder def initialize @routes = {} end

def get(path, &block) @routes[path] = { method: :get, handler: block } end

def post(path, &block) @routes[path] = { method: :post, handler: block } end

def routes @routes end end

DSL usage

builder = RouteBuilder.new builder.instance_eval do get "/users" do "List of users" end

post "/users" do "Create user" end end

puts builder.routes

Dynamic Class Creation

Create class dynamically

MyClass = Class.new do define_method :greet do "Hello from dynamic class" end end

puts MyClass.new.greet

Create class with inheritance

Parent = Class.new do def parent_method "From parent" end end

Child = Class.new(Parent) do def child_method "From child" end end

child = Child.new puts child.parent_method puts child.child_method

Object Extension

module Greetable def greet "Hello!" end end

obj = Object.new obj.extend(Greetable) puts obj.greet # "Hello!"

Only this instance has the method

another_obj = Object.new

another_obj.greet # NoMethodError

Binding and eval

def get_binding(param) local_var = "local value" binding end

b = get_binding("test")

Evaluate code in the binding context

puts eval("param", b) # "test" puts eval("local_var", b) # "local value"

instance_eval with binding

class MyClass def initialize @value = 42 end end

obj = MyClass.new puts obj.instance_eval { @value } # 42

TracePoint

trace = TracePoint.new(:call, :return) do |tp| puts "#{tp.event}: #{tp.method_id} in #{tp.defined_class}" end

trace.enable

def my_method "Hello" end

my_method

trace.disable

Best Practices

  • Use metaprogramming sparingly - it can make code hard to understand

  • Always implement respond_to_missing? when using method_missing

  • Prefer define_method over class_eval when possible

  • Document metaprogramming heavily - it's not obvious what's happening

  • Use public_send over send to respect visibility

  • Cache metaprogrammed methods to avoid repeated definition

  • Test metaprogrammed code thoroughly - bugs can be subtle

Anti-Patterns

❌ Don't overuse method_missing - it's slow and hard to debug ❌ Don't use eval with user input - major security risk ❌ Don't metaprogram when simple code works - clarity over cleverness ❌ Don't forget to call super in method_missing ❌ Don't create methods without documenting them - IDE support breaks

Common Use Cases

  • ORMs (ActiveRecord) - Dynamic finders, associations

  • DSLs - Route definitions, configurations

  • Decorators - Method wrapping and enhancement

  • Mocking/Stubbing - Test frameworks

  • Attribute definition - Custom accessors with behavior

Related Skills

  • ruby-oop - Understanding classes and modules

  • ruby-blocks-procs-lambdas - For callbacks and dynamic behavior

  • ruby-gems - Many gems use metaprogramming extensively

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

typescript-type-system

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

c-systems-programming

No summary provided by upstream source.

Repository SourceNeeds Review