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

Вы можете freeze экземпляры объектов в конце initialize метода:

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

Pure curiosity :) –

+0

Вы можете заморозить класс. И объекты, если на то пошло. –

4

Я не утверждаю, что это хорошая идея, но только б/с это довольно интересно, здесь есть решение, которое будет вызывать исключение, когда создается новый ivar, но также позволяет изменять определенные переменные экземпляра (в отличие от класса freezing). Просто бросил это вместе, есть undoubtably некоторые вопросы ж/нее, в том числе тот факт, что он дублирует каждый метод :)

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 

Использование

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

Существует способ - это Hacky (но весело) который не предназначен для производства (и относительно медленный). Моя примерная реализация работает только для одного объекта, но может быть расширена для поддержки многих объектов.

Давайте предположим, что следующие настройки:

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 мы можем установить Proc, который называется 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 метод возвращает).

В отличие от замораживания объекта (с a.freeze) (который я бы рекомендовал для любого приложения реального мира), этот способ позволяет изменять все допустимые переменные экземпляра, запрещая новые.

Это даже работает, если вы непосредственно назначаете переменные экземпляра (в то время как метод Alex, вероятно, быстрее, но зависит от методов доступа).

Смежные вопросы