Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/gdotdesign/cr-melon
Class based Http APIs in crystal
https://github.com/gdotdesign/cr-melon
Last synced: 4 months ago
JSON representation
Class based Http APIs in crystal
- Host: GitHub
- URL: https://github.com/gdotdesign/cr-melon
- Owner: gdotdesign
- Created: 2016-07-08T06:01:23.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2019-01-08T09:47:31.000Z (about 6 years ago)
- Last Synced: 2024-08-03T17:12:51.943Z (6 months ago)
- Language: Crystal
- Homepage:
- Size: 17.6 KB
- Stars: 12
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: Readme.md
Awesome Lists containing this project
- awesome-crystal - cr-melon - Class based Http APIs (Framework Components)
README
# Melon
Class based Http APIs in crystal## Description
This is a proof of concept of creating a shard that allows people to create
APIs with **classes** with the following fetaures:
- DSL for building the APIs (get, put, post, etc...)
- Inherit form an other APIs
- Mounting other APIs at an endpoint
- Inspectable routes and APIs for documentation[Example](examples/simple.cr):
```crystal
require "../src/melon"USERS = [
{name: "John Doe"},
{name: "Someone Else"},
]class Users < Melon::Api
description "Users Endpoint"get description: "Get all users" do
json USERS
end
endclass Root < Melon::Api
description "My Awesome API"get description: "Simple GET request." do
ok "text/plain", "Hello there!"
endpost do
ok "text/plain", "You have posted something."
endmount Users, "users"
endMelon.print_routes Root
Melon.listen Root, 8080# Root - My Awesome API
# ----------------------------------------
# ├─ GET - / # Simple GET request.
# ├─ POST - /
# └─┬─ API - /users # Users Endpoint
# └─ GET - / # Get all users
# ----------------------------------------
# Listening on http://0.0.0.0:8080
```## Explanation
This implementation is heavily uses macros and a pattern matching to make it
work:
- macros create a subclass `class Route%id < Route # Route__temp_25`
- macros create overloaded methods with the subclass
`def handle_route(id : Route__temp_25)`
- macros save the instances of these classes in a registry for access with
metadata `Registry[path] = Route%id.new "some metadata"`
- overloaded methods are run with the subclass from the matching route
in the registry `instance.handle_route(Registry[path])`
- a fallback implemention is needed for matching the base class `def handle_route(id : Route)`
for compability (to actually compile) which will never be called
- all created routes are inspectable in the registryThis behavior used can further explained with the following code:
```crystal
# Define a class
class A
end# Define a registry
REGISTRY = {} of String => A# Define the which will have the DSL
class B
# Define the DSL method
macro make_print(key, text)
# Create a sub class with a unique name
class A%name < A
end# Save an instance of that class in the registry
REGISTRY[{{key}}] = A%name.new# Create an overloaded method that responds to that class only
def handle_print(id : A%name)
# Run things here
puts {{text}}
end
end# Create a fallback method for compability
def handle_print(id : A)
puts "fallback"
end# Have a method call the overloaded functions
def print(key)
handle_print REGISTRY[key]
end# Actually make the overloaded methods
make_print "a", "hello"
make_print "b", "bye"
endb = B.new
b.print "a" # "hello"
b.print "b" # "bye"
```