Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feat-refactor-fns
Browse files Browse the repository at this point in the history
  • Loading branch information
erdos committed Oct 13, 2024
2 parents a255ecf + 0ab758d commit 0d620f4
Show file tree
Hide file tree
Showing 15 changed files with 152 additions and 46 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: clean prepare lint pom jar uberjar javadoc compile clj-test java-test visual-test coverage test all
.PHONY: clean prepare lint pom install jar uberjar javadoc compile clj-test java-test visual-test coverage test all
.DEFAULT_GOAL := all

clean:
Expand All @@ -13,6 +13,9 @@ lint: clean
pom: clean
clojure -T:build pom

install: clean
clojure -T:build install

jar: clean
clojure -T:build jar

Expand Down
68 changes: 45 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Stencil Template Engine

Stencil is an open source templating engine that transforms Office Open XML documents (mostly Microsoft
Office's Word `.docx` files) from Java programs. It has a simple syntax and no programming is needed to write document templates.
Office's Word `.docx` files) from the JVM. It has a simple syntax and no programming is needed to write document templates.

<p align="center"><img src="https://raw.githubusercontent.com/erdos/stencil/master/docs/graphics.svg?sanitize=true" alt="stencil flow"/></p>

Expand All @@ -20,59 +20,81 @@ them to make your template more readable.

## Features

- Works with `docx` and `pptx` files
- Simple value substitution, conditional and repeating blocks
- Substituting HTML text for dynamic text formatting
- Dynamically replace images and links in the template
- Show/hide rows and columns in tables
- 📄 **Multiple Formats:** Works with `docx` and `pptx` files
- 💻 **Simple syntax:** For value substitution, conditional and repeating blocks
- 🔧 **Extendable:** Dozens of [built-in functions](https://stencil.erdos.dev/Functions.html) callable from the template
- 📰 **Dynamic content:** Substituting HTML text for dynamic text formatting
- 🌄 **Images and links:** Dynamically replace images and links in the template
- 👀 **Tables:** Show/hide rows and columns in tables
- 📐 **Programmable:** Offers API for Java and Clojure. Deployable as a Docker container.

## Getting Started with the Library
## 📖 Getting Started with the Library

- See the [Example templates](examples)
- Read the [Documentation](https://stencil.erdos.dev)
- Read about the [Java API](docs/GettingStarted.md#java-api) and the [Clojure API](docs/GettingStarted.md#clojure-api).
- Reference the [Java API](docs/GettingStarted.md#java-api) and the [Clojure API](docs/GettingStarted.md#clojure-api).


## Getting Started with the Service
## 🐳 Getting Started with the Service

The project has a simple [service implementation](https://github.com/erdos/stencil/tree/master/service), which is available on GitHub Packages as a [Container image](https://github.com/users/erdos/packages/container/package/stencil).


## Version
## 👉 Version

**Latest stable** version is `0.6.1`

**Latest snapshot** version is `0.6.2-SNAPSHOT`

**Latest stable** version is `0.6.0`
Previous versions are available on the [Stencil Clojars](https://clojars.org/io.github.erdos/stencil-core) page.

**Latest snapshot** version is `0.6.1-SNAPSHOT`
<details>
<summary><b>For Java with Maven</b></summary>

If you are using Maven, add the followings to your `pom.xml`:
If you are using Maven, add the followings to your `pom.xml`:

The dependency:
1. The dependency:

``` xml
<dependency>
<groupId>io.github.erdos</groupId>
<artifactId>stencil-core</artifactId>
<version>0.6.0</version>
<version>0.6.1</version>
</dependency>
```

And the [Clojars](https://clojars.org) repository:
2. And the [Clojars](https://clojars.org) repository:

``` xml
<repository>
<id>clojars.org</id>
<url>https://repo.clojars.org</url>
</repository>
```
```
</details>

Alternatively, if you are using Leiningen, add the following to
the `:dependencies` section of your `project.clj`
file: `[io.github.erdos/stencil-core "0.6.0"]`
<details>
<summary><b>For Java with Gradle</b></summary>

Add to the `dependencies` section of your `build.gradle` file: `implementation('io.github.erdos/stencil-core:0.6.1')`
</details>

<details>
<summary><b>For Clojure with Leiningen</b></summary>

If you are using Leiningen, add the following to the `:dependencies` section of your `project.clj` file:

`[io.github.erdos/stencil-core "0.6.1"]`
</details>

<details>
<summary><b>For Clojure with deps.edn</b></summary>

Add `io.github.erdos/stencil-core {:mvn/version "0.6.1"}`
</details>

Previous versions are available on the [Stencil Clojars](https://clojars.org/io.github.erdos/stencil-core) page.


## License
## 😎 License

Copyright (c) Janos Erdos. All rights reserved. The use and distribution terms
for this software are covered by the Eclipse Public License 2.0
Expand Down
20 changes: 19 additions & 1 deletion build.clj
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(ns build
(:require [clojure.tools.build.api :as b]
(:require [clojure.java.io :as io]
[clojure.tools.build.api :as b]
[clojure.tools.build.util.file :as file]))

(def build-folder "target")
Expand Down Expand Up @@ -29,6 +30,14 @@
:javac-opts ["-source" "8" "-target" "8"]})
(b/copy-file {:src "java-src/io/github/erdos/stencil/standalone/help.txt"
:target "target/classes/io/github/erdos/stencil/standalone/help.txt"})

;; generate service provider config for function providers
(let [services (io/file "target/classes/META-INF/services/io.github.erdos.stencil.functions.FunctionsProvider")]
(io/make-parents services)
(doseq [f (file-seq (clojure.java.io/file jar-content))
[_ a] (re-seq #"^target/classes/(.*\$Provider).class$" (str f))
:let [clazz (str (.replace a "/" ".") "\n")]]
(spit services, clazz :append true)))
(spit (str jar-content "/stencil-version") version)
opts)

Expand Down Expand Up @@ -76,6 +85,15 @@
(println "Built JAR file")
opts)

(defn install [opts]
(jar opts)
(b/install {:basis basis
:lib lib
:version version
:jar-file jar-file-name
:class-dir jar-content})
opts)

(defn java-test [_]
(def basis (b/create-basis {:project "deps.edn" :aliases [:junit]}))

Expand Down
2 changes: 1 addition & 1 deletion deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
org.slf4j/slf4j-api {:mvn/version "2.0.9"}}
:paths ["src" "target/classes"]
:aliases
{:stencil/version "0.6.1-SNAPSHOT"
{:stencil/version "0.6.2-SNAPSHOT"

:build
{:deps {org.clojure/clojure {:mvn/version "1.12.0-beta1"}
Expand Down
11 changes: 10 additions & 1 deletion docs/Functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ Expects one number argument containing a list with numbers. Sums up the numbers

## Custom functions

You can register custom implementations of `io.github.erdos.stencil.functions.Function` or the `stencil.functions/call-fn` multimethod.
You can register custom implementations of `io.github.erdos.stencil.functions.Function` or the `stencil.functions/call-fn` multimethod.
If you implement the `call-fn` multimethod, the namespace containing these implementations should be loaded before rendering a document.
(Keep in mind, that `call-fn` implementations always have priority over `io.github.erdos.stencil.functions.Function` implementations)

Clojure example:

Expand Down Expand Up @@ -271,3 +273,10 @@ public class FirstFuncion implements Function {

API.render(preparedTemplate, fragments, data, Arrays.asList(new FirstFunction()));
```


### Automatic registration of custom functions

Stencil uses the JVM's [ServiceLoader](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) facility to load function provider implementations.
If you want to register your custom functions automatically, implement the `io.github.erdos.stencil.functions.FunctionsProvider` interface,
and add these implementations to your extension library's `META-INF/services/io.github.erdos.stencil.functions.FunctionsProvider` file.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.erdos.stencil.functions;

import java.util.Arrays;
import java.util.Collection;

/**
Expand Down Expand Up @@ -72,4 +73,11 @@ public Object call(Object... arguments) {
public String getName() {
return name().toLowerCase();
}

public static class Provider implements FunctionsProvider {
@Override
public Iterable<Function> functions() {
return Arrays.asList(values());
}
}
}
7 changes: 7 additions & 0 deletions java-src/io/github/erdos/stencil/functions/DateFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,11 @@ private static Optional<LocalDateTime> maybeLocalDateTime(Object obj) {
public String getName() {
return name().toLowerCase();
}

public static class Provider implements FunctionsProvider {
@Override
public Iterable<Function> functions() {
return asList(values());
}
}
}
31 changes: 21 additions & 10 deletions java-src/io/github/erdos/stencil/functions/FunctionEvaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,49 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

public final class FunctionEvaluator {
private final static Map<String, Function> preloaded;

private final Map<String, Function> functions = new HashMap<>();
static {
preloaded = new HashMap<>();
for (FunctionsProvider provider : ServiceLoader.load(FunctionsProvider.class)) {
for (Function fn : provider.functions()) {
registerFunction(preloaded, fn);
}
}
}

private final Map<String, Function> functions;

{
registerFunctions(BasicFunctions.values());
registerFunctions(StringFunctions.values());
registerFunctions(NumberFunctions.values());
registerFunctions(DateFunctions.values());
registerFunctions(LocaleFunctions.values());
this.functions = new HashMap<>(preloaded);
}

private void registerFunction(Function function) {
private static void registerFunction(Map<String, Function> map, Function function) {
if (function == null)
throw new IllegalArgumentException("Registered function must not be null.");
functions.put(function.getName().toLowerCase(), function);
Function present = map.put(function.getName().toLowerCase(), function);
if (present != null)
throw new IllegalArgumentException("Function with name has already been registered.");
}

/**
* Registers a function to this evaluator engine.
* Registered functions can be invoked from inside template files.
*
* @param functions any number of function instances.
* @param functions list of functions to register
*/

@SuppressWarnings("WeakerAccess")
public void registerFunctions(Function... functions) {
for (Function function : functions) {
registerFunction(function);
registerFunction(this.functions, function);
}
}


/**
* Calls a function by name.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.github.erdos.stencil.functions;

public interface FunctionsProvider {
Iterable<Function> functions();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.erdos.stencil.functions;

import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Locale;

import static java.util.Locale.forLanguageTag;
Expand Down Expand Up @@ -53,4 +54,11 @@ private static String formatting(Function function, java.util.function.Function<
public String getName() {
return name().toLowerCase();
}

public static class Provider implements FunctionsProvider {
@Override
public Iterable<Function> functions() {
return Arrays.asList(values());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.erdos.stencil.functions;

import java.util.Arrays;

/**
* Common numeric functions.
*/
Expand Down Expand Up @@ -73,4 +75,11 @@ private static Number maybeNumber(Object... arguments) {
return (Number) arguments[0];
}
}

public static class Provider implements FunctionsProvider {
@Override
public Iterable<Function> functions() {
return Arrays.asList(values());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,11 @@ public Object call(Object... arguments) throws IllegalArgumentException {
public String getName() {
return name().toLowerCase();
}

public static class Provider implements FunctionsProvider {
@Override
public Iterable<Function> functions() {
return Arrays.asList(values());
}
}
}
8 changes: 3 additions & 5 deletions service/Dockerfile.graalvm
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ COPY . /app
WORKDIR /app

RUN mv "$(lein uberjar | sed -n 's/^Created \(.*standalone\.jar\)/\1/p')" app-standalone.jar

FROM ghcr.io/graalvm/graalvm-ce:java8-21.1.0 AS BASE

RUN gu install native-image
FROM container-registry.oracle.com/graalvm/native-image:22 AS BASE

COPY --from=CLJ /app/app-standalone.jar /app/app-standalone.jar
WORKDIR /app
Expand All @@ -32,10 +29,11 @@ COPY --from=BASE /lib64/libdl.so.2 /lib64/libdl.so.2
COPY --from=BASE /lib64/libpthread.so.0 /lib64/libpthread.so.0
COPY --from=BASE /lib64/libz.so.1 /lib64/libz.so.1
COPY --from=BASE /lib64/librt.so.1 /lib64/librt.so.1
COPY --from=BASE /lib64/libcrypt.so.1 /lib64/libcrypt.so.1
COPY --from=BASE /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2
# COPY --from=BASE /lib64/libfreebl3.so /lib64/libfreebl3.so

COPY --from=BASE /app/app-standalone /

CMD ["/app-standalone"]

EXPOSE 8080
4 changes: 2 additions & 2 deletions service/project.clj
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
(defproject io.github.erdos/stencil-service "0.6.1-SNAPSHOT"
(defproject io.github.erdos/stencil-service "0.6.2-SNAPSHOT"
:description "Web service for the Stencil templating engine"
:url "https://github.com/erdos/stencil"
:license {:name "Eclipse Public License - v 2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.11.1"]
[io.github.erdos/stencil-core "0.6.1-SNAPSHOT"]
[io.github.erdos/stencil-core "0.6.2-SNAPSHOT"]
[org.slf4j/slf4j-api "2.0.9"]
[org.mozilla/rhino-engine "1.7.14"]
[http-kit "2.7.0"]
Expand Down
Loading

0 comments on commit 0d620f4

Please sign in to comment.