From: "Ara.T.Howard" Date: 2004-04-22T01:54:06+09:00 Subject: Re: yaml doesn't save Proc members 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.... 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 ===============================================================================