Сохранение динамических классов Ruby - PullRequest
5 голосов
/ 12 июня 2009

У меня вопрос из любопытства. Если у меня есть класс ruby, а затем я динамически добавляю к нему методы класса, переменные класса и т. Д. Во время выполнения, есть ли у меня возможность сохранить определение измененного класса, чтобы в следующий раз при запуске приложения я мог использовать его снова?

Ответы [ 5 ]

4 голосов
/ 12 июня 2009

Нет встроенного способа сделать это. Маршал не может спасти методы. Если эти методы и переменные генерируются систематическим образом, вы можете сохранить данные, необходимые классу для их воссоздания. Например, если у вас есть метод make_special_method(purpose, value), который определяет эти методы, создайте массив аргументов, которые нужно передать этим методам, и прочитайте его, когда вы захотите восстановить состояние класса.

2 голосов
/ 12 июня 2009

В зависимости от того, что именно вы имеете в виду, есть несколько способов сделать это.

В простейшем случае вы добавили переменные или методы в уже существующий класс, как в этом примере:

class String
  def rot13
    return self.tr('a-z', 'n-za-m')
  end
end

Здесь мы добавили метод rot13 к классу String. Как только этот код будет запущен, каждая строка в вашей программе сможет # rot13. Таким образом, если у вас есть код, которому нужны строки с поддержкой rot13, вы просто должны убедиться, что приведенный выше код выполняется перед рассматриваемым кодом, например, помещая код rot13 в файл и требуя его (). Очень просто!

Но, возможно, вы добавили переменную класса в класс и хотите сохранить не только ее существование, но и значение, как в:

class String
  @@number_of_tr_calls_made = 0
  # Fix up #tr so that it increments @@number_of_tr_calls_made
end

Теперь, если вы хотите сохранить значение @@ number_of_tr_calls_made, вы можете сделать это так же, как и с любым другим сериализуемым значением Ruby: через библиотеку Marshal. Также просто!

Но что-то в том, как вы сформулировали свой вопрос, заставляет меня подозревать, что вы делаете что-то вроде этого:

greeting = "Hello"
class <<greeting
  def rot13
    return self.tr('a-z', 'n-za-m')
  end
end
encrypted_greeting = greeting.rot13

Это очень отличается от того, что мы сделали в первом примере. Этот фрагмент кода дал каждой строке в вашей программе возможность самому rot13. Этот код предоставляет эту силу только объекту, указанному под названием «приветствие». Внутренне Ruby делает это, создавая анонимный подкласс Singleton String, добавляя к нему метод rot13 и изменяя класс приветствия для этого анонимного подкласса.

Проблема здесь в том, что синглтоны не могут быть маршалами (чтобы понять почему, попытайтесь выяснить, как сохранить инвариант синглтона, когда любой вызов Marshal.load может генерировать копии существующих объектов синглтона). Теперь в иерархии наследования приветствия есть Singleton, поэтому, если вы хотите сохранить и загрузить его, вы попадаете в нужное место. Вместо этого создайте подкласс:

class HighlySecurableString < String
  def rot13
    return self.tr('a-z', 'n-za-m')
  end
end
greeting = HighlySecurableString.new("hello")
1 голос
/ 12 июня 2009

Простое упорядочение объекта (как уже говорили другие) не сработает. Давайте посмотрим на пример. Рассмотрим этот класс:

class Extras
  attr_accessor :contents
  def test
    puts "This instance of Extras is OK. Contents is: " + @contents.to_s
  end

  def add_method( name )
    self.class.send :define_method, name.to_sym do
      puts "Called " + name.to_s
    end
  end
end

Теперь давайте напишем программу, которая создает экземпляр, добавляет к нему метод и сохраняет его на диск:

require 'extras'

fresh = Extras.new
fresh.contents = 314
fresh.test # outputs "This instance of Extras is OK. Contents is: 314"
fresh.add_method( :foo )
fresh.foo # outputs "Called foo"

serial = Marshal.dump( fresh )
file = File.new "dumpedExample", 'w'
file.write serial

Таким образом, мы можем назвать нормальный метод test и динамический метод foo. Давайте посмотрим, что произойдет, если мы напишем программу, которая загружает экземпляр Example, который был сохранен на диск:

require 'extras'

file = File.new 'dumpedExample', 'r'
serial = file.read

reheated = Marshal.load( serial )
reheated.test # outputs "This instance of Extras is OK. Contents is 314"
reheated.foo # throws a NoMethodError exception 

Итак, мы видим, что хотя экземпляр (включая значения переменных-членов) был сохранен, динамического метода не было.

С точки зрения дизайна, вероятно, лучше поместить весь добавленный код в модуль и снова загрузить его в класс при следующем запуске программы. Нам нужен хороший пример того, как вы могли бы использовать его, чтобы действительно знать это.

Если вам требуется дополнительная информация для воссоздания методов, попросите модуль сохранить их как переменные-члены. Реализуйте included в модуле и заставьте его искать эти переменные-члены, когда он входит в класс.

1 голос
/ 12 июня 2009
0 голосов
/ 12 июня 2009

Вы редактируете класс на лету, и вы хотите сохранить это? Вы можете попробовать использовать модуль Marshal, он позволит вам сохранять объекты в файл и динамически считывать их обратно в память.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...