Yaml doesn't save Proc members

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:inobject_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:inload’
/data/ruby/lib/ruby/1.9/yaml.rb:39:in load' test/fuct_test_knot_yaml.rb:55:intest_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…

···


I don’t like spinach, and I’m glad I don’t, because if I
liked it I’d eat it, and I just hate it.
– Clarence Darrow
Rasputin :: Jack of All Trades - Master of Nuns

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

···

On Thu, 22 Apr 2004, Dick Davies wrote:

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…

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
URL :: Solar-Terrestrial Physics Data | NCEI
TRY :: for l in ruby perl;do $l -e “print "\x3a\x2d\x29\x0a"”;done
===============================================================================

Dick Davies wrote:

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

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.

···

On Apr 21, 2004, at 8:59 AM, Dick Davies wrote:

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…


I don’t like spinach, and I’m glad I don’t, because if I
liked it I’d eat it, and I just hate it.
– Clarence Darrow
Rasputin :: Jack of All Trades - Master of Nuns

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.

···

At 02:49 22/04/2004 +0900, you wrote:

Dick Davies wrote:

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


Web: @jhr is virteal, virtually real
Phone: +33 (0) 4 92 27 74 17