You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by bu...@apache.org on 2014/11/18 22:25:57 UTC
svn commit: r929693 - in /websites/staging/isis/trunk: cgi-bin/ content/
content/documentation.html
content/reference/recognized-annotations/about.html
content/tutorials/apacheconeu-2014.html
Author: buildbot
Date: Tue Nov 18 21:25:56 2014
New Revision: 929693
Log:
Staging update by buildbot for isis
Modified:
websites/staging/isis/trunk/cgi-bin/ (props changed)
websites/staging/isis/trunk/content/ (props changed)
websites/staging/isis/trunk/content/documentation.html
websites/staging/isis/trunk/content/reference/recognized-annotations/about.html
websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html
Propchange: websites/staging/isis/trunk/cgi-bin/
------------------------------------------------------------------------------
--- cms:source-revision (original)
+++ cms:source-revision Tue Nov 18 21:25:56 2014
@@ -1 +1 @@
-1640425
+1640429
Propchange: websites/staging/isis/trunk/content/
------------------------------------------------------------------------------
--- cms:source-revision (original)
+++ cms:source-revision Tue Nov 18 21:25:56 2014
@@ -1 +1 @@
-1640425
+1640429
Modified: websites/staging/isis/trunk/content/documentation.html
==============================================================================
--- websites/staging/isis/trunk/content/documentation.html (original)
+++ websites/staging/isis/trunk/content/documentation.html Tue Nov 18 21:25:56 2014
@@ -477,7 +477,7 @@
<ul>
<li><a href="intro/resources/downloadable-presentations.html">Downloadable Presentations</a></li>
-<li><strong><a href="intro/resources/editor-templates.html">IDE templates</strong></a>** (IntelliJ and Eclipse)</li>
+<li><strong><a href="intro/resources/editor-templates.html">IDE templates</a></strong> (IntelliJ and Eclipse)</li>
<li><a href="intro/resources/icons.html">Icons</a></li>
<li><strong><a href="intro/resources/cheat-sheet.html">Cheat Sheet</a></strong>
</div>
Modified: websites/staging/isis/trunk/content/reference/recognized-annotations/about.html
==============================================================================
--- websites/staging/isis/trunk/content/reference/recognized-annotations/about.html (original)
+++ websites/staging/isis/trunk/content/reference/recognized-annotations/about.html Tue Nov 18 21:25:56 2014
@@ -436,7 +436,7 @@
<td></td>
</tr>
<tr>
- <td><a href="./ActionOrder-deprecated.html">@ActionOrder</a></td>
+ <td><a href="./ActionOrder-deprecated.html" style="text-decoration: line-through;" >@ActionOrder</a></td>
<td>UI</td>
<td>Order of buttons and menu items representing actions.
</td>
@@ -535,7 +535,7 @@
<td></td>
</tr>
<tr>
- <td><a href="./Debug-deprecated.html">@Debug</a></td>
+ <td><a href="./Debug-deprecated.html" style="text-decoration: line-through;" >@Debug</a></td>
<td>UI</td>
<td>Action only invocable in debug mode.
</td>
@@ -591,7 +591,7 @@
<td></td>
</tr>
<tr>
- <td><a href="./Exploration-deprecated.html">@Exploration</a></td>
+ <td><a href="./Exploration-deprecated.html" style="text-decoration: line-through;" >@Exploration</a></td>
<td>UI</td>
<td>Action available in special 'exploration' mode.
</td>
@@ -607,7 +607,7 @@
<td></td>
</tr>
<tr>
- <td><a href="./FieldOrder-deprecated.html">@FieldOrder</a></td>
+ <td><a href="./FieldOrder-deprecated.html" style="text-decoration: line-through;" >@FieldOrder</a></td>
<td>UI</td>
<td>Order of properties and collections
</td>
@@ -632,7 +632,7 @@
<td></td>
</tr>
<tr>
- <td><a href="./Idempotent-deprecated.html">@Idempotent</a></td>
+ <td><a href="./Idempotent-deprecated.html" style="text-decoration: line-through;" >@Idempotent</a></td>
<td>Domain</td>
<td>Replaced by @ActionSemantics.
</td>
@@ -640,7 +640,7 @@
<td>Y</td>
</tr>
<tr>
- <td><a href="./Ignore-deprecated.html">@Ignore</a></td>
+ <td><a href="./Ignore-deprecated.html" style="text-decoration: line-through;" >@Ignore</a></td>
<td>Domain</td>
<td>Replaced by @Programmatic.
</td>
@@ -822,7 +822,7 @@
<td></td>
</tr>
<tr>
- <td><a href="./PostsPropertyChangedEvent-deprecated.html">@PostsPropertyChangedEvent</a></td>
+ <td><a href="./PostsPropertyChangedEvent-deprecated.html" style="text-decoration: line-through;" >@PostsPropertyChangedEvent</a></td>
<td>Domain</td>
<td>Replaced by @PropertyInteraction
</td>
@@ -873,7 +873,7 @@
<td></td>
</tr>
<tr>
- <td><a href="./QueryOnly-deprecated.html">@QueryOnly</a></td>
+ <td><a href="./QueryOnly-deprecated.html" style="text-decoration: line-through;" >@QueryOnly</a></td>
<td>Domain</td>
<td>Replaced by @ActionSemantics.
</td>
@@ -913,7 +913,7 @@
<td></td>
</tr>
<tr>
- <td><a href="./Resolve-deprecated.html">@Resolve</a></td>
+ <td><a href="./Resolve-deprecated.html" style="text-decoration: line-through;" >@Resolve</a></td>
<td>UI</td>
<td>Replaced by @Render.
</td>
Modified: websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html
==============================================================================
--- websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html (original)
+++ websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html Tue Nov 18 21:25:56 2014
@@ -417,7 +417,9 @@
</h1>
</div>
-<p>A half-day tutorial on developing domain-driven apps using Apache Isis.</p>
+<p><div class="note">
+A half-day tutorial on developing domain-driven apps using Apache Isis.
+</div></p>
<h2>Run the archetype</h2>
@@ -448,7 +450,7 @@ mvn clean install
<pre><code>mvn jetty:run
</code></pre>
-<h4>DEMO</h4>
+<h2>Using the app</h2>
<ul>
<li>install fixtures</li>
@@ -459,22 +461,24 @@ mvn clean install
<h2>Dev environment</h2>
+<p>Set up an IDE and import the project to be able to run and debug the app</p>
+
<h4>Configure</h4>
<ul>
<li>IDE:
<ul>
-<li><a href="http://isis.apache.org/intro/getting-started/ide/intellij.html">IntelliJ</a></li>
-<li><a href="http://isis.apache.org/intro/getting-started/ide/eclipse.html">Eclipse</a></li>
+<li>configure <a href="http://isis.apache.org/intro/getting-started/ide/intellij.html">IntelliJ</a>, import app</li>
+<li>configure <a href="http://isis.apache.org/intro/getting-started/ide/eclipse.html">Eclipse</a>, import app</li>
</ul></li>
-<li><a href="http://isis.apache.org/intro/resources/editor-templates.html">IDE Editor templates</a></li>
+<li>Set up IDE <a href="http://isis.apache.org/intro/resources/editor-templates.html">editor templates</a></li>
</ul>
<h4>Run</h4>
<ul>
<li>Run the app from within the IDE</li>
-<li>Run with different deploymentTypes:
+<li>Run with different deploymentTypes, note whether <code>@Prototype</code> actions are available or not:
<ul>
<li><code>--type SERVER_PROTOTYPE</code></li>
<li><code>--type SERVER</code></li>
@@ -485,17 +489,20 @@ mvn clean install
<ul>
<li><code>myapp</code> : parent module</li>
-<li><code>myapp-dom</code>
+<li><code>myapp-dom</code>: domain objects module
+<ul>
+<li>entity: <code>dom.simple.SimpleObject</code></li>
+<li>repository: <code>dom.simple.SimpleObjects</code></li>
+</ul></li>
+<li><code>myapp-fixture</code>: fixtures module
<ul>
-<li><code>dom.simple.SimpleObject</code></li>
-<li><code>dom.simple.SimpleObjects</code></li>
+<li>fixture script:<code>fixture.simple.SimpleObjectsFixture</code></li>
</ul></li>
-<li><code>myapp-fixture</code>
+<li><code>myapp-integtests</code>: integration tests module</li>
+<li><code>myapp-webapp</code>: webapp module
<ul>
-<li><code>fixture.simple.SimpleObjectsFixture</code></li>
+<li>(builds the WAR file)</li>
</ul></li>
-<li><code>myapp-integtests</code></li>
-<li><code>myapp-webapp</code></li>
</ul>
<h2>Testing</h2>
@@ -557,6 +564,8 @@ mvn clean install
<p><img src="http://yuml.me/a070d071" alt="" /></p>
+<p>which in yuml.me's DSL is:</p>
+
<pre>
[Visit|-checkIn:DateTime;-checkout:DateTime;-diagnosis:String|+checkin();+checkout();+addNote()]->[Pet|-name:String;-species:PetSpecies]
[Owner|-firstName:String;-lastName:String]<0..1-0..*>[Pet]
@@ -566,7 +575,7 @@ mvn clean install
<ul>
<li>rename the <code>SimpleObject</code> class</li>
-<li>rename the <code>name</code> property</li>
+<li>rename the <code>SimpleObject</code> class' <code>name</code> property</li>
<li>specify a <a href="http://isis.apache.org/how-tos/how-to-01-040-How-to-specify-a-title-for-a-domain-entity.html">title</a></li>
<li>specify an <a href="http://isis.apache.org/how-tos/how-to-01-070-How-to-specify-the-icon-for-a-domain-entity.html">icon</a></li>
</ul>
@@ -602,7 +611,8 @@ mvn clean install
<h2>Actions</h2>
<ul>
-<li>update the domain property (<code>SimpleObject#name</code> above, renamed by now)</li>
+<li>update the domain action (<code>SimpleObject#name</code> above, renamed by now)</li>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/Named.html">@Named</a> annotation to specify the name of action parameters</li>
<li>use <a href="http://isis.apache.org/reference/recognized-annotations/ActionSemantics.html">@ActionSemantics</a> annotation to indicate the semantics of the action (safe/query-only, idempotent or non-idempotent)</li>
<li>annotate safe action as <a href="http://isis.apache.org/reference/recognized-annotations/Bookmarkable.html">@Bookmarkable</a>
<ul>
@@ -615,8 +625,8 @@ mvn clean install
<ul>
<li>add Chrome extensions
<ul>
-<li>install Postman</li>
-<li>install JSON-View</li>
+<li>install <a href="https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en">Postman</a></li>
+<li>install <a href="https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc?hl=en">JSON-View</a></li>
</ul></li>
<li>browse to Wicket viewer, install fixtures</li>
<li>browse to the http://localhost:8080/restful API</li>
@@ -626,7 +636,10 @@ mvn clean install
<h2>Specify Action semantics</h2>
<ul>
-<li>note the HTTP methods exposed in the REST API</li>
+<li>experiment changing [@ActionSemantics] on actions
+<ul>
+<li>note the HTTP methods exposed in the REST API change</li>
+</ul></li>
</ul>
<h2>Value properties</h2>
@@ -634,16 +647,23 @@ mvn clean install
<ul>
<li>add some <a href="http://isis.apache.org/how-tos/how-to-01-030-How-to-add-a-property-to-a-domain-entity.html">value properties</a>; also:
<ul>
-<li><a href="http://isis.apache.org/components/objectstores/jdo/mapping-mandatory-and-optional-properties.html">optional vs mandatory</a></li>
-<li><a href="http://isis.apache.org/components/objectstores/jdo/mapping-joda-dates.html">joda date/time</a></li>
-<li><a href="http://isis.apache.org/components/objectstores/jdo/mapping-bigdecimals.html">bigdecimals</a></li>
-<li><a href="http://isis.apache.org/components/objectstores/jdo/mapping-blobs.html">blob/clobs</a></li>
+<li>for string properties</li>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/MultiLine.html">@Multiline</a> annotation to render a text area instead of a text box</li>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/MaxLength.html">@MaxLength</a> annotation to specify the maximum number of characters allowable</li>
+<li>use <a href="http://isis.apache.org/components/objectstores/jdo/mapping-joda-dates.html">joda date/time</a> properties</li>
+<li>use <a href="http://isis.apache.org/components/objectstores/jdo/mapping-bigdecimals.html">bigdecimals</a> properties</li>
+<li>use <a href="http://isis.apache.org/components/objectstores/jdo/mapping-blobs.html">blob/clobs</a> properties</li>
+<li>specify whether <a href="http://isis.apache.org/components/objectstores/jdo/mapping-mandatory-and-optional-properties.html">optional or mandatory</a></li>
+</ul></li>
+<li>TODO: enums</li>
+<li>update the corresponding domain service for creating new instances
+<ul>
+<li>for all non-optional properties will either need to prompt for a value, or calculate some suitable default</li>
</ul></li>
-<li>update the corresponding domain service
+<li>change the implementation of title, if need be
<ul>
-<li>if not optional</li>
+<li>might prefer to use <a href="http://isis.apache.org/reference/recognized-annotations/Title.html">@Title</a> annotation rather than the <code>title()</code> method</li>
</ul></li>
-<li>update the title, if need be</li>
<li><a href="http://isis.apache.org/how-tos/how-to-01-080-How-to-specify-the-order-in-which-properties-or-collections-are-displayed.html">order the properties</a> using the <a href="http://isis.apache.org/reference/recognized-annotations/MemberOrder.html">@MemberOrder</a> annotation and <a href="http://isis.apache.org/reference/recognized-annotations/MemberGroupLayout.html">@MemberGroupLayout</a> annotation
<ul>
<li>see also this <a href="http://isis.apache.org/components/viewers/wicket/static-layouts.html">static layouts</a> documentation</li>
@@ -673,7 +693,7 @@ mvn clean install
<h2>Collections</h2>
<ul>
-<li>Ensure that all domain classes implement <code>Comparable</code>
+<li>Ensure that all domain classes implement <code>java.lang.Comparable</code>
<ul>
<li>use the <a href="http://isis.apache.org/reference/Utility.html">ObjectContracts</a> utility class to help implement <code>Comparable</code> (also <code>equals()</code>, <code>hashCode()</code>, <code>toString()</code>)</li>
</ul></li>
@@ -681,7 +701,7 @@ mvn clean install
<ul>
<li>Use <code>SortedSet</code> as the class</li>
</ul></li>
-<li>TODO: @Render (http://isis.apache.org/reference/recognized-annotations/Render.html)</li>
+<li>Use the @Render (http://isis.apache.org/reference/recognized-annotations/Render.html) annotation to indicate if the collection should be visible or hidden by default</li>
<li>optional: Use the <a href="http://isis.apache.org/reference/recognized-annotations/SortedBy.html">@SortedBy</a> annotation to specify a different comparator than the natural ordering</li>
</ul>
@@ -701,7 +721,9 @@ mvn clean install
<li>Delete the <code>@MemberOrder</code> annotations and use the associated <a href="http://isis.apache.org/components/viewers/wicket/dynamic-layouts.html">.layout.json</a> file to specify layout hints instead</li>
</ul>
-<h2>Business rules: See it!</h2>
+<h2>Business rules</h2>
+
+<h3>See it!</h3>
<ul>
<li>Use the <a href="http://isis.apache.org/reference/recognized-annotations/Hidden.html">@Hidden</a> annotation to make properties/collections/actions invisible
@@ -711,22 +733,27 @@ mvn clean install
<li>Use the <code>hideXxx()</code> supporting method on <a href="http://isis.apache.org/how-tos/how-to-02-010-How-to-hide-a-property.html">properties</a>, <a href="http://isis.apache.org/how-tos/how-to-02-020-How-to-hide-a-collection.html">collections</a> and <a href="http://isis.apache.org/how-tos/how-to-02-030-How-to-hide-an-action.html">actions</a> to make a property/collection/action invisible according to some imperative rule</li>
</ul>
-<h2>Business rules: Use it!</h2>
+<h3>Use it!</h3>
<ul>
<li>Use the <a href="http://isis.apache.org/reference/recognized-annotations/Disabled.html">@Disabled</a> annotation to make properties read-only/actions non-invokable ('greyed out')</li>
<li>Use the <code>disabledXxx()</code> supporting method on <a href="http://isis.apache.org/how-tos/how-to-02-050-How-to-prevent-a-property-from-being-modified.html">properties</a> and <a href="http://isis.apache.org/how-tos/how-to-02-070-How-to-prevent-an-action-from-being-invoked.html">actions</a> to make a property/action disabled according to some imperative rule</li>
</ul>
-<h2>Business rules: Do it!</h2>
+<h3>Do it!</h3>
<ul>
-<li>Use the <a href="http://isis.apache.org/reference/recognized-annotations/RegEx.html">@Regex</a> annotation on string properties or action parameters to validate strings</li>
-<li>Use the <a href="">@MinLength</a></li>
+<li>Validate string properties or action paramters:
+<ul>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/RegEx.html">@Regex</a> annotation to specify a pattern</li>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/MinLength.html">@MinLength</a> annotation to indicate a minimum number of characters</li>
+</ul></li>
+<li>For any data type:
+<ul>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/MustSatisfy.html">@MustSatisfy</a> annotation to specify an arbitrary constraint</li>
+</ul></li>
</ul>
-<p>(or more generally the <a href="">@MustSatisfy</a> annotation) to specify </p>
-
<p>Use the <code>validateXxx()</code> supporting method on [properties](</p>
<ul>
@@ -750,6 +777,10 @@ mvn clean install
<p>.layout.json</p>
+<p>CSS</p>
+
+<p>@HomePage</p>
+
<p>view models</p>
<p>Customising the REST API</p>
@@ -771,1718 +802,7 @@ mvn clean install
<p>composite fixture scripts (a la Estatio)</p>
-<h2>Stuff to do...</h2>
-
-<ul>
-<li>rename domain class</li>
-<li>rename domain service</li>
-<li>fixture scripts</li>
-</ul>
-
-<p>Tests</p>
-
-<ul>
-<li>delete the BDD specs</li>
-</ul>
-
-<h2>Dele</h2>
-
-<h2>Rename domain class, domain service, fixtures</h2>
-
-<h4>SimpleObject</h4>
-
-<p>rename to <code>ConferenceSession</code></p>
-
-<h4>SimpleObjects</h4>
-
-<p>rename to <code>ConferenceSessions</code></p>
-
-<h4>SimpleObjectsFixture</h4>
-
-<p>rename to <code>ConferenceSessionsFixture</code></p>
-
-<h4>myapp-webapp/src/main/webapp/WEB-INF/isis.properties</h4>
-
-<pre>
-isis.services = \
- 10:dom.simple.SimpleObjects,\
- ...
-
-isis.fixtures=fixture.simple.SimpleObjectsFixture
-</pre>
-
-<p>to</p>
-
-<pre>
-isis.services = \
- 10:dom.simple.ConferenceSessions,\
- ...
-
-isis.fixtures=fixture.simple.ConferenceSessionsFixture
-</pre>
-
-<h4>ConferenceSessionsFixture</h4>
-
-<pre>
-isisJdoSupport.executeUpdate("delete from \"SimpleObject\"");
-</pre>
-
-<p>to:</p>
-
-<pre>
-isisJdoSupport.executeUpdate("delete from \"ConferenceSession\"");
-</pre>
-
-<h4>icon</h4>
-
-<ul>
-<li>add icon, <code>icons/OverheadProjector.png</code> -> <code>myapp/dom/src/main/resources/images/ConferenceSession.png</code></li>
-</ul>
-
-<h4>ConferenceSessions</h4>
-
-<ul>
-<li>getId()</li>
-</ul>
-
-<p><em>a "nice to have", but has an effect on the URLs exposed in the REST API</em></p>
-
-<pre>
-public String getId() {
- return "conferenceSessions";
-</div>
-</pre>
-
-<ul>
-<li><code>iconName()</code></li>
-</ul>
-
-<pre>
-public String iconName() {
- return "ConferenceSession";
-</div>
-</pre>
-
-<h4>ConferenceSession</h4>
-
-<p><em>a "nice to have", but has an effect on the URLs exposed in the REST API</em></p>
-
-<pre>
-@ObjectType("SESSION")
-</pre>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>menu service changed to "Conference Sessions"</li>
-<li>tooltip on icon/title changed to "Conference Session"</li>
-<li>icon changed</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-030</code></p>
-
-<h2>Refactor domain class</h2>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>rename <code>name</code> property to <code>sessionTitle</code></li>
-<li>update <code>compareTo()</code></li>
-</ul>
-
-<pre>
-public int compareTo(ConferenceSession other) {
- return ObjectContracts.compare(this, other, "sessionTitle");
-</div>
-</pre>
-
-<ul>
-<li>review title</li>
-</ul>
-
-<pre>
-@Title
-public String getSessionTitle() {
- return sessionTitle;
-</div>
-</pre>
-
-<h4>ConferenceSessionsFixture</h4>
-
-<ul>
-<li>Better fixture data</li>
-</ul>
-
-<pre>
-private void installObjects() {
- create("RRRADDD! Apache Isis");
- create("Best practices for AngularJS");
- create("Refactor your specs!");
- create("Why Kotlin?");
- create("Introduction to the Play Framework");
- create("Object Oriented Design in the Wild");
-</div>
-</pre>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>table shows sessionTitle column</li>
-<li>object form shows sessionTitle property</li>
-<li>object page labelled with title</li>
-<li>updated data</li>
-</ul>
-
-<p>also</p>
-
-<ul>
-<li>discuss injected services into fixtures, entities, services etc.</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-040</code></p>
-
-<h2>Add date scalar property</h2>
-
-<h4>ConferenceSession</h4>
-
-<pre>
-// //////////////////////////////////////
-// date (property)
-// //////////////////////////////////////
-
-private LocalDate date;
-
-@javax.jdo.annotations.Persistent
-@javax.jdo.annotations.Column(allowsNull = "true")
-@MemberOrder(sequence = "1")
-public LocalDate getDate() {
- return date;
-</div>
-
-public void setDate(final LocalDate date) {
- this.date = date;
-</div>
-</pre>
-
-<h4>ConferenceSessionsFixture</h4>
-
-<pre>
-private ConferenceSession create(final String name) {
- ...
- session.setDate(clockService.now().plusDays((int)(Math.random()*5)));
- ...
-</div>
-</pre>
-
-<p>and</p>
-
-<pre>
-private ClockService clockService;
-
-public final void injectClockService(final ClockService clockService) {
- this.clockService = clockService;
-</div>
-</pre>
-
-<p>></p>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>now has a date property in title and in form</li>
-<li>can edit, obviously</li>
-<li>is optional</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-050</code></p>
-
-<h2>Add enum scalar property</h2>
-
-<ul>
-<li>type property</li>
-</ul>
-
-<h5>ConferenceSession</h5>
-
-<pre>
-// //////////////////////////////////////
-// Type (property)
-// //////////////////////////////////////
-
-public enum Type {
- KEYNOTE, ALL_DAY_TUTORIAL, SESSION, OTHER
-</div>
-
-private Type Type;
-@javax.jdo.annotations.Column(allowsNull="false")
-@MemberOrder(sequence = "1")
-public Type getType() {
- return Type;
-</div>
-public void setType(final Type Type) {
- this.Type = Type;
-</div>
-</pre>
-
-<h5>ConferenceSessions</h5>
-
-<ul>
-<li>update the <code>create(...)</code> method</li>
-</ul>
-
-<pre>
-public ConferenceSession create(
- final @Named("Name") String name,
- final @Named("Type") ConferenceSession.Type type) {
- final ConferenceSession obj = newTransientInstance(ConferenceSession.class);
- obj.setSessionTitle(name);
- obj.setType(type);
- persistIfNotAlready(obj);
- return obj;
-</div>
-</pre>
-
-<h5>ConferenceSessionsFixture</h5>
-
-<ul>
-<li>update the fixture data</li>
-</ul>
-
-<pre>
-private void installObjects() {
- create("RRRADDD! Apache Isis", ConferenceSession.Type.SESSION);
- create("Best practices for AngularJS", ConferenceSession.Type.SESSION);
- create("Refactor your specs!", ConferenceSession.Type.SESSION);
- create("Why Kotlin?", ConferenceSession.Type.OTHER);
- create("Introduction to the Play Framework", ConferenceSession.Type.SESSION);
- create("Object Oriented Design in the Wild", ConferenceSession.Type.ALL_DAY_TUTORIAL);
-</div>
-
-private ConferenceSession create(final String name, final Type type) {
- return simpleObjects.create(name, type);
-</div>
-</pre>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>list all
-<ul>
-<li>table with new property</li>
-</ul></li>
-<li>show object form</li>
-<li>edit object details
-<ul>
-<li>both are mandatory</li>
-</ul></li>
-<li>create new object</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-060</code></p>
-
-<h2>UI hints</h2>
-
-<h5>`ConferenceSession</h5>
-
-<ul>
-<li>sessionTitle, then type, then date</li>
-</ul>
-
-<pre>
-@MemberOrder(sequence="1")
-public String getSessionTitle() {
-</pre>
-
-<ul>
-<li>then type</li>
-</ul>
-
-<pre>
-@MemberOrder(sequence = "2")
-public Type getType() {
-</pre>
-
-<ul>
-<li>then date (hide in table)</li>
-</ul>
-
-<pre>
-@Hidden(where=Where.ALL_TABLES)
-@MemberOrder(name="Scheduling", sequence = "3")
-public LocalDate getDate() {
-</pre>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>table shows only title, type properties (in that order)</li>
-<li>order form shows
-<ul>
-<li>title, type properties grouped together in the default 'General' group</li>
-<li>date property separately in a new 'Scheduling' group</li>
-</ul></li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-070</code></p>
-
-<h2>Add another type (and domain service)</h2>
-
-<h4>Speaker</h4>
-
-<pre><code>package dom.simple;
-
-import javax.jdo.annotations.IdentityType;
-import javax.jdo.annotations.VersionStrategy;
-
-import org.apache.isis.applib.DomainObjectContainer;
-import org.apache.isis.applib.annotation.Bookmarkable;
-import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.ObjectType;
-import org.apache.isis.applib.annotation.Title;
-import org.apache.isis.applib.util.ObjectContracts;
-
-@javax.jdo.annotations.PersistenceCapable(identityType=IdentityType.DATASTORE)
-@javax.jdo.annotations.DatastoreIdentity(
- strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY,
- column="id")
-@javax.jdo.annotations.Version(
- strategy=VersionStrategy.VERSION_NUMBER,
- column="version")
-@ObjectType("SPEAKER")
-@Bookmarkable
-public class Speaker implements Comparable<Speaker> {
-
-
- // //////////////////////////////////////
- // givenName (property)
- // //////////////////////////////////////
-
- private String givenName;
-
- @MemberOrder(sequence = "1")
- @javax.jdo.annotations.Column(allowsNull="false")
- @Title(sequence="2", prepend=", ")
- public String getGivenName() {
- return givenName;
- }
-
- public void setGivenName(final String name) {
- this.givenName = name;
- }
-
-
- // //////////////////////////////////////
- // familyName (property)
- // //////////////////////////////////////
-
- private String familyName;
-
- @MemberOrder(sequence = "2")
- @javax.jdo.annotations.Column(allowsNull="false")
- @Title(sequence="1")
- public String getFamilyName() {
- return familyName;
- }
-
- public void setFamilyName(final String familyName) {
- this.familyName = familyName;
- }
-
-
- // //////////////////////////////////////
- // compareTo
- // //////////////////////////////////////
-
- @Override
- public int compareTo(Speaker other) {
- return ObjectContracts.compare(this, other, "givenName, familyName");
- }
-
-
- // //////////////////////////////////////
- // Injected
- // //////////////////////////////////////
-
- @SuppressWarnings("unused")
- private DomainObjectContainer container;
- public void injectDomainObjectContainer(final DomainObjectContainer container) {
- this.container = container;
- }
-
-}
-</code></pre>
-
-<h4>Speakers</h4>
-
-<pre><code>package dom.simple;
-
-import java.util.List;
-
-import org.apache.isis.applib.AbstractFactoryAndRepository;
-import org.apache.isis.applib.annotation.ActionSemantics;
-import org.apache.isis.applib.annotation.ActionSemantics.Of;
-import org.apache.isis.applib.annotation.Bookmarkable;
-import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Named;
-
-public class Speakers extends AbstractFactoryAndRepository {
-
- @Override
- public String getId() {
- return "speakers";
- }
-
- public String iconName() {
- return "Speaker";
- }
-
- @Bookmarkable
- @ActionSemantics(Of.SAFE)
- @MemberOrder(sequence = "1")
- public List<Speaker> listAll() {
- return allInstances(Speaker.class);
- }
-
- @MemberOrder(sequence = "2")
- public Speaker create(
- final @Named("Given name") String givenName,
- final @Named("Family name") String familyName) {
- Speaker speaker = getContainer().newTransientInstance(Speaker.class);
- speaker.setGivenName(givenName);
- speaker.setFamilyName(familyName);
- getContainer().persistIfNotAlready(speaker);
- return speaker;
- }
-}
-</code></pre>
-
-<h5>ConferenceSessionsFixture</h5>
-
-<ul>
-<li>install()</p>
-
-<pre><code>isisJdoSupport.executeUpdate("delete from \"Speaker\"");
-</code></pre></li>
-<li>installObjects()</p>
-
-<pre><code>createSpeaker("Dan", "Haywood");
-createSpeaker("Misko", "Hevery");
-createSpeaker("Cyrille", "Martraire");
-createSpeaker("Svetlana", "Haywood");
-createSpeaker("James", "Ward");
-createSpeaker("Jessica", "Kerr");
-</code></pre></li>
-<li>createSpeaker() method:</p>
-
-<pre><code>private Speaker createSpeaker(final String givenName, final String familyName) {
- return speakers.create(givenName, familyName);
-}
-</code></pre></li>
-<li>and inject new domain service:</p>
-
-<pre><code>private Speakers speakers;
-public final void injectSpeakers(final Speakers speakers) {
- this.speakers = speakers;
-}
-</code></pre></li>
-</ul>
-
-<h5>isis.properties</h5>
-
-<ul>
-<li>number indicates the ordering in the menu</p>
-
-<pre><code>isis.services = \
- 10:dom.simple.ConferenceSessions,\
- 20:dom.simple.Speakers,\
-</code></pre></li>
-</ul>
-
-<h4>icon</h4>
-
-<ul>
-<li>add icon, <code>icons/Customer.gif</code> -> <code>myapp/dom/src/main/resources/images/Speaker.gif</code></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>new domain service</li>
-<li>list, create</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-080</code></p>
-
-<h2>Add reference property</h2>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>'speaker' property (of type 'Speaker')</p>
-
-<pre><code>// //////////////////////////////////////
-// speaker (property)
-// //////////////////////////////////////
-
-
-private Speaker speaker;
-
-
-@javax.jdo.annotations.Column(allowsNull="true")
-@MemberOrder(sequence = "3")
-public Speaker getSpeaker() {
- return speaker;
-}
-
-
-public void setSpeaker(final Speaker speaker) {
- this.speaker = speaker;
-}
-</code></pre></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>displays field, no way to associate :-(</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-090</code></p>
-
-<h2>Add autoComplete (and repository queries)</h2>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>autoComplete method:</p>
-
-<pre><code>public List<Speaker> autoCompleteSpeaker(String search) {
- return speakers.findByGivenOrFamilyName(search);
-}
-</code></pre></li>
-<li>inject service:</p>
-
-<pre><code>private Speakers speakers;
-
-
-public final void injectSpeakers(final Speakers speakers) {
- this.speakers = speakers;
-}
-</code></pre></li>
-</ul>
-
-<h5>Speakers</h5>
-
-<ul>
-<li>finder (hidden because only intended to be called programmatically)</p>
-
-<pre><code>@Hidden
-public List<Speaker> findByGivenOrFamilyName(String search) {
- return getContainer().allMatches(
- new QueryDefault<Speaker>(Speaker.class,
- "findByGivenOrFamilyName",
- "givenOrFamilyName", ".*"+search+".*"));
-}
-</code></pre></li>
-</ul>
-
-<h5>Speaker</h5>
-
-<ul>
-<li>Isis configured to use JDO, so add the annotation</p>
-
-<pre><code>@javax.jdo.annotations.Queries({
- @javax.jdo.annotations.Query(
- name="findByGivenOrFamilyName", language="JDOQL",
- value="SELECT "
- + "FROM dom.simple.Speaker "
- + "WHERE givenName.matches(:givenOrFamilyName) "
- + " || familyName.matches(:givenOrFamilyName)")
-})
-</code></pre></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>can now add speaker
-<ul>
-<li>using either given or family name</li>
-</ul></li>
-<li>Speakers finder is not visible</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-100</code></p>
-
-<h2>Update title (also, prototype actions)</h2>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>title imperatively, rather than declaratively</p>
-
-<pre><code>public String title() {
- final TitleBuffer buf = new TitleBuffer();
- if(getSessionTitle().length()>20) {
- buf.append(getSessionTitle().substring(0, 20)).append("...");
- } else {
- buf.append(getSessionTitle());
- }
- if(getSpeaker() != null) {
- buf.append("(").append(container.titleOf(getSpeaker())).append(")");
- }
- return buf.toString();
-}
-</code></pre></li>
-</ul>
-
-<h4>DEMO:</h4>
-
-<ul>
-<li>metamodel validation exception</li>
-</ul>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>remove <code>@Title</code> from <code>#sessionTitle</code></li>
-</ul>
-
-<h4>ConferenceSessions</h4>
-
-<ul>
-<li>Add prototyping action to domain service</p>
-
-<pre><code>@Bookmarkable
-@Prototype
-@ActionSemantics(Of.SAFE)
-public ConferenceSession firstOne() {
- return listAll().get(0);
-}
-</code></pre></li>
-</ul>
-
-<h4>DEMO:</h4>
-
-<ul>
-<li>now runs ok</li>
-<li>prototype action styled differently</li>
-<li>add speaker to session</li>
-<li>title changes</li>
-<li>(optional) run in SERVER mode</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-110</code></p>
-
-<h2>Another new entity, Tag</h2>
-
-<p><em>the motivation for this is that we want to add a new collection of tags from ConferenceSession</em></p>
-
-<h4>Tag</h4>
-
-<ul>
-<li>new entity (note the <code>compareTo</code>, <code>toString</code>, using Isis helper methods)</p>
-
-<pre><code>package dom.simple;
-
-
-import javax.jdo.annotations.IdentityType;
-import javax.jdo.annotations.VersionStrategy;
-
-
-import org.apache.isis.applib.DomainObjectContainer;
-import org.apache.isis.applib.annotation.Bookmarkable;
-import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.ObjectType;
-import org.apache.isis.applib.annotation.Title;
-import org.apache.isis.applib.util.ObjectContracts;
-
-
-@javax.jdo.annotations.PersistenceCapable(identityType=IdentityType.DATASTORE)
-@javax.jdo.annotations.DatastoreIdentity(
- strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY,
- column="id")
-@javax.jdo.annotations.Version(
- strategy=VersionStrategy.VERSION_NUMBER,
- column="version")
-@ObjectType("TAG")
-@Bookmarkable
-public class Tag implements Comparable<Tag> {
-
-
-<pre><code>// //////////////////////////////////////
-// name (property)
-// //////////////////////////////////////
-
-
-private String name;
-
-
-@javax.jdo.annotations.Column(allowsNull = "true")
-@Title
-@MemberOrder(sequence = "1")
-public String getName() {
- return name;
-}
-
-
-public void setName(final String name) {
- this.name = name;
-}
-
-
-// //////////////////////////////////////
-// compareTo
-// //////////////////////////////////////
-
-
-@Override
-public int compareTo(Tag other) {
- return ObjectContracts.compare(this, other, "name");
-}
-
-
-// //////////////////////////////////////
-// toString
-// //////////////////////////////////////
-
-
-@Override
-public String toString() {
- return ObjectContracts.toString(this, "name");
-}
-
-
-// //////////////////////////////////////
-// Injected
-// //////////////////////////////////////
-
-
-@SuppressWarnings("unused")
-private DomainObjectContainer container;
-public void injectDomainObjectContainer(final DomainObjectContainer container) {
- this.container = container;
-}
-</code></pre>
-
-}
-</code></pre></li>
-</ul>
-
-<h4>Tags</h4>
-
-<ul>
-<li>new domain service; all actions are hidden</p>
-
-<pre><code>package dom.simple;
-
-
-import java.util.List;
-
-
-import org.apache.isis.applib.AbstractFactoryAndRepository;
-import org.apache.isis.applib.annotation.Hidden;
-
-
-public class Tags extends AbstractFactoryAndRepository {
-
-
-<pre><code>@Override
-public String getId() {
- return "tags";
-}
-public String iconName() {
- return "Tag";
-}
-
-
-@Hidden
-public List&lt;Tag&gt; listAll() {
- return allInstances(Tag.class);
-}
-@Hidden
-public Tag create(final String name) {
- final Tag obj = newTransientInstance(Tag.class);
- obj.setName(name);
- persistIfNotAlready(obj);
- return obj;
-}
-</code></pre>
-
-}
-</code></pre></li>
-</ul>
-
-<h4>ConferenceSessionsFixture</h4>
-
-<ul>
-<li>installObjects()</p>
-
-<pre><code>createTag("UX");
-createTag("Mobile");
-createTag("Java");
-createTag("Agile");
-createTag(".NET");
-createTag("Test");
-createTag("Mastery");
-createTag("Web");
-createTag("Architecture");
-createTag("Dev Ops");
-createTag("Cloud");
-createTag("Languages");
-createTag("Tools");
-createTag("Team");
-createTag("Database");
-createTag("Javascript");
-createTag("Keynote");
-</code></pre></li>
-<li>createTag() method</p>
-
-<pre><code>private Tag createTag(final String name) {
- return tags.create(name);
-}
-</code></pre></li>
-<li>inject new service</p>
-
-<pre><code>private Tags tags;
-public final void injectTags(final Tags tags) {
- this.tags = tags;
-}
-</code></pre></li>
-</ul>
-
-<h4>isis.properties</h4>
-
-<ul>
-<li>no number required, since not visible in UI</p>
-
-<pre><code>isis.services = \
- 10:dom.simple.ConferenceSessions,\
- 20:dom.simple.Speakers,\
- dom.simple.Tags,\
-</code></pre></li>
-</ul>
-
-<h4>icon</h4>
-
-<ul>
-<li>add icon, <code>icons/RedFlag.png</code> -> <code>myapp/dom/src/main/resources/images/Tag.png</code></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>(no visible change, since Tags actions are all hidden) </li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-120</code></p>
-
-<h2>Add collection</h2>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>tags collection</p>
-
-<pre><code>// //////////////////////////////////////
-// tags (collection)
-// //////////////////////////////////////
-
-
-@javax.jdo.annotations.Join
-@javax.jdo.annotations.Element(dependent = "false")
-private SortedSet<Tag> tags = new TreeSet<Tag>();
-
-
-@Render(Render.Type.EAGERLY)
-@Disabled
-@MemberOrder(sequence = "1")
-public SortedSet<Tag> getTags() {
- return tags;
-}
-
-
-public void setTags(final SortedSet<Tag> tags) {
- this.tags = tags;
-}
-</code></pre></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>view the DDL</li>
-<li>open object</li>
-<li>can't add tags :-(</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-130</code></p>
-
-<h2>Add actions</h2>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>addTag action</p>
-
-<pre><code>// //////////////////////////////////////
-// addTag (action)
-// //////////////////////////////////////
-
-
-@MemberOrder(sequence = "1")
-public ConferenceSession addTag(final Tag tag) {
- getTags().add(tag);
- return this;
-}
-</code></pre></li>
-<li>removeTag action</p>
-
-<pre><code>// //////////////////////////////////////
-// removeTag (action)
-// //////////////////////////////////////
-
-
-@MemberOrder(sequence = "2")
-public ConferenceSession removeTag(final Tag tag) {
- getTags().remove(tag);
- return this;
-}
-</code></pre></li>
-</ul>
-
-<h4>ConferenceSessionsFixture</h4>
-
-<ul>
-<li>create(...) method, add three (random) tags for each session</p>
-
-<pre><code>for (Tag tag : random(tags.listAll(), 3)) {
- session.addTag(tag);
-}
-</code></pre></li>
-<li>supporting random(...) method</p>
-
-<pre><code>private List<Tag> random(List<Tag> tags, int num) {
- List<Tag> availableTags = Lists.newArrayList(tags);
- List<Tag> selectedTags = Lists.newArrayList();
- while(selectedTags.size()<num) {
- int selected = (int)(availableTags.size() * Math.random());
- try {
- selectedTags.add(availableTags.remove(selected));
- } catch(Exception ex) {}
- }
- return selectedTags;
-}
-</code></pre></li>
-</ul>
-
-<h4>DEMO:</h4>
-
-<ul>
-<li>view</li>
-<li>need to get a reference to the tag again...</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-140</code></p>
-
-<h2>Add choices</h2>
-
-<p><em>an alternative to using autoComplete, useful if the list of choices is relatively short</em></p>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>inject Tags repo</p>
-
-<pre><code>private Tags tagRepo;
-
-
-public final void injectTags(final Tags tags) {
- this.tagRepo = tags;
-}
-</code></pre></li>
-<li>added the "choices" supporting method for addTag action (all tags not yet added)</p>
-
-<pre><code>public Collection<Tag> choices0AddTag() {
- List<Tag> tags = Lists.newArrayList(tagRepo.listAll());
- tags.removeAll(getTags());
- return tags;
-}
-</code></pre></li>
-<li>added the "choices" supporting method for removeTag action (only those tags previously added)</p>
-
-<pre><code>public Collection<Tag> choices0RemoveTag() {
- return getTags();
-}
-</code></pre></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>can now add/remove tags, yay!</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-150</code></p>
-
-<h2>More UI Hints (dynamic, this time)</h2>
-
-<h4>DEMO:</h4>
-
-<ul>
-<li>download layout</li>
-<li>edit column spans</li>
-<li>copy <code>ConferenceSession.layout.json</code> (below) to <code>myapp-dom/src/main/java/dom/simple</code></li>
-<li>refresh layout</p>
-
-<pre><code>{
- "columns": [
- {
- "span": 6,
- "memberGroups": {
- "General": {
- "members": {
- "sessionTitle": {},
- "type": {},
- "speaker": {}
- }
- },
- "Scheduling": {
- "members": {
- "date": {}
- }
- }
- }
- },
- {
- "span": 0,
- "memberGroups": {}
- },
- {
- "span": 0,
- "memberGroups": {}
- },
- {
- "span": 6,
- "collections": {
- "tags": {
- "actions": {
- "addTag": {},
- "removeTag": {}
- }
- }
- }
- }
- ],
- "actions": {
- "downloadLayout": {},
- "refreshLayout": {}
- }
-}
-</code></pre></li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-160</code></p>
-
-<h2>Declarative and imperative business rules</h2>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>add RegEx pattern and a maximum length to the 'sessionTitle' property (latter using JDO annotation)</p>
-
-<pre><code>@javax.jdo.annotations.Column(allowsNull="false",length=40)
-@RegEx(validation="[^%]+")
-public String getSessionTitle() {
-</code></pre></li>
-<li>can't add more than 4 tags</p>
-
-<pre><code>public String disableAddTag(Tag tag) {
- return getTags().size() >= 4? "Cannot add more than 4 tags": null;
-}
-</code></pre></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>cannot save session whose sessionTitle property has > 40 chars</li>
-<li>cannot add more than 4 tags to a session</li>
-*
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-170</code></p>
-
-<h2>Other business rules (hiding, validation)</h2>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>hideAddTag </p>
-
-<pre><code>public boolean hideAddTag() {
- return getTags().size() >= 4;
-}
-</code></pre></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>action becomes hidden</li>
-</ul>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>offer tags that are already in the collection, but then prevent using validation</p>
-
-<pre><code>public Collection<Tag> choices0AddTag() {
- List<Tag> tags = Lists.newArrayList(tagRepo.listAll());
- //tags.removeAll(getTags());
- return tags;
-}
-
-
-public String validateAddTag(Tag tag) {
- return getTags().contains(tag)? "Already added that tag": null;
-}
-</code></pre></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>can attempt to add a tag, but prevented</li>
-</ul>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>backing out the above changes</p>
-
-<pre><code>public Collection<Tag> choices0AddTag() {
- List<Tag> tags = Lists.newArrayList(tagRepo.listAll());
- tags.removeAll(getTags());
- return tags;
-}
-
-
-// public String validateAddTag(Tag tag) {
-// return getTags().contains(tag)? "Already added that tag": null;
-// }
-
-
-// public boolean hideAddTag() {
-// return getTags().size() >= 4;
-// }
-
-
-public String disableAddTag(Tag tag) {
- return getTags().size() >= 4? "Cannot add more than 4 tags": null;
-}
-</code></pre></li>
-</ul>
-
-<h2>Contributed Actions etc</h2>
-
-<h4>ConferenceSessions</h4>
-
-<ul>
-<li>add action, will be contributed to Tag, as both action and a collection </p>
-
-<pre><code>// //////////////////////////////////////
-// Contributions
-// //////////////////////////////////////
-
-
-// @NotContributed
-@NotInServiceMenu
-public List<ConferenceSession> findByTag(Tag tag) {
- return allMatches(new QueryDefault<ConferenceSession>(
- ConferenceSession.class, "findByTag", "tag", tag));
-}
-</code></pre></li>
-</ul>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>declare JDO query</p>
-
-<pre><code>@javax.jdo.annotations.Queries({
- @javax.jdo.annotations.Query(
- name="findByTag", language="JDOQL",
- value="SELECT "
- + "FROM dom.simple.ConferenceSession "
- + "WHERE tags.contains(:tag)")
-})
-</code></pre></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>view Tag; shows the sessions that are associated with it</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-180</code></p>
-
-<h2>Bookmarks</h2>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>bookmarked objects
-<ul>
-<li>eg <code>ConferenceSession</code></li>
-</ul></li>
-<li>bookmarked actions
-<ul>
-<li>eg <code>ConferenceSessions#listAll</code></li>
-</ul></li>
-</ul>
-
-<h2>CSS</h2>
-
-<h4>myapp-webapp/src/main/webapp/css/application.css</h4>
-
-<ul>
-<li>example custom CSS already defined for "x-highlight" and "x-caution" CSS classes </li>
-</ul>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>addTag, add annotation</p>
-
-<pre><code>@CssClass("x-highlight")
-public ConferenceSession addTag(final Tag tag) {
-</code></pre></li>
-<li>removeTag, add annotation</p>
-
-<pre><code>@CssClass("x-caution")
-public ConferenceSession removeTag(final Tag tag) {
-</code></pre></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>view session object</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-190</code></p>
-
-<h2>Widgets, also @HomePage</h2>
-
-<p><em>show Conference sessions on a calendar view. This uses a "third-party" component, up on github</em></p>
-
-<h4>myapp/pom.xml</h4>
-
-<ul>
-<li>parent module, scope=import for dependency management</p>
-
-<pre><code><dependencyManagement>
- <dependencies>
- ...
-
-
-<pre><code> &lt;dependency&gt;
- &lt;groupId&gt;com.danhaywood.isis.wicket&lt;/groupId&gt;
- &lt;artifactId&gt;danhaywood-isis-wicket-fullcalendar2&lt;/artifactId&gt;
- &lt;version&gt;1.3.0&lt;/version&gt;
- &lt;type&gt;pom&lt;/type&gt;
- &lt;scope&gt;import&lt;/scope&gt;
- &lt;/dependency&gt;
-
-
-&lt;/dependencies&gt;
-</code></pre>
-
-</dependencyManagement>
-</code></pre></li>
-</ul>
-
-<h4>myapp-dom/pom.xml</h4>
-
-<ul>
-<li>dom project depends on modules 'applib' (minimal coupling, defines an interface and value type)</p>
-
-<pre><code><dependencies>
- ...
-
-
-<pre><code>&lt;dependency&gt;
- &lt;groupId&gt;com.danhaywood.isis.wicket&lt;/groupId&gt;
- &lt;artifactId&gt;danhaywood-isis-wicket-fullcalendar2-applib&lt;/artifactId&gt;
-&lt;/dependency&gt;
-</code></pre>
-
-</dependencies>
-</code></pre></li>
-</ul>
-
-<h4>myapp-webapp/pom.xml</h4>
-
-<ul>
-<li>webapp project references the actual widget UI implementation</p>
-
-<pre><code><dependencies>
- ...
-
-
-<pre><code>&lt;dependency&gt;
- &lt;groupId&gt;com.danhaywood.isis.wicket&lt;/groupId&gt;
- &lt;artifactId&gt;danhaywood-isis-wicket-fullcalendar2-ui&lt;/artifactId&gt;
-&lt;/dependency&gt;
-</code></pre>
-
-</dependencies>
-</code></pre></li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-200</code></p>
-
-<h4>ConferenceSession</h4>
-
-<ul>
-<li>declare it implements the <code>CalendarEventable</code> interface</p>
-
-<pre><code>public class ConferenceSession implements Comparable<ConferenceSession>
- , com.danhaywood.isis.wicket.fullcalendar2.applib.CalendarEventable {
-</code></pre></li>
-<li>and implement the required methods:</p>
-
-<pre><code> // //////////////////////////////////////
- // CalendarEventable impl
- // //////////////////////////////////////
-
-
-<pre><code>@Programmatic
-@Override
-public String getCalendarName() {
- return "date";
-}
-
-
-@Programmatic
-@Override
-public CalendarEvent toCalendarEvent() {
- return new CalendarEvent(
- getDate().toDateTimeAtStartOfDay(),
- "date",
- container.titleOf(this));
-}
-</code></pre>
-
-</code></pre></li>
-</ul>
-
-<h2>ConferenceSessions</h2>
-
-<ul>
-<li>indicate that the listAll action should be called for the home page</p>
-
-<pre><code>@HomePage
-@Bookmarkable
-@ActionSemantics(Of.SAFE)
-@MemberOrder(sequence = "1")
-public List<ConferenceSession> listAll() {
- return allInstances(ConferenceSession.class);
-}
-</code></pre></li>
-</ul>
-
-<h4>DEMO</h4>
-
-<ul>
-<li>all sessions displayed automatically on home page</li>
-<li>switch to calendar view (top right, new icon should appear)</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-210</code></p>
-
-<h2>View models</h2>
-
-<h4>SpeakerViewModel</h4>
-
-<ul>
-<li>shows summary info of speaker, their sessions, the tags of those sessions</p>
-
-<pre><code>package dom.simple;
-
-
-import java.util.List;
-import java.util.Set;
-
-
-import org.apache.isis.applib.DomainObjectContainer;
-import org.apache.isis.applib.ViewModel;
-import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Render;
-import org.apache.isis.applib.annotation.Render.Type;
-import org.apache.isis.applib.annotation.Title;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.bookmark.BookmarkService;
-
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.io.BaseEncoding;
-
-
-public class SpeakerViewModel implements ViewModel {
-
-
-<pre><code>// //////////////////////////////////////
-// ViewModel impl
-// //////////////////////////////////////
-
-
-@Override
-public String viewModelMemento() {
- Bookmark bookmark = bookmarkService.bookmarkFor(getSpeaker());
- return encode(bookmark);
-}
-
-
-@Override
-public void viewModelInit(String memento) {
- Bookmark bookmark = decode(memento);
- setSpeaker((Speaker) bookmarkService.lookup(bookmark));
-}
-
-
-static String encode(Bookmark bookmark) {
- return BaseEncoding.base32().encode(bookmark.toString().getBytes());
-}
-
-
-private static Bookmark decode(String memento) {
- return new Bookmark(new String(BaseEncoding.base32().decode(memento)));
-}
-
-
-// //////////////////////////////////////
-// speaker (property)
-// //////////////////////////////////////
-
-
-private Speaker speaker;
-
-
-@javax.jdo.annotations.Column(allowsNull = "true")
-@Title
-@MemberOrder(sequence = "1")
-public Speaker getSpeaker() {
- return speaker;
-}
-
-
-public void setSpeaker(final Speaker speaker) {
- this.speaker = speaker;
-}
-
-
-// //////////////////////////////////////
-// numberOfSessions (property)
-// //////////////////////////////////////
-
-
-@MemberOrder(sequence = "2")
-public int getNumberOfSessions() {
- return getPresenting().size();
-}
-
-
-// //////////////////////////////////////
-// presenting (collection)
-// //////////////////////////////////////
-
-
-@Render(Type.EAGERLY)
-@MemberOrder(sequence = "1")
-public List&lt;ConferenceSession&gt; getPresenting() {
- return container.allMatches(ConferenceSession.class, new Predicate&lt;ConferenceSession&gt;() {
- @Override
- public boolean apply(ConferenceSession input) {
- return input.getSpeaker() == getSpeaker();
- }
- });
-}
-
-
-// //////////////////////////////////////
-// tags (collection)
-// //////////////////////////////////////
-
-
-@Render(Type.EAGERLY)
-@MemberOrder(sequence = "2")
-public List&lt;Tag&gt; getTags() {
- return Lists.newArrayList(
- Iterables.concat(
- Iterables.transform(
- getPresenting(),
- new Function&lt;ConferenceSession, Set&lt;Tag&gt;&gt;(){
- @Override
- public Set&lt;Tag&gt; apply(ConferenceSession input) {
- return input.getTags();
- }
- })
- ));
-}
-
-
-// //////////////////////////////////////
-// Injected
-// //////////////////////////////////////
-
-
-private DomainObjectContainer container;
-public final void injectDomainObjectContainer(final DomainObjectContainer container) {
- this.container = container;
-}
-
-
-private BookmarkService bookmarkService;
-public final void injectBookmarkService(final BookmarkService bookmarkService) {
- this.bookmarkService = bookmarkService;
-}
-</code></pre>
-
-}
-</code></pre></li>
-</ul>
-
-<h4>Speakers</h4>
-
-<ul>
-<li>new action to return all speakers as home page (should really be in its own application service...)</p>
-
-<pre><code>// //////////////////////////////////////
-// view models
-// //////////////////////////////////////
-
-
-@ActionSemantics(Of.SAFE)
-@HomePage
-public List<SpeakerViewModel> all() {
- return Lists.newArrayList(Iterables.transform(listAll(),
- new Function<Speaker, SpeakerViewModel>() {
- @Override
- public SpeakerViewModel apply(Speaker input) {
- Bookmark bookmark = bookmarkService.bookmarkFor(input);
- return getContainer().newViewModelInstance(
- SpeakerViewModel.class,
- SpeakerViewModel.encode(bookmark));
- }
- })
- );
-}
-</code></pre></li>
-<li>and inject in the framework-provided <code>BookmarkService</code> </p>
-
-<pre><code>// //////////////////////////////////////
-// Injected
-// //////////////////////////////////////
-
-
-private BookmarkService bookmarkService;
-
-
-public final void injectBookmarkService(final BookmarkService bookmarkService) {
- this.bookmarkService = bookmarkService;
-}
-</code></pre></li>
-</ul>
-
-<h4>ConferenceSessions</h4>
-
-<ul>
-<li>remove @HomePage annotation from the earlier <code>listAll()</code> action</li>
-</ul>
-
-<h4>ConferenceSessionsFixture</h4>
-
-<ul>
-<li>create(...) set up random speaker with each session</p>
-
-<pre><code>session.setSpeaker(randomSpeaker());
-</code></pre></li>
-<li>randomSpeaker() supporting method</p>
-
-<pre><code>private Speaker randomSpeaker() {
- List<Speaker> all = speakers.listAll();
- while(true) {
- try {
- int selected = (int)(all.size() * Math.random());
- return all.get(selected);
- } catch(Exception ex){}
- }
-}
-</code></pre></li>
-</ul>
-
-<h4>Demo</h4>
-
-<ul>
-<li>hit the home page</li>
-<li>select a speaker view model</li>
-</ul>
-
-<h5>Checkpoint:</h5>
-
-<p><code>git reset --hard checkpoint-220</code></p>
-
-<h2>REST API</h2>
-
-<h4>DEMO</h4>
-
-<ul>
-<li><a href="http://localhost:8080/restful">http://localhost:8080/restful</a></li>
-</ul>
+<p>integration tests</p>