我收到以下错误:
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 :(得分: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
事情应该有效。
我希望我能清楚地解释一下这里发生了什么,但我不能。相反,我将通过在MRI源周围徘徊并尝试通过实验来梳理行为来总结我发现的内容。这种方法非常容易出错,但通常我们只使用Ruby。
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 = █
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文化。
如果有人能澄清为什么会发生这种情况,我将不胜感激。