Crowdhailer
Sinatra-inspired web routing DSL, includes streaming. Interested in feedback
I have started experimenting with a web routing Domain Specific Language(DSL).
The DSL is inspired from working with several minimal web frameworks including Sinatra, roda etc. The heritage is not overly important as the question is about macro best practice. I wanted a simple DSL but my particular requirement was to match on url before method.
My first attempt looked like the following.
route "/user/:id" do
:GET ->
IO.inspect(id) # This implicit variable matches the identifier from the URL
IO.inspect(request) # This implicit variable is always added to the context
IO.inspect(config) # Same as request
# actual work here to return request
:POST ->
# Other methods etc
end
However to create this DSL I have needed to use Macro.var, and this can mess with the hygiene of a macro. To see what would be required to not implicitly set variables I got to the following DSL.
route "/users/:id", [user_id] do
get(request, config) ->
IO.inspect(user_id) # explicitly named in route macro
IO.inspect(request) # explicitly named in get macro
IO.inspect(config) # Same as request
# actual work here to return request
post(r) ->
IO.inspect(r) # Named r for brevity
# config not used so not matched on
end
I see the trade off between the two as follows, version one is slightly more succinct but at the cost of being more “magic”. In addition this magic messing with the hygiene of the macro may have further side effects that I don’t know about yet.
Because of this I am leaning towards favoring the second more explicit DSL but would be interested in other opinions.
Update:
I have abandoned efforts to write a Sinatra style routing DSL. As any useful API must be documented I have decided to use that documentation as a router. My implementation of this is Raxx.Blueprint that will parse an API Blueprint file and generate forward requests to controllers based on that.
Most Liked
Crowdhailer
Here is what an example chat server currently looks like, with this DSL
defmodule Example do
use Tokumei
config :port, 8080
config :static, "./public"
config :templates, "./templates"
route "/" do
get() ->
ok(home_page())
post(%{body: body}) ->
{:ok, %{message: message}} = PublishForm.parse(body)
{:ok, _} = ChatRoom.publish(message)
redirect("/")
end
route "/updates" do
get() ->
{:ok, _} = ChatRoom.join()
SSE.stream(:updates)
end
SSE.streaming :updates do
{:message, message} ->
{:send, %{event: "chat", data: "message"}
_ ->
{:nosend}
end
end
Hopefully it the majority of the example is self explanatory.I hope to push the first version to hex soon
OvermindDL1
I subscribe to the Python credo of “Explicit is better than Implicit”. ^.^
Crowdhailer
Final comment to close of this thread.
I have abandoned efforts to write a Sinatra style routing DSL. As any useful API must be documented I have decided to use that documentation as a router. My implementation of this is Raxx.Blueprint that will parse an API Blueprint file and generate forward requests to controllers based on that.
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








