Integration with Clojure

83 views
Skip to first unread message

bill robertson

unread,
Feb 2, 2016, 5:26:27 PM2/2/16
to mustache.java
Hello,

With a little bit of work, I was able to get Mustache.java to work directly with Clojure data structures. The problem mentioned here (https://groups.google.com/forum/#!topic/mustachejava/e8EjcdYUY3E) was still an issue.

It required a very thin shim so as to override the behavior in IterableCode::handle, basically it checks for Iterable first before checking for functional behavior. This picks up Clojure lists/vectors (which are also Callable) and seqs. The shim code is here: https://github.com/billrobertson42/mustache.clojure/tree/master/java/mustache/clojure/glue

I have some simple tests that exercise conversion of some basic clojure data structures, and they work fine with Clojure maps, lists, vectors, seqs and can also call Clojure functions just fine.

I have two questions.

First, Could the behavior in IterableCode::handle be changed to check for an Iterable interface before a functional interface in Mustache.java? This would eliminate the need for a separate library with shim code for Clojure's sake.

Second, Clojure maps frequently use keywords as map keys. Given the framework of Mustache.java, where would be the best place to translate key lookup in maps so that a user could pass a map with keywords for keys to a be transformed w/o having to deeply inspect and transform the input data to the template?

An example of this is in line 54 of this file. https://github.com/billrobertson42/mustache.clojure/blob/master/test/mustache/core_test.clj

Thanks!



bill robertson

unread,
Feb 2, 2016, 7:52:05 PM2/2/16
to mustache.java
I'm looking at ReflectionObject::findWrapper as a place to start to figure out how to handle keywords.

bill robertson

unread,
Feb 3, 2016, 12:13:05 AM2/3/16
to mustache.java
Keyword maps now work. Although I'm sure there's a better way to integrate it with Mustache.java than what I figured out. If you have time to take a look and provide feedback I'd appreciate it. There's not too much code there.

BTW, I really like Mustache.java. It's highly performant, and I appreciate that it does a good job of implementing the spec. I used it last year in a Clojure project, and ended up performing a deep conversion of data structures to their equivalent java.util counterparts. I wasn't happy with that solution, and it seemed to me that there ought to be a better way.

-Bill

Sam Pullara

unread,
Feb 3, 2016, 3:06:28 AM2/3/16
to mustac...@googlegroups.com
I will definitely check it out. Thanks for the kind words!

Sam

--
You received this message because you are subscribed to the Google Groups "mustache.java" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mustachejava...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Sam

unread,
Feb 4, 2016, 4:04:25 PM2/4/16
to mustache.java
It looks like an OK way to do it. I think that changing the order may regress a customer that expects the current order so I'm going to keep it that way. Feel free in your Clojure version to do what makes sense natively in Clojure.

Sam


On Wednesday, February 3, 2016 at 12:06:28 AM UTC-8, Sam wrote:
I will definitely check it out. Thanks for the kind words!

Sam
To unsubscribe from this group and stop receiving emails from it, send an email to mustachejava+unsubscribe@googlegroups.com.

bill robertson

unread,
Feb 5, 2016, 10:36:20 AM2/5/16
to mustache.java
Thanks for the feedback.

-Bill
To unsubscribe from this group and stop receiving emails from it, send an email to mustachejava...@googlegroups.com.

bill robertson

unread,
Jun 4, 2016, 6:07:08 PM6/4/16
to mustache.java
Hello,

I've been working on making sure that mustache.clojure passes the specs provided by the mustache spec, and with the exception of the whitespace differences in mustache.java it's good, except for one exception. In this case, I'm producing the same result as mustache.java, but it looks like it's not the right answer.

The test is the "Dotted Names - Context Precedence" test from interpolation.yml.  Here's the test for reference.

  - name: Dotted Names - Context Precedence
    desc: Dotted names should be resolved against former resolutions.
    data:
      a: { b: { } }
      b: { c: 'ERROR' }
    template: '{{#a}}{{b.c}}{{/a}}'
    expected: ''

I rewrote my test in Java as a double check to see if I did something incorrect in the clojure test implementation, and it comes up with the same result, which also provides a simple compile-able example, which I will list below.

It ends up printing the following:

Testing template: {{#a}}{{b.c}}{{/a}}
  With data: {"a": {"b": {}}, "b": {"c": "ERROR"}}
result: 'ERROR'

Am I missing something?

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import java.io.File;
import java.io.StringWriter;
import java.nio.file.Files;
import java.util.Map;

public class Foo {
   
    public static String transform(String json, String templateText) throws Exception {
        Files.write(new File("template.mustache").toPath(), templateText.getBytes("UTF-8"));
        Mustache m = new DefaultMustacheFactory(new File(".")).compile("template.mustache");
        Map data = new ObjectMapper().readValue(json, Map.class);
        StringWriter sw = new StringWriter();
        m.execute(sw, data);
        return sw.toString();               
    }
   
    public static void main(String[] args) throws Exception {
        String json = "{\"a\": {\"b\": {}}, \"b\": {\"c\": \"ERROR\"}}";
        String template = "{{#a}}{{b.c}}{{/a}}";
       
        System.out.println("Testing template: "+ template);
        System.out.println("  With data: "+json);
        String output = transform(json, template);
        System.out.println("result: '" + output + "'");
    }
}

Thanks!


On Tuesday, February 2, 2016 at 5:26:27 PM UTC-5, bill robertson wrote:

Sam Pullara

unread,
Jun 4, 2016, 6:50:32 PM6/4/16
to mustac...@googlegroups.com
I'm looking into it. We actually run all the spec tests as part of the build including that one.

Sam Pullara

unread,
Jun 4, 2016, 6:52:49 PM6/4/16
to mustac...@googlegroups.com
My guess is that ObjectMapper does the wrong thing — perhaps returning nothing when you have an empty object? In the spec tests I have a JsonMap that I think properly converts JsonNodes to a Map and doesn't elide nodes. I am going out to dinner but will have time tomorrow to verify what the issue might be but that is my guess.

Sam

Sam Pullara

unread,
Jun 4, 2016, 7:14:41 PM6/4/16
to mustac...@googlegroups.com
I quickly figured out a couple things. It looks like the .json tests that I run from the spec are not complete relative to the .yml files. Certainly a surprise to me. This is interesting. My assumption, which apparently wasn't being tested as this test isn't present in the .json files, was that if the branch was not found completely, it is as if it isn't found and it keeps search the scopes. In this case we have a {a:{b:{}},b:{c:"ERROR"}} and then try to dereference {{#a}}{{b.c}}{{/a}}. It does initially check the a.b segment but because it doesn't reach c, it returns to the higher scope and resolves from b which is successful. For example, if the data is {a:{b:{c:""}},b:{c:"ERROR"}} you get the same result as the test that they are running. I do think their test is correct though and I will have to change the way dot notation is evaluated. It doesn't appear that my implementation is equivalent to {{#a}}{{#b}}{{c}}{{/b}}{{/a}} and I think it should be. Thanks for the bug report. It is the first one in a long time!

Sam

bill robertson

unread,
Jun 5, 2016, 9:37:59 AM6/5/16
to mustache.java
I think I know what you're talking about. I'd speculate that in ReflectionObjectHandler.findWrapper, when it uses map.containsKey. I could see how that could cause it to skip over that scope.

BTW, my tests used Snake Yaml to parse the spec's yaml files.

Cheers

Sam

unread,
Jun 5, 2016, 6:41:28 PM6/5/16
to mustache.java
Just checked in a fix for this issue and also switched over to using the YAML files since it seems like they are the definitive versions. It was a pretty simple fix. Basically if we have made any progress down the dot notation path we need to still generate a wrapper rather than going on to search the next scope.


Thanks for the bug report and I will likely generate a release for this change.

Sam


On Tuesday, February 2, 2016 at 5:26:27 PM UTC-5, bill robertson wrote:

bill robertson

unread,
Jun 5, 2016, 6:45:34 PM6/5/16
to mustache.java
Cool! I'll pick it up.

Thanks

bill robertson

unread,
Jun 13, 2016, 12:45:49 PM6/13/16
to mustache.java
I've released an updated version of mustache.clojure, 0.3.0, that passes all the spec tests with the exception of the same whitespace differences as mustache.java.

https://github.com/billrobertson42/mustache.clojure
Reply all
Reply to author
Forward
0 new messages