Skip to content
define_method and Dynamic Methods
step 1/5

Reading — step 1 of 5

Learn

~1 min readMetaprogramming

define_method lets you create methods at runtime. Used heavily by Rails (has_many, validates).

class Config
  [:host, :port, :db_name, :user].each do |attr|
    define_method(attr) { @config[attr] }
    define_method("#{attr}=") { |v| @config[attr] = v }
  end

  def initialize
    @config = {}
  end
end

c = Config.new
c.host = "localhost"
c.port = 5432
puts c.host    # "localhost"

This generates 8 methods (4 readers + 4 writers) without writing each by hand.

Naming with send / public_send:

class User
  attr_accessor :name, :email
end

user = User.new
fields = { name: "Ada", email: "[email protected]" }
fields.each { |k, v| user.send("#{k}=", v) }

send calls a method by name (string or symbol). public_send respects access modifiers. send bypasses them — useful but dangerous.

instance_variable_set / instance_variable_get:

user.instance_variable_set(:@name, "Linus")
puts user.instance_variable_get(:@name)

The @ prefix is required.

method_defined? and respond_to?:

User.method_defined?(:name)         # true if instance method exists
user.respond_to?(:name)             # true if instance can answer

Real-world example: Rails-style attr_accessor from scratch:

class Module
  def my_attr_accessor(*names)
    names.each do |name|
      define_method(name) { instance_variable_get("@#{name}") }
      define_method("#{name}=") { |v| instance_variable_set("@#{name}", v) }
    end
  end
end

class Foo
  my_attr_accessor :a, :b, :c
end

Reopening Module like this adds my_attr_accessor to ALL classes — Ruby's permissive inheritance. Powerful + occasionally dangerous.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…