I'm trying to write a Ruby DSL module that can be used to construct a hash with nested keys, with syntax like:
Tree.new do
node :building do
string :name, 'Museum'
node :address do
string :street, 'Wallaby Way'
string :country, 'USA'
end
end
end.to_h
The intended output of that invocation would be this hash:
{
"building": {
"name": "Museum",
"address": {
"street": "Wallaby Way",
"country": "USA"
}
}
}
I may need to support several levels of depth for the nodes, but can't quite work out how to model that.
I've got this so far, initializing the tree class with an empty hash, and evaluating the block passed to it:
class Tree
def initialize(&block)
@tree = {}
instance_eval &block
end
def to_h
@tree
end
def node(name, &block)
@tree[name] = block.call
end
def string(name, value)
@tree[name] = value
end
end
But it gives me this flat structure:
=> {:name=>"Museum", :street=>"Wallaby Way", :country=>"USA", :address=>"USA", :building=>"USA"}
I think I'm sort of on the right lines of trying to call the block within node
, but can't quite work out how to have each node apply its contents to its part of the hash, rather than @tree[name]
, which is obviously that key on the top level hash?
CodePudding user response:
You need to initialize a new tree when you call node.
class Tree
def initialize(&block)
@tree = {}
instance_eval &block
end
def to_h
@tree
end
def node(name, &block)
@tree[name] = (Tree.new &block).to_h
end
def string(name, value)
@tree[name] = value
end
end