Add submit_rule examples from Gerrit User Summit 2012.

Shawn had a different approach on writing Prolog submit rules
than what I initially wrote in the Prolog cookbook. Include
most of the Shawn's examples into the cookbook and relate them
to the already existing examples to give readers more choice
when writing own submit rules.

Some existing examples where reformatted to match the formatting
style of the new examples.

Change-Id: I1cda434cca3cd5806748974df053b94ed3bc1389
Signed-off-by: Sasa Zivkov <sasa.zivkov@sap.com>
diff --git a/Documentation/prolog-cookbook.txt b/Documentation/prolog-cookbook.txt
index 24463ba5..97da42d 100644
--- a/Documentation/prolog-cookbook.txt
+++ b/Documentation/prolog-cookbook.txt
@@ -347,7 +347,8 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(submit(label('Any-Label-Name', ok(_)))).
+  submit_rule(submit(W)) :-
+    W = label('Any-Label-Name', ok(_)).
 ====
 
 In this case we make no use of facts about the change. We don't need it as we are simply
@@ -365,7 +366,9 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(submit(label('Code-Review', ok(_)), label('Verified', ok(_)))).
+  submit_rule(submit(CR, V)) :-
+    CR = label('Code-Review', ok(_)),
+    V = label('Verified', ok(_)).
 ====
 
 Since for every change all label statuses are `'ok'` every change will be submittable.
@@ -380,7 +383,8 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(submit(label('Any-Label-Name', reject(_)))).
+  submit_rule(submit(R)) :-
+    R = label('Any-Label-Name', reject(_)).
 ====
 
 Since for any change we return only one label with status `reject`, no change
@@ -396,13 +400,17 @@
 [caption=""]
 ====
   % In the UI this will show: Need Any-Label-Name
-  submit_rule(submit(label('Any-Label-Name', need(_)))).
+  submit_rule(submit(N)) :-
+    N = label('Any-Label-Name', need(_)).
 
   % We could define more "need" labels by adding more rules
-  submit_rule(submit(label('Another-Label-Name', need(_)))).
+  submit_rule(submit(N)) :-
+    N = label('Another-Label-Name', need(_)).
 
   % or by providing more than one need label in the same rule
-  submit_rule(submit(label('X-Label-Name', need(_)), label('Y-Label-Name', need(_)))).
+  submit_rule(submit(NX, NY)) :-
+    NX = label('X-Label-Name', need(_)),
+    NY = label('Y-Label-Name', need(_)).
 ====
 
 In the UI this will show:
@@ -429,8 +437,11 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(label('Some-Condition', need(_))).
-  submit_rule(label('Another-Condition', ok(_))).
+  submit_rule(submit(N)) :-
+    N = label('Some-Condition', need(_)).
+
+  submit_rule(submit(OK)) :-
+    OK = label('Another-Condition', ok(_)).
 ====
 
 The 'Need Some-Condition' will not be show in the UI because of the result of
@@ -441,8 +452,11 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(label('Another-Condition', ok(_))).
-  submit_rule(label('Some-Condition', need(_))).
+  submit_rule(submit(OK)) :-
+    OK = label('Another-Condition', ok(_)).
+
+  submit_rule(submit(N)) :-
+    N = label('Some-Condition', need(_)).
 ====
 
 The result of the first rule will stop search for any further solutions.
@@ -459,7 +473,8 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(submit(label('Author-is-John-Doe', need(_)))).
+  submit_rule(submit(Author)) :-
+    Author = label('Author-is-John-Doe', need(_)).
 ====
 
 This will show:
@@ -472,9 +487,12 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(submit(label('Author-is-John-Doe', need(_)))).
-  submit_rule(submit(label('Author-is-John-Doe', ok(_))))
-    :- gerrit:commit_author(_, 'John Doe', _).
+  submit_rule(submit(Author)) :-
+    Author = label('Author-is-John-Doe', need(_)).
+
+  submit_rule(submit(Author)) :-
+    gerrit:commit_author(_, 'John Doe', _),
+    Author = label('Author-is-John-Doe', ok(_)).
 ====
 
 In the second rule we return `ok` status for the `'Author-is-John-Doe'` label
@@ -490,9 +508,12 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(submit(label('Author-is-John-Doe', need(_)))).
-  submit_rule(submit(label('Author-is-John-Doe', ok(_))))
-    :- gerrit:commit_author(_, _, 'john.doe@example.com').
+  submit_rule(submit(Author)) :-
+    Author = label('Author-is-John-Doe', need(_)).
+
+  submit_rule(submit(Author)) :-
+    gerrit:commit_author(_, _, 'john.doe@example.com'),
+    Author = label('Author-is-John-Doe', ok(_)).
 ====
 
 or by user id (assuming it is 1000000):
@@ -500,9 +521,12 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(submit(label('Author-is-John-Doe', need(_)))).
-  submit_rule(submit(label('Author-is-John-Doe', ok(_))))
-    :- gerrit:commit_author(user(1000000), _, _).
+  submit_rule(submit(Author)) :-
+    Author = label('Author-is-John-Doe', need(_)).
+
+  submit_rule(submit(Author)) :-
+    gerrit:commit_author(user(1000000), _, _),
+    Author = label('Author-is-John-Doe', ok(_)).
 ====
 
 or by a combination of these 3 attributes:
@@ -510,13 +534,16 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(submit(label('Author-is-John-Doe', need(_)))).
-  submit_rule(submit(label('Author-is-John-Doe', ok(_))))
-    :- gerrit:commit_author(_, 'John Doe', 'john.doe@example.com').
+  submit_rule(submit(Author)) :-
+    Author = label('Author-is-John-Doe', need(_)).
+
+  submit_rule(submit(Author)) :-
+    gerrit:commit_author(_, 'John Doe', 'john.doe@example.com'),
+    Author = label('Author-is-John-Doe', ok(_)).
 ====
 
-Example 7: Make change submittable if commit message starts with "Trivial fix"
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Example 7: Make change submittable if commit message starts with "Fix "
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Besides showing how to make use of the commit message text the purpose of this
 example is also to show how to match only a part of a string symbol. Similarly
 like commit author the commit message is provided as a string symbol which is
@@ -534,9 +561,12 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(submit(label('Commit-Message-starts-with-Trivial-Fix', need(_)))).
-  submit_rule(submit(label('Commit-Message-starts-with-Trivial-Fix', ok(_))))
-    :- gerrit:commit_message(M), name(M, L), starts_with(L, "Trivial Fix").
+  submit_rule(submit(Fix)) :-
+    Fix = label('Commit-Message-starts-with-Fix', need(_)).
+
+  submit_rule(submit(Fix)) :-
+    gerrit:commit_message(M), name(M, L), starts_with(L, "Fix "),
+    Fix = label('Commit-Message-starts-with-Fix', ok(_)).
 
   starts_with(L, []).
   starts_with([H|T1], [H|T2]) :- starts_with(T1, T2).
@@ -555,30 +585,74 @@
 .rules.pl
 [caption=""]
 ====
-  submit_rule(submit(label('Commit-Message-starts-with-Trivial-Fix', need(_)))).
-  submit_rule(submit(label('Commit-Message-starts-with-Trivial-Fix', ok(_))))
-    :- gerrit:commit_message_matches('^Trivial Fix').
+  submit_rule(submit(Fix)) :-
+    Fix = label('Commit-Message-starts-with-Fix', need(_)).
+
+  submit_rule(submit(Fix)) :-
+    gerrit:commit_message_matches('^Fix '),
+    Fix = label('Commit-Message-starts-with-Fix', ok(_)).
 ====
 
-Reusing the default submit policy
----------------------------------
+The previous example could also be written so that it first checks if the commit
+message starts with 'Fix '. If true then it sets OK for that category and stops
+further backtracking by using the cut `!` operator:
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(Fix)) :-
+    gerrit:commit_message_matches('^Fix '),
+    Fix = label('Commit-Message-starts-with-Fix', ok(_)),
+    !.
+
+  % Message does not start with 'Fix ' so Fix is needed to submit
+  submit_rule(submit(Fix)) :-
+    Fix = label('Commit-Message-starts-with-Fix', need(_)).
+====
+
+The default submit policy
+-------------------------
 All examples until now concentrate on one particular aspect of change data.
 However, in real-life scenarios we would rather want to reuse Gerrit's default
-submit policy and extend/change it for our specific purpose. In other words, we
-would like to keep all the default policies (like the `Verified` category,
-vetoing change, etc...) and only extend/change an aspect of it. For example, we
-may want to disable the ability for change authors to approve their own changes
-but keep all other policies the same.
+submit policy and extend/change it for our specific purpose.  This could be
+done in one of the following ways:
 
+* understand how the default submit policy is implemented and use that as a
+  template for implementing custom submit rules,
+* invoke the default submit rule implementation and then perform further
+  actions on its return result.
+
+Default submit rule implementation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The default submit rule with the two default categories, `Code-Review` and
+`Verified`, can be implemented as:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(V, CR)) :-
+    gerrit:max_with_block(-2, 2, 'Code-Review', CR),
+    gerrit:max_with_block(-1, 1, 'Verified', V).
+====
+
+Once this implementation is understood it can be customized to implement
+project specific submit rules. Note, that this implementation hardcodes
+the two default categories. Introducing a new category in the database would
+require introducing the same category here or a `submit_filter` in a parent
+project would have to care about including the new category in the result of
+this `submit_rule`.  On the other side, this example is easy to read and
+understand.
+
+Reusing the default submit policy
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 To get results of Gerrits default submit policy we use the
-`gerrit:default_submit` predicate. This means that if we write a submit rule like:
+`gerrit:default_submit` predicate.  The `gerrit:default_submit(X)` includes all
+categories from the database.  This means that if we write a submit rule like:
 
 .rules.pl
 [caption=""]
 ====
   submit_rule(X) :- gerrit:default_submit(X).
 ====
-
 then this is equivalent to not using `rules.pl` at all. We just delegate to
 default logic. However, once we invoke the `gerrit:default_submit(X)` we can
 perform further actions on the return result `X` and apply our specific
@@ -592,7 +666,7 @@
   project_specific_policy(R, S) :- ...
 ====
 
-The following examples build on top of the default submit policy.
+In the following examples both styles will be shown.
 
 Example 8: Make change submittable only if `Code-Review+2` is given by a non author
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -600,6 +674,8 @@
 satisfied if there is at least one `Code-Review+2` from a non author. All other
 default policies like the `Verified` category and vetoing changes still apply.
 
+Reusing the `gerrit:default_submit`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 First, we invoke `gerrit:default_submit` to compute the result for the default
 submit policy and then add the `Non-Author-Code-Review` label to it.  The
 `Non-Author-Code-Review` label is added with status `ok` if such an approval
@@ -615,7 +691,8 @@
     S =.. [submit | R].
 
   add_non_author_approval(S1, S2) :-
-    gerrit:commit_author(A), gerrit:commit_label(label('Code-Review', 2), R),
+    gerrit:commit_author(A),
+    gerrit:commit_label(label('Code-Review', 2), R),
     R \= A, !,
     S2 = [label('Non-Author-Code-Review', ok(R)) | S1].
   add_non_author_approval(S1, [label('Non-Author-Code-Review', need(_)) | S1]).
@@ -638,6 +715,44 @@
 if the `cut` in the first rule is not reached and it only happens if a
 predicate before the `cut` fails.
 
+Don't use `gerrit:default_submit`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Let's implement the same submit rule the other way, without reusing the
+`gerrit:default_submit`:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(CR, V)) :-
+    base(CR, V),
+    CR = label(_, ok(Reviewer)),
+    gerrit:commit_author(Author),
+    Author \= Reviewer,
+    !.
+
+  submit_rule(submit(CR, V, N)) :-
+    base(CR, V),
+    N = label('Non-Author-Code-Review', need(_)).
+
+  base(CR, V) :-
+    gerrit:max_with_block(-2, 2, 'Code-Review', CR),
+    gerrit:max_with_block(-1, 1, 'Verified', V).
+====
+
+The latter implementation is probably easier to understand and the code looks
+cleaner. Note, however, that the latter implementation will always return the
+two standard categories only (`Code-Review` and `Verified`) even if a new
+category has beeen inserted into the database. To include the new category
+the `rules.pl` would need to be modified or a `submit_filter` in a parent
+project would have to care about including the new category in the result
+of this `submit_rule`.
+
+The former example, however, would include any newly added category as it
+invokes the `gerrit:default_submit` and then modifies its result.
+
+Which of these two behaviors is desired will always depend on how a particular
+Gerrit server is managed.
+
 Example 9: Remove the `Verified` category
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 A project has no build and test. It consists of only text files and needs only
@@ -646,6 +761,17 @@
 We also want the UI to not show the `Verified` category in the table with
 votes and on the voting screen.
 
+This is quite simple without reusing the 'gerrit:default_submit`:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(CR)) :-
+    gerrit:max_with_block(-2, 2, 'Code-Review', CR).
+====
+
+Implementing the same rule by reusing `gerrit:default_submit` is a bit more complex:
+
 .rules.pl
 [caption=""]
 ====
@@ -679,6 +805,27 @@
 The `remove_verified_category` and `add_non_author_approval` predicates are the
 same as defined in the previous two examples.
 
+Without reusing the `gerrit:default_submit` the same example may be implemented
+as:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(CR)) :-
+    base(CR),
+    CR = label(_, ok(Reviewer)),
+    gerrit:commit_author(Author),
+    Author \= Reviewer,
+    !.
+
+  submit_rule(submit(CR, N)) :-
+    base(CR),
+    N = label('Non-Author-Code-Review', need(_)).
+
+  base(CR) :-
+    gerrit:max_with_block(-2, 2, 'Code-Review', CR),
+====
+
 Example 11: Remove the `Verified` category from all projects
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Example 9, implements `submit_rule` that removes the `Verified` category from
@@ -700,7 +847,32 @@
   remove_verified_category([H|T], [H|R]) :- remove_verified_category(T, R).
 ====
 
-Example 12: 1+1=2 Code-Review
+Example 12: On release branches require DrNo in addition to project rules
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A new category 'DrNo' is added to the database and is required for release
+branches. To mark a branch as a release branch we use `drno('refs/heads/branch')`.
+
+.rules.pl
+[caption=""]
+====
+  drno('refs/heads/master').
+  drno('refs/heads/stable-2.3').
+  drno('refs/heads/stable-2.4').
+  drno('refs/heads/stable-2.5').
+  drno('refs/heads/stable-2.5').
+
+  submit_filter(In, Out) :-
+    gerrit:change_branch(Branch),
+    drno(Branch),
+    !,
+    In =.. [submit | I],
+    gerrit:max_with_block(-1, 1, 'DrNo', DrNo),
+    Out =.. [submit, DrNo | I].
+
+  submit_filter(In, Out) :- In = Out.
+====
+
+Example 13: 1+1=2 Code-Review
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 In this example we introduce accumulative voting to determine if a change is
 submittable or not. We modify the standard Code-Review to be accumulative, and make the
@@ -736,7 +908,34 @@
     S =.. [submit | Labels].
 ====
 
-Example 13: Master and apprentice
+Implementing the same example without using `gerrit:default_submit`:
+
+.rules.pl
+[caption=""]
+====
+  submit_rule(submit(CR, V)) :-
+    sum(2, 'Code-Review', CR),
+    gerrit:max_with_block(-1, 1, 'Verified', V).
+
+  % Sum the votes in a category. Uses a helper function score/2
+  % to select out only the score values the given category.
+  sum(VotesNeeded, Category, label(Category, ok(_))) :-
+    findall(Score, score(Category, Score), All),
+    sum_list(All, Sum),
+    Sum >= VotesNeeded,
+    !.
+  sum(VotesNeeded, Category, label(Category, need(VotesNeeded))).
+
+  score(Category, Score) :-
+    gerrit:commit_label(label(Category, Score), User).
+
+  % Simple Prolog routine to sum a list of integers.
+  sum_list(List, Sum)   :- sum_list(List, 0, Sum).
+  sum_list([X|T], Y, S) :- Z is X + Y, sum_list(T, Z, S).
+  sum_list([], S, S).
+====
+
+Example 14: Master and apprentice
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 The master and apprentice example allow you to specify a user (the `master`)
 that must approve all changes done by another user (the `apprentice`).
@@ -773,7 +972,7 @@
   add_apprentice_master(S, S).
 ====
 
-Example 14: Only allow Author to submit change
+Example 15: Only allow Author to submit change
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 This example adds a new needed category `Patchset-Author` for any user that is
 not the author of the patch. This effectively blocks all users except the author