Permalink
Browse files

Introduce types for forward compiler compatiblity

After Crystal 0.15, compiler will require declare the types used
by instance variables on classes.

This require changes to the usage of `Radix::Tree` by introducing
the type of payload elements it will handle:

    # Will only support symbols as payload
    tree = Radix::Tree(Symbol).new
    tree.add "/", :root

    # Error: cannot add node with anything other than Symbol
    tree.add "/meaning-of-life", 42

The changes ensure future compatibility with Crystal and also
enforces a more declarative usage of `Radix::Tree`.

If necessary, you can combine multiple types to ensure a tree
can contain all the wide range of payloads you need:

    tree = Radix::Tree.new(Foo | Bar | Symbol).new
    tree.add "/", :root
    tree.add "/foo", foo_instance

This change includes:

- Tree, Node and Result has been updated to require types.
- Node is capable of have optional payload (from defined type).
- Documentation has been updated to reflect this change.
  • Loading branch information...
1 parent 18bf9b1 commit 9003075ec7cdd485eb793eb741f9ba2794c697b6 @luislavena committed Apr 14, 2016
Showing with 190 additions and 109 deletions.
  1. +9 −0 CHANGELOG.md
  2. +32 −6 README.md
  3. +23 −17 spec/radix/node_spec.cr
  4. +13 −13 spec/radix/result_spec.cr
  5. +55 −31 spec/radix/tree_spec.cr
  6. +34 −20 src/radix/node.cr
  7. +14 −12 src/radix/result.cr
  8. +10 −10 src/radix/tree.cr
View
@@ -4,6 +4,15 @@ All notable changes to Radix project will be documented in this file.
This project aims to comply with [Semantic Versioning](http://semver.org/),
so please check *Changed* and *Removed* notes before upgrading.
+## [Unreleased]
+### Fixed
+- Improve forward compatibility with newer versions of the compiler by adding
+ missing types to solve type inference errors.
+
+### Changed
+- `Radix::Tree` now requires the usage of a type which will be used as node's
+ payload. See [README](README.md) for details.
+
## [0.2.1] - 2016-03-15
### Fixed
- Correct `Result#key` incorrect inferred type.
View
@@ -1,11 +1,11 @@
# Radix Tree
-[![Build Status](https://travis-ci.org/luislavena/radix.svg?branch=master)](https://travis-ci.org/luislavena/radix)
-[![docrystal.org](http://docrystal.org/badge.svg?style=round)](http://docrystal.org/github.com/luislavena/radix)
-
[Radix tree](https://en.wikipedia.org/wiki/Radix_tree) implementation for
Crystal language
+[![Build Status](https://travis-ci.org/luislavena/radix.svg?branch=master)](https://travis-ci.org/luislavena/radix)
+[![docrystal.org](http://docrystal.org/badge.svg?style=round)](http://docrystal.org/github.com/luislavena/radix)
+
## Installation
Add this to your application's `shard.yml`:
@@ -18,12 +18,14 @@ dependencies:
## Usage
+### Building Trees
+
You can associate a *payload* with each path added to the tree:
```crystal
require "radix"
-tree = Radix::Tree.new
+tree = Radix::Tree(Symbol).new
tree.add "/products", :products
tree.add "/products/featured", :featured
@@ -34,6 +36,30 @@ if result.found?
end
```
+The types allowed for payload are defined on Tree definition:
+
+```crystal
+tree = Radix::Tree(Symbol).new
+
+# Good, since Symbol is allowed as payload
+tree.add "/", :root
+
+# Compilation error, Int32 is not allowed
+tree.add "/meaning-of-life", 42
+```
+
+Can combine multiple types if needed:
+
+```crystal
+tree = Radix::Tree(Int32 | String | Symbol).new
+
+tree.add "/", :root
+tree.add "/meaning-of-life", 42
+tree.add "/hello", "world"
+```
+
+### Lookup and placeholders
+
You can also extract values from placeholders (as named segments or globbing):
```crystal
@@ -53,8 +79,8 @@ Please see `Radix::Tree#add` documentation for more usage examples.
Pretty much all Radix implementations have their limitations and this project
is no exception.
-When designing and adding *paths* to build a Tree, please consider that two
-different named parameters cannot share the same level:
+When designing and adding *paths* to a Tree, please consider that two different
+named parameters cannot share the same level:
```crystal
tree.add "/", :root
@@ -4,7 +4,7 @@ module Radix
describe Node do
describe "#key=" do
it "accepts change of key after initialization" do
- node = Node.new("abc")
+ node = Node(Nil).new("abc")
node.key.should eq("abc")
node.key = "xyz"
@@ -23,39 +23,45 @@ module Radix
node.payload.should eq(1_000)
end
+ # This example focuses on the internal representation of `payload`
+ # as inferred from supplied types and default values.
+ #
+ # We cannot compare `typeof` against `property!` since it excludes `Nil`
+ # from the possible types.
it "makes optional to provide a payload" do
- node = Node.new("abc")
+ node = Node(Int32).new("abc")
node.payload?.should be_falsey
+ typeof(node.@payload).should eq(Int32 | Nil)
end
end
describe "#priority" do
it "calculates it based on key size" do
- node = Node.new("a")
+ node = Node(Nil).new("a")
node.priority.should eq(1)
- node = Node.new("abc")
+ node = Node(Nil).new("abc")
node.priority.should eq(3)
end
it "returns zero for catch all (globbed) key" do
- node = Node.new("*filepath")
+ node = Node(Nil).new("*filepath")
node.priority.should eq(0)
- node = Node.new("/src/*filepath")
+ node = Node(Nil).new("/src/*filepath")
node.priority.should eq(0)
end
it "returns one for keys with named parameters" do
- node = Node.new(":query")
+ node = Node(Nil).new(":query")
node.priority.should eq(1)
- node = Node.new("/search/:query")
+ node = Node(Nil).new("/search/:query")
node.priority.should eq(1)
end
it "changes when key changes" do
- node = Node.new("a")
+ node = Node(Nil).new("a")
node.priority.should eq(1)
node.key = "abc"
@@ -71,10 +77,10 @@ module Radix
describe "#sort!" do
it "orders children by priority" do
- root = Node.new("/")
- node1 = Node.new("a")
- node2 = Node.new("bc")
- node3 = Node.new("def")
+ root = Node(Int32).new("/")
+ node1 = Node(Int32).new("a", 1)
+ node2 = Node(Int32).new("bc", 2)
+ node3 = Node(Int32).new("def", 3)
root.children.push(node1, node2, node3)
root.sort!
@@ -85,10 +91,10 @@ module Radix
end
it "orders catch all and named parameters lower than others" do
- root = Node.new("/")
- node1 = Node.new("*filepath")
- node2 = Node.new("abc")
- node3 = Node.new(":query")
+ root = Node(Int32).new("/")
+ node1 = Node(Int32).new("*filepath", 1)
+ node2 = Node(Int32).new("abc", 2)
+ node3 = Node(Int32).new(":query", 3)
root.children.push(node1, node2, node3)
root.sort!
@@ -5,15 +5,15 @@ module Radix
describe "#found?" do
context "a new instance" do
it "returns false when no payload is associated" do
- result = Result.new
+ result = Result(Nil).new
result.found?.should be_false
end
end
context "with a payload" do
it "returns true" do
- node = Node.new("/", :root)
- result = Result.new
+ node = Node(Symbol).new("/", :root)
+ result = Result(Symbol).new
result.use node
result.found?.should be_true
@@ -24,15 +24,15 @@ module Radix
describe "#key" do
context "a new instance" do
it "returns an empty key" do
- result = Result.new
+ result = Result(Nil).new
result.key.should eq("")
end
end
context "given one used node" do
it "returns the node key" do
- node = Node.new("/", :root)
- result = Result.new
+ node = Node(Symbol).new("/", :root)
+ result = Result(Symbol).new
result.use node
result.key.should eq("/")
@@ -41,9 +41,9 @@ module Radix
context "using multiple nodes" do
it "combines the node keys" do
- node1 = Node.new("/", :root)
- node2 = Node.new("about", :about)
- result = Result.new
+ node1 = Node(Symbol).new("/", :root)
+ node2 = Node(Symbol).new("about", :about)
+ result = Result(Symbol).new
result.use node1
result.use node2
@@ -54,8 +54,8 @@ module Radix
describe "#use" do
it "uses the node payload" do
- node = Node.new("/", :root)
- result = Result.new
+ node = Node(Symbol).new("/", :root)
+ result = Result(Symbol).new
result.payload?.should be_falsey
result.use node
@@ -64,8 +64,8 @@ module Radix
end
it "allow not to assign payload" do
- node = Node.new("/", :root)
- result = Result.new
+ node = Node(Symbol).new("/", :root)
+ result = Result(Symbol).new
result.payload?.should be_falsey
result.use node, payload: false
Oops, something went wrong.

0 comments on commit 9003075

Please sign in to comment.