Moving towards testability: Model class does not have any public fields, rather instance onces
1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Mon Jan 21 11:55:27 2013 +0100
1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Mon Jan 21 13:43:40 2013 +0100
1.3 @@ -24,7 +24,7 @@
1.4 *
1.5 * @author Jaroslav Tulach <jtulach@netbeans.org>
1.6 */
1.7 -@ExtraJavaScript(resource = "org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js")
1.8 +@ExtraJavaScript(resource = "/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js")
1.9 public class Knockout {
1.10 /** used by tests */
1.11 static Knockout next;
2.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Mon Jan 21 11:55:27 2013 +0100
2.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Mon Jan 21 13:43:40 2013 +0100
2.3 @@ -94,28 +94,27 @@
2.4 try {
2.5 w.append("package " + pkg + ";\n");
2.6 w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n");
2.7 - w.append("class ").append(className).append(" {\n");
2.8 + w.append("final class ").append(className).append(" {\n");
2.9 w.append(" private static boolean locked;\n");
2.10 - w.append(" private ").append(className).append("() {}\n");
2.11 - for (String id : pp.ids()) {
2.12 - String tag = pp.tagNameForId(id);
2.13 - String type = type(tag);
2.14 - w.append(" ").append("public static final ").
2.15 - append(type).append(' ').append(cnstnt(id)).append(" = new ").
2.16 - append(type).append("(\"").append(id).append("\");\n");
2.17 - }
2.18 - w.append(" static {\n");
2.19 + w.append(" public ").append(className).append("() {\n");
2.20 if (!initializeOnClick((TypeElement) e, w, pp)) {
2.21 return false;
2.22 }
2.23 w.append(" }\n");
2.24 + for (String id : pp.ids()) {
2.25 + String tag = pp.tagNameForId(id);
2.26 + String type = type(tag);
2.27 + w.append(" ").append("public final ").
2.28 + append(type).append(' ').append(cnstnt(id)).append(" = new ").
2.29 + append(type).append("(\"").append(id).append("\");\n");
2.30 + }
2.31 List<String> propsGetSet = new ArrayList<String>();
2.32 Map<String,Collection<String>> propsDeps = new HashMap<String, Collection<String>>();
2.33 generateComputedProperties(w, e.getEnclosedElements(), propsGetSet, propsDeps);
2.34 generateProperties(w, p.properties(), propsGetSet, propsDeps);
2.35 - w.append(" private static org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
2.36 + w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
2.37 if (!propsGetSet.isEmpty()) {
2.38 - w.write("public static void applyBindings() {\n");
2.39 + w.write("public " + className + " applyBindings() {\n");
2.40 w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(");
2.41 w.write(className + ".class, new " + className + "(), ");
2.42 w.write("new String[] {\n");
2.43 @@ -129,8 +128,7 @@
2.44 }
2.45 sep = ",\n";
2.46 }
2.47 - w.write("\n });\n}\n");
2.48 - //w.write("static { applyBindings(); }\n");
2.49 + w.write("\n });\n return this;\n}\n");
2.50 }
2.51 w.append("}\n");
2.52 } finally {
2.53 @@ -271,12 +269,12 @@
2.54 final String tn = typeName(p);
2.55 String[] gs = toGetSet(p.name(), tn);
2.56
2.57 - w.write("private static " + tn + " prop_" + p.name() + ";\n");
2.58 - w.write("public static " + tn + " " + gs[0] + "() {\n");
2.59 + w.write("private " + tn + " prop_" + p.name() + ";\n");
2.60 + w.write("public " + tn + " " + gs[0] + "() {\n");
2.61 w.write(" if (locked) throw new IllegalStateException();\n");
2.62 w.write(" return prop_" + p.name() + ";\n");
2.63 w.write("}\n");
2.64 - w.write("public static void " + gs[1] + "(" + tn + " v) {\n");
2.65 + w.write("public void " + gs[1] + "(" + tn + " v) {\n");
2.66 w.write(" if (locked) throw new IllegalStateException();\n");
2.67 w.write(" prop_" + p.name() + " = v;\n");
2.68 w.write(" if (ko != null) {\n");
2.69 @@ -312,7 +310,7 @@
2.70 final String sn = ee.getSimpleName().toString();
2.71 String[] gs = toGetSet(sn, tn);
2.72
2.73 - w.write("public static " + tn + " " + gs[0] + "() {\n");
2.74 + w.write("public " + tn + " " + gs[0] + "() {\n");
2.75 w.write(" if (locked) throw new IllegalStateException();\n");
2.76 int arg = 0;
2.77 for (VariableElement pe : ee.getParameters()) {
3.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Mon Jan 21 11:55:27 2013 +0100
3.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Mon Jan 21 13:43:40 2013 +0100
3.3 @@ -23,6 +23,7 @@
3.4 import org.apidesign.bck2brwsr.htmlpage.api.Page;
3.5 import org.apidesign.bck2brwsr.htmlpage.api.Property;
3.6 import static org.testng.Assert.*;
3.7 +import org.testng.annotations.BeforeMethod;
3.8 import org.testng.annotations.Test;
3.9
3.10 /**
3.11 @@ -34,25 +35,31 @@
3.12 @Property(name = "unrelated", type = long.class)
3.13 })
3.14 public class ModelTest {
3.15 + private Model model;
3.16 + private static Model leakedModel;
3.17 +
3.18 + @BeforeMethod
3.19 + public void createModel() {
3.20 + model = new Model();
3.21 + }
3.22 +
3.23 @Test public void classGeneratedWithSetterGetter() {
3.24 - Class<?> c = Model.class;
3.25 - assertNotNull(c, "Class for empty page generated");
3.26 - Model.setValue(10);
3.27 - assertEquals(10, Model.getValue(), "Value changed");
3.28 + model.setValue(10);
3.29 + assertEquals(10, model.getValue(), "Value changed");
3.30 }
3.31
3.32 @Test public void computedMethod() {
3.33 - Model.setValue(4);
3.34 - assertEquals(16, Model.getPowerValue());
3.35 + model.setValue(4);
3.36 + assertEquals(16, model.getPowerValue());
3.37 }
3.38
3.39 @Test public void derivedPropertiesAreNotified() {
3.40 MockKnockout my = new MockKnockout();
3.41 MockKnockout.next = my;
3.42
3.43 - Model.applyBindings();
3.44 + model.applyBindings();
3.45
3.46 - Model.setValue(33);
3.47 + model.setValue(33);
3.48
3.49 assertEquals(my.mutated.size(), 2, "Two properties changed: " + my.mutated);
3.50 assertTrue(my.mutated.contains("powerValue"), "Power value is in there: " + my.mutated);
3.51 @@ -60,14 +67,15 @@
3.52
3.53 my.mutated.clear();
3.54
3.55 - Model.setUnrelated(44);
3.56 + model.setUnrelated(44);
3.57 assertEquals(my.mutated.size(), 1, "One property changed");
3.58 assertTrue(my.mutated.contains("unrelated"), "Its name is unrelated");
3.59 }
3.60
3.61 @Test public void computedPropertyCannotWriteToModel() {
3.62 + leakedModel = model;
3.63 try {
3.64 - String res = Model.getNotAllowedWrite();
3.65 + String res = model.getNotAllowedWrite();
3.66 fail("We should not be allowed to write to the model: " + res);
3.67 } catch (IllegalStateException ex) {
3.68 // OK, we can't read
3.69 @@ -75,8 +83,9 @@
3.70 }
3.71
3.72 @Test public void computedPropertyCannotReadToModel() {
3.73 + leakedModel = model;
3.74 try {
3.75 - String res = Model.getNotAllowedRead();
3.76 + String res = model.getNotAllowedRead();
3.77 fail("We should not be allowed to read from the model: " + res);
3.78 } catch (IllegalStateException ex) {
3.79 // OK, we can't read
3.80 @@ -90,12 +99,12 @@
3.81
3.82 @ComputedProperty
3.83 static String notAllowedRead() {
3.84 - return "Not allowed callback: " + Model.getUnrelated();
3.85 + return "Not allowed callback: " + leakedModel.getUnrelated();
3.86 }
3.87
3.88 @ComputedProperty
3.89 static String notAllowedWrite() {
3.90 - Model.setUnrelated(11);
3.91 + leakedModel.setUnrelated(11);
3.92 return "Not allowed callback!";
3.93 }
3.94
4.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Mon Jan 21 11:55:27 2013 +0100
4.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Mon Jan 21 13:43:40 2013 +0100
4.3 @@ -43,9 +43,11 @@
4.4 */
4.5 @Page(xhtml="TestPage.html")
4.6 public class PageController {
4.7 + private static final TestPage PAGE = new TestPage();
4.8 +
4.9 @On(event = CLICK, id="pg.button")
4.10 static void updateTitle() {
4.11 - TestPage.PG_TITLE.setText("You want this window to be named " + TestPage.PG_TEXT.getValue());
4.12 + PAGE.PG_TITLE.setText("You want this window to be named " + PAGE.PG_TEXT.getValue());
4.13 }
4.14
4.15 @On(event = CLICK, id={ "pg.title", "pg.text" })
4.16 @@ -53,6 +55,6 @@
4.17 if (!id.equals("pg.title")) {
4.18 throw new IllegalStateException();
4.19 }
4.20 - TestPage.PG_TITLE.setText(id);
4.21 + PAGE.PG_TITLE.setText(id);
4.22 }
4.23 }
5.1 --- a/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java Mon Jan 21 11:55:27 2013 +0100
5.2 +++ b/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java Mon Jan 21 13:43:40 2013 +0100
5.3 @@ -17,6 +17,7 @@
5.4 */
5.5 package org.apidesign.bck2brwsr.mavenhtml;
5.6
5.7 +import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
5.8 import org.apidesign.bck2brwsr.htmlpage.api.On;
5.9 import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*;
5.10 import org.apidesign.bck2brwsr.htmlpage.api.Page;
5.11 @@ -29,34 +30,54 @@
5.12 * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5.13 */
5.14 @Page(xhtml="Calculator.xhtml", properties = {
5.15 - @Property(name = "display", type = double.class)
5.16 + @Property(name = "memory", type = double.class),
5.17 + @Property(name = "display", type = double.class),
5.18 + @Property(name = "operation", type = String.class),
5.19 + @Property(name = "hover", type = boolean.class)
5.20 })
5.21 public class App {
5.22 - private static double memory;
5.23 - private static String operation;
5.24 + private static final Calculator CALC = new Calculator().applyBindings();
5.25
5.26 @On(event = CLICK, id="clear")
5.27 static void clear() {
5.28 - memory = 0;
5.29 - operation = null;
5.30 - Calculator.setDisplay(0);
5.31 + CALC.setMemory(0);
5.32 + CALC.setOperation(null);
5.33 + CALC.setDisplay(0);
5.34 }
5.35
5.36 @On(event = CLICK, id= { "plus", "minus", "mul", "div" })
5.37 static void applyOp(String op) {
5.38 - memory = Calculator.getDisplay();
5.39 - operation = op;
5.40 - Calculator.setDisplay(0);
5.41 + CALC.setMemory(CALC.getDisplay());
5.42 + CALC.setOperation(op);
5.43 + CALC.setDisplay(0);
5.44 + }
5.45 +
5.46 + @On(event = MOUSE_OVER, id= { "result" })
5.47 + static void attemptingIn(String op) {
5.48 + CALC.setHover(true);
5.49 + }
5.50 + @On(event = MOUSE_OUT, id= { "result" })
5.51 + static void attemptingOut(String op) {
5.52 + CALC.setHover(false);
5.53 }
5.54
5.55 @On(event = CLICK, id="result")
5.56 static void computeTheValue() {
5.57 - switch (operation) {
5.58 - case "plus": Calculator.setDisplay(memory + Calculator.getDisplay()); break;
5.59 - case "minus": Calculator.setDisplay(memory - Calculator.getDisplay()); break;
5.60 - case "mul": Calculator.setDisplay(memory * Calculator.getDisplay()); break;
5.61 - case "div": Calculator.setDisplay(memory / Calculator.getDisplay()); break;
5.62 - default: throw new IllegalStateException(operation);
5.63 + CALC.setDisplay(compute(
5.64 + CALC.getOperation(),
5.65 + CALC.getMemory(),
5.66 + CALC.getDisplay()
5.67 + ));
5.68 + CALC.setMemory(0);
5.69 + }
5.70 +
5.71 + private static double compute(String op, double memory, double display) {
5.72 + switch (op) {
5.73 + case "plus": return memory + display;
5.74 + case "minus": return memory - display;
5.75 + case "mul": return memory * display;
5.76 + case "div": return memory / display;
5.77 + default: throw new IllegalStateException(op);
5.78 }
5.79 }
5.80
5.81 @@ -64,12 +85,26 @@
5.82 static void addDigit(String digit) {
5.83 digit = digit.substring(1);
5.84
5.85 - double v = Calculator.getDisplay();
5.86 + double v = CALC.getDisplay();
5.87 if (v == 0.0) {
5.88 - Calculator.setDisplay(Integer.parseInt(digit));
5.89 + CALC.setDisplay(Integer.parseInt(digit));
5.90 } else {
5.91 - String txt = Double.toString(v) + digit;
5.92 - Calculator.setDisplay(Double.parseDouble(txt));
5.93 + String txt = Double.toString(v);
5.94 + if (txt.endsWith(".0")) {
5.95 + txt = txt.substring(0, txt.length() - 2);
5.96 + }
5.97 + txt = txt + digit;
5.98 + CALC.setDisplay(Double.parseDouble(txt));
5.99 }
5.100 }
5.101 +
5.102 + @ComputedProperty
5.103 + public static String displayPreview(
5.104 + double display, boolean hover, double memory, String operation
5.105 + ) {
5.106 + if (!hover) {
5.107 + return "Type numbers and perform simple operations! Press '=' to get result.";
5.108 + }
5.109 + return "Attempt to compute " + memory + " " + operation + " " + display + " = " + compute(operation, memory, display);
5.110 + }
5.111 }
6.1 --- a/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml Mon Jan 21 11:55:27 2013 +0100
6.2 +++ b/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml Mon Jan 21 13:43:40 2013 +0100
6.3 @@ -81,7 +81,7 @@
6.4 <script src="/bck2brwsr.js"></script>
6.5 <script src="/vm.js"></script>
6.6 <script type="text/javascript">
6.7 - vm.loadClass('org.apidesign.bck2brwsr.mavenhtml.Calculator');
6.8 + vm.loadClass('org.apidesign.bck2brwsr.mavenhtml.App');
6.9 </script>
6.10
6.11 <hr/>
7.1 --- a/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java Mon Jan 21 11:55:27 2013 +0100
7.2 +++ b/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java Mon Jan 21 13:43:40 2013 +0100
7.3 @@ -36,37 +36,39 @@
7.4 @Property(name = "hover", type = boolean.class)
7.5 })
7.6 public class App {
7.7 + private static final Calculator CALC = new Calculator().applyBindings();
7.8 +
7.9 @On(event = CLICK, id="clear")
7.10 static void clear() {
7.11 - Calculator.setMemory(0);
7.12 - Calculator.setOperation(null);
7.13 - Calculator.setDisplay(0);
7.14 + CALC.setMemory(0);
7.15 + CALC.setOperation(null);
7.16 + CALC.setDisplay(0);
7.17 }
7.18
7.19 @On(event = CLICK, id= { "plus", "minus", "mul", "div" })
7.20 static void applyOp(String op) {
7.21 - Calculator.setMemory(Calculator.getDisplay());
7.22 - Calculator.setOperation(op);
7.23 - Calculator.setDisplay(0);
7.24 + CALC.setMemory(CALC.getDisplay());
7.25 + CALC.setOperation(op);
7.26 + CALC.setDisplay(0);
7.27 }
7.28
7.29 @On(event = MOUSE_OVER, id= { "result" })
7.30 static void attemptingIn(String op) {
7.31 - Calculator.setHover(true);
7.32 + CALC.setHover(true);
7.33 }
7.34 @On(event = MOUSE_OUT, id= { "result" })
7.35 static void attemptingOut(String op) {
7.36 - Calculator.setHover(false);
7.37 + CALC.setHover(false);
7.38 }
7.39
7.40 @On(event = CLICK, id="result")
7.41 static void computeTheValue() {
7.42 - Calculator.setDisplay(compute(
7.43 - Calculator.getOperation(),
7.44 - Calculator.getMemory(),
7.45 - Calculator.getDisplay()
7.46 + CALC.setDisplay(compute(
7.47 + CALC.getOperation(),
7.48 + CALC.getMemory(),
7.49 + CALC.getDisplay()
7.50 ));
7.51 - Calculator.setMemory(0);
7.52 + CALC.setMemory(0);
7.53 }
7.54
7.55 private static double compute(String op, double memory, double display) {
7.56 @@ -83,16 +85,16 @@
7.57 static void addDigit(String digit) {
7.58 digit = digit.substring(1);
7.59
7.60 - double v = Calculator.getDisplay();
7.61 + double v = CALC.getDisplay();
7.62 if (v == 0.0) {
7.63 - Calculator.setDisplay(Integer.parseInt(digit));
7.64 + CALC.setDisplay(Integer.parseInt(digit));
7.65 } else {
7.66 String txt = Double.toString(v);
7.67 if (txt.endsWith(".0")) {
7.68 txt = txt.substring(0, txt.length() - 2);
7.69 }
7.70 txt = txt + digit;
7.71 - Calculator.setDisplay(Double.parseDouble(txt));
7.72 + CALC.setDisplay(Double.parseDouble(txt));
7.73 }
7.74 }
7.75
7.76 @@ -105,8 +107,4 @@
7.77 }
7.78 return "Attempt to compute " + memory + " " + operation + " " + display + " = " + compute(operation, memory, display);
7.79 }
7.80 -
7.81 - static {
7.82 - Calculator.applyBindings();
7.83 - }
7.84 }