metaprogramming
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.
What this skill does
# 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
```ruby
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?
```ruby
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
```ruby
# 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
```ruby
# 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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.