rocker
rocker copied to clipboard
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.
Along the same lines, named arguments for templates would allow for default arguments. Most of my templates don't need custom CSS or JS, but some do. It would be great if those values of my main.rocker.html
could default to empty.
Hey Ned,
That's a great question. Rocker does support named parameters in a builder-style syntax. If you look at the generated .java files, you'll notice every argument has a corresponding method that sets it too. Now what we don't have (by design) is a static "template" method that takes zero arguments. You could theoretically pass nulls to the static template method then call each setter method by itself to set the variable.
Why did we require arguments to match in the static template method? Well, mainly because in our experience when you add a new argument, you need to understand the impact on its use across templates in your app. The best way to do that is to tap into the compiler itself to make sure you handle the case everywhere. While perhaps irritating at first, if you maintain an app for a period of time you'll actually come to appreciate it. When you refactor your templates, having the java compiler bless your code by having it compile successfully gives you great confidence your refactor was successful.
What do we suggest for lots of common variables you use everywhere? We suggest you use a common application base template for that kind of use. You can inject variables there and then access them in your templates w/o having to pass them around. If you are passing a bunch of variables around from template to template, I'd suggest you simplify the model down or move a lot of that to a common base template.
If you use Rocker's null safety operator, just pass nulls for custom css or js.
-Joe
On Mon, Sep 11, 2017 at 2:17 PM, Ned Twigg [email protected] wrote:
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.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/fizzed/rocker/issues/70, or mute the thread https://github.com/notifications/unsubscribe-auth/AAjwAgkIMynUtG8w86-c_BQIK-VBX6peks5shXT8gaJpZM4PTi8g .
Thanks, great to know about the builder option! Rocker supports positional arguments with compile-time checking @navbar.template(false)
, or a builder with limited compile-time checking @(new navbar().isDiscourse(false))
. The user has a choice - you can have readable arguments, or compile-time validation that you passed all the arguments, but you can't have both.
It's not trivial, but I think there's a third option, which is for the Rocker compiler to automatically transform @navbar.template(isDiscourse: false)
into @navbar.template(false)
.
If I have a template like this: @args (String item, int qty)
then I could call it like this @cart.template(item: "Cheese", qty: 3)
or like this @cart.template(qty: 3, item: 'Cheese')
, and Rocker would turn both of them into @navbar.template("Cheese", 3)
. But if I did @cart.template(item: "Cheese")
or @cart.template(qty: 3)
then the Rocker compiler yells at me because I didn't supply all the arguments. Now the user doesn't need to make a choice - the arguments are easy to read, and you can be sure that you supplied all the required arguments.
The Java compiler still does all of the type-checking (making sure it's a String or int), but Rocker has to take care of putting the arguments in the right place, and making sure that every required argument was passed.
Obviously this wouldn't be a minor feature, but it also seems like it wouldn't be monumental.
If Rocker was capable of this much, it could then be extended further to this: @args (String item, int qty = 1)
. Now qty is an optional argument that defaults to 1 if it wasn't specified explicitly.
None of this would need changes in the .java
files that Rocker generates, it would just need the Rocker compiler itself to get a teensy bit smarter. It would need a List<String> args
for every template so that it could reorder the arguments and make sure they're all there, and a Map<String, String>
for the default arguments so that it could supply missing values where appropriate.
I've got a flight coming up, I cloned the source to see if I could do some tinkering and see how hard an implementation would be.
cmd> mvn -pl rocker-test-java8 surefire:test
... zillion errors because code wasn't generated ...
cmd> mvn test
... fails on rocker-gradle-plugin, it seems the gradle dep is not present
I'm a maven moron and gradle evangelist. Is there a quickstart for how to run the tests and/or setup a dev environment?
In the main project directory, you should be able to just run mvn test
and it'll compile and test everything. Are you running a recent version of
maven? mvn -v
? Anything above 3.2.5 should work, but 3.5.0 would be
better.
mvn -am -pl rocker-test-java8 test
should also work and build all
dependencies and tests up to that specific module. That would exclude the
gradle plugin since that's later in the build process. The -am
flag is
critical.
-Joe
On Thu, Sep 14, 2017 at 3:12 AM, Ned Twigg [email protected] wrote:
I've got a flight coming up, I cloned the source to see if I could do some tinkering and see how hard an implementation would be.
cmd> mvn -pl rocker-test-java8 surefire:test ... zillion errors because code wasn't generated ... cmd> mvn test ... fails on rocker-gradle-plugin, it seems the gradle dep is not present
I'm a maven moron and gradle evangelist. Is there a quickstart for how to run the tests and/or setup a dev environment?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/fizzed/rocker/issues/70#issuecomment-329394451, or mute the thread https://github.com/notifications/unsubscribe-auth/AAjwAk_WipdU_sCcW_yc_MfVr3g3bJgmks5siNHogaJpZM4PTi8g .
Thanks for the -am -pl
tip, that works and is all I need to tinker. The gradle project still fails for me, but I don't need it. Just FYI:
cmd> mvn -v
Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-10T08:41:47-08:00)
Maven home: /usr/local/Cellar/maven/3.3.9/libexec
Java version: 1.8.0_111, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.12.6", arch: "x86_64", family: "mac"
cmd> mvn test
....
Running com.fizzed.rocker.gradle.RockerGradlePluginTest
Tests run: 5, Failures: 5, Errors: 0, Skipped: 0, Time elapsed: 0.015 sec <<< FAILURE!
com.fizzed.rocker.gradle.RockerGradlePluginTest.testJavaFileIsCreatedInOutputDirectory() Time elapsed: 0.006 sec <<< FAILURE!
java.lang.Error: Unresolved compilation problems:
Test cannot be resolved to a type
Project cannot be resolved to a type
ProjectBuilder cannot be resolved
The method assertTrue(boolean) is undefined for the type RockerGradlePluginTest
(lots more like it)
Maybe the Gradle issue is simply windows-related since Rocker builds & tests in Travis-CI (clean room). I can take a look later, but glad to hear otherwise its working.
On Thu, Sep 14, 2017 at 3:19 PM, Ned Twigg [email protected] wrote:
Thanks for the -am -pl tip, that works and is all I need to tinker. The gradle project still fails for me, but I don't need it. Just FYI:
cmd> mvn -v Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-10T08:41:47-08:00) Maven home: /usr/local/Cellar/maven/3.3.9/libexec Java version: 1.8.0_111, vendor: Oracle Corporation Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre Default locale: en_US, platform encoding: UTF-8 OS name: "mac os x", version: "10.12.6", arch: "x86_64", family: "mac"
cmd> mvn test .... Running com.fizzed.rocker.gradle.RockerGradlePluginTest Tests run: 5, Failures: 5, Errors: 0, Skipped: 0, Time elapsed: 0.015 sec <<< FAILURE! com.fizzed.rocker.gradle.RockerGradlePluginTest.testJavaFileIsCreatedInOutputDirectory() Time elapsed: 0.006 sec <<< FAILURE! java.lang.Error: Unresolved compilation problems: Test cannot be resolved to a type Project cannot be resolved to a type ProjectBuilder cannot be resolved The method assertTrue(boolean) is undefined for the type RockerGradlePluginTest (lots more like it)
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/fizzed/rocker/issues/70#issuecomment-329582798, or mute the thread https://github.com/notifications/unsubscribe-auth/AAjwAsfNT6owLwlOz4YaCexlOlKIQIEXks5siXxbgaJpZM4PTi8g .