Change whenjobs --set syntax (NOTE: breaks compatibility).
authorRichard W.M. Jones <rjones@redhat.com>
Sat, 10 Mar 2012 12:14:32 +0000 (12:14 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Sat, 10 Mar 2012 12:23:09 +0000 (12:23 +0000)
Change the whenjobs --set syntax to allow multiple variables to be set
atomically.  The new syntax looks like:

  whenjobs --set variable=value [variable=value ...]

NOTE: This breaks compatibility with whenjobs <= 0.5.

README
lib/whenproto.x
tests/jobs/t100_counter.ml
tests/jobs/t101_updown.ml
tests/jobs/t102_manyjobs.ml
tests/jobs/t200_ocaml_jobnames.ml
tests/jobs/t201_ocaml_set_variable.ml
tools/tutorial.ml
tools/whenjobs.ml
tools/whenjobs.pod

diff --git a/README b/README
index f1c7596..9aa9207 100644 (file)
--- a/README
+++ b/README
@@ -18,7 +18,7 @@ language.  Read the whenjobs(1) man page for full information.
   <<
     cd /my/git/repo
     tag=`git-describe --tags`
-    whenjobs --set version $tag
+    whenjobs --set version=$tag
   >>
 
   (* When the 'version' variable changes (ie. a new release is
@@ -34,14 +34,14 @@ language.  Read the whenjobs(1) man page for full information.
     git reset --hard $version
     ./configure
     make clean all check dist
-    whenjobs --set successful_local_build $version
+    whenjobs --set successful_local_build=$version
   >>
 
   (* In parallel, build on a remote machine. *)
   when changes version :
   <<
     ssh remote ./do_build $version
-    whenjobs --set successful_remote_build $version
+    whenjobs --set successful_remote_build=$version
   >>
 
   (* Only when the new release has been successfully built on local
index f7ea513..a1407fb 100644 (file)
@@ -89,7 +89,7 @@ typedef job job_list<>;
 program When {
   version V1 {
     status reload_file (void) = 1;
-    status set_variable (variable_name, variable) = 2;
+    status set_variable (variable_name, variable) = 2; /* obsolete */
     variable get_variable (variable_name) = 3;
     variable_name_list get_variable_names (void) = 4;
     status exit_daemon (void) = 5;
index 78471ec..b6ac29c 100644 (file)
 when reloaded () :
 <<
   echo $JOBSERIAL $JOBNAME >\> $HOME/test_output
-  whenjobs --set counter 0 --type int
+  whenjobs --set --type int counter=0
 >>
 
 every 2 seconds :
 <<
   echo $JOBSERIAL $JOBNAME $counter >\> $HOME/test_output
-  whenjobs --set counter $(($counter+1)) --type int
+  whenjobs --set --type int counter=$(($counter+1))
 >>
 
 when counter = 3 :
index 791a58b..6eb94c2 100644 (file)
 when reloaded () :
 <<
   echo $JOBSERIAL $JOBNAME >\> $HOME/test_output
-  whenjobs --set counter 0 --type int
+  whenjobs --set --type int counter=0
 >>
 
 every second :
 <<
   echo $JOBSERIAL $JOBNAME >\> $HOME/test_output
-  whenjobs --set counter $(($counter+1)) --type int
+  whenjobs --set --type int counter=$(($counter+1))
 >>
 
 when counter < 5 && counter mod 2 == 0 :
index a586345..41feb77 100644 (file)
 when reloaded () :
 <<
   echo $JOBSERIAL $JOBNAME >\> $HOME/test_output
-  whenjobs --set counter 0 --type int
+  whenjobs --set --type int counter=0
 >>
 
 job "counter 1"
 every second :
 <<
   echo $JOBSERIAL $JOBNAME >\> $HOME/test_output
-  whenjobs --set counter $(($counter+1)) --type int
+  whenjobs --set --type int counter=$(($counter+1))
 >>
 
 job "counter 2"
 every second :
 <<
   echo $JOBSERIAL $JOBNAME >\> $HOME/test_output
-  whenjobs --set counter $(($counter+1)) --type int
+  whenjobs --set --type int counter=$(($counter+1))
 >>
 
 job "counter 3"
 every second :
 <<
   echo $JOBSERIAL $JOBNAME >\> $HOME/test_output
-  whenjobs --set counter $(($counter+1)) --type int
+  whenjobs --set --type int counter=$(($counter+1))
 >>
 
 job "counter 4"
 every second :
 <<
   echo $JOBSERIAL $JOBNAME >\> $HOME/test_output
-  whenjobs --set counter $(($counter+1)) --type int
+  whenjobs --set --type int counter=$(($counter+1))
 >>
 
 when increases counter :
index c003700..35d74d4 100644 (file)
@@ -24,7 +24,7 @@ job (prefix ^ "job")
 every 2 seconds :
 <<
   echo $JOBSERIAL $JOBNAME >\> $HOME/test_output
-  whenjobs --set counter 1
+  whenjobs --set counter=1
 >>
 
 job (prefix ^ "finished")
index 50ee5ab..6d8dad4 100644 (file)
@@ -24,7 +24,7 @@ let () =
 every second :
 <<
   echo $JOBSERIAL $JOBNAME $counter >\> $HOME/test_output
-  whenjobs --set counter $(($counter+1))
+  whenjobs --set --type int counter=$(($counter+1))
 >>
 
 when counter > 100 :
index b0df108..397b96f 100644 (file)
@@ -31,7 +31,7 @@ every 10 minutes :
   # Get free blocks in /home
   free=`stat -f -c %b /home`
   # Set the variable 'free_space'
-  whenjobs --type int --set free_space $free
+  whenjobs --set --type int free_space=$free
 >>
 
    Use 'when <expr> : << >>' runs the shell script only when
index ff166a6..cfef9d6 100644 (file)
@@ -71,10 +71,23 @@ let jobsdir =
 let rec main () =
   (* Parse the command line arguments. *)
   let mode = ref None in
-  let typ = ref "string" in
+  let typ = ref `String in
 
   let set_mode m () = mode := Some m in
 
+  let set_type t =
+    typ :=
+      match t with
+      | "bool"|"boolean" -> `Bool
+      | "string" -> `String
+      | "int" -> `Int
+      | "float"|"double" -> `Float
+      | "unit" -> `Unit
+      | _ ->
+        eprintf "whenjobs: --type: unknown type (%s)\n" t;
+        exit 1
+  in
+
   let display_version () =
     printf "%s %s\n" Config.package_name Config.package_version;
     exit 0
@@ -96,15 +109,20 @@ let rec main () =
     "--set", Arg.Unit (set_mode `Set), " Set the variable";
     "--start", Arg.Unit (set_mode `Start), "name Start a job manually";
     "--tail", Arg.Unit (set_mode `Tail), "serial Tail job output";
-    "--type", Arg.Set_string typ, "bool|int|float|string|unit Set the variable type";
+    "--type", Arg.String set_type, "bool|int|float|string|.. Set the variable type";
     "--upload", Arg.Unit (set_mode `Upload), " Upload the script";
     "--variables", Arg.Unit (set_mode `Variables), " Display all variables and values";
     "-V", Arg.Unit display_version, " Display version number and exit";
     "--version", Arg.Unit display_version, " Display version number and exit";
   ] in
 
-  let args = ref [] in
-  let anon_fun str = args := str :: !args in
+  (* anon_fun normally just collects up the anonymous arguments as
+   * strings, and most modes just use 'args' as a list of strings.
+   * However for `Set mode we need to record the type of each argument
+   * as well, so we keep that in a separate list (argtypes).
+   *)
+  let argtypes = ref [] in
+  let anon_fun str = argtypes := (str, !typ) :: !argtypes in
 
   let usage_msg = "\
 Whenjobs is a powerful but simple cron replacement.
@@ -118,7 +136,7 @@ Editing the script:
 Get and set variables:
 
   whenjobs --get variable
-  whenjobs --set variable value
+  whenjobs --set variable=value
 
 Start and stop the per-user daemon:
 
@@ -132,17 +150,8 @@ Options:
   Arg.parse argspec anon_fun usage_msg;
 
   let mode = !mode in
-  let args = List.rev !args in
-
-  let typ = match !typ with
-    | "bool"|"boolean" -> `Bool
-    | "string" -> `String
-    | "int" -> `Int
-    | "float"|"double" -> `Float
-    | "unit" -> `Unit
-    | t ->
-      eprintf "whenjobs: --type: unknown type (%s)\n" t;
-      exit 1 in
+  let argtypes = List.rev !argtypes in
+  let args = List.map fst argtypes in
   let nr_args = List.length args in
   let arg1 = match args with [] -> "" | a::_ -> a in
 
@@ -166,13 +175,18 @@ Options:
     upload_file ()
 
   | Some `Set ->
-    if List.length args != 2 then (
-      eprintf "whenjobs --set variable value\n";
-      eprintf "If 'value' contains spaces, you may need to quote it.\n";
+    if nr_args = 2 && not (String.contains arg1 '=') then (
+      eprintf "'whenjobs --set variable value' is the old whenjobs <= 0.5 syntax!\n";
+      eprintf "You need to change this to:\n";
+      eprintf "  whenjobs --set variable=value\n";
       suggest_help ();
       exit 1
     );
-    set_variable (List.hd args) (List.hd (List.tl args)) typ
+    (* Just ignore the case where no variables are defined, to make
+     * it easier to write shell scripts.
+     *)
+    if nr_args > 0 then
+      set_variables argtypes
 
   | Some `Get ->
     if nr_args != 1 then (
@@ -311,39 +325,27 @@ and upload_file () =
   );
   stop_client client
 
-and set_variable name value typ =
-  let value = match typ with
-    | `Bool ->
-      (match value with
-      | "true"|"t"|"yes"|"y"|"on"|"1" -> `bool_t true
-      | "false"|"f"|"no"|"n"|"off"|"0" -> `bool_t false
-      | _ ->
-        eprintf "whenjobs: variable does not have a boolean value\n";
-        exit 1
-      )
-    | `String -> `string_t value
-    | `Int ->
-      (try ignore (big_int_of_string value)
-       with Failure _ ->
-         eprintf "whenjobs: variable is not an integer\n";
-         exit 1
-      );
-      `int_t value (* the string is what we pass over the wire *)
-    | `Float ->
-      (try `float_t (float_of_string value)
-       with Failure _ ->
-         eprintf "whenjobs: variable is not a floating point number\n";
-         exit 1
-      )
-    | `Unit ->
-      if value <> "" then (
-        eprintf "whenjobs: unit variables must be empty strings\n";
-        exit 1
-      );
-      `unit_t in
+and set_variables argtypes =
+  let vars = List.map (
+    fun (def, typ) ->
+      (* 'def' should have the form "name=value".  The value part may
+       * be missing, but the equals sign is required.
+       *)
+      let i =
+        try String.index def '='
+        with Not_found ->
+          eprintf "whenjobs: set: missing = sign in variable definition\n";
+          suggest_help ();
+          exit 1 in
+      let name = String.sub def 0 i in
+      let value = String.sub def (i+1) (String.length def - (i+1)) in
+      let value = value_of_string value typ in
+      { Whenproto_aux.sv_name = name; sv_value = value }
+  ) argtypes in
+  let vars = Array.of_list vars in
 
   let client = start_client () in
-  (match Whenproto_clnt.When.V1.set_variable client (name, value) with
+  (match Whenproto_clnt.When.V1.set_variables client vars with
   | `ok -> ()
   | `error msg ->
     eprintf "whenjobs: set: %s\n" msg;
@@ -494,6 +496,36 @@ and string_of_variable = function
   | `int_t i -> i (* passed on the wire as a string *)
   | `float_t f -> string_of_float f
 
+and value_of_string value = function
+  | `Bool ->
+    (match value with
+    | "true"|"t"|"yes"|"y"|"on"|"1" -> `bool_t true
+    | "false"|"f"|"no"|"n"|"off"|"0" -> `bool_t false
+    | _ ->
+      eprintf "whenjobs: variable does not have a boolean value\n";
+      exit 1
+    )
+  | `String -> `string_t value
+  | `Int ->
+    (try ignore (big_int_of_string value)
+     with Failure _ ->
+       eprintf "whenjobs: variable is not an integer\n";
+       exit 1
+    );
+    `int_t value (* the string is what we pass over the wire *)
+  | `Float ->
+    (try `float_t (float_of_string value)
+     with Failure _ ->
+       eprintf "whenjobs: variable is not a floating point number\n";
+       exit 1
+    )
+  | `Unit ->
+    if value <> "" then (
+      eprintf "whenjobs: unit variables must be empty strings\n";
+      exit 1
+    );
+    `unit_t
+
 let () =
   try main ()
   with
index 466bdb1..5750646 100644 (file)
@@ -14,7 +14,7 @@ Editing the jobs script:
 Get and set variables:
 
  whenjobs --get variable
- whenjobs --set variable value [--type bool|int|float|string]
+ whenjobs --set variable=value [variable=value ...]
  whenjobs --variables
 
 Start and stop the per-user daemon:
@@ -43,7 +43,7 @@ Periodic jobs are written like this:
  <<
    # Get the current load average.
    load=`awk '{print $1}' /proc/loadavg`
-   whenjobs --set load $load --type float
+   whenjobs --set --type float load=$load
  >>
 
 When-statements let you create jobs that run based on variables set
@@ -80,7 +80,7 @@ command line tool to examine and set variables:
 
  $ whenjobs --variables
  load=0.9
- $ whenjobs --set cat sushi
+ $ whenjobs --set cat=sushi
  $ whenjobs --get cat
  sushi
 
@@ -150,7 +150,7 @@ source, eg:
 
  whenjobs --lib $builddir/lib -e
 
-=item B<--set> variable value
+=item B<--set> variable=value [variable=value ...]
 
 =item B<--type> bool|int|float|string|unit
 
@@ -158,18 +158,33 @@ I<--set> sets the variable named C<variable> to the new C<value>.  The
 variable is created if it does not already exist.  Note that setting a
 variable can cause jobs to run immediately.
 
-To unset a variable, set it to the empty string:
+To unset a variable, set it to the empty string like this:
 
- whenjobs --set var ""
+ whenjobs --set var=
 
 By default variables are strings.  You can also set the type of a
-variable when setting it by adding the optional I<--type> parameter:
+variable when setting it by adding the optional I<--type> parameter.
+The I<--type> parameter should come I<before> the variable
+declaration, like this:
 
- whenjobs --set free_space 10000 --type int
+ whenjobs --set --type int free_space=10000
 
 See the discussion of variable types in the L</REFERENCE> section
 below.
 
+You can set multiple variables.  When setting multiple variables in a
+single command, the values are all changed in a single atomic
+operation.
+
+ whenjobs --set cat=sushi food=fish
+
+When using I<--type> and multiple variables, the type changes the
+remaining command line parameters until the next I<--type>, eg:
+
+ whenjobs --set cat=sushi --type float weight=3.5 --type string food=fish
+
+(C<cat> and C<food> are strings, and C<weight> is a float).
+
 =item B<--start> "job name"
 
 Start the job immediately and unconditionally.