Back to list
bastos

metaprogramming

by bastos

Bastos' Claude Code Ruby Plugin Marketplace

0🍴 0📅 Jan 24, 2026

SKILL.md


name: metaprogramming description: This skill should be used when the user asks about "metaprogramming", "DSL", "domain-specific language", "method_missing", "define_method", "class_eval", "instance_eval", "module_eval", "hooks", "included", "extended", "inherited", "singleton class", "eigenclass", "dynamic methods", "eval", or needs guidance on Ruby metaprogramming techniques. version: 1.0.0

Ruby Metaprogramming

Guide to Ruby metaprogramming techniques, DSL creation, and dynamic code generation.

When to Use Metaprogramming

Use metaprogramming when:

  • Building DSLs for configuration or testing
  • Reducing repetitive code patterns
  • Creating flexible APIs
  • Implementing frameworks

Avoid when:

  • Simple methods would suffice
  • Code clarity is more important than conciseness
  • Debugging would become difficult

Dynamic Method Definition

define_method

class Calculator
  OPERATIONS = { add: :+, subtract: :-, multiply: :*, divide: :/ }.freeze

  OPERATIONS.each do |operation, operator|
    define_method(operation) do |a, b|
      a.send(operator, b)
    end
  end
end

calc = Calculator.new
calc.add(2, 3)       # => 5
calc.multiply(4, 5)  # => 20

method_missing and respond_to_missing?

class FlexibleStruct
  def initialize(attributes = {})
    @attributes = attributes
  end

  def method_missing(name, *args)
    attribute = name.to_s.chomp("=").to_sym

    if name.to_s.end_with?("=")
      @attributes[attribute] = args.first
    elsif @attributes.key?(attribute)
      @attributes[attribute]
    else
      super
    end
  end

  def respond_to_missing?(name, include_private = false)
    attribute = name.to_s.chomp("=").to_sym
    @attributes.key?(attribute) || super
  end
end

person = FlexibleStruct.new(name: "Alice")
person.name       # => "Alice"
person.age = 30
person.age        # => 30
person.respond_to?(:name)  # => true

Class and Module Evaluation

class_eval / module_eval

# Add methods to a class dynamically
String.class_eval do
  def shout
    upcase + "!"
  end
end

"hello".shout  # => "HELLO!"

# With string evaluation (use sparingly)
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
  def #{method_name}
    @#{attribute}
  end
RUBY

instance_eval

# Evaluate in context of an object
class Config
  attr_accessor :host, :port

  def configure(&block)
    instance_eval(&block)
  end
end

config = Config.new
config.configure do
  self.host = "localhost"
  self.port = 3000
end

Hooks and Callbacks

Module Hooks

module Trackable
  def self.included(base)
    base.extend(ClassMethods)
    base.class_eval do
      # Add instance-level behavior
      attr_accessor :tracked_at
    end
  end

  def self.extended(base)
    # Called when module is extended
  end

  module ClassMethods
    def track_creation
      define_method(:initialize) do |*args|
        super(*args)
        @tracked_at = Time.now
      end
    end
  end
end

class User
  include Trackable
  track_creation
end

Class Hooks

class BaseModel
  def self.inherited(subclass)
    subclass.instance_variable_set(:@fields, [])
    subclass.extend(ClassMethods)
  end

  module ClassMethods
    def field(name)
      @fields << name
      attr_accessor name
    end

    def fields
      @fields
    end
  end
end

class User < BaseModel
  field :name
  field :email
end

User.fields  # => [:name, :email]

Method Hooks

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

  module ClassMethods
    def method_added(name)
      return if @_adding_method
      return if name == :initialize

      @_adding_method = true
      original = instance_method(name)

      define_method(name) do |*args, &block|
        puts "Calling #{name} with #{args}"
        original.bind(self).call(*args, &block)
      end

      @_adding_method = false
    end
  end
end

DSL Creation

Configuration DSL

class ServerConfig
  attr_reader :settings

  def initialize
    @settings = {}
  end

  def self.configure(&block)
    config = new
    config.instance_eval(&block)
    config
  end

  def host(value)
    @settings[:host] = value
  end

  def port(value)
    @settings[:port] = value
  end

  def ssl(enabled: true, &block)
    @settings[:ssl] = { enabled: enabled }
    SSLConfig.new(@settings[:ssl]).instance_eval(&block) if block
  end

  class SSLConfig
    def initialize(settings)
      @settings = settings
    end

    def certificate(path)
      @settings[:certificate] = path
    end

    def key(path)
      @settings[:key] = path
    end
  end
end

config = ServerConfig.configure do
  host "localhost"
  port 3000
  ssl enabled: true do
    certificate "/path/to/cert.pem"
    key "/path/to/key.pem"
  end
end

Builder DSL

class HTMLBuilder
  def initialize
    @html = []
  end

  def method_missing(tag, content = nil, **attrs, &block)
    attr_str = attrs.map { |k, v| %( #{k}="#{v}") }.join
    @html << "<#{tag}#{attr_str}>"

    if block
      nested = HTMLBuilder.new
      nested.instance_eval(&block)
      @html << nested.to_s
    elsif content
      @html << content
    end

    @html << "</#{tag}>"
    self
  end

  def respond_to_missing?(*)
    true
  end

  def to_s
    @html.join
  end
end

html = HTMLBuilder.new
html.div(class: "container") do
  h1 "Welcome"
  p "Hello, World!"
  ul do
    li "Item 1"
    li "Item 2"
  end
end

puts html.to_s
# <div class="container"><h1>Welcome</h1><p>Hello, World!</p>...

The Object Model

Singleton Classes

obj = Object.new

# Access singleton class
obj.singleton_class

# Define singleton methods
def obj.greet
  "Hello!"
end

# Or using singleton_class
obj.singleton_class.define_method(:farewell) { "Goodbye!" }

# Class methods are singleton methods on Class objects
class User
  def self.count  # Defined on User's singleton class
    @count ||= 0
  end
end

Ancestors and Method Lookup

module A; end
module B; end
module C; end

class Parent
  include A
end

class Child < Parent
  include B
  prepend C  # Prepend inserts before the class
end

Child.ancestors
# => [C, Child, B, Parent, A, Object, Kernel, BasicObject]

# Method lookup follows ancestors chain

Prepend vs Include

module Logging
  def save
    puts "Before save"
    super
    puts "After save"
  end
end

class Record
  prepend Logging  # Logging#save called before Record#save

  def save
    puts "Saving..."
  end
end

Record.new.save
# Before save
# Saving...
# After save

Additional Resources

Reference Files

  • references/metaprogramming-patterns.md - Common metaprogramming patterns and anti-patterns

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon