귀하의 문제는 당신이 여기에 너무 많은 것들에 대한 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를 사용하는 것이 일반적입니다.
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. [...]
이, 루비와 보통 아주 상세하지 않거나 대단히 유용 할 정도로 구체적으로입니다.
우리가 소스를 보면 class_eval
실제로 단지 scope
인수에 Qnil
값 eval_string_with_cref
를 호출 eval_under
호출 specific_eval
호출 rb_mod_module_eval
in vm_eval.c
것을 우리는 볼 수 있습니다.
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
는 소스 코드 문자열을 컴파일하는 데 사용됩니다 : 그 scope
이에 의해 처리됩니다. 나는 MRI 소스에 대단히 익숙하지 않다. 그러나 그것은 의도적으로 범위를 class_eval
에 사용하도록 설정 한 것처럼 보인다.당신은 혼자가 이름을두면
[:"pancakes house", {:type=>String}]
"pancakes house"
하지만 문자열 인수 field
전화 :
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
말할 것이다 :
단순화 된 예는 도움이 될 다음
def eval_mongo(klass, field)
klass.class_eval <<-EOS
field '#{field}', type: String
field
EOS
end
그것도 작동하고있다 :
(210)
["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"
기호와 0
:
> s = 'a' :'b'
SyntaxError: (irb):2: syntax error, unexpected ':', expecting end-of-input
s = 'a' :'b'
^
아마도 여러 가지 다른 시간에 평가되고 있으며, 문자열이고 무엇을하지 무엇 루비 대해 혼란스러워지고 있습니다.
이 동작은 놀랍고 예기치 않게 발생하므로이 버그 또는 잘못된 기능을 호출하기 때문에 유감스럽게 생각합니다. class_eval
의 행동이 (정당화와 의외의 행동에 대한 근거가있는) 더 잘 지정 되었다면 좋을 것입니다.하지만 그것은 루비 문화를 빨리 벗어나는 것처럼 보입니다.
왜 이런 일이 발생하는지 분명히 알 수 있다면 감사하겠습니다.
'field : # {_field}, type : # {_type}'이 아니어야합니까? 문자열이 아닌 기호를 만들고 싶습니다. 맞습니까? – marc
@marc 문자열이없는 last_apple 정보는 공백이있는 잘못된 메서드를 생성하고 정의되지 않은 정보 오류가 발생하기 때문에 문자열이 필요합니다. – Donato
그게 효과가있다. 어떤 Ruby 버전을 사용하고 있습니까? 'eval'을 사용하면 항상 걱정이됩니다. 그래서'klass.send (_field.to_sym, _type) '을 사용하여 이것을 피할 수 있기를 바랍니다. – tadman