2017-01-25 2 views
1

Я пытаюсь понять, есть ли способ использовать send для вызова метода с динамическими аргументами (* args, ** kwrags), в то время как метод имеет только аргументы по умолчанию.Как отправить динамические аргументы методам с аргументами по умолчанию

Учитывая следующий класс:

class Worker 
    def perform(foo='something') 
    # do something 
    end 
end 

У меня есть модуль, который необходимо проверить некоторые условия. Если условия не выполняются, то метод #perform должен работать:

module SkippableWorker 
    def self.included(base) 
    base.class_eval do 
     alias_method :skippable_perform, :perform 
     define_method :perform do |*args, **keyword_args| 
     return if skip? 
     self.send(:skippable_perform, *args, **keyword_args) 
     end 

     define_method :skip? do 
     # checks whether to skip or not 
     end 
    end 
    end 
end 

После того, как в том числе модуля в классе работника, я сталкиваюсь с проблемой в команду отправки модуля:

self.send(:skippable_perform, *args, **keyword_args) 

If когда хочет вызвать #perform и использовать аргументы по умолчанию, то вызов будет выполнен без каких-либо аргументов вообще:

Worker.new.perform() 

Но команда посыла я n модуль переводит * args и ** kwargs в [] и {}. Таким образом, вызов #perform заканчивается для отправки (: выполнить, [], {}), что вызывает несоответствие суммы аргумента ArgumentError для аргументов или, по крайней мере, отправляет значение, которое не означает отправить ([] вместо default 'something').

Любые идеи, как преодолеть это?

+2

Если вы удалите kwargs из уравнения, все [начинает работать] (http://pastebin.com/qt84MvnR). –

+0

Благодаря @SergioTulentsev, это действительно работает, и я ценю подробный ответ. Попытка, если кто-нибудь придумает идею для kwargs. –

ответ

2

Проблема

Лучшее объяснение, которое я нашел, было here.

**{} не анализируется так же, как **kwargs, даже если kwargs - пустой хеш.

keyword_args.empty?

Одним простым решением было бы проверить, пуст ли kwargs перед вызовом skippable_perform.

Полный код становится:

module SkippableWorker 
    def self.included(base) 
    base.class_eval do 
     alias_method :skippable_perform, :perform 
     define_method :perform do |*args, **keyword_args| 
     return if skip? 
     if keyword_args.empty? 
      self.send(:skippable_perform, *args) 
     else 
      self.send(:skippable_perform, *args, **keyword_args) 
     end 
     end 

     define_method :skip? do 
     puts self.class 
     if rand > 0.5 
      puts " Skipping" 
      true 
     end 
     end 
    end 
    end 
end 

class Worker 
    def perform(foo='standard arg') 
    puts " #{foo}" 
    end 
    include SkippableWorker 
end 

class AnotherWorker 
    def perform(bar:'standard kwarg') 
    puts " #{bar}" 
    end 
    include SkippableWorker 
end 

Worker.new.perform 
Worker.new.perform 
Worker.new.perform('another arg') 
Worker.new.perform('another arg') 
AnotherWorker.new.perform 
AnotherWorker.new.perform 
AnotherWorker.new.perform(bar: 'another kwarg') 
AnotherWorker.new.perform(bar: 'another kwarg') 

В зависимости от rand, она могла бы выход:

Worker 
    standard arg 
Worker 
    Skipping 
Worker 
    Skipping 
Worker 
    another arg 
AnotherWorker 
    standard kwarg 
AnotherWorker 
    standard kwarg 
AnotherWorker 
    Skipping 
AnotherWorker 
    another kwarg 

Метод # Параметры

Другой метод должен был бы проверить, какие параметры ожидаются от skippable_perform :

method(:skippable_perform).parameters.any?{|type,name| type == :key} 

и адаптировать define_method в зависимости от результата.

+0

Спасибо, хорошие идеи! –

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