语法错误,意外':',期待输入结束

时间:2014-11-28 20:48:56

标签: ruby mongoid

我收到以下错误:

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做结束。事实上,它在控制台中工作正常,我之前使用它,所以我知道这不是问题。那么问题是什么呢?

1 个答案:

答案 0 :(得分: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

fieldeval_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
事情应该有效。


我希望我能清楚地解释一下这里发生了什么,但我不能。相反,我将通过在MRI源周围徘徊并尝试通过实验来梳理行为来总结我发现的内容。这种方法非常容易出错,但通常我们只使用Ruby。

documentation说:

  

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

     

mod 的上下文中计算字符串或块,除了在给出块时,常量/类变量查找不受影响。 [...]

与往常一样,Ruby不是非常详细或具体到非常有用。

如果我们查看来源,我们会看到class_eval实际上rb_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'
         ^

也许不同的事情正在被评估到不同的时间,而且Ruby对于什么是字符串以及什么不是什么感到困惑。

这种行为对我来说是令人惊讶和意想不到的,所以我很想把它称为一个错误或者可能是错误的,我可能会遗漏一些明显的东西。如果class_eval的行为被更好地指定(具有令人惊讶的行为的理由和理由)会很好,但这似乎违背了快速的松散的Ruby文化。

如果有人能澄清为什么会发生这种情况,我将不胜感激。