never_had_a_name
never_had_a_name

Reputation: 93146

Using methods from two different scopes?

How do I use methods from two different namespaces?

class Bar
  def self.configure &block
    new.instance_eval &block
  end

  def method2
    puts "from Bar"
  end
end

class Foo
  def method1
    puts "from Foo"
  end

  def start
    Bar.configure do
      method1
      method2
    end
  end
end

Foo.new.start

In the above example the method1 can't be called because it is not from the Bar scope. How do I make methods from both scopes callable at the same time?

Upvotes: 0

Views: 249

Answers (4)

Harish Shetty
Harish Shetty

Reputation: 64363

I would simplify your code as follows:

class Bar
  def self.configure &block
    obj = new
    block.call(obj)
    obj
  end

  def method2
    puts "from Bar"
  end
end

class Foo
  def method1
    puts "from Foo"
  end

  def start
    Bar.configure do |obj|
      method1
      obj.method2
    end
  end
end

Foo.new.start

Block logic is clean and implementation doesn't require context switching. You are using the standard ruby functionality of passing parameters to the block.

Upvotes: 1

Shinya
Shinya

Reputation: 146

Maybe, this is the easiest way. It doesn't need to modify Bar.

class Bar
  def self.configure &block
    new.instance_eval &block
  end

  def method2
    puts "from Bar"
  end
end

class Foo
  def method1
    puts "from Foo"
  end

  def start
    foo = self # add
    Bar.configure do
      foo.method1 # modify
      method2
    end
  end
end

Foo.new.start

Upvotes: 1

horseyguy
horseyguy

Reputation: 29895

The trick is to forward missing method calls to the instance of Foo:

class Bar
    def self.configure(&block)
        o = new
        context = eval('self', block.binding)

       class << o; self; end.send(:define_method, :method_missing) do |meth, *args|
           context.send(meth, *args)
       end

       o.instance_eval &block
    end

    def method2
        puts "from Bar"
    end
end

class Foo
    def method1
        puts "from Foo"
    end

   def start
       Bar.configure do
           method1
           method2
       end
   end
end

Foo.new.start #=> "from Foo"
              #=> "from Bar"

Upvotes: 3

Sudhanshu
Sudhanshu

Reputation: 2871

Try this:

class Bar                              
  def initialize(foo)
    puts "init"
    @f = foo 
  end 

  def self.configure(foo, &block)
    new(foo).instance_eval &block
  end 

  def method2
    puts "from Bar"
  end 
end 

class Foo 
  def method1
    puts "from Foo"
  end 

  def start
    Bar.configure(self) do
      @f.method1
      method2
    end 
  end 
end 

This makes @f a class level instance variable of Bar, which is set when you initialize an object of Bar using new(foo) in Bar.configure. The block being passed assumes the existence of @f, which contains a reference to the object of class Foo.

It's a convoluted way of doing this - I can't think of anything better though. It'd be interesting to know the use case.

Upvotes: 1

Related Questions