yaml doesn't save Proc members

D

Dick Davies

I'm twiddling around with a dependency tree implementation -
a bit like rake, but for things like keeping track of what servers
need shutting down if spamassassin has to bounce for some reason.

It's still very theoretical, but effectively you have a node which
has

: a name (symbol)
: a dependency list (array of nodes)
: a 'users' list (array of nodes which depend on *it*)
: a block (which gets called to 'resolve' this dependency

I thought I'd bugger about with YAML to save and load the tree,
but although the arrays all get laid down nicely, the blocks don't.

When I call to_yaml, the proc is saved as an empty block,
and when YAML.load() tries to pull it back, I get:

NoMethodError: allocator undefined for Proc
/data/ruby/lib/ruby/1.9/yaml.rb:171:in `load'
/data/ruby/lib/ruby/1.9/yaml.rb:171:in `object_maker'
/data/ruby/lib/ruby/1.9/yaml/rubytypes.rb:36
/data/ruby/lib/ruby/1.9/yaml/rubytypes.rb:34:in `call'
/data/ruby/lib/ruby/1.9/yaml.rb:39:in `load'
/data/ruby/lib/ruby/1.9/yaml.rb:39:in `load'
test/fuct_test_knot_yaml.rb:55:in `test_yaml_block'

So I guess I need another way to attach behaviour to a node...
would singleton methods have the same problem ?
I'm not big on inheritance....
 
A

Ara.T.Howard

I'm twiddling around with a dependency tree implementation -
a bit like rake, but for things like keeping track of what servers
need shutting down if spamassassin has to bounce for some reason.

It's still very theoretical, but effectively you have a node which
has

: a name (symbol)
: a dependency list (array of nodes)
: a 'users' list (array of nodes which depend on *it*)
: a block (which gets called to 'resolve' this dependency

I thought I'd bugger about with YAML to save and load the tree,
but although the arrays all get laid down nicely, the blocks don't.

When I call to_yaml, the proc is saved as an empty block,
and when YAML.load() tries to pull it back, I get:

NoMethodError: allocator undefined for Proc
/data/ruby/lib/ruby/1.9/yaml.rb:171:in `load'
/data/ruby/lib/ruby/1.9/yaml.rb:171:in `object_maker'
/data/ruby/lib/ruby/1.9/yaml/rubytypes.rb:36
/data/ruby/lib/ruby/1.9/yaml/rubytypes.rb:34:in `call'
/data/ruby/lib/ruby/1.9/yaml.rb:39:in `load'
/data/ruby/lib/ruby/1.9/yaml.rb:39:in `load'
test/fuct_test_knot_yaml.rb:55:in `test_yaml_block'

So I guess I need another way to attach behaviour to a node...
would singleton methods have the same problem ?
I'm not big on inheritance....

if the resolve code is not too dynamic you could create a command class that
does the work differently based on how it is constructed:

class Resolver
def initialize(type, opts)
case type
when :foo
@type = :foo
@a, @b = opts[:a], opts[:b]
when :bar
@type = :bar
@c, @d = opts[:c], opts[:d]
end
end

def resovle deps
case type
when :foo
# do something with deps and @a and @b
when :bar
# do something with deps and @c and @d
end
end
end


even simpler, and nice and ugly, is to do something like:

~ > cat foo.rb
resolve_code = 'lambda{|x| p x}'
resolve = eval resolve_code
resolve.call 42

~ > ruby foo.rb
42

if you never store the actual proc object as an instance var you'll not have
any problems marshaling/demarshaling - or you could define a custom to_yaml
method...


another approach i've taken with marshal is

~ > cat foo.rb
class Node
def initialize(*args)
@args = args
@a, @b = @args
end
def dump
@args.dump
end
def to_s; "#{ @a }#{ @b }"; end
def self.load data
args = data.load
self.new *args
end
end

n = Node.new(1 << 2, 1 << 1)
n = Marshal.load(Marshal.dump(n))
puts n

~ > ruby foo.rb
42

you could do the same with to_yaml - simply re-create the object instead of
restoring it...


-a
--
===============================================================================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
| URL :: http://www.ngdc.noaa.gov/stp/
| TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
===============================================================================
 
F

Florian Gross

Dick said:
I thought I'd bugger about with YAML to save and load the tree,
but although the arrays all get laid down nicely, the blocks don't.

When I call to_yaml, the proc is saved as an empty block,
and when YAML.load() tries to pull it back, I get:

NoMethodError: allocator undefined for Proc

I've written some code which will allow to serialize Procs under
specific conditions. It's limited and won't work with everything even if
it were more complete than my current version.

It might however be able to do what you want, in this case.

Anyway, it's available at
http://noegnud.sourceforge.net/flgr/proc_source.rb -- it might be worth
a try.

Regards,
Florian Gross
 
M

Mark Hubbart

I'm twiddling around with a dependency tree implementation -
a bit like rake, but for things like keeping track of what servers
need shutting down if spamassassin has to bounce for some reason.

It's still very theoretical, but effectively you have a node which
has

: a name (symbol)
: a dependency list (array of nodes)
: a 'users' list (array of nodes which depend on *it*)
: a block (which gets called to 'resolve' this dependency

I thought I'd bugger about with YAML to save and load the tree,
but although the arrays all get laid down nicely, the blocks don't.

Ruby pretty much discards the code once it has been compiled; there is
no way to get it back without re-parsing the script and selecting out
the original definition.

I would suggest that you might want to store the block/proc as a string
form the beginning, and eval it when you want the actual block:

block = "{|n| n * 3 }"
(0..5).map &eval("proc " + block)

that way you can store the block in a yaml document, albeit as a string.
 
J

Jean-Hugues ROBERT

I've written some code which will allow to serialize Procs under specific
conditions. It's limited and won't work with everything even if it were
more complete than my current version.

It might however be able to do what you want, in this case.

Anyway, it's available at
http://noegnud.sourceforge.net/flgr/proc_source.rb -- it might be worth a try.

Regards,
Florian Gross

I had a look at it. It basically uses Proc##inspect() to get the file/line
where the proc is defined. Then, using IRB's Lexer, the proc's definition
is grabbed. Some additional stuff handles cases where proc isn't defined
in a file (if def in some eval() for example).

I liked the code and I am injecting it (after some major refactoring) in my
own code.

Yamlizing Proc/s is for sure cool.

Thanks Florian.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top