2014-11-28 4 views
1

Я получаю следующее сообщение об ошибке:ошибка синтаксиса, неожиданный «:», ожидая конца входного текста

syntax error, unexpected ':', expecting end-of-input 
      field :'lastapple info', type: String 
       ^

с помощью этого метода прямо здесь:

def eval_mongo(klass, field) 
    _field = field['field'].to_sym 
    _type = FieldType.where(_id: field['field_type_id']).first.type_from_field 
    klass.class_eval <<-EOS 
      field :'#{ _field }', type: #{ _type } 
    EOS 
    end 

Я смотрю на это и я не может понять, где ошибка. Поскольку я использую heredocs, я не требую завершения для class_eval. На самом деле, он отлично работает в консоли, и я использовал его раньше, поэтому я знаю, что это не может быть проблемой. Итак, в чем проблема?

+0

Не должно быть 'field: # {_field}, type: # {_type}'? Вы хотите создать символ, а не строку? – marc

+0

@marc Мне нужна строка, потому что информация last_apple без строки приведет к недопустимому методу с пробелом и вызовет неопределенную информацию об ошибке. – Donato

+0

Это должно сработать. Какую версию Ruby вы используете? Использование 'eval' всегда вызывает большое беспокойство, поэтому я надеюсь, что вы сможете избежать этого, используя' klass.send (_field.to_sym, _type) ' – tadman

ответ

1

Ваша проблема заключается в том, что вы пытаетесь использовать имя field слишком много вещей здесь:

def eval_mongo(klass, field) 
    _field = field['field'].to_sym 
    _type = FieldType.where(_id: field['field_type_id']).first.type_from_field 
    klass.class_eval <<-EOS 
    field :'#{ _field }', type: #{ _type } 
    EOS 
end 

field является аргументом eval_mongo, но вы также можете использовать его в качестве имени метода класса внутри звонок class_eval. Внутри class_eval Ruby думает, что вы хотите аргумент field, чтобы получить синтаксическую ошибку. Если вы называете аргумент f вместо:

def eval_mongo(klass, f) 
    _field = f['field'].to_sym 
    _type = FieldType.where(_id: f['field_type_id']).first.type_from_field 
    klass.class_eval <<-EOS 
    field :'#{ _field }', type: #{ _type } 
    EOS 
end 

то все должно работать.

Хотелось бы, чтобы я мог дать четкое объяснение того, что здесь происходит, но я не могу. Вместо этого я подведу итог тому, что я обнаружил, бродив по источнику магнитно-резонансной томографии и пытаясь дразнить поведение посредством экспериментов. Этот подход очень подвержен ошибкам, но часто у нас есть Ruby.

The documentation говорит:

class_eval(string [, filename [, lineno]]) → obj

Evaluates the string or block in the context of mod, except that when a block is given, constant/class variable lookup is not affected. [...]

Это, как обычно, с Ruby, не достаточно подробно или достаточно конкретным, чтобы быть очень полезным.

Если посмотреть на источник, мы увидим, что на самом деле class_evalrb_mod_module_eval in vm_eval.c который просто вызывает specific_eval который вызывает eval_under который вызывает eval_string_with_cref со значением Qnil для scope аргумента. Это scope обрабатывается следующим образом:

if (!NIL_P(scope)) { 
    /* ... */ 
} 
else { 
    rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp); 

    if (cfp != 0) { 
     block = *RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp); 
     base_block = &block; 
     base_block->self = self; 
     base_block->iseq = cfp->iseq; /* TODO */ 
    } 
    else { 
     rb_raise(rb_eRuntimeError, "Can't eval on top of Fiber or Thread"); 
    } 
} 

, а затем base_block используется для компиляции строки исходного кода. Я не очень хорошо знаком с источником MRI, но похоже, что он намеренно настраивает возможности использования области вокруг class_eval.

Упрощенный пример может помочь:

class K 
    def self.field(*args) 
    puts args.inspect 
    end 
end 

def eval_mongo(klass, f) 
    klass.class_eval <<-EOS 
    field :'#{f}', type: String 
    f 
    EOS 
end 

puts eval_mongo(K, 'pancakes house').inspect 

Что скажут:

[:"pancakes house", {:type=>String}] 
"pancakes house" 

Если оставить имя в одиночку, но называть field с строковым аргументом:

def eval_mongo(klass, field) 
    klass.class_eval <<-EOS 
    field '#{field}', type: String 
    field 
    EOS 
end 

затем он также работает и говорит:

["pancakes house", {:type=>String}] 
"pancakes house" 

, но если мы будем использовать field в качестве имени аргумента и использовать символ:

def eval_mongo(klass, field) 
    klass.class_eval <<-EOS 
    field :'#{field}', type: String 
    field 
    EOS 
end 

мы получаем нашу синтаксическую ошибку:

in `class_eval': (eval):1: syntax error, unexpected ':', expecting end-of-input (SyntaxError) 
    field :'pancakes house', type: String 
     ^

Интересно, что вы получите тот же синтаксис если вы попытаетесь использовать несколько скрытую функцию вставки строк в Ruby:

> s = 'a' 'b' 
=> "ab" 

с символом:

> s = 'a' :'b' 
SyntaxError: (irb):2: syntax error, unexpected ':', expecting end-of-input 
s = 'a' :'b' 
     ^

Возможно, разные вещи оцениваются в разное время и Руби путаться о том, что это строка, а что нет.

Такое поведение для меня неожиданно и неожиданно, поэтому я соблазнился назвать это ошибкой или, возможно, ошибкой, я мог бы пропустить что-то очевидное. Было бы неплохо, если бы поведение class_eval было лучше указано (с обоснованием и обоснованием для удивительного поведения), но это, похоже, противоречит быстрой культуре Ruby.

Буду признателен, если кто-нибудь сможет разъяснить, почему это происходит.

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