README.md in sycamore-0.1.0 vs README.md in sycamore-0.2.0

- old
+ new

@@ -2,14 +2,15 @@ # Sycamore > _"The Egyptians' Holy Sycamore also stood on the threshold of life and death, connecting the two worlds."_ > -- [Wikipedia: Tree of Life](http://en.wikipedia.org/wiki/Tree_of_life) +[![Gem Version](https://badge.fury.io/rb/sycamore.svg)](http://badge.fury.io/rb/sycamore) [![Travis CI Build Status](https://secure.travis-ci.org/marcelotto/sycamore.png)](https://travis-ci.org/marcelotto/sycamore?branch=master) [![Coverage Status](https://coveralls.io/repos/marcelotto/sycamore/badge.png)](https://coveralls.io/r/marcelotto/sycamore) [![Inline docs](http://inch-ci.org/github/marcelotto/sycamore.png)](http://inch-ci.org/github/marcelotto/sycamore) -[![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://rubydoc.org/gems/spread2rdf/frames) +[![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://rubydoc.org/gems/sycamore/frames) [![Gitter Chat](http://img.shields.io/badge/chat-gitter.im-orange.svg)](https://gitter.im/marcelotto/sycamore) [![License](http://img.shields.io/license/MIT.png?color=green)](http://opensource.org/licenses/MIT) **Sycamore is an implementation of an unordered tree data structure.** @@ -60,11 +61,11 @@ $ gem install sycamore ## Usage -I will introduce Sycamore's Tree API by comparing it with [Rubys native Hash API](http://ruby-doc.org/core-2.2.3/Hash.html). +I will introduce Sycamore's Tree API by comparing it with [Ruby's Hash API](http://ruby-doc.org/core-2.2.3/Hash.html). In the following I'll always write `Tree` for the Sycamore tree class, instead of the fully qualified `Sycamore::Tree`. By default, this global `Tree` constant is not available. If you want this, you'll have to ```ruby require 'sycamore/extension' @@ -90,11 +91,11 @@ ```ruby tree = Tree.new tree.empty? # => true ``` -No additional arguments are supported at the time. As you'll see, for a `Sycamore::Tree` the functionality of the Hash constructor to specify the default value behaviour is of too little value to justify its use in the default constructor. The decision of its use can be tracked in [issue #1](). +No additional arguments are supported at the time. As you'll see, for a `Sycamore::Tree` the functionality of the Hash constructor to specify the default value behaviour is of too little value to justify its use in the default constructor, so I'd like to reserve them for something more useful. The `[]` operator creates a new `Tree` and adds the arguments as its initial input. It can handle a single node value, a collection of nodes or a complete tree. ```ruby Tree[1] # => #<Sycamore::Tree:0x3fcfe51a5a3c {1=>n/a}> @@ -164,17 +165,24 @@ tree[:y][2].node # => "a" tree[:x][1].node # => nil tree.node # Sycamore::NonUniqueNodeSet: multiple nodes present: [:x, :y] ``` +The bang variant `node!` raises an error when the node set is empty, instead of returning `nil`. + +```ruby +tree[:y][2].node! # => "a" +tree[:x][1].node! # => # Sycamore::EmptyNodeSet: no node present +``` + As opposed to Hash, the `[]` operator of `Sycamore::Tree` also supports multiple arguments which get interpreted as a path. ```ruby tree[:y, 2].node # => "a" ``` -For compatibility with Ruby 2.3 hashes, this can also be done with the `dig` method. +For compatibility with Ruby 2.3 Hashes, this can also be done with the `dig` method. ```ruby tree.dig(:y, 2).node # => "a" ``` @@ -245,14 +253,19 @@ ```ruby tree.include?(x: 1, y: 2) # => true ``` +`to_h` returns the tree as a Hash. +```ruby +tree.to_h # => {:x=>1, :y=>{2=>"a"}} +``` + ### Accessing absent trees -There is another major difference to a hash, which is in fact just a consequence of the already mentioned difference, that the access methods (except `fetch`) **always** return trees, when asked for children: They even return a child tree, when it does not exist. When you ask a hash for a non-existent element with the `[]` operator, you'll get a `nil`, which is an incarnation of the null-problem and the cause of many bug tracking sessions. +There is another major difference in the access method behaviour of a Scyamore tree in comparison to hashes: The child access methods even return a tree when it does not exist. When you ask a hash for a non-existent element with the `[]` operator, you'll get a `nil`, which is an incarnation of the null-problem and the cause of many bug tracking sessions. ```ruby hash = {x: 1, y: {2 => "a"}} hash[:z] # => nil hash[:z][3] # => NoMethodError: undefined method `[]' for nil:NilClass @@ -272,11 +285,11 @@ Sycamore::Nothing.empty? # => true Sycamore::Nothing.size # => 0 Sycamore::Nothing[42] # => #<Sycamore::Nothing> ``` -Sycamore adheres to a strict [command-query-separation (CQS)](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation). A method is either a command changing the state of the tree and returning `self` or a query method, which only computes and returns the results of the query, but leaves the state unchanged. The only exception to this strict separation is made, when it is necessary in order to preserve hash compatibility. All query methods are supported by the `Sycamore::Nothing` tree with empty tree semantics. +Sycamore adheres to a strict [command-query-separation (CQS)](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation). A method is either a command changing the state of the tree and returning `self` or a query method, which only computes and returns the results of the query, but leaves the state unchanged. The only exception to this strict separation is made, when it is necessary in order to preserve Hash compatibility. All query methods are supported by the `Sycamore::Nothing` tree with empty tree semantics. Among the command methods are two subclasses: additive command methods, which add elements and destructive command methods, which remove elements. These are further refined into pure additive and pure destructive command methods, which either support additions or deletions only, not both operations at once. The `Sycamore::Tree` extends Ruby's reflection API with class methods to retrieve the respective methods: `query_methods`, `command_methods`, `additive_command_methods`, `destructive_command_methods`, `pure_additive_command_methods`, `pure_destructive_command_methods`. ```ruby Tree.command_methods @@ -434,10 +447,16 @@ ```ruby tree[:foo] = [] tree[:foo] = {} ``` +To remove a child tree entirely, you can assign `Nothing` to the parent node. + +```ruby +tree[:foo] = Nothing +``` + Note that these values are interpreted similarly inside tree structures, i.e. empty Enumerables become empty child trees, while `Nothing` or `nil` are used as place holders for the explicit negation of a child. ```ruby puts Tree[ a: { b: nil }, c: { d: []} ] >Tree[:a=>:b, :c=>{:d=>[]}] @@ -495,9 +514,26 @@ ```ruby Tree['some possibly very big data chunk' => [1, 2]].each_path.to_a # => [#<Sycamore::Path["some possibly very big data chunk",1]>, # #<Sycamore::Path["some possibly very big data chunk",2]>] +``` + + +### Searching in trees + +`search` returns the set of all paths to child trees containing a node or tree. + +```ruby +tree = Tree[ 1 => {a: 'foo'}, 2 => :b, 3 => [:a, :b, :c] ] +tree.search :a # => [#<Sycamore::Path[1]>, #<Sycamore::Path[1]>] +tree.search a: 'foo' # => [#<Sycamore::Path[1]>] +``` + +If you search for multiple nodes, only the paths to child trees containing all of the given nodes are returned. + +```ruby +tree.search [:b, :c] # => [#<Sycamore::Path[3]>] ``` ## Getting help