Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/l-briand/ktm
Mustache template engine in kotlin multiplatform
https://github.com/l-briand/ktm
kotlin kotlin-multiplatform multiplatform multiplatform-kotlin-library mustache template-engine template-engine-html template-engine-kotlin template-processor
Last synced: about 2 months ago
JSON representation
Mustache template engine in kotlin multiplatform
- Host: GitHub
- URL: https://github.com/l-briand/ktm
- Owner: L-Briand
- License: mit
- Created: 2023-06-01T11:57:11.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2024-07-25T07:21:11.000Z (6 months ago)
- Last Synced: 2024-07-25T11:33:09.018Z (6 months ago)
- Topics: kotlin, kotlin-multiplatform, multiplatform, multiplatform-kotlin-library, mustache, template-engine, template-engine-html, template-engine-kotlin, template-processor
- Language: Kotlin
- Homepage: https://mustache.github.io/
- Size: 772 KB
- Stars: 17
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.MD
- License: LICENSE
Awesome Lists containing this project
README
# KTM
A [Mustache](https://mustache.github.io) implementation in pure Kotlin Multiplatform.
To see how it compares to [Mustache.java](https://github.com/spullara/mustache.java)
look [here](benchmark/README.MD)# Import from maven
### Library
Multiplatform:```kotlin
repositories {
mavenCentral()
}
val commonMain by getting {
dependencies {
implementation("net.orandja.ktm:core:2.0.0")
}
}
```Jvm:
```kotlin
repositories {
mavenCentral()
}
dependencies {
implementation("net.orandja.ktm:core:2.0.0")
}
```### Ksp code generator plugin
Enable ksp plugin:
```kotlin
plugins {
// ...
id("com.google.devtools.ksp") version "1.9.22-1.0.17" // or later
}
```
Multiplatform:```kotlin
repositories {
mavenCentral()
}
dependencies {
add("kspJvm", "net.orandja.ktm:ksp:2.0.0")
// add("kspJs", "net.orandja.ktm:ksp:2.0.0")
// add("kspNative", "net.orandja.ktm:ksp:2.0.0")
// ...
}
```Jvm :
```kotlin
dependencies {
// ...
ksp("net.orandja.ktm:ksp:2.0.0")
}```
> [!NOTE]
> If you provide ksp arguments.
>
> ```kotlin
> ksp {
> arg("ktm.auto_adapters_package", "com.example")
> }
> ```
>
> This will generate the `com.example.AutoKtmAdaptersModule` with all generated
> adapters from `@KtmContext` annotated classes.
>
> Then set it as default with
> ```kotlin
> Ktm.setDefaultAdapters(AutoKtmAdaptersModule)
> ```# Examples
Render a document with automatically generated adapter
```kotlin
@KtmContext
data class User(val firstName: String, val lastName: String) {
@KtmName("name")
fun fullName() = "$firstName $lastName"
}Ktm.setDefaultAdapters {
+UserKtmAdapter
}// or Ktm.setDefaultAdapters(AutoKtmAdaptersModule)
// if you have setup a package name for itval document = "Hello {{ name }}".toMustacheDocument()
val data = User("John", "Doe")assert("Hello John Doe" == document.render(data))
```Render a document with custom build contexts
```kotlin
val document = "Hello {{ name }}".toMustacheDocument()
val context = Ktm.ctx.make {
"firstName" by "John"
"lastName" by "Doe"
"name" by delegateValue { "${findValue("firstName")} ${findValue("lastName")}" }
}
assert("Hello John Doe" == document.render(data))
```Custom context can be useful when composing.
```kotlin
@KtmContext
data class User(val name: String)
Ktm.setDefaultAdapters { +UserKtmAdapter }val john = User("John")
val documents = Ktm.ctx.make {
"content" by "Hello {{ name }}".toMustacheDocument()
// If you do not transform it to a mustache document,
// it will be done on the fly when rendering
"header" by "Header for {{ name }}"
}val template = "{{> header }}\n\n{{> content }}".toMustacheDocument()
val context = Ktm.ctx.make {
like(documents)
like(john)
}assert("Header for John\nHello John" == template.render(context))
```# QuickStart
Mustache API is bundled into the `Ktm` object.
You will use it when you create documents, contexts, or use adapters.To render a document, you need two things:
## 1. A template
Something like `Hello {{ name }}`. This needs to be parsed to be used.
Use the `Ktm.doc` object to read and parse the documents into usable objects.
If you are running kotlin with a JVM, you can use extension functions to parse files
and IO with `.file(File)`, `.path(Path)`, `.resource(name: String)`,
`.inputStream(InputStream)` or `.reader(Reader)`.Example:
```kotlin
val mustacheTemplate: String = "Hello {{ name }}"
var document: MDocument
// default way
document = Ktm.doc.string(mustacheTemplate)
// quick way, only work on strings
document = mustacheTemplate.toMustacheDocument()
```## 2. A context
You generally need some kind of map `key: value` to match your mustache tags.
To create such map, use the `Ktm.ctx` object.
With it, you can create template contexts anywhere in your code.Let's assume this document:
```handlebars
{{# greeting }}
Hello {{ name }},
{{/ greeting }}
Today's tasks:
{{# tasks }}
- {{ . }}
{{/ tasks }}
```### Creating context by hand
You can build a context by hand with methods in `Ktm.ctx`.
```kotlin
val context = Ktm.ctx.ctxMap(
"greeting" to Ktm.ctx.ctxMap(
"name" to Ktm.ctx.string("John")
)
)
```This method is quite cumbersome, to mitigate this, and write things more quickly you
can use the `make` function.The `by` keyword can be used to associate key value pairs in the context.
Also, every method in `Ktm.ctx` can be used in the `make` scope.```kotlin
val context = Ktm.ctx.make {
"greeting" by make {
"name" by "Jon"
}
"tasks" by ctxList(value("Eat"), value("Work"))
}
```In a more simple way, you can also use kotlin's maps and list to define your data
```kotlin
val context = Ktm.ctx.make {
"greeting" by mapOf("name" to "Jon")
"tasks" by listOf("Eat", "Work")
}
```Maybe you don't have the full scope of your context, and some parts need to be
dynamic.Here we have a function that defines the tasks without knowing `name`.
We then add it to the main context.
During render, it will call the lambda and search for the `name` tag.```kotlin
val tasks = Ktm.ctx.makeList {
+"Call for lunch."
+delegateValue {
"Welcome ${findValue("name")} to the office."
}
}val context: MContext = Ktm.ctx.make {
"name" by "John"
"tasks" by tasks
}
```### Creating context with ksp
Create a class representing your data and annotate it with `@KtmContext`.
```kotlin
@KtmContext
data class User(@KtmName("name") val user: String)
```The ksp plugin will take these classes and create
adapters (`UserKtmAdapter`, `TasksKtmAdapter`) in the same package.
It will create bindings for any property or function inside it.Adapters are key components for Ktm. From a given type (here `User`) it has the
ability convert it into `MContext`.```kotlin
val context: MContext = TasksKtmAdapter.toMustacheContext(User("John"))
assert("John" == "{{ name }}".render(context))
```Generally, you want to set up sets of adapters to transform all your objects
into context easily. Then use it when you build contexts.```kotlin
val adapters = Ktm.adapters.make {
+UserKtmAdapter
}
val context = Ktm.ctx.make(adapters) {
"content" by User("John")
}
assert("John", "{{ content.name }}".render(context))
```Or set a new default set of adapters altogether for every time you create a context.
```kotlin
Ktm.setDefaultAdapters {
+UserKtmAdapter
}
// now 'Ktm.adapters' contains adapter for User.
// and by default 'Ktm.ctx.make' use 'Ktm.adapters'
val userContext = Ktm.adapters.contextOf(User("John"))
val contentContext = Ktm.ctx.make {
"content" by User("John")
}
assert("John" == "{{ name }}".render(userContext))
assert("John" == "{{ content.name }}".render(userContext))
```You can always add your own adapter on top of others.
```kotlin
@KtmContext
data class User(val user: String)
Ktm.setDefaultAdapters { +UserKtmAdapter }val customAdapters = Ktm.adapters.make {
+KtmAdapter { adapters, value ->
Ktm.ctx.make {
"userName" by value.user
}
}
}
val context = customAdapters.contextOf(User("John"))
assert("John", "{{userName}}".render())
```#### KtmAdapter Modules
If you set the `ksp` argument:
```kotlin
ksp {
arg("ktm.auto_adapters_package", "my.pkg")
}
```This will generate the `my.pkg.AutoKtmAdaptersModule` with all generated
adapters from `@KtmContext` annotated classes. You can then set it as default with:```kotlin
Ktm.setDefaultAdapters(AutoKtmAdaptersModule)
```Extends the class `KtmAdapterModule` to create custom modules. You can later create
sets of adapters with them.# Deep dive
- [Create documents](docs/mdocs/create_documents.md)