Reputation: 87
I'm trying to do something like this in Ruby:
class A
def b(c)
Class.new do
def d
c
end
end
end
end
class B < A.b('whoa'); end
#
# What I want:
#
B.new.d # => 'whoa'
#
# What I get:
#
# NameError: undefined local variable or method `d' for B:0xfdfyadayada
Is there any way to do this?
The context is I'm trying to get some code reuse by constructing classes that are mostly similar except for some minor config.
I'm looking for an API similar to Sequel::Model where you can do something like:
class Order < Sequel::Model(:order)
Upvotes: 1
Views: 59
Reputation: 79733
The contents of a method definition using the def
keyword are not lexically scoped the way blocks are. In other words you can’t do something like this:
foo = 7
def bar
# Code here inside the method definition can't
# "see" foo, so this doesn't work.
foo
end
However you can use the define_method
method to dynamically define a method using a block, and the block will be able to refer to local variables in its outer scope. So in your case you could do this (I’ve also changed def b
to def self.b
, which I think is what you meant):
class A
def self.b(c)
Class.new do
define_method(:d) do # N.B. the d here is a symbol
c
end
end
end
end
class B < A.b('whoa'); end
B.new.d # => 'whoa'
This would also work if you created other classes:
class C < A.b('dude'); end
C.new.d # => 'dude'
# B still works
B.new.d # => 'whoa'
Upvotes: 4
Reputation: 9523
Assuming that b
is a class method (i.e def self.b
instead of def b
), you could store c
in a class variable.
class A
def self.b(c)
Class.new do
@@c = c
def d
@@c
end
end
end
end
class B < A.b('whoa'); end
puts B.new.d # => whoa
There could be a better solution, I haven't looked at the source code of Sequel. This is just the first thing that came to my mind.
Upvotes: 0