babashka

https://github.com/babashka/babashka. Also see #sci, #nbb and #babashka-circleci-builds .
grazfather 2021-02-09T00:28:33.405100Z

the fact that process.clj is only about 250 lines is pretty damn impressive to me

1💯
maxp 2021-02-09T03:31:04.405200Z

I've missed io/writer

2021-02-09T06:13:16.406300Z

2021-02-09T06:13:21.406600Z

@brdloush Ran some benchmarks against the main babashka script which returns the headers, HTML, and CSS

2021-02-09T06:14:50.407100Z

For context this is running within a docker container on my local

2021-02-09T06:29:24.407300Z

Also stored https://github.com/eccentric-j/clj-cgi-example#benchmarks

tengstrand 2021-02-09T08:00:57.415300Z

Good morning old guy (noticed that you @borkdude turned 40)! We will probably rework https://github.com/polyfy/polylith #polylith a bit and decouple it from #tools-deps (the migration to the new version will be automated) but still use tools.deps as the default target "platform". I have used https://freemarker.apache.org before for code generation, which has worked out really well. The only problem is that it doesn't support opening files for writing within the templates themselves. Now I got an idea, and that is to use babashka for that. My idea is to make it mandatory in the next version of Polylith (it's an idea so far, nothing decided yet). Then people could write their own templates and produce whatever output they want, like support for Leingingen, Boot, producing diagrams (like https://www.yworks.com/products/yed), documentation, and so on. Some of these outputs will probably be supported out of the box anyway, but users of Polylith would be able to write their own artefacts also. One option would be that babashka added templating support out of the box (more integrated) or we could put the templating related code in its own library that could be used to script things. Basically you need to read a template into memory (which will be represented as a hash map and then in the script, loop any of the data structures in there and write the output to a file. I have most of the code already, because I did something similar recently in another project. The difference now is that I realized that babashka could do it much more elegant.

borkdude 2021-02-09T08:34:54.416200Z

@tengstrand welcome! You can re-use your templating logic in babashka if you load it as a library perhaps?

borkdude 2021-02-09T08:35:35.416600Z

If you could show some examples, that could be helpful.

borkdude 2021-02-09T08:36:55.417900Z

Another possibility would be that you would make your own custom polylith binary CLI with all the specific libs you need for polylith and use sci for glue code. Basically a spin-off from bb. But let's see if it can be done with bb proper first.

tengstrand 2021-02-09T10:29:48.418Z

I will experiment a bit, and come back to you! Thanks.

borkdude 2021-02-09T12:30:22.418800Z

Welcome again poly @teng

tengstrand 2021-02-09T12:37:06.418900Z

Thanks! Thought I joint the channel already. I think I really need support for https://freemarker.apache.org because I need a really powerful template engine, and that’s the best I have seen so far. The use case is to be able to transform the data structure you get when you read a Polylith workspace into files. I will post two templatefiles that I use in another project to give you an idea.

tengstrand 2021-02-09T12:37:21.419200Z

marcos.ftl:

<#macro playerLink player dir>
  <#local profile = "">
  <#list players as p>
    <#if p.profile?? && p.name == player>
      <#local profile = p.profile>
    </#if>
  </#list>
<#if profile == "">
${player}
<#else>
&lt;a href="${dir}/Spelarprofiler/${profile}"&gt;${player}&lt;/a&gt;&lt;/#if&gt;&lt;/#macro&gt;

&lt;#macro playerLinkAlias alias&gt;
  &lt;#local profile = ""&gt;
  &lt;#list players as p&gt;
    &lt;#if p.profile?? &amp;&amp; p.alias == alias&gt;
      &lt;#local profile = p.profile&gt;
    &lt;/#if&gt;
  &lt;/#list&gt;
&lt;#if profile == ""&gt;
${alias}
<#else>
&lt;a href="Spelarprofiler/${profile}"&gt;${alias}&lt;/a&gt;&lt;/#if&gt;&lt;/#macro&gt;

&lt;#macro round x&gt;
  &lt;#local result = ""&gt;
  &lt;#if x &lt; 0&gt;
     &lt;#local value = -x&gt;
     &lt;#local sign = "-"&gt;
     &lt;#local color = "#A00000"&gt;
  <#else>
     &lt;#local value = x&gt;
     &lt;#local sign = "+"&gt;
     &lt;#local color = "#008000"&gt;
  &lt;/#if&gt;
  &lt;#if x != 0&gt;
&lt;font color="${color}"&gt;${sign}${value?string["0.#"]}&lt;/font&gt;&lt;/#if&gt;&lt;/#macro&gt;

&lt;#macro arrow x&gt;
  &lt;#if x lt 0&gt;
    &lt;#local arrow = "&amp;#10138;"&gt;
    &lt;#local color = "#008000"&gt;
  &lt;#elseif x == 0&gt;
    &lt;#local arrow = "&amp;nbsp;&amp;nbsp;&amp;nbsp;"&gt;
    &lt;#local color = "#000000"&gt;
  <#else>
    &lt;#local arrow = "&amp;#10136;"&gt;
    &lt;#local color = "#A00000"&gt;
  &lt;/#if&gt;
&lt;font color="${color}"&gt;${arrow}&lt;/font&gt;&lt;/#macro&gt;

&lt;#function videolink url&gt;
 &lt;#if url = ""&gt;
  &lt;#return ""&gt;
 <#else>
   &lt;#local str = "&lt;a href='"&gt;
   &lt;#local str += url&gt;
   &lt;#local str += "' target='_blank'&gt;Stream&lt;/a&gt;"&gt;
   &lt;#return str&gt;
 &lt;/#if&gt;
&lt;/#function&gt;

&lt;#function firstRed poangjakt&gt;
  &lt;#local index = 12&gt;
  &lt;#local length = poangjakt.rankinglista?size - 1&gt;
  &lt;#list length..11 as i&gt;
    &lt;#if poangjakt.rankinglista[i].points != poangjakt.rankinglista[11].points&gt;
      &lt;#local index = i&gt;
    &lt;/#if&gt;
  &lt;/#list&gt;
&lt;#return index&gt;
&lt;/#function&gt;

&lt;#function percentclass percent&gt;
  &lt;#if percent gte 80&gt;
    &lt;#return "percent80"&gt;
  &lt;#elseif percent gte 70&gt;
    &lt;#return "percent70"&gt;
  &lt;#elseif percent gte 60&gt;
    &lt;#return "percent60"&gt;
  &lt;#elseif percent gte 50&gt;
    &lt;#return "percent50"&gt;
  &lt;#elseif percent gte 40&gt;
    &lt;#return "percent40"&gt;
  &lt;#elseif percent gte 30&gt;
    &lt;#return "percent30"&gt;
  &lt;#elseif percent gte 20&gt;
    &lt;#return "percent20"&gt;
  &lt;#elseif percent gte 10&gt;
    &lt;#return "percent10"&gt;
  <#else>
    &lt;#return "percent0"&gt;
  &lt;/#if&gt;
&lt;/#function&gt;

tengstrand 2021-02-09T12:38:05.419400Z

ranking.ftl:

&lt;#include "macros.ftl"&gt;

&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;Uppsalarankingen&lt;/title&gt;
    &lt;meta http-equiv="Content-Type" content= "text/html; charset=UTF-8"&gt;
  &lt;/head&gt;
  &lt;body bgcolor="#FFFFFF" text="#000000" link="#000000" alink="#000000" vlink="#000000"&gt;
    &lt;p&gt;&lt;font color="#CCCCCC"&gt;&lt;b&gt;&lt;font size="5" color="#000000"&gt;Uppsalarankingen&lt;font size="3"&gt; (per ${lastTournamentDate})&lt;/font&gt;&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;/p&gt;

    &lt;table&gt;
      &lt;tr&gt;
        &lt;td&gt;&amp;nbsp;&amp;nbsp;&lt;/td&gt;
        &lt;td&gt;&lt;a href="rankinghistorik.html"&gt;Ratinghistorik&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/td&gt;
        &lt;td&gt;&lt;a href="diagram-1599/ratingdiagram.html"&gt;Diagram &lt; 1600&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/td&gt;
        &lt;td&gt;&lt;a href="diagram-1600/ratingdiagram.html"&gt;Diagram &gt;= 1600&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;

    &lt;br&gt;

    &lt;table width="70%" border="0"&gt;
      &lt;tr&gt;
        &lt;td width="24%" align="center"&gt;&lt;font color="#000000"&gt;&lt;b&gt;Placering&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;
        &lt;td width="40%"&gt;&lt;font color="#000000"&gt;&lt;b&gt;Namn&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;
        &lt;td width="30%"&gt;&lt;font color="#000000"&gt;&lt;b&gt;Rating&lt;/b&gt;&lt;/font&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;#list ratings as player&gt;
      &lt;tr&gt;
        &lt;td width="24%" height="25" align="center"&gt;&lt;font color="#000000"&gt;${player.rank} &lt;@arrow player.rankchange /&gt;&lt;/font&gt;&lt;/td&gt;
        &lt;td width="40%" height="25"&gt;&lt;p&gt;&lt;@playerLink player.name "."/&gt;&lt;/p&gt;&lt;/td&gt;
        &lt;td width="30%" height="25"&gt;${player.rating?floor?string["0"]} &amp;nbsp;&lt;@round player.change /&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;#if player.rank == 8&gt;
    &lt;/table&gt;
      &lt;p&gt;&lt;font color="#000000"&gt;Gräns för deltagande i Uppsalamästerskapen.&lt;/font&gt;&lt;/p&gt;
    &lt;table width="70%" border="0"&gt;
      &lt;/#if&gt;
      &lt;/#list&gt;
    &lt;/table&gt;
    &lt;#assign now = .now&gt;
    &lt;p&gt;&lt;font color="#666666"&gt;Uppdaterad ${now?string["EEEE yyyy-MM-dd HH:mm"]}&lt;/font&gt;&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;

tengstrand 2021-02-09T12:41:55.419600Z

Then I have written some Clojure code that uses the template and produces files. Maybe you have some good examples of how code generation is solved in babashka today? Or any ideas what the best way to go would be (you suggested two ways already in the other thread).

borkdude 2021-02-09T12:57:14.419800Z

@tengstrand @teng I'm not so familiar with freemarker, although I've heard about it. It looks similar to selmer.

borkdude 2021-02-09T12:58:21.420Z

So is there a clojure library wrapping this somewhere that you are using currently?

borkdude 2021-02-09T12:59:13.420200Z

@teng This templating lib works with babashka from source: https://github.com/weavejester/comb You can use it like this:

(require '[babashka.deps :as deps])

(deps/add-deps '{:deps {comb/comb {:mvn/version "0.1.1"}}})

(require '[comb.template :as template])

(template/eval "&lt;% (dotimes [x 3] %&gt;foo&lt;% ) %&gt;") ;;=&gt; "foofoofoo"

borkdude 2021-02-09T13:02:36.420600Z

If freemarker is a high requirement, then babashka will currently not work since it doesn't have this library and babashka can not run this from source since it's a Java project. Then you will probably will have to make a JVM-based tool which you can then optionally compile to native using GraalVM native-image for fast startup.

borkdude 2021-02-09T13:02:54.420800Z

If you need eval in that binary, you can use sci: https://github.com/borkdude/sci

borkdude 2021-02-09T13:03:12.421200Z

That is the same interpreter that is used in babashka.

tengstrand 2021-02-09T13:21:12.421500Z

Okay, interesting. Looks like Comb doesn’t meet my requirements, like support for macros. Thanks for the tip anyway. It would be really cool if babashka could support Freemarker though, but in the meantime, I will try out the things you have suggested. Thanks!

borkdude 2021-02-09T13:24:58.421700Z

I have the feeling that selmer is more well known / popular in the clojure community than freemarker. That said, babashka does support ways to extend with non-default stuff. 1) feature flags. https://github.com/borkdude/babashka/blob/master/doc/build.md a feature flag for freemarker is welcome. You will have to compile and distribute bb with freemarker yourself as it won't be in the default distribution (until it's clear that this is a thing many people want) 2) pods. these are extra CLIs to which babashka shells out for additional features. See https://github.com/babashka/pod-registry. I'm not sure if pods fit well here since they have data in / data out semantics. I would have to see the clojure API for freemarker to see if this works.

tengstrand 2021-02-09T13:31:56.422100Z

Okay cool, now I have plenty of things to look into! :-)

borkdude 2021-02-09T19:48:53.422900Z

Released bb 0.2.10... there was a stupid error in if: (if false 10 20) returned nil. This only happened on literally false and nil and was due to an optimization that I blindly applied from the 2-arity to the 3-arity version of if. 🤦

6👏
2021-02-09T21:12:42.423900Z

Probably age 😋

22😅