Как десериализовать занятия в Psych? - PullRequest
4 голосов
/ 25 апреля 2011

Как мне десериализовать в Psych для возврата существующего объекта, такого как объект класса?

Чтобы выполнить сериализацию класса, я могу сделать

require "psych"

class Class
  yaml_tag 'class'
  def encode_with coder
    coder.represent_scalar 'class', name
  end
end

yaml_string = Psych.dump(String) # => "--- !<class> String\n...\n" 

, но если я попытаюсьвыполняя Psych.load, я получаю анонимный класс, а не класс String.

Обычный метод десериализации - Object#init_with(coder), но он только изменяет состояние существующего анонимного класса, тогда как яжелая, чтобы класс String.

Psych::Visitors::ToRuby#visit_Psych_Nodes_Scalar(o) имел случаи, когда вместо того, чтобы модифицировать существующие объекты с помощью init_with, они удостоверяются, что в первую очередь создается нужный объект (например, вызывая Complex(o.value) для десериализациикомплексное число), но я не думаю, что мне следовало бы использовать этот метод.

Обречен ли я работать с излучением низкого или среднего уровня, или я что-то упустил?

Справочная информация

Я опишу проект, зачем ему нужны классы, и почему ему нужна (де) сериализация.

Проект

Цели Small Eigen Colliderсоздать случайный вкусks для запуска Ruby.Первоначальная цель состояла в том, чтобы увидеть, возвращали ли разные реализации Ruby (например, Rubinius и JRuby) одинаковые результаты при выполнении одних и тех же случайных задач, но я обнаружил, что это также хорошо для обнаружения способов обойти ошибки Rubinius и YARV.

Каждая задача состоит из следующих элементов:

receiver.send(method_name, *parameters, &block)

, где receiver - случайно выбранный объект, method_name - имя случайно выбранного метода, а *parameters -массив случайно выбранных объектов.&block не очень случайный - в основном это эквивалентно {|o| o.inspect}.

Например, если получатель был "a", имя метода было бы: casecmp, а параметры было ["b"], тогда вы бывызывать

"a".send(:casecmp, "b") {|x| x.inspect}

, что эквивалентно (так как блок не имеет значения)

"a".casecmp("b")

Small Eigen Collider запускает этот код и регистрирует эти входы, а также возвращаемое значение.В этом примере большинство реализаций Ruby возвращают -1, но на одном этапе Rubinius возвращает +1.(Я подал это как ошибку https://github.com/evanphx/rubinius/issues/518, и сопровождающие Rubinius исправили ошибку)

Зачем нужны классы

Я хочу иметь возможность использовать объекты классов в своем Small EigenКоллайдер.Обычно они являются получателями, но они также могут быть одним из параметров.

Например, я обнаружил, что один из способов segfault YARV - это сделать

Thread.kill(nil)

В этом случае, receive - объект класса Thread, а параметры - [nil].(Отчет об ошибке: http://redmine.ruby -lang.org / Issues / Show / 4367 )

Почему требуется (де) сериализация

Малый собственный коллайдер нуждается в сериализации дляпара причин.

Во-первых, использование генератора случайных чисел для генерации серии случайных задач каждый раз нецелесообразно.JRuby имеет другой встроенный генератор случайных чисел, так что даже при наличии одного и того же начального числа PRNG он будет выполнять разные задачи для YARV.Вместо этого я создаю список случайных задач один раз (первый запуск ruby ​​bin / small_eigen_collider), инициализирую сериализацию списка задач в tasks.yml, а затем выполняю последующие запуска программы (используя разныеРеализации Ruby) читайте в этом файле tasks.yml, чтобы получить список задач.

Еще одна причина, по которой мне нужна сериализация, заключается в том, что я хочу иметь возможность редактировать список задач.Если у меня длинный список задач, приводящий к ошибке сегментации, я хочу сократить список до минимума, необходимого для возникновения ошибки сегментации.Например, следующая ошибка https://github.com/evanphx/rubinius/issues/643,

ObjectSpace.undefine_finalizer(:symbol)

сама по себе не вызывает ошибку сегментации, и при этом

Symbol.all_symbols.inspect

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

Имеет ли смысл десериализация, возвращающая существующие объекты класса, в этом контексте, или вы думаете, что есть лучший способ?

Ответы [ 2 ]

1 голос
/ 09 июня 2011

Сопровождающий Psych реализовал сериализацию и десериализацию классов и модулей . Теперь в Ruby!

1 голос
/ 07 июня 2011

Статус-кво моих текущих исследований:

Чтобы получить желаемое поведение, вы можете использовать мой обходной путь, упомянутый выше.

Вот пример красиво отформатированного кода:

string_yaml  = Psych.dump(Marshal.dump(String))
  # => "--- ! \"\\x04\\bc\\vString\"\n"
string_class = Marshal.load(Psych.load(string_yaml))
  # => String

Ваш хак с модификацией Class, возможно, никогда не сработает, потому что реальная обработка класса не реализована в psych / yaml.

Вы можете взять этот репо tenderlove / psych , который является автономнымlib.

(Gem: psych - чтобы загрузить его, используйте: gem 'psych'; require 'psych' и выполните проверку с помощью Psych::VERSION)

Как вы можете видеть в строка 249-251 обработка объектов с помощью анонимного класса Class не обрабатывается.

Вместо того, чтобы ставить обезьяны в класс, я рекомендую внести свой вклад в библиотеку Psych, расширив обработку этого класса.

Так что, на мой взгляд, окончательный результат yaml должен выглядеть примерно так: "--- !ruby/class String"

После одной ночи, подумав об этом, я могу сказать, что эта функция была бы действительно хороша!


Обновление

Найдено крошечноеРационал, который, кажется, работает по назначению:

Суть кода: gist.github.com / 1012130 (с описательными комментариями)

...