Git Product home page Git Product logo

rocker's Introduction

Rocker Templates by Fizzed

Maven Central

Java 8 Java 11 Java 17 Java 21

Overview

Rocker is a Java 8+ optimized, near zero-copy rendering, speedy template engine that produces statically typed, plain java object templates that are compiled along with the rest of your project. No more "warm-up" time in production, slow reflection-based logic, or nasty surprises that should have been caught during development.

Write your templates using an intuitive, tagless syntax with standard Java expressions for logic, iteration, and values. Use Rocker's special ? presence operator for null-safe evaluation. All the heavy lifting is done by the Rocker parser during development -- which keeps the runtime dependencies down to just a handful of classes. Rocker will parse your templates and generate well-documented Java source files (so you can easily inspect and understand how it works).

Includes the following features:

  • Templates are runtime compatible with Java 8+ -- using Lambdas and type inference under-the-hood
  • Near zero-copy rendering
  • Hot reload support in two flavors
  • Elegant, intuitive, tagless syntax that infers when your logic ends for control / dynamic content. All dynamic / control code uses standard Java syntax.
  • A special ? presence operator extends syntax for simplified handling of null values.
  • Parsed templates become normal POJOs with defined arguments -- allowing you to tap into your IDEs code completion, syntax highlighting, etc.
  • Support for injecting intermediate application-specific super classes during parsing & generating phase -- thereby creating your own app-specific template engine where you can make implicit variables/methods available to all templates.
  • Since templates are just Java classes -- your logic / dynamic content can call out to any other Java code. Your templates can be as advanced or as simple as you need. No reflection used.
  • No runtime configuration/engine required -- there isn't any sort of RockerEngine class required to execute templates. Each compiled template is ready-to-go and knows how to render itself.
  • Templates retain enough information about the original template to throw exceptions at runtime (during render()) that let you track down the problematic line in the original template source file.

Sponsorship & Support

Project by Fizzed, Inc. (Follow on Twitter: @fizzed_inc)

Developing and maintaining opensource projects requires significant time. If you find this project useful or need commercial support, we'd love to chat. Drop us an email at [email protected]

Project sponsors may include the following benefits:

  • Priority support (outside of Github)
  • Feature development & roadmap
  • Priority bug fixes
  • Privately hosted continuous integration tests for their unique edge or use cases

Performance

Based on the following template benchmark, Rocker is the clear winner. ~250% faster than Freemarker while also requiring orders-of-magnitude less memory.

Template Comparison

Quick Start

Most templates are used for websites, so here is a quick sample showing how Rocker templates work and can call each other during the rendering process. Create a template containing a common header and footer as well as a placeholder for body content. Create template src/main/java/views/main.rocker.html

@args (String title, RockerBody content)

<html>
    <head>
        <title>@title</title>
    </head>
    <body>
    @content
    </body>
</html>

The template we actually plan on showing to a user will render its content within the context of the common/header footer. In Java terms, it's passing a block of rendering code to be executed within another template. Create template src/main/java/views/index.rocker.html

@args (String message)

@views.main.template("Home") -> {
    <h1>Hello @message!</h1>
}

Hey, what about the RockerBody content argument? We cover it in more detail in the syntax readme, but for now just understand that its the only special type of argument and instructs Rocker that a template expects a "body" to be passed to it.

The Rocker parser will generate a Java source file for each template. They will be target/generated-sources/rocker/views/main.java and target/generated-sources/rocker/views/index.java. In your application, you can render the index template like so.

static public void main(String[] args) {

    String output = views.index.template("World")
        .render()
        .toString();

}

The output will equal:

<html>
    <head>
        <title>Home</title>
    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>

Once you generate the Java sources and peek inside the code, it's simple to see how this works. The views.index class creates a views.main template instance and hands off rendering to it -- while also passing a block of itself that it will render when views.main calls the @content variable. The syntax is identical to how a lambda is defined in Java 8 (implemented with lambdas for Java 8 and anonymous inner classes for Java 6/7). Rocker does a number of things behind the scenes to make sure templates that create other templates share the same rendering context (output buffer, application-specific context/implicit state).

Syntax

Checkout the SYNTAX.md file for a comprehensive deep dive on the rocker syntax.

Framework integrations

Rocker has a growing list of frameworks that it has been seamlessly integrated with. If you want to link to a new framework added, please file an issue or submit a PR:

Near zero-copy rendering

Static (plain text) for each Rocker template is (by default) stored internally as static byte arrays already converted into your target charset (e.g. UTF-8). When a template is rendered -- the static byte arrays are reused across all requests. Rocker renders to an optimized output stream that stores a composite (linked list) view of the reused byte arrays plus your dynamic content. Since templates consist mostly of static content rendered into the same charset over and over again, rather than allocating new memory, copying that content, and then converting it into your target charset for each request -- Rocker simply uses a pointer to it over and over again. This technique produces fast and memory efficient renders.

Let's say you have a template consisting of 9000 bytes of plain static text and 1000 bytes of dynamic content. Without this optimization, it would require ~100MB of memory to service 10000 requests (10000 bytes x 10000 requests). With this optimization, it would require ~10MB of memory to service 10000 requests (1000 bytes x 10000 requests). Besides lower memory, you also cut out 90MB of memory copies and 90MB of UTF-8 String->byte conversions. A pretty useful optimization.

No reflection

Everything is compiled by your project's compiler along with your other Java source code. Any dynamic code in your template is ultimately converted into standard Java and compiled. No reflection used.

Hot reloading

Version 0.10.0 introduced support for hot reloading templates during development. Hot reloading allows you to modify the template source code, save it, and have the changes active on the next request -- without having to restart your JVM. Rocker offers two different flavors of hot reloading for flexibility.

Flavor 1: static interface, dynamic rendering

The major feature of Rocker templates is that your templates are compile-time checked for usage, arguments, logic, etc. by the Java compiler.

In version 0.10.0 the underlying structure of a template was modified where a template generates two underlying classes. Each template generates a model class (its interface) and an implementation class (its renderer). Your application will only interact directly with the model, therefore allowing Rocker to dynamically recompile and reload the implementation class.

The major benefit of flavor one is that your application code remains the same and is compile-time checked by the Java compiler, while the template content can be modified and automatically reloaded at runtime. Only in the case where you actually change the template arguments, will you need to restart your application.

Flavor 2: dynamic interface, dynamic rendering

If you prefer the convenience of fully dynamic templates, flavor two supports hot reloading of both the template model class (its interface) as well as the implementation class (its renderer). Your application will lose some of the compile-time checking and a small performance hit, but gain the convenience of everything being reloadable. The way your application will use templates is different as well.

import com.fizzed.rocker.Rocker

...

// dynamic interfaces, dynamic implementation
String rendered = Rocker.template("views/index.rocker.html")
    .bind("val", "ValueA")
    .render()
    .toString();

The template path and arguments will be runtime-checked. Please note that each bindable value must match the name and type declared in your template.

In case your bindable map may contain more values that than the required ones a relaxed bind is available. The relaxed alternative will not fail rendering if an attribute is extra to the required list. For example:

@args (String name)
Hello ${name}!

Will render in relaxed mode as:

Map map = new HashMap();
map.put("name", "Joe");
map.put("age", 42);

Rocker.template("views/hello.rocker.html")
    .relaxedBind(map)
    .render();
// -> Hello Joe!

Activate hot reloading

Support for hot reloading is added to your generated templates by default in version 0.10.0. If you'd like to disable support, set the configuration/system property rocker.optimize to true during your build. Since the code is present in your templates by default, you merely need to turn it on at runtime.

Add dependency

The rocker-compiler dependency needs to be added to your build. This dependency only needs to be present during development and can be removed in production. In Maven, this means you'll want to add the dependency in the provided scope.

<dependency>
    <groupId>com.fizzed</groupId>
    <artifactId>rocker-compiler</artifactId>
    <version>1.3.0</version>
    <scope>provided</scope>
</dependency>

Enable at runtime

Activate hot reloading at runtime. You can activate hot reloading either with a system property or programmatically. For activating hot reloading with a system property in maven.

mvn -Drocker.reloading=true ...rest of args...

Alternatively, you can activate hot reloading programmatically.

import com.fizzed.rocker.runtime.RockerRuntime

...

RockerRuntime.getInstance().setReloading(true);

Try out hot reloading

There is a simple example demonstrating hot reload in action. This project uses Blaze to help script tasks. Run the following

java -jar blaze.jar hot_reload

Point your browser to http://localhost:8080

Then modify & save rocker-test-reload/src/test/java/views/index.rocker.html and refresh your browser.

Getting started

Rocker consists of two components - the parser/generator and the runtime. To use Rocker in your project, add the runtime dependency to your application, then enable the parser in your build tool followed by creating your first template.

Add dependency

Rocker is published to Maven central. To add as a dependency in Maven:

<dependency>
    <groupId>com.fizzed</groupId>
    <artifactId>rocker-runtime</artifactId>
    <version>1.3.0</version>
</dependency>

<!-- for hot-reloading support only during development -->
<dependency>
    <groupId>com.fizzed</groupId>
    <artifactId>rocker-compiler</artifactId>
    <version>1.3.0</version>
    <scope>provided</scope>
</dependency>

To add as a dependency in Gradle:

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'com.fizzed', name: 'rocker-runtime', version: '1.3.0'
    // add rocker-compiler dependency as needed
}

Integrate parser/generator in build tool

Rocker supports Maven and Gradle out-of-the box.

Maven

Add the following to your pom

<build>
    <plugins>
        <plugin>
            <groupId>com.fizzed</groupId>
            <artifactId>rocker-maven-plugin</artifactId>
            <version>1.3.0</version>
            <executions>
                <execution>
                    <id>generate-rocker-templates</id>
                    <phase>generate-sources</phase>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

By default, Rocker will recursively process any template files ending with .rocker.html in src/main/java. The directory the template is saved will become the standard Java package the generated Java classes will be placed into. The generated Java source files will be saved to target/generated-sources/rocker. The plugin will take care of adding this generated directory to your sources root.

The following properties are supported:

  • templateDirectory is the base directory to recursively start from when locating and parsing template files. The Java package a template will be generated to will use this directory as its base. So if you have ${templateDirectory}/views/mytemplate.rocker.html then Rocker will generate ${outputDirectory}/views/mytemplate.java. Defaults to ${project.build.sourceDirectory}.

  • outputDirectory is the directory the parser will generate sources for templates. Defaults to ${project.build.directory}/generated-sources/rocker

  • classDirectory is the directory the hot reload feature will (re)compile classes to at runtime. Defaults to ${project.build.outputDirectory}

  • failOnError determines whether any parsing/generating errors cause Maven to fail. Defaults to true.

  • skip determines whether execution of the plugin should be skipped. Defaults to false.

  • touchFile is the file to "touch" after successfully generating Java sources. Useful for triggering other workflow. Many IDEs will not automatically reload generated sources for code completion unless either explicitly told to reload OR if the maven pom.xml file is changed. Thus, this value is by default set to ${basedir}/pom.xml. It's usually harmless to keep this enabled.

  • skipTouch disables touchFile. Defaults to false.

  • addAsSources will add the outputDirectory to maven as sources to be compiled. Defaults to true.

  • addAsTestSources will adds the outputDirectory to maven as test sources to be compiled. Defaults to false. If true, this is evaluated before addAsSources and effectively tells maven to compile your templates as test code.

The following properties are also supported, but it's important to understand these are essentially passthrough overrides to the parser and they all default to Rocker's default value.

  • javaVersion is the Java version you'd like your templates compile & runtime compatible with. Defaults to the Java version of the JVM executing maven (e.g. "1.8").

  • optimize determines if hot reloading support will be removed from the generated templates. False by default.

  • extendsClass is the class that all template implementations should extend. Useful for application-specific intermediate classes that you'd like all templates to extend. Defaults to Rocker's default.

  • extendsModelClass is the class that all template models should extend. Useful for application-specific intermediate classes that you'd like all template models to extend. Defaults to Rocker's default.

  • discardLogicWhitespace determines whether whitespace in templates that is determined to be only a part of logic/control blocks should be discarded. Helps make rendered content look more professional, while still keeping much of your formatting intact. Defaults to Rocker's default.

  • targetCharset is the target charset for template output. Defaults to Rocker's default.

  • suffixRegex is the regular expression to use to find templates to parse. Defaults to Rocker's default.

  • markAsGenerated adds a @Generated annotation to the generated classes. The Retention is CLASS so that the annotation can be used by tools that only rely on the class files and not on the source code. Defaults to Rocker's default.

Gradle

Thanks to @victory and @mnlipp for contributing the gradle plugin. @etiennestuder also had an alternative Gradle plugin you may want to consider as well. Rocker's gradle plugin is published to gradle.org. Just add the following to your build script:

plugins {
  id "com.fizzed.rocker" version "1.3.0"
}

sourceSets {
    main {
        rocker {
            srcDir('src/main/java')
        }
    }
}

rocker {
    // (All settings are shown with their defaults)
    // 
    // Skips building templates all together
    skip false
    // Base directory for generated java sources, actual target is sub directory 
    // with the name of the source set. The value is passed through project.file(). 
    outputBaseDirectory = "$buildDir/generated-src/rocker"
    // Base directory for the directory where the hot reload feature 
    // will (re)compile classes to at runtime (and where `rocker-compiler.conf`
    // is generated, which is used by RockerRuntime.getInstance().setReloading(true)).
    // The actual target is a sub directory with the name of the source set. 
    // The value is passed through project.file().
    classBaseDirectory = "$buildDir/classes"
    failOnError true
    skipTouch true
    // must not be empty when skipTouch is equal to false
    touchFile ""
    javaVersion '1.8'
    extendsClass null
    extendsModelClass null
    optimize null
    discardLogicWhitespace null
    targetCharset null
    suffixRegex null
    postProcessing null
    markAsGenerated null
}

Create first template

The template syntax is described in detail below, but for now create a new file in ${templateDirectory}/views/HelloWorld.rocker.html

@*
 Example of hello world
*@
@args (String message)

Hello @message!

Use compiled template

Time to compile your project and starting using the template. You can call it from java like so:

static public void main(String[] args) {

    String output = views.HelloWorld
        .template("World")
        .render()
        .toString();

}

Use optimized output

Rocker is heavily optimized (by default) to output templates as byte arrays. The default RockerOutput a template will render to is of the type com.fizzed.rocker.runtime.ArrayOfByteArraysOutput. This is an excellent choice for byte arrays or asynchronous IO. However, the framework has the capability for optimized rendering to Strings (or other custom outputs).

To efficiently render to a String:

import com.fizzed.rocker.runtime.StringBuilderOutput;

static public void main(String[] args) {

    StringBuilderOutput output = views.HelloWorld
        .template("World")
        .render(StringBuilderOutput.FACTORY);

    String text = output.toString();

}

To efficiently render to an OutputStream:

import com.fizzed.rocker.runtime.OutputStreamOutput;

static public void main(String[] args) throws Exception {

    final OutputStream os = new FileOutputStream(new File("test"));

    OutputStreamOutput output = views.HelloWorld
        .template("World")
        .render((contentType, charsetName) -> new OutputStreamOutput(contentType, os, charsetName));

}

Please note that if there is an exception during the render the OutputStream would have a partial template rendered (up to the point of the exception). In most cases it would be better to render to the default com.fizzed.rocker.runtime.ArrayOfByteArraysOutput and write its buffer of byte arrays out directly to your OutputStream.

Other demos?

There are numerous demos of Rocker in action. From parsing templates into a model to asynchronously sending results in an HTTP server. This project uses Blaze to help script tasks. Run the following for a complete list:

java -jar blaze.jar -l

License

Copyright (C) 2015+ Fizzed, Inc.

This work is licensed under the Apache License, Version 2.0. See LICENSE for details.

rocker's People

Contributors

bendem avatar breskeby avatar dependabot[bot] avatar drauf avatar igorbolic avatar jamesbarnett91 avatar jameskleeh avatar jfendler avatar jjlauer avatar jonmort avatar jshinobi avatar ldaley avatar mark-vieira avatar mnlipp avatar mreuvers avatar nedtwigg avatar pmlopes avatar timyates avatar victory avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rocker's Issues

@?xyz is not null-safe when type of xyz is RockerContent

Having this:

@args (RockerContent head)
...
@?head

will blow up at runtime if head is null.

I tracked it down to the invocation of:

__internal.renderValue(head, true);

This method implementation does not look at the nullSafe parameter but always calls render on c, even if c is null.

com.fizzed.rocker.runtime.DefaultRockerTemplate.Internal#renderValue(com.fizzed.rocker.RockerContent, boolean)
public void renderValue(RockerContent c, boolean nullSafe) throws RenderingException, IOException {
            // delegating rendering this chunk of content to itself
            c.render();
}

Named arguments when a template calls another template

I'm converting a bunch of Play 1 templates to Rocker. I love Rocker's typesafety, performance, and simplicity, but I wish that I could pass named arguments, rather than positional arguments. Here's an example:

Delegating to a subtemplate in play 1:
#{misc.lib title: 'JScriptBox',
	desc:	'Make your scripting API language-independent',
	img:	'https://github.com/diffplug/jscriptbox/raw/master/jscriptbox.png',
	github:	'https://github.com/diffplug/jscriptbox',
	tags:	['utility', 'javascript'] /}

The same delegation in Rocker:
@opensourcelib.template(
	"JScriptBox",
	"Make your scripting API language-independent",
	"https://github.com/diffplug/jscriptbox/raw/master/jscriptbox.png",
	"https://github.com/diffplug/jscriptbox",
	Arrays.asList("utility", "javascript"))

I think named arguments would help the readability a lot. It's even worse for templates that take a single boolean parameter, e.g.

Play 1: #{navbar isDiscourse: false /}
Rocker: @navbar.template(false)

In java, I would always write the code above like this, which I can't do in a template setting.

boolean isDiscourse = false;
navbar.template(isDiscourse);

A big part of what's so great about rocker is that it just reuses Java rather reinventing the wheel, so perhaps named arguments are too much reinventing. But for every case where I have a template calling another template, I wish that I could pass named arguments. Might be worth a special case in the parser.

Omit LAST_MODIFIED static header in templates when optimized is true

When using the optimized option in the Rocker compiler the hot reloading feature is disabled. Since the LAST_MODIFIED field in the compiled templates is only used for this purpose it should be omitted when this option is used. The use case here is that we want to cache these compiled templates and reuse the outputs in conjunction with Gradle build cache. Having different outputs for the same inputs breaks this capability. Currently we are manually stripping these fields out.

Readme: RockerRuntime.setReloading(..) does not exist

Within the hot reloading section of the readme, the following line of code is shown:

RockerRuntime.setReloading(true);

However, this does not compile because RockerRuntime has no static setReloading(..) method.

Maybe it should be RockerRuntime.getInstance().setReloading(true)?

Single letter filename not supported

N.rocker.layout will break rocker's maven plugin.

Failed to execute goal com.fizzed:rocker-maven-plugin:0.16.0:generate (generate-rocker-templates) on project server: Invalid template name format (unable find first dot character) -> [Help 1]

invalid comment abbrevation of escaped unicode

static public class Template ....LavinaRockerTemplate {
// \u042F \u043F\u043E\u043C\u043D\u044E \u0447\u0443\u0434\u043D\u043E\u0435 \u043C\u0433\u043D\u043E\u0432\u0435\u043D\u044C\u0435: \u041F\u0435\u0440\u0435\u0434\u043E \u043C\u043D\u043E\u0439 \u044F\u0432\u0438\u043B\u0430\u0441\u044C \u0442\u044B, \u041A\u0430\u043A \u043C\u0438\u043C\u043E\u043B\u0435\u0442\u043D\u043E\u0435 \u0432\u0438\u0434\u0435\u043D\u044C\u0435, \u041A\u0430\u043A \u0433\u0435\u043D\u0438\u0439 \u0447\u0438\u0441\u0442\u043E\u0439 \u043A\u0440\u0430\u0441\u043E\u044...
37 static private final byte[] PLAIN_TEXT_0_0;
38
39 static {
40 PlainTextUnloadedClassLoader loader = PlainTextUnloadedClassLoader.tryLoad(welcomeText.class.getName() + "$PlainText", "UTF-8");
41 PLAIN_TEXT_0_0 = loader.tryGet("PLAIN_TEXT_0_0");

com.fizzed.rocker.runtime.CompileDiagnosticException: Unable to compile rocker template(s) with 1 errors.
[ERROR] .../layouts/welcomeText.rocker.html
java: .../layouts/welcomeText.java:[36,509] illegal unicode escape

the method StringUtils.abbreviate in JavaGenerator should go before escaping,
or the better way is not to escape unicode at all, because unicode symbols will already be in the source file, and it does not make difference whether you would it escape in comment,
but it does make difference when you debug template in your own language for understanding what's going on.

Btw, thank you for your great engine.
From my expirience ninja + statically typed, tagless view - really enjoyable thing

No @raw mode?

Would it be feasible to set a configuration flag that explicitly prevents @raw from being used? Should be opt-in. Desired behavior would be that @raw throws a compile time exception if this mode is enabled.

The motivation is to prevent XSS from accidentally happening. I think in most cases Rocker partials could be used to render desired content.

Add @SuppressWarnings("unused") to generated code

I always want my projects to be "warning free" because usually warnings do indicate some kind of problem.

When using rocker, I get warnings because of unused imports for simple templates. I can exclude the directory with rocker generated source from the check, of course. But it would also be very simple to fix this without user intervention by adding a @SuppressWarnings("unused") to the generated code, e.g.:

/*
 * Auto generated code to render template org/jgrapes/http/demo/httpserver/SubmitResult.rocker.html
 * Do not edit this file. Changes will eventually be overwritten by Rocker parser!
 */
@SuppressWarnings("unused")
public class SubmitResult extends com.fizzed.rocker.runtime.DefaultRockerModel {
}

Informative toString() for DefaultRockerModel.

The current implementation of DefaultRockerModel#toString() throws an exception. This is generally suboptimal practice as toString() can be called in surprising circumstances.

I propose to submit a patch that does the following:

  1. Adds Map<String, ?> getArguments() to RockerModel
  2. Update JavaGenerator to implement this method, that constructs such a map on each invocation (i.e. no caching)
  3. Change DefaultRockerModel#toString() to emit something like:
TemplateClassSimpleName[arg1=value1,arg2=value2]

This would be much more useful in exception messages and debugger UIs.

Compiled classes have missing package declaration.

Compiled templates create java files that lacks the package declaration on the first line, this causes issues on several IDE's and compile environments.

At least a property to enable package inclusion/exclusion should be added to fit all project needs.

// <-- Package with the same path as outputDirectory should go here!!!

import java.io.IOException;
import com.fizzed.rocker.ForIterator;
import com.fizzed.rocker.RenderingException;
import com.fizzed.rocker.RockerContent;
import com.fizzed.rocker.RockerOutput;
import com.fizzed.rocker.runtime.DefaultRockerTemplate;
import com.fizzed.rocker.runtime.PlainTextUnloadedClassLoader;

/*
 * Auto generated code to render template /TestTempalte.rocker.html
 * Do not edit this file. Changes will eventually be overwritten by Rocker parser!
 */
public class TestTempalte extends com.fizzed.rocker.runtime.DefaultRockerModel {

...

"else if" support

I like the idea of being able to write it as elif, but at the moment, neither else if not else @if works.

@args is messy

On static templates passing all params on template(arg1, arg2, arg3 ...) can get really really messy when dealing with complex templates since quickly you have to check which param corresponds to a @tag.
A much better approach would be to use a map of strings to assign values to the corresponding tag and pass the map as the template argument.

HashMap<String, String> hmap = new HashMap<String, String>();

hmap.put("name", "fizzed");
hmap.put("last", "rocker");
hmap.put("age", "25");

String result = MyTemplate.template(hmap).render().toString();

Typesafe API for using ArrayOfByteArraysOutput

IMO, Rocker's best feature is that it exposes template output as chunks of byte[] and reuses constant components. However, there's no straightforward way to leverage this. To this, you need to cast and make some assumptions about internals.

The RockerModel class provides:

RockerOutput render() throws RenderingException;

RockerOutput doesn't expose getArrays() of ArrayOfByteArraysOutput. The only usage option I can see is to cast the result of render() to ArrayOfByteArraysOutput, but it's not clear when this is not safe.

What's the plan here?

Consider adding a Spring Boot example

Let's get rid of shitty Thymeleaf in Spring boot.

Spring guys have already created an issue for that.

I guess that many people would use Rocker but they just don't know how to integrate it.

Discarding whitespace from templates

Hi,

when creating HTML templates, I'm using indentation, empty lines, etc. quite generously to help with the readability during development. However, such is not needed (and unnecessarily blows up the resulting page) in production.

What would be nice is if Rocker could be configured (comparable to the "discardLogicWhitespace" option) to automatically discard all (or some) whitespace, including but not necessarily limited to,

  • empty lines
  • spaces and/or tabs at the beginning and end of every line
  • sets of more than one space/tab within lines

One simple approach would be to simply add code for this into PlainText.unescape(), but at least for the items above special cases such as "pre" elements or "CDATA" sections should probably not receive such treatment and would have to be parsed separately. There might be other cases where the original strings should be left unaltered which I can't think of right now.
Also, for non-HTML templates this might be an issue, but then again it should probably be configurable on/off on a per-template basis anyway.

What do you think? Or is there any other (preferable) solution to this in your opinion? (I'm using Rocker within Ninja applications, but I don't think messing with the Result renderables there would be the right approach either.)

Change the semantics of the @ operator, to reduce nullpointerexceptions

I would prefer it, if null values would not cause NullPointerExceptions, as it makes data modelling rather annoying. My setup sometimes contains empty values, and I find the proposed way of @Optional.ofNullable rather cumbersome and redundant. My current workaround is a method toString inside my own default RockerTemplate which can take a potentially null value and returns either "" or a default value that was passed along with it.

There could maybe be a both way solution with a maven config?

Great work

Hello, I'm using ninja framwork with rythm engine

I'm a big fan of .net razor syntax, as it is clear and short and very easy to use.
I'm using rythm engine as it is quite the same as razor.

I'm very interested in rocker, except for one thing, I definitely can't miss the hot reloading feature. I understand why you didn't made it, but I'm convinced that this is a must have, especially for our designers.

I'm not an expert in this domain, but I believe that even if the class loader and compile coding is horrible inside rythm code, they have found a good way to do it.

The big plus of your approach, is that everything is compiled into a strongly typed class, which is a very good point.

Maybe a good way to implement hot reloading is to break the strongly typed access to views in the controllers, and fall back to something more classic like.

What I try to mean is that there will be no strong reference to the generated rocker classes from the application, but only in the other way. This allowing to compile generated java classes without recompiling the whole project, which makes UI developping much much faster...

Well I'm sure you understand what I try to (hardly) explain...

Any way, goog work, keep on going !

Note: not everything in rythm is interesting and should be ported, but I think some of them can be usefull, like master pages (I mean the use of @section and @render), and the ability to create functions (see @def, looks like it is the same as calling another template, but with @def one can put all functions in the same file).

If you're interested, I can have a closer look to rythm which I use every day to determine the must have items missing in rocker - I'm sure it's only a very small number of them.

Whatever you decide, bravo

Clarify which use case each RockerOutputFactory implementation fits

Reading the docs I naively implemented my first templates using MailTemplate.template(args).render().toString(). Reading a bit more between the lines, I realised the default RockerOutputFactory is actually ArrayOfByteArraysOutput (it's also the one mentioned in the README as the "optimised output"). Its toString method is implemented as

@Override
public String toString() {
    // super inneffecient method to convert to string
    // since byte arrays are guaranteed to be split real chars we need to
    // construct the entire array first before doing final convert to string
    byte[] bytes = toByteArray();
    return new String(bytes, this.charset);
}

As the comment says, this is not at all an optimised way to render the template and the documentation should be updated to make this very clear.

I propose to add a section explaining which RockerOutputFactory are available and when to use them. The examples relying on toString should be updated to use StringBuilderOutput.FACTORY (I believe this is the most efficient implementation if you're going to use toString).
The "Use optimized output" should also be updated to precise that it's only optimised if you use asReadableByteChannel.

rocker fails to compile if javascript "else" statements present

this code failed to parse with error:

<script>
    var time = 12;
    if (time < 10) {
        greeting = "Good morning";
    } else if (time < 20) {
        greeting = "Good day";
    } else {
        greeting = "Good evening";
    }
</script>

Parsing failed for
...github/rocker/java6test/src/test/java/rocker/IfElseBlockMixedJavascript.rocker.html:[9,4] rocker/IfElseBlockMixedJavascript.rocker.html:[9,4] extraneous input '} else {' expecting {'{', '}', COMMENT, PLAIN, '@'}

Unfortunatly antlr4 project too complicated for me to fix issue.

Consider compiling rocker with `-Werror`

When digging around, I noticed lots of raw types and other things that the compiler will emit warnings for. Using -Werror would force these to be dealt with.

I'm not sure how to do this with Maven.

Improve Rocker.template() for more complex classloading environments

This method effectively loads templates via: https://github.com/alkemist/rocker/blob/69881f8863d2f3adcebeea6a308b326b588e5f87/runtime/src/main/java/com/fizzed/rocker/runtime/DefaultRockerBootstrap.java#L99-L99

This assumes that the classloader that loaded the Rocker runtime can see the target class.

Two (non mutually exclusive) potential options:

  1. Change DefaultRockerBootstrap.model() to use Thread.currentThread().getContextClassLoader()
  2. Add a Rocker.template() variant that takes a classloader

I think it probably makes sense to do both. In most environments where classloading is complex, the context classloader is set to something with good general visibility. Option (2) is potentially useful for even more complex environments. I'd probably be tempted to just do (1) though, and see if anyone ever needs (2).

Support for fully embedded execution

Currently Rocker only supports working with templates on a classpath; you cannot just pass him a template String, map of variables and say "Render this for me".

Spaces in empty @args list

Rocker is rather picky about any spaces in an empty list of arguments.

This all works:

@args()
@args ()
@args ( String myString )

but this doesn't:

@args( )

and fails with the rather "unintuitive" error message "Invalid java variable: name part never found".

"else if" breaks template includes

The example below generates invalid code. Removing the else if, fixes it.

templates/test/Layout.rocker.html

@args (String title, RockerBody content)

<!DOCTYPE html>
<html>
<head>
    <title>@title</title>
</head>
<body>

    @content

</body>
</html>

templates/test/Bug.rocker.html

@args()

@templates.test.Layout.template("title") -> {
    @if (true) {
        hi
    } else if (true) {
        nope
    }
}

Generated code:

package templates.test;

import java.io.IOException;
import com.fizzed.rocker.ForIterator;
import com.fizzed.rocker.RenderingException;
import com.fizzed.rocker.RockerContent;
import com.fizzed.rocker.RockerOutput;
import com.fizzed.rocker.runtime.DefaultRockerTemplate;
import com.fizzed.rocker.runtime.PlainTextUnloadedClassLoader;

/*
 * Auto generated code to render template templates/test/Bug.rocker.html
 * Do not edit this file. Changes will eventually be overwritten by Rocker parser!
 */
public class Bug extends com.fizzed.rocker.runtime.DefaultRockerModel {

    static public final com.fizzed.rocker.ContentType CONTENT_TYPE = com.fizzed.rocker.ContentType.HTML;
    static public final String TEMPLATE_NAME = "Bug.rocker.html";
    static public final String TEMPLATE_PACKAGE_NAME = "templates.test";
    static public final String HEADER_HASH = "69072368";
    static public final long MODIFIED_AT = 1491381164000L;
    static public final String[] ARGUMENT_NAMES = { };

    static public Bug template() {
        return new Bug();
    }

    @Override
    protected DefaultRockerTemplate buildTemplate() throws RenderingException {
        // optimized for performance (via rocker.optimize flag; no auto reloading)
        return new Template(this);
    }

    static public class Template extends com.fizzed.rocker.runtime.DefaultRockerTemplate {

        //         hi\n
        static private final byte[] PLAIN_TEXT_0_0;
        //         nope\n
        static private final byte[] PLAIN_TEXT_1_0;

        static {
            PlainTextUnloadedClassLoader loader = PlainTextUnloadedClassLoader.tryLoad(Bug.class.getClassLoader(), Bug.class.getName() + "$PlainText", "UTF-8");
            PLAIN_TEXT_0_0 = loader.tryGet("PLAIN_TEXT_0_0");
            PLAIN_TEXT_1_0 = loader.tryGet("PLAIN_TEXT_1_0");
        }

        public Template(Bug model) {
            super(model);
            __internal.setCharset("UTF-8");
            __internal.setContentType(CONTENT_TYPE);
            __internal.setTemplateName(TEMPLATE_NAME);
            __internal.setTemplatePackageName(TEMPLATE_PACKAGE_NAME);
        }

        @Override
        protected void __doRender() throws IOException, RenderingException {
            // ValueClosureBegin @ [3:1]
            __internal.aboutToExecutePosInTemplate(3, 1);
            __internal.renderValue(templates.test.Layout.template("title").__body(() -> {
                // IfBlockBegin @ [4:5]
                __internal.aboutToExecutePosInTemplate(4, 5);
                if (true) {
                    // PlainText @ [4:17]
                    __internal.aboutToExecutePosInTemplate(4, 17);
                    __internal.writeValue(PLAIN_TEXT_0_0);
                    // IfBlockElseIf @ [6:5]
                    __internal.aboutToExecutePosInTemplate(6, 5);
                } else if (true) {
                    // PlainText @ [6:23]
                    __internal.aboutToExecutePosInTemplate(6, 23);
                    __internal.writeValue(PLAIN_TEXT_1_0);
                    // IfBlockEnd @ [4:5]
                    __internal.aboutToExecutePosInTemplate(4, 5);
                } // if end @ [4:5]
                // ValueClosureEnd @ [3:1]
                __internal.aboutToExecutePosInTemplate(3, 1);
            } // value closure end @ [3:1]
        }
    }

    private static class PlainText {

        static private final String PLAIN_TEXT_0_0 = "        hi\n";
        static private final String PLAIN_TEXT_1_0 = "        nope\n";

    }

}

@with limitations, lack of real scoped code blocks and functions

I was wondering if there's a more intuitive or simple approach to declare variables within a templates.
I find the current @with implementation quite limiting, since I want to handle all the templete logic on the templete itself (Without calling custom methods on a separated class)

Currently I'm forced to declare all the variables that my template depends inside a @with

Template:

@with(
  String a = "hello", 
  String b = "world"
) {
  This is an example:
   a: @a
   b: @b
}

Output:

This is an example:
a: hello
b: world

This means that if want to use "a" and "b" on all my template I have to put all my template inside the with curly bracers (Note: I need all logic to be on the template so args wont do the trick)

I think this is inconvenient and restrictive, a different approach would be to allow some sort of scoped code shim that allow arbitrary java code to be written on the template with the scope of that template, in a similar way that other languages like php does it.

for example ( As an example draft)

Template:

@block {
  String a = "hello";
  String b = "world";
}

This is an example:
a: @a
b: @b

Output:

This is an example:
a: hello
b: world

Here @A and @b are within the template scope itself.

Also functions/methods on templates will come in handy for example someone could write a template holding functions and import that template on a different one, currently this behaviour can only be mimic by having each "function" or reusable piece of code as an independent template and importing it with args as parameters when desired

one again as a draft example: (note @1 and @2 acts as param1 and param2 and content blocks are used as pseudo functions, but ideally they should have their own scope and access to the template scope to avoid the need on @with within them)

Template one:

@add => {
  @with(int result = @1 + @2) {
     @result 
   }
}

@subtract => {
  @with(int result = @1 - @2) {
     @result 
   }
}

Template two:

@import resources.one.rocker as templateOne

@block {
 int a = 2;
 int b = 3;
}

@a plus @b is @templateOne.add(@a, @b)
@a minus @b is @templateOne.subtract(@a, @b)

Output:

2 plus 3 is 5
2 minus 3 is -1

Any thoughts about this??? I'll personally would like to develop this in collaboration of your team as long as you think I'ts a good idea, I'm really loving rocker and I believe this is a huge enhancement to its posibilites.

Only DefaultRockerModel templates can be inlined into other templates

This is due to: https://github.com/alkemist/rocker/blob/25e408f0f4d605c25946df53577254e3a3737103/runtime/src/main/java/com/fizzed/rocker/runtime/DefaultRockerTemplate.java#L262-L262

It's not clear to me whether it's a requirement that all RockerModel instances are actually implementations of DefaultRockerModel. If that's the case, it might be worth making this more explicit. Right now, DefaultRockerModel appears to be an internal implementation detail. This isn't actually the case though, as far as I can tell.

Enhance @with by supporting multiple arguments

Hey Joe,

As said, this is a useful enhancement. I intend to add this (creating this issue to track). It will look like this:

@with (String s=list.get(0), List<Long> list=x.getIt()) {
 ...
}

And an equivalent java 8 version. The change will be backwards compatible, so the single argument switch will remains working as is.

There is however one thing I am not sure about, the null-safe with statement.
If multiple arguments are present, and the with statement is tagged with '?'.
What shall we do:

  • Execute the block inside, if all the arguments are non-null
  • Forbid the null-safe with if there's more than 1 argument
  • ...

Example:

@with? (String s=list.get(0), List<Long> list=x.getIt()) {
 ..
}
else {

}

What are your thoughts?

Thank you - not really an issue ;)

This project looks very interesting.

I've been using Google Dagger for Dependency Injection and appreciate the benefits of generating
code over Runtime manupilation and reflection.

I've been hunting for a good template engine and this looks just right.

Thanks for taking the time to write this tool

  • James.

Local variable declareation in templates.

My template like this:

fetch(url).then(function(response) {
    response.json().then(function (@raw(SwaggerUtil.responseTypeName(entryValue.getJsonObject("responses")) == "void" ? "" : data)) {
        resolve(@raw(SwaggerUtil.responseTypeName(entryValue.getJsonObject("responses")) == "void" ? "" : data));
    });
})

But SwaggerUtil.responseTypeName(.. function calling 2 times.
Can I use it like following

@localvar rParam = @SwaggerUtil.responseTypeName(entryValue.getJsonObject("responses"))  == "void" ? "" : data
fetch(url).then(function(response) {
    response.json().then(function (rParam )) {
        resolve(rParam );
    });
})

Render final keyword for @for loops in java source or source may not compile

Hi there,

I have been using Rocker for our new development project as I accidentally found it and like the concepts. Unfortunately I ran into the following issue while trying it out, see the sample code below.

@for (ModelClass modelClass : modelClasses) { @with (List<Method> methodsFound = modelClass.getMethods()) { @if (!methodsFound.isEmpty()) { // Class: @modelClass.getFullyQualifiedName() } } }
The line Class: @modelClass.getFullyQualifiedName() will not compile, because modelClass is rendered as non-final variable in the template java source (java compiler message: "Cannot refer to the non-final local variable modelClass defined in an enclosing scope").

Afaict the easiest way to deal with this is render these assignments with the final keyword always, that would apply to anything where things are assigned like in the different @for loops (and possibly more?).

The above or e.g. class ForStatement needs to deal with an optional 'final' keyword (and probably more different statements).

I am happy to help you fix this, if you tell me what solution you prefer (I know antlr4 quite well too). But only if you want me to do so. If so, I'll make a pull request then.

I hope you can help out as it's blocking our progress.

Add support for Stream::forEach

Adding support for Stream::forEach iteration would a useful feature I think. At this moment, I need to collect the stream into a List<T> to be able to use it with @for :

myStream.collect(Collectors.toList());

IDE support for templates

Hi,

I'm very impressed about the performance of rocker but another thing is important to become a great template engine - IDE support..

Do you have a plugin for any of the major IDEs? Or plans for creating one?

I am an IntelliJ fan and would of course prefer support for that...

Advertised null safe syntax doesn't work

The null safe syntax advertised in the docs doesn't work as expected.

The example shows @?user.getName() which generates __internal.renderValue(user.getName(), true);.

If user is null, that will throw a NPE.

Confusing naming

I've pondered quite some time about the names used and I find them confusing -- at least if you start with the common interpretation.

A model usually manages your data over time. But instances of RockerModel are short lived. After invoking the render method once, they can no longer be used. I've therefore started to call my rocker sources XyzRenderer.rocker.html. This generates XyzRenderer and I can call new XyzRenderer().render(...) which makes sense.

Of course, looking at the examples, the suggested use is XyzRenderer.template(...).render(...). But the template(...) method is not properly named, however you look at it. It returns a RockerModel and should therefore actually be named model(...) (or, if you follow my interpretation renderer(...)). Its current name (template(...)) suggests that it returns an instance of the inner class XyzRenderer.Template, which it certainly does not.

I suppose the name template() was inspired by the use case "including another template". But, at least to me,

@views.MyOther.renderer()

would look very natural (I include another template by calling its renderer, obvious, isn't it).

Maven plugin does not work with Java 9

Hi,

in Vert.x we have integrated the rocker template engine. We are currently testing our builds with Java 9 and we found an issue when running the rocker-maven-plugin with Java 9:

[ERROR] Failed to execute goal com.fizzed:rocker-maven-plugin:0.22.0:generate (generate-rocker-templates) on project vertx-web-templ-rocker: Execution generate-rocker-templates of goal com.fizzed:rocker-maven-plugin:0.22.0:generate failed: begin 0, end 3, length 1 -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal com.fizzed:rocker-maven-plugin:0.22.0:generate (generate-rocker-templates) on project vertx-web-templ-rocker: Execution generate-rocker-templates of goal com.fizzed:rocker-maven-plugin:0.22.0:generate failed: begin 0, end 3, length 1
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:212)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
	at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
	at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
	at org.apache.maven.cli.MavenCli.execute(MavenCli.java:863)
	at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
	at org.apache.maven.cli.MavenCli.main(MavenCli.java:199)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
	at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.PluginExecutionException: Execution generate-rocker-templates of goal com.fizzed:rocker-maven-plugin:0.22.0:generate failed: begin 0, end 3, length 1
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:145)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207)
	... 20 more
Caused by: java.lang.StringIndexOutOfBoundsException: begin 0, end 3, length 1
	at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3116)
	at java.base/java.lang.String.substring(String.java:1885)
	at com.fizzed.rocker.maven.GenerateMojo.execute(GenerateMojo.java:117)
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
	... 21 more

Support for direct interpolation of bytes into template output

My specific use case is that my HTML template is to include a large JSON string. What I'd like to be able to do, is inject this into my template as UTF-8 bytes instead of as a String. This is purely for performance as it will reduce object churn by allowing direct usage of the bytes produced by the JSON encoder I am using (i.e. Jackson) instead of turning the bytes into a String, just to be turned back into bytes.

Besides the syntax/construct used to denote that a value being interpolated into the output should be treated as bytes, I can see that there's a safety issue here of encoding alignment. I don't have any ideas on how to resolve that beyond putting the responsibility of the user of this construct to ensure that the given bytes are encoded using the same output encoding as the template.

Unable to activate Rocker template reloading, when rocker-compiler is present

Hi, thank you for rocker, I have a problem with hot reloading, could you help plz?

Source here - https://github.com/volyx/undertow-rocker
Run with mvn exec:java

[INFO] 
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ undertow-rocker ---
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[WARNING] 
java.lang.RuntimeException: Unable to activate Rocker template reloading. Did you forget to include 'rocker-compiler' as an optional/provided dependency?
	at com.fizzed.rocker.runtime.RockerRuntime.buildReloadingRockerBootstrap(RockerRuntime.java:106)
	at com.fizzed.rocker.runtime.RockerRuntime.setReloading(RockerRuntime.java:67)
	at test.Main.main(Main.java:11)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:282)
	at java.lang.Thread.run(Thread.java:748)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE

support for property expressions

Like @foo where foo can be:

  • public final? field
  • foo() method (no arg, with return type)
  • isFoo() method
  • getFoo() method

To be clear you can write @foo in your template and compiler translate to correct expression.

It add extra work to the rocker compiler but think will be good addition.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.