2015-06-13 2 views
1

Если вы запустите код, указанный ниже, вы получите сообщение об ошибке.Как изменить контекст лямбда?

class C 
    def self.filter_clause param_1 
    puts param_1 
    yield # context of this param is class B 
    end 

    def hi 
    "hello" 
    end 
end 

class B 
    def self.filter(value, lambda) 
    code = lambda { filter_clause(value, &lambda) } 
    C.instance_exec(&code) 
    end 

    filter(:name, ->{ hi }) 
end 

Ошибка

NameError: undefined local variable or method `hi' for B:Class 
from (pry):17:in `block in <class:B>' 

Из моего понимания причины для этого лямбда работает под контексте class B. Поэтому он не может найти метод def hi. Я не могу понять, как заставить его работать в контексте class C.

По существу, я хочу, чтобы иметь возможность вводить метод, вызываемый в другом классе, который принимает аргумент и блок.

Например:

filter_clause("value", ->{ hi })

Возможно ли это?

Не уверен, если у меня есть смысл.

+0

Где находится этот класс A? – steenslag

+0

Извините, удалили ссылку на A. –

ответ

3

Вы близко. Если вы хотите, чтобы lambda (/ block, really) выполнялся в контексте экземпляра C (поскольку hi - это метод экземпляра на C), тогда вам нужно создать его экземпляр, а затем экземпляр_exec - блок в этом новом экземпляре:

class C 
    def self.filter_clause param_1, &block 
    puts new.instance_exec &block 
    end 

    def hi 
    "hello" 
    end 
end 

class B 
    def self.filter(value, lambda) 
    code = lambda { filter_clause(value, &lambda) } 
    C.instance_exec(&code) 
    end 

    filter(:name, ->{ hi }) 
end 

# => hello 
+0

'instance_exec' внутри' instance_exec'? Ожидаемое удовольствие: D Независимо, это действительно Ruby. –

+0

В игре есть две лямбда, и они оба должны отскочить. Мы не просто повторяем одну и ту же лямбду дважды. Lambda 'code' получает отскок для выполнения на' C', а анонимная лямбда, переданная в 'filter', получает отскок для выполнения на экземпляре' C'. –

+0

Правда, но один из контекстов не используется в любом случае. –

2

Вы не согласны передать контекст в блок? Если нет, то что-то вроде этого будет работать:

class C 
    def self.filter_clause param_1 
    puts param_1 
    yield self 
    end 

    def hi 
    "hello" 
    end 
end 

class B 
    def self.filter(value, lambda) 
    code = lambda { filter_clause(value, &lambda) } 
    C.instance_exec(&code) 
    end 

    filter(:name, ->(klass){ klass.new.hi }) # "hello" 
end 
+0

Спасибо, но я не хочу указывать контекст при вызове фильтра, если это возможно. Мне уже удалось получить версию этой работы. Похоже, он раздувает код. Я надеялся, что так или иначе я смогу понять это. Я мог бы потенциально обновить контекст в методе 'def self.filter', но я не уверен, как изменить контекст лямбда в' def self.filter'. –

Смежные вопросы