2015-07-26 1 views
0

Я пишу DSL. Я не хочу, чтобы пользователям приходилось приводить аргументы для передачи строк, поэтому я переписываю method_missing, чтобы преобразовать неизвестный метод в строку. В следующем примере create является методом DSL, и я хотел, чтобы пользователь вводил arg1 и arg2 без кавычек.Почему `gsub` вызывает` to_hash`?

def method_missing(m, *arg) 
    m.to_s 
end 
def create(*args) 
    arg1.gsub(#do something here) 
end 

create arg1 arg2 

Однако это вызывает и ошибок, когда я использую gsub на «строка»:

'gsub': can't convert String to Hash (String#to_hash gives String) (TypeError) 

Я думаю method_missing перезапись испортили его, так как он выглядит как gsub звонит String#to_hash, который а не метод в String, поэтому он направляется на method_missing.

Мне интересно, почему gsub звонит String#to_hash, или есть ли другой способ позволить пользователям DSL не печатать кавычки без перезаписи method_missing.

+0

Какова связь между 'arg1',' arg2' и 'args' в' create'? – sawa

+0

arg1,2 - это просто аргументы, переданные методу create, возможно, я должен добавить что-то вроде arg1 = arg.first перед gsub-линией, поэтому его менее запутанное значение – yuzhoul

ответ

1

String#gsub делает разные вещи в зависимости от числа аргументов и типов, а если блок был дан:

gsub(pattern, replacement) → new_str 
gsub(pattern, hash) → new_str 
gsub(pattern) {|match| block } → new_str 
gsub(pattern) → enumerator 

Второй документирована как:

Если второй аргументом является Hash, а согласованный текст является одним из его ключей, соответствующее значение является заменой строки.

Но как отличить его от первого? Оба имеют два аргумента! Это немного сложно, но в вашем случае Ruby (ну, ссылочная реализация, называемая CRuby или MRI, если быть точным) начинается с проверки того, имеет ли второй аргумент внутренний тип T_HASH (это не так, как это наиболее вероятно T_STRING из-за #to_s) , то он проверяет, можно ли вызывать #to_hash. Либо потому, что он отвечает на него, либо вместо этого может быть #method_missing. Вы определили его, так что Ruby называет его. Однако он не возвращает T_HASH, и это является причиной исключения, которое вы опубликовали.

Возможное решение является определяющим main.method_missing и не Object#method_missing (как String наследуется от Object):

def self.method_missing(m, *arg) 
    m.to_s 
end 

Однако я рекомендую придерживаться кавычки или написать свой собственный небольшой парсер для этого типа файла, если он не должен придерживаться синтаксиса Ruby. Использование *_missing может быть причиной ошибочных или бесполезных сообщений об ошибках.Или даже нет (я думаю, create arg1 arg2 должен был быть create arg1, arg2).

+0

Спасибо за подробное объяснение – yuzhoul

0

gsub, вероятно, сам использует где-то method_missing, поэтому кажется, что определение его во всем мире вызывает внутренние проблемы при вызове метода. Если вы собираетесь использовать method_missing убедитесь, что вы всегда определить его в модуле или классе:

module CoolDSL 
    def self.method_missing(m, *arg) 
    m.to_s 
    end 

    def self.create(*args) 
    args[0].gsub(/1/, "2") 
    end 

    def self.do_thing 
    create arg1 arg2 
    end 
end 

CoolDSL.do_thing 

Естественно, что это не совсем полезно, как DSL, так что вы хотите, чтобы узнать силу instance_eval и yield. Мне нравится this guide.

+0

hmm. Не знаете, как это руководство решает проблему отсутствия необходимости добавлять кавычки к аргументам, переданным методу dsl. Это слова «Пользователь», «Почта» на самом деле являются предопределенными классами. – yuzhoul

+0

Я упоминал об этом как о хорошем месте для начала, если вы хотите узнать еще несколько трюков Ruby DSL. Мой ответ объясняет, почему вы получили эту ошибку с gsub. (Просто убедитесь, что вы правильно используете свой метод_missing) – Nate

+0

'gsub' не использует' method_missing', а 99% времени вы используете 'method_missing', вы делаете что-то не так, что иногда будете сожалеть о будущем – bjhaid