Moving towards testability: Model class does not have any public fields, rather instance onces model
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 21 Jan 2013 13:43:40 +0100
branchmodel
changeset 5054198be34b516
parent 500 f9e80d48e9b4
child 506 305a67fe4ed2
Moving towards testability: Model class does not have any public fields, rather instance onces
javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java
javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java
javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java
javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java
javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java
javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml
javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java
     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  }