Джо Ван Дайк спросил список рассылки Ruby:
Hi,
In Ruby, I guess you can't marshal a lambda/proc object, right? Is that possible in lisp or other languages?
What I was trying to do:
l = lamda { ... }
Bj.submit "/path/to/ruby/program", :stdin => Marshal.dump(l)
So, I'm sending BackgroundJob a lambda object, which contains the context/code for what to do. But, guess that wasn't possible. I ended up marshaling a normal ruby object that contained instructions for what to do after the program ran.
Joe

Попробуйте ruby2ruby
Вы не можете маршалировать Lambda или Proc. Это связано с тем, что оба они считаются замыканиями, что означает, что они закрываются вокруг памяти, в которой они были определены, и могут ссылаться на нее. (Чтобы их упорядочить, вам нужно будет маршалировать всю память, к которой они могли получить доступ в момент создания.)
Однако, как указал Гай, вы можете использовать ruby2ruby, чтобы получить строку программы. То есть вы можете упорядочить строку, представляющую код Ruby, а затем повторно оценить ее позже.
Ruby2ruby некоторое время работает над MRI 1.9. Ripper тоже классный и идет с МРТ (с 1.9).
Если вы заинтересованы в получении строковой версии кода Ruby с помощью Ruby2Ruby, вам может понравиться эта ветка.
вы также можете просто ввести свой код в виде строки:
code = %{
lambda {"hello ruby code".split(" ").each{|e| puts e + "!"}}
}
затем выполните его с помощью eval
eval code
который вернет рубиновую ламду.
использование формата %{} экранирует строку, но закрывает только несоответствующую фигурную скобку. то есть вы можете вставлять скобки, такие как %{ [] {} }, и они все еще закрыты.
большинство выделителей текстового синтаксиса не понимают, что это строка, поэтому по-прежнему отображают обычную подсветку кода.
Я нашел, что proc_to_ast лучше всего справляется с задачей: https://github.com/joker1007/proc_to_ast.
Точно работает в ruby 2+, и я создал PR для совместимости с ruby 1.9.3+ (https://github.com/joker1007/proc_to_ast/pull/3)
Это интересное решение, хотя, похоже, оно требует, чтобы исходный код процедуры присутствовал на диске. Это имеет смысл; было бы нетривиально (возможно, невозможно) декомпилировать байт-код YARV обратно в AST. YARV отбрасывает исходный AST, когда он больше не нужен, но пользовательский компилятор байт-кода может сохранить исходный AST, что позволяет этому методу также работать с динамически генерируемыми процедурами (т.е. созданными с помощью eval).
Если proc определен в файле, U может получить местоположение файла proc, затем сериализовать его, а затем после десериализации использовать местоположение, чтобы снова вернуться в процесс.
proc_location_array = proc.source_location
после десериализации:
file_name = proc_location_array[0]
line_number = proc_location_array[1]
proc_line_code = IO.readlines(file_name)[line_number - 1]
proc_hash_string = proc_line_code[proc_line_code.index("{")..proc_line_code.length]
proc = eval("lambda #{proc_hash_string}")
Когда-то это было возможно с помощью внутреннего рубина (https://github.com/cout/ruby-internal), например:
p = proc { 1 + 1 } #=> #<Proc>
s = Marshal.dump(p) #=> #<String>
u = Marshal.load(s) #=> #<UnboundProc>
p2 = u.bind(binding) #=> #<Proc>
p2.call() #=> 2
Есть некоторые предостережения, но прошло много лет, и я не могу вспомнить детали. Например, я не уверен, что произойдет, если переменная является динварой в привязке, в которую она выгружается, и локальной в привязке, где она повторно привязана. Сериализация AST (на MRI) или байт-кода (на YARV) нетривиальна.
Приведенный выше код работает с YARV (до 1.9.3) и MRI (до 1.8.7). Нет причин, по которым его нельзя заставить работать на Ruby 2.x с небольшим усилием.
ruby2ruby работает только на 1.8, официального способа десериализации байт-кода 1.9 пока нет.