语法错误,意外的“:”,期望输入端。

时间:2022-07-09 22:54:16

I am getting the following error:

我得到了以下错误:

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

with this method right here:

用这个方法

  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

I stare at this and I cannot figure out where the error is. Since I am using heredocs, I do not require a do end for class_eval. In fact, it works fine in the console and I used it before so I know this cannot be the issue. So then what is the issue?

我盯着这个,我不知道错误在哪里。由于我使用的是这些文档,所以我不需要对class_eval进行结束。实际上,它在控制台很好用,我以前也用过,所以我知道这不是问题。那么问题是什么呢?

1 个解决方案

#1


1  

Your problem is that you're trying to use the field name for too many things in here:

你的问题是,你试图用字段名来表示太多的东西:

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 is an argument to eval_mongo but you also want to use it as a class method name inside the class_eval call. Inside the class_eval, Ruby thinks you want the field argument so you get a syntax error. If you name the argument f instead:

字段是eval_mongo的参数,但您也希望在class_eval调用中使用它作为类方法名。在class_eval中,Ruby认为您想要字段参数,因此会出现语法错误。如果你把参数命名为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

then things should work.

那东西应该工作。


I wish I could offer a clear explanation of what's going on here but I can't. Instead, I'll summarize what I found by wandering around the MRI source and trying to tease out the behavior through experimentation. This approach is very error prone but it is often all we have with Ruby.

我希望我能清楚地解释这里发生了什么,但我做不到。相反,我将总结我在核磁共振源周围徘徊时发现的东西,并尝试通过实验来梳理这些行为。这种方法很容易出错,但通常我们都使用Ruby。

The documentation says:

文档表示:

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

class_eval(string [, filename [, lineno]])。

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

在mod上下文中计算字符串或块,但在给定块时,常量/类变量查找不受影响。[…]

This is, as usual with Ruby, not quite detailed or specific enough to be terribly useful.

这和Ruby通常一样,没有非常详细或具体到非常有用。

If we look at the source, we'll see that class_eval is actually rb_mod_module_eval in vm_eval.c which just calls specific_eval which calls eval_under which calls eval_string_with_cref with a Qnil value for the scope argument. That scope is handled by this:

如果我们查看源代码,我们会看到在vm_eval中,class_eval实际上是rb_mod_module_eval。这只调用特定的eval,它调用eval_string_with_cref,它的范围参数为Qnil值。该范围由以下方式处理:

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");
    }
}

and then the base_block is used to compile the source code string. I'm not terribly familiar with the MRI source but that looks like it is intentionally setting things up to use the scope around the class_eval.

然后使用base_block编译源代码字符串。我对MRI的来源不是很熟悉,但这看起来像是故意设置的,以使用class_eval的范围。

A simplified example might help:

一个简化的例子可能会有帮助:

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

That will say:

会说:

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

If you leave the name alone but call field with a string argument:

如果你只留下名字,但使用字符串参数调用字段:

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

then it also works and says:

然后它也会工作,说:

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

but if we use field as the argument name and use a symbol:

但如果我们使用字段作为参数名称并使用符号:

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

we get our syntax error:

我们得到了语法错误:

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

Interestingly enough, you'll get the same syntax error if you try use the somewhat obscure string pasting feature of Ruby:

有趣的是,如果您尝试使用Ruby的有点晦涩的字符串粘贴特性,您会得到相同的语法错误:

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

with a symbol:

一个符号:

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

Perhaps different things are being evaluated to different times and Ruby is getting confused about what's a string and what isn't.

也许不同的事物正在被评估到不同的时间,而Ruby则被混淆了什么是字符串,什么不是。

This behavior is surprising and unexpected to me so I'm tempted to call this a bug or perhaps a misfeature, I could be missing something obvious though. It would be nice if class_eval's behavior was better specified (with justification and rationale for surprising behavior) but that seems to go against the fast'n'loose Ruby culture.

这种行为让我感到惊讶和意外,所以我想称它为bug或者是一个错误的特性,但是我可能会遗漏一些明显的东西。如果class_eval的行为能更好地指定(为令人惊讶的行为提供理由和理由),那就更好了,但这似乎违背了快速的Ruby文化。

I would appreciate it if anyone could clarify why this happens.

如果有人能解释为什么会这样,我会很感激。

#1


1  

Your problem is that you're trying to use the field name for too many things in here:

你的问题是,你试图用字段名来表示太多的东西:

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 is an argument to eval_mongo but you also want to use it as a class method name inside the class_eval call. Inside the class_eval, Ruby thinks you want the field argument so you get a syntax error. If you name the argument f instead:

字段是eval_mongo的参数,但您也希望在class_eval调用中使用它作为类方法名。在class_eval中,Ruby认为您想要字段参数,因此会出现语法错误。如果你把参数命名为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

then things should work.

那东西应该工作。


I wish I could offer a clear explanation of what's going on here but I can't. Instead, I'll summarize what I found by wandering around the MRI source and trying to tease out the behavior through experimentation. This approach is very error prone but it is often all we have with Ruby.

我希望我能清楚地解释这里发生了什么,但我做不到。相反,我将总结我在核磁共振源周围徘徊时发现的东西,并尝试通过实验来梳理这些行为。这种方法很容易出错,但通常我们都使用Ruby。

The documentation says:

文档表示:

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

class_eval(string [, filename [, lineno]])。

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

在mod上下文中计算字符串或块,但在给定块时,常量/类变量查找不受影响。[…]

This is, as usual with Ruby, not quite detailed or specific enough to be terribly useful.

这和Ruby通常一样,没有非常详细或具体到非常有用。

If we look at the source, we'll see that class_eval is actually rb_mod_module_eval in vm_eval.c which just calls specific_eval which calls eval_under which calls eval_string_with_cref with a Qnil value for the scope argument. That scope is handled by this:

如果我们查看源代码,我们会看到在vm_eval中,class_eval实际上是rb_mod_module_eval。这只调用特定的eval,它调用eval_string_with_cref,它的范围参数为Qnil值。该范围由以下方式处理:

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");
    }
}

and then the base_block is used to compile the source code string. I'm not terribly familiar with the MRI source but that looks like it is intentionally setting things up to use the scope around the class_eval.

然后使用base_block编译源代码字符串。我对MRI的来源不是很熟悉,但这看起来像是故意设置的,以使用class_eval的范围。

A simplified example might help:

一个简化的例子可能会有帮助:

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

That will say:

会说:

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

If you leave the name alone but call field with a string argument:

如果你只留下名字,但使用字符串参数调用字段:

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

then it also works and says:

然后它也会工作,说:

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

but if we use field as the argument name and use a symbol:

但如果我们使用字段作为参数名称并使用符号:

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

we get our syntax error:

我们得到了语法错误:

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

Interestingly enough, you'll get the same syntax error if you try use the somewhat obscure string pasting feature of Ruby:

有趣的是,如果您尝试使用Ruby的有点晦涩的字符串粘贴特性,您会得到相同的语法错误:

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

with a symbol:

一个符号:

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

Perhaps different things are being evaluated to different times and Ruby is getting confused about what's a string and what isn't.

也许不同的事物正在被评估到不同的时间,而Ruby则被混淆了什么是字符串,什么不是。

This behavior is surprising and unexpected to me so I'm tempted to call this a bug or perhaps a misfeature, I could be missing something obvious though. It would be nice if class_eval's behavior was better specified (with justification and rationale for surprising behavior) but that seems to go against the fast'n'loose Ruby culture.

这种行为让我感到惊讶和意外,所以我想称它为bug或者是一个错误的特性,但是我可能会遗漏一些明显的东西。如果class_eval的行为能更好地指定(为令人惊讶的行为提供理由和理由),那就更好了,但这似乎违背了快速的Ruby文化。

I would appreciate it if anyone could clarify why this happens.

如果有人能解释为什么会这样,我会很感激。