agrafix / Spock

Another Haskell web framework for rapid development
https://www.spock.li
678 stars 56 forks source link

subcomponent with var #65

Closed wedens closed 7 years ago

wedens commented 8 years ago

It'd be nice to be able to be able to nest routes with variables:

subcomponent var $ \(id' :: Int) -> do
  get "/" $ f id'
  get "/abc" $ g id'

Probably it's already possible, but I don't understand how.

agrafix commented 8 years ago

There's already work in progress on this, but it's not possible yet. I'm currently removing the untyped routing and after that refactoring I will implement this.

wedens commented 8 years ago

I've started writing routes standalone like:

usersRoute = "admin" <//> "users"
userRoute = usersRoute <//> (var :: Var UserId)
deleteUserRoute = userRoute <//> "delete"

and then using this routes in spock application:

get usersRoute usersAction
post usersRoute newUserAction
post deleteUserRoute deleteUserAction

with this approach I'm able to re-use this routes for reverse-routing (e.g. in templates).

agrafix commented 8 years ago

Yes, that's the idea! The problem is that this will not work properly for subcomponents (yet). I'll have to find a good solution for this first...

tdietert commented 8 years ago

I find myself wanting the same thing. Is this a feature solvable by someone else other than @agrafix, or is it deeply routed in the library internals such that it would take quite a while for someone unfamiliar with spock-core to contribute this functionality?

agrafix commented 8 years ago

@tdietert : Personally I would discourage using subcomponents all the way, because they break route referencing and reusability. If you still would like to implement this, I think the proper way to do would be using the contexts feature because otherwise with the syntax from above it will make the routing tree somewhat dynamic.

tdietert commented 8 years ago

@agrafix: What do you mean by "using subcomponents all the way, because they break route referencing and reusability"? If you don't agree that this is a good thing to do, my first instinct is to trust the library author.

agrafix commented 8 years ago

If you define your routes like @wedens describes:

usersRoute = "admin" <//> "users"
userRoute = usersRoute <//> (var :: Var UserId)
deleteUserRoute = userRoute <//> "delete"

You can wire them in your server:

do get usersRoute usersAction
   post usersRoute newUserAction
   post deleteUserRoute deleteUserAction

But also use them to actually render them in the frontend/templates:

renderRoute userRoute (UserId 5) -- returns: /admin/users/5

If you'd put a subcomponent into your wiring logic above, then renderRoute would return the wrong route.

It will also make it possible to share the APIs between client and server (see https://www.spock.li/2016/08/25/shareable-apis.html ).

I will add a warning/notice about this in the docs. If you use the routes like described here, you can still come pretty close to this because routes are composable:

-- routes:
myBaseRoute = "basic" <//> "route" <//> var

mySpecialRoute = myBaseRoute <//> "special"
myOtherSpecialRoute = myBaseRoute <//> "other" <//> var

-- hooking in server:
do get mySpecialRoute $ \v1 -> undefined
   get myOtherSpecialRoute $ \v1 v2 -> undefined

For that reason, this I will mark this as "wont fix" and try to document better what the alternative would look like.