3

Ruby에서 인스턴스 변수 생성을 거부하여 무인 변수가 '실수로 생성되는 것을 방지하고 싶습니다.Ruby에서 인스턴스 변수 생성을 거부하는 방법

My class: 

class Test 
    def initialize 
    @a = 'Var A' 
    end 

    def make_new 
    @b = 'Var B' <-- I would like to deny creation of any variables that were not defined during the init 
    end 
end 

답변

3

할 수 있습니다 initialize 방법의 끝에 freeze 객체 인스턴스 :

class Test 
    def initialize 
    @a = 'Var A' 
    freeze 
    end 

    def make_new 
    @b = 'Var B' # I would like to deny creation of any variables that were not defined during the init 
    end 
end 

t=Test.new 
p t.instance_variable_get :@a 
# "Var A" 
t.make_new 
#a.rb:24:in `make_new': can't modify frozen Test (RuntimeError) 
#  from a.rb:30:in `<main>' 
t.instance_variable_set :@c, 'Var C' 
# a.rb:31:in `instance_variable_set': can't modify frozen Test (RuntimeError) 
#  from a.rb:31:in `<main>' 
class << t 
    @d = 'Var D' 
end 
#a.rb:33:in `singletonclass': can't modify frozen Class (RuntimeError) 
#  from a.rb:32:in `<main>' 
p t.instance_variable_get :@d 
+0

이것은 다음과 같은 경향이 있습니다 : http://m.onkey.org/ruby-i-don-t-like-3-object-freeze –

+1

이것은 다른 인스턴스 변수의 실수로 인한 생성을 막는 것 이상의 역할을합니다. – Max

+2

나는 이것이 매우 도움이 될 것이라고 생각하지 않는다. 예 :'p t.instance_variable_set : @a, 7 # => RuntimeError : 고정 된 테스트를 수정할 수 없습니다 .' –

1

그런 식으로 정의 된 실수로 인스턴스 변수가 생성되는 것을 방지 할 수있는 방법이 없습니다. 왜 이걸하고 싶어? 그러한 코드를 달성하기 위해 당신은 무엇을 원할 것입니까?

+2

순수한 호기심 :) –

+0

하지만 수업을 정지시킬 수는 있습니다. 그리고 사물들. –

4

나는 이것이 좋은 생각이라고 주장하지만 가지 흥미로운 단지 B/C, 여기에하는 솔루션입니다하지 않습니다 새로운 ivar이 생성 될 때 예외를 던지지만 정의 된 인스턴스 변수를 수정할 수 있습니다 (freezing 클래스와 다름). 그냥 모든 방법 : 방법이

class Test 
    extend IvarBlocker 

    def initialize 
    @a = 1 
    end 

    def set_b 
    @b = 2 
    end 

    def set_a 
    @a = 6 
    end 
end 

t = Test.new #=> #<Test:0x007f87f13c41e8 @a=1> 
t.set_b #=> RuntimeError: New Ivar assigned 
t.set_a #=> 6 
2

module IvarBlocker 
    def method_added(method) 
    alias_name = "__#{method}_orig" 
    return if method == :initialize || method_defined?(alias_name) || method.match(/__.*_orig/) 

    alias_method alias_name, method 
    define_method(method) do |*args| 
     ivars_before = instance_variables.dup 
     send(alias_name, *args).tap { raise "New Ivar assigned" if !(instance_variables - ivars_before).empty? } 
    end 
    end 
end 

사용을 복제한다는 사실을 포함한 IT/w undoubtably 몇 가지 문제가있다, 이것을 함께 던졌다 - 해키 (그러나 재미) 생산을위한 것이 아니며 상대적으로 느리다. 샘플 구현은 단일 객체에 대해서만 작동하지만 많은 객체를 지원하도록 확장 될 수 있습니다.

는의는 다음 설치를 가정 해 봅시다 :

class Foo 
    def initialize 
    @a = :foo 
    end 
    def set_b; @b = 3; end 
    def set_c; @c = 7; end 
end 

def freeze_variables_of(obj) 
    frozen_variables = obj.instance_variables 
    set_trace_func lambda {|event, file, line, id, binding, classname| 
    if classname == obj.class 
     this = binding.eval 'self' 
     if this == obj 
     (this.instance_variables - frozen_variables).each {|var| this.remove_instance_variable var} 
     end 
    end 
    } 
end 

set_trace_func의 사용으로 우리는 very often라고 (일반적으로 더 문마다 한 번 이상)되는 발동을 설정할 수 있습니다. 그 Proc에서 인스턴스 변수를 확인하고 원치 않는 변수를 제거 할 수 있습니다.

의 예를 살펴 보자 :

a = Foo.new 
# => #<Foo:0x007f6f9db75cc8 @a=:foo> 

a.set_b; a 
# => #<Foo:0x007f6f9db75cc8 @a=:foo, @b=3> 

freeze_variables_of a 
a.set_c; a 
# => #<Foo:0x007f6f9db75cc8 @a=:foo, @b=3> 

우리는 볼 그 일을 한 후 "정지", set_c는 인스턴스 변수 (실제로 변수가 set_c 메서드가 반환하는 바로 그 순간에 제거)를 설정할 수 없습니다.

(실제 응용 프로그램에서 권장하는) 객체를 고정하는 것과는 달리 (이 방법을 사용하면 모든 허용 된 인스턴스 변수를 수정하고 새로운 인스턴스 변수를 금지 할 수 있습니다.

인스턴스 변수를 직접 할당하는 경우 (Alex의 방법은 더 빠르지 만 접근 방법에 의존하는 반면)이 방법도 작동합니다.