Allow usage of `make spec` to run project's specs against default
Crystal compiler or one that can be supplied with `CRYSTAL`
environment variable.
Also provide ways to run in automated mode thanks to watchexec.
[skip ci]
Properly recognize and organize non-ascii keys into nodes, allowing
usage with entries in other languages.
With this change, it is possible to use 2 or 3 bytes wide characters
(Unicode) without issues:
tree = Radix::Tree(Symbol).new
tree.add "/", :root
tree.add "/日本語", :japanese
tree.add "/日本は難しい", :japanese_is_difficult
Which produces the following node hierarchy:
# ( 1) / (:root)
# ( 6) 日本
# (12) は難しい (:japanese_is_difficult)
# ( 3) 語 (:japanese)
And lookup works as expected:
result = tree.find "/日本は難しい"
puts result.found? # => true
Given the following nodes in a tree:
# ( 8) /product
# ( 4) /new
# ( 1) s
tree = Radix::Tree(Symbol).new
tree.add "/products", :products
tree.add "/product/new", :product_new
It failed to properly identify `/products` during lookup:
result = tree.find "/products"
result.found? # => false
Caused by incorrect comparsion of `s` remaining path against `/new`
node instead of continue comparison with the next one.
Fixes#21
The order in which nodes were inserted into a tree might produce
failures in lookup mechanism.
tree = Radix::Tree(Symbol).new
tree.add "/one/:id", :one
tree.add "/one-longer/:id", :two
result = tree.find "/one-longer/10"
# expected `true`
result.found? # => false
In above example, reversing the order of insertion solved the issue,
but exposed the naive sorting/prioritization mechanism used.
This change improves that by properly identifying the kind of node
being evaluated and compared against others of the same kind.
It is now possible to know in advance if a node contains an special
condition (named parameter or globbing) or is a normal one:
node = Radix::Node(Nil).new("a")
node.normal? # => true
node = Radix::Node(Nil).new(":query")
node.named? # => true
node = Radix::Node(Nil).new("*filepath")
node.glob? # => true
Which helps out with prioritization of nodes:
- A normal node ranks higher than a named one
- A named node ranks higher than a glob one
- On two of same kind, ranks are based on priority
With this change in place, above example works as expected:
tree = Radix::Tree(Symbol).new
tree.add "/one/:id", :one
tree.add "/one-longer/:id", :two
result = tree.find "/one-longer/10"
result.found? # => true
Fixes#18
Given two similar keys, one short and one with named parameter, lookup
was incorrectly picking up the named parameter version instead of the
specific one:
tree = Radix::Tree(Symbol).new
tree.add "/tag-edit/:id", :edit_tag
tree.add "/tag-edit2", :alternate_edit_tag
result = tree.find("/tag-edit2")
result.found? # => false
The order of insertion (named before specific) was causing the
lookup mechanism to validate it before checking the other options.
With the changes present here, short or long keys will be affected
anymore by named or globbed keys present when sharing part of the
key.
Fixeskemalcr/kemal#293
Crystal 0.20 introduced a change so `MemoryIO` is know known as
`IO::Memory`.
Since no current spec was using the deprecation redirect, there was
warning raised.
Fixes#16
Lookup was failing when part of the given path matched a key at the
first character, even when the rest of the key didn't match.
It was not possible place a catch all an another key at the same
level that started with same character.
This change ensures that all shared part between path and key is
compared prior assuming usage of that node as part of the lookup.
Closes#14
Catch all parameter was limited to be used *after* a slash (`/`) on
a path (ie. `/members/*trailing`).
Attempts to use it immediately after the path (ie. `/members*trailing`)
was not properly identified or captured.
This change ensures catch all can be used as globbing, allowing capture
of anything after the matching path.
Closes#12
When adding new nodes into the tree, the lookup mechanism failed
to detect same named parameter was already added to it, raising
an exception.
Example:
tree = Radix::Tree(Symbol).new
tree.add "/members/:id/edit", :member_edit
tree.add "/members/export", :members_export
tree.add "/members/:id/videos", :member_videos # Exception
This fix ensures that comparison is done key by key, even if they
are inserted into the tree in a different order.
Closes#9
Introduce `Radix::VERSION` to be used at runtime by users of the
library for any purpose this will help them (expose them, use
conditional blocks or similar).
Also add spec that checks that defined versions in both `shard.yml`
and `Radix::VERSION` matches to avoid messing up releases.
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.
`Result#key` is expected to be `String`, yet Crystal considered
that by branching is possible result to be `nil`.
This was caused by *return-early* approach:
def foo
return @foo if @foo
@foo = "awesome"
end
pp typeof(foo) # => String?
Changed to instead use memoization technique.
Remove Ruby's *symbols everywhere* approach to define getter/setters
or properties. Crystal's parser and macros do not require symbols
for these options.
Also add type annotations to some elements aiming to improve quality
of the documentation generated (more accurate expected types).
Deal with named parameters under same level (shared) and raise
proper `SharedKeyError` exception.
This is a non-backward compatible change aims to solve result
mapping issues and tree lookup.
Now README covers this under *Caveats* section and offers an
alternative organization of the paths used on the tree.
To avoid potential issues when using `master` instead of a locked
release, bump the version.