Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/obsidian/oak
A Crystal radix implementation
https://github.com/obsidian/oak
Last synced: about 1 month ago
JSON representation
A Crystal radix implementation
- Host: GitHub
- URL: https://github.com/obsidian/oak
- Owner: obsidian
- License: mit
- Created: 2017-12-09T03:25:04.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2021-05-06T15:49:44.000Z (over 3 years ago)
- Last Synced: 2024-08-01T17:32:05.499Z (4 months ago)
- Language: Crystal
- Homepage:
- Size: 85.9 KB
- Stars: 15
- Watchers: 3
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
- awesome-crystal - oak - A flexible Radix Tree implementation (Algorithms and Data structures)
- awesome-crystal - oak - A flexible Radix Tree implementation (Algorithms and Data structures)
README
# Oak
Another [radix tree](https://en.wikipedia.org/wiki/Radix_tree) implementation for crystal-lang[![Build Status](https://img.shields.io/travis/obsidian/oak.svg)](https://travis-ci.org/obsidian/oak)
[![Latest Tag](https://img.shields.io/github/tag/obsidian/oak.svg)](https://github.com/obsidian/oak/tags)## Installation
Add this to your application's `shard.yml`:
```yaml
dependencies:
oak:
github: obsidian/oak
```## Usage
### Building Trees
You can associate one or more *payloads* with each path added to the tree:
```crystal
require "oak"tree = Oak::Tree(Symbol).new
tree.add "/products", :products
tree.add "/products/featured", :featuredresults = tree.search "/products/featured"
if result = results.first?
puts result.payload # => :featured
end
```The types allowed for a payload are defined on Tree definition:
```crystal
tree = Oak::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 = Oak::Tree(Int32 | String | Symbol).newtree.add "/", :root
tree.add "/meaning-of-life", 42
tree.add "/hello", "world"
```### Lookup and placeholders
You can also extract values from placeholders (as named or globbed segments):
```crystal
tree.add "/products/:id", :productresult = tree.find "/products/1234"
if result
puts result.params["id"]? # => "1234"
end
```Please see `Oak::Tree#add` documentation for more usage examples.
## Optionals
Oak has the ability to add optional paths, i.e. `foo(/bar)/:id`, which will expand
into two routes: `foo/bar/:id` and `foo/:id`. In the following example, both results
will match and return the same payload.```crystal
tree.add "/products(/free)/:id", :productif result = tree.find "/products/1234"
puts result.params["id"]? # => "1234"
puts result.payload # => :product
endif result = tree.find "/products/free/1234"
puts result.params["id"]? # => "1234"
puts result.payload # => :product
end
```## Caveats
### Multiple results
Due the the dynamic nature of this radix tree, and to allow for a more flexible
experience for the implementer, the `.search` method will return a list of results.
Alternatively, you can interact with the results by providing a block.```crystal
matching_payload = nil
@tree.search(path) do |result|
unless matching_payload
context.request.path_params = result.params
matching_payload = result.payloads.find do |payload|
payload.matches_constraints? context.request
end
matching_payload.try &.call(context)
end
end
```### Multiple Leaves
In order to allow for a more flexible experience for the implementer, this
implementation of radix will not error if a multiple payloads are added at the
same path/key. You can either call the `.payload` method to grab the first payload,
or you can use the `.payloads` method, which will return all the payloads.### Shared Keys
When designing and adding *paths* to a Tree, please consider that two different
named parameters cannot share the same level:```crystal
tree.add "/", :root
tree.add "/:post", :post
tree.add "/:category/:post", :category_post # => Radix::Tree::SharedKeyError
```This is because different named parameters at the same level will result in
incorrect `params` when lookup is performed, and sometimes the value for
`post` or `category` parameters will not be stored as expected.To avoid this issue, usage of explicit keys that differentiate each path is
recommended.For example, following a good SEO practice will be consider `/:post` as
absolute permalink for the post and have a list of categories which links to
a permalink of the posts under that category:```crystal
tree.add "/", :root
tree.add "/:post", :post # this is post permalink
tree.add "/categories", :categories # list of categories
tree.add "/categories/:category", :category # listing of posts under each category
```
## Roadmap* [X] Support multiple payloads at the same level in the tree.
* [X] Return multiple matches when searching the tree.
* [X] Support optionals in the key path.
* [ ] Overcome shared key caveat.## Implementation
This project has been inspired and adapted from:
[luislavena](https://github.com/luislavena/radix)## Contributing
1. Fork it ( https://github.com/obsidian/oak/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request## Contributors
- [Jason Waldrip](https://github.com/jwaldrip) - creator, maintainer