end
def marshal_dump
Struct.new('Human',:name,:birthdate,:beginning)
str = Struct::Human.new(@name, @birthdate, @beginning)
str
end
def marshal_load(str)
self.instance_eval do
initialize(str.name, str.birthdate, str.beginning)
end
end
# Прочие методы...
end
p1 = Person.new('Rudy',Time.now - (14 * 365 * 86400), 100)
p [p1.name, p1.age, p1.balance] # ['Rudy', 14.0, 197.99315994394]
str = Marshal.dump(p1)
p2 = Marshal.load(str)
p [p2.name, p2.age, p2.balance] # ['Rudy', 14.0, 197.99315994394]
При сохранении объекта этого типа атрибуты age
и balance
не сохраняются. А когда объект восстанавливается, они вычисляются заново. Заметьте: метод marshal_load
предполагает, что объект существует; это один из немногих случаев, когда метод initialize
приходится вызывать явно (обычно это делает метод new
).
10.2.3. Ограниченное «глубокое копирование» в ходе маршалинга
В Ruby нет операции «глубокого копирования». Методы dup
и clone
не всегда работают, как ожидается. Объект может содержать ссылки на вложенные объекты, а это превращает операцию копирования в игру «собери палочки».
Ниже предлагается способ реализовать глубокое копирование с некоторыми ограничениями, обусловленными тем, что наш подход основан на использовании класса Marshal
со всеми присущими ему недостатками:
def deep_copy(obj)
Marshal.load(Marshal.dump(obj))
end
a = deep_copy(b)
10.2.4. Обеспечение устойчивости объектов с помощью библиотеки PStore
Библиотека PStore
реализует хранение объектов Ruby в файле. Объект класса PStore
может содержать несколько иерархий объектов Ruby. У каждой иерархии есть корень, идентифицируемый ключом. Иерархии считываются с диска в начале транзакции и записываются обратно на диск в конце.
require 'pstore'
# Сохранить.
db = PStore.new('employee.dat') db.transaction do
db['params'] = {'name' => 'Fred', 'age' => 32,
'salary' => 48000 }
end
# Восстановить.
require 'pstore'
db = Pstore.new('employee.dat')
emp = nil
db.transaction { emp = db['params'] }
Обычно внутри блока транзакции используется переданный ему объект PStore
. Но можно получить и сам вызывающий объект, как показано в примере выше.
Эта техника ориентирована на транзакции; в начале блока обрабатываемые данные читаются с диска. А в конце прозрачно для программиста записываются на диск.
Мы можем завершить транзакцию досрочно, вызвав метод commit
или abort
. В первом случае все изменения сохраняются, во втором отбрасываются. Рассмотрим более длинный пример:
require 'pstore'
# Предполагается, что существует файл с двумя объектами.
store = PStore.new('objects')
store.transaction do |s|
a = s['my_array'] h = s['my_hash']
# Опущен воображаемый код, манипулирующий объектами
# a, h и т. д.
# Предполагается, что переменная 'condition' может
# принимать значения 1, 2, 3...
case condition
when 1
puts 'Отмена.'
s.abort # Изменения будут потеряны.
when 2
puts 'Фиксируем и выходим.'
s.commit # Изменения будут сохранены.
when 3
# Ничего не делаем...
end
puts 'Транзакция дошла до конца.'