Hello. 1st of all, ktor is super. I've built a sam...
# ktor
a
Hello. 1st of all, ktor is super. I've built a sample project with it in no time. I have some difficulties it coupling locations and views (built with html dsl). My views are refactored into separate classes, like this one, for the main template.
d
what do you mean by coupling locations and views?
a
As you see, template references locations https://ktor.io/servers/features/locations.html they are defined elsewhere in the project:
Copy code
@Location("/products")
class Products
@Location("/platforms")
class Platforms
@Location("/deviceModels")
class DeviceModels
so instead of using error-prone string literals inside template, like
a(href = "/products") { +"Products" }
one is able to
a(href = href(Products())) { +"Products" }
-- the feature is great!
The problem is to resolve the location (
href(Products())
) inside the template. Corresponding function
href
is available only in Routing context. For now I am using the trick:
Copy code
var href: (Any) -> String = { "" }

fun Routing.commonRoutes() {

    href = fun(location: Any): String { return application.locations.href(location) }
  // etc.
and wonder if more elegant solution might exist?
Or maybe ktor framework will add some global function to resolve location even out of Routing context?
d
Aha, let me check
I’m checking why href depends on the Routing/ApplicationCall context. Still I guess you can create a
Template
subclass with a
call: ApplicationCall
, and make a href method or extension method for that class that calls the right method with the right context
I believe that the context is used to cache the information per type
a
Your suggestion is correct, yet the code would become little too verbose in my scenario:
Copy code
fun Routing.productRoutes() {
    get<Products> { _ ->
        call.respondHtmlTemplate(
                ListTemplate(
                        title = "All products",
                        tableModel = allProductTableModel(),
                        href = fun(location: Any) = this@productRoutes.application.locations.href(location)
                )
        ) {}
    }
}
same code for every get or post
instead of
Copy code
fun Routing.productRoutes() {
    get<Products> { _ ->
        call.respondHtmlTemplate(
                ListTemplate(title = "All products", tableModel = allProductTableModel())
        ) {}
    }
}
and pithiness is what we love kotlin (and ktor) for
o
I admit
Locations
is a feature that needs much more attention, but unfortunately right now we don’t have enough resource. It’s not about coding, more about designing it better.
In general, we don’t really allow static context, it doesn’t work well with multiple servers in the single process, doesn’t work great with multithreading, etc. So you will still have to pass some context into a template.
All you need is
Location
feature instance, so it shouldn’t be too hard?
If you’re fine with some statics, you can have a global lateinit variable that you initialize with an
Application
instance and use it everywhere.
But I would suggest against it 🙂
a
Current solution is nothing more then hack, that would break in multithread environment, I agree completely here.
o
Static application (or Location instance) won’t break in multithreaded environment, given you have only one app in a JVM and you initialize it once and never change.
d
you can also make your html builder suspend, and use the
coroutineContext
as a dependency container 🤨, like a scala implicit. Though I wouldn’t do that either the main problem with globals (in addition to multithreading issues) is testing or multiple applications, you normally want to start things from scratch. Even when in control at the beginning, it is a bad practice in general
a
It’s not about coding, more about designing it better.
So I provided you with feedback from a real world application. Anyway, thank you for a wonderful framework.
o
Thanks for feedback 🙂 It would be awesome if you could post an issue to GitHub so we have it handy when we’ll work on the design.
@Deactivated User coroutine context is a nice idea indeed! However it would require changes to kotlinx.html I believe
a
or Location instance
I do not see that Location instance would be of any use though. E.g. there are actually @Location("/products") class Products { @Location("/{id}") class Item(val id: Int) } and later:
Copy code
private fun ProductModel.asRowModel(): RowModel {
    return RowModel(listOf(RefCell(id) { href(Products.Item(id)) }, PlainCell(name)))
}
It is possible to embed links into list view. Each to corresponding item:
href(Products.Item(id))
for every item in the list
o
I mean
Locations
feature instance, like
application.locations
a
Ah, I see
d
@orangy I thought that the DSL was inline already. But just checked and it is not the case, so yeah, it wouldn’t work directly. You would have to get the links before the DSL.
a
👍 1