Rorshark
Rorshark

Reputation: 87

Class-like closures in ruby

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

Answers (2)

matt
matt

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

Tamer Shlash
Tamer Shlash

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

Related Questions