anchor.diff:      %% Using list_to_atom here so that the extractor module
       %% does not need to be pre-loaded.  
       list_to_atom(List);
  -to_modfun(Atom) when is_list(Atom) ->
  +to_modfun(Atom) when is_atom(Atom) ->
       Atom;
   to_modfun(Val) ->
       throw({"cannot convert to module/function name", Val}).
  
positive.code: %% Run the extraction function against the RiakObject to get a list of
  %% search fields and data
  -spec run_extract(riak_object(), extractdef()) -> search_fields().
  run_extract(RiakObject, {{modfun, Mod, Fun}, Args}) ->
      Mod:Fun(RiakObject, Args);
  run_extract(_, _) ->
      throw({error, not_implemented}).
  
  -ifdef(TEST).
  
  
negative.code:             Precommit == IndexHook
      end.
  
  run_mod_fun_extract_test() ->
      %% Try the anonymous function
      TestObj = conflict_test_object(),
      Extractor = validate_extractor({{modfun, ?MODULE, run_mod_fun_extract_test_fun}, undefined}),
      ?assertEqual([{"data",<<"some data">>}],
                   run_extract(TestObj, Extractor)).
   
  run_mod_fun_extract_test_fun(O, _Args) ->
      StrVals = [binary_to_list(B) || B <- riak_object:get_values(O)],
      Data = string:join(StrVals, " "),
      [{"data", list_to_binary(Data)}].
  
  run_qfun_extract_test() ->
      %% Try the anonymous function
      TestObj = conflict_test_object(),
      Fun1 = fun(O, _Args) ->
                     StrVals = [binary_to_list(B) || B <- riak_object:get_values(O)],
                     Data = string:join(StrVals, " "),
                     [{"data", list_to_binary(Data)}]
             end,
      Extractor = validate_extractor({{qfun, Fun1}, undefined}),
      ?assertEqual([{"data",<<"some data">>}],
                   run_extract(TestObj, Extractor)).
   
      
  
  anon_js_extract_test() ->
      maybe_start_app(sasl),
      maybe_start_app(erlang_js),
      {ok, JsSup} = riak_kv_js_sup:start_link(),
      {ok, JsMgr} = riak_kv_js_manager:start_link(2),
  
      %% Anonymous JSON function with default argument
      %% Join together all the values in a search field
      %% called "data" and the argument as "arg"
      JustObjectSource = "function(o) {
                  var vals = [];
                  for (var i = 0; i < o.values.length; i++) {
                    vals.push(o.values[i].data);
                  }
                  data = vals.join(\" \");
                  return {\"data\":data};
                }",
      ObjectArgSource = "function(o,a) {
                  var vals = [];
                  for (var i = 0; i < o.values.length; i++) {
                    vals.push(o.values[i].data);
                  }
                  data = vals.join(\" \");
                  return {\"data\":data, \"arg\":a.f};
                }",
   
      %% Try the anonymous function
      O = conflict_test_object(),
      Extractor1 = validate_extractor({{jsanon, JustObjectSource}, undefined}),
      ?assertEqual([{"data",<<"some data">>}],
                   run_extract(O, Extractor1)),
                   
      %% Anonymous JSON function with provided argument
      %% Arg = {struct [{<<"f">>,<<"v">>}]},
      Arg = {struct, [{<<"f">>,<<"v">>}]},
      Extractor2 = validate_extractor({{jsanon, ObjectArgSource}, Arg}),
      ?assertEqual([{"data",<<"some data">>}, 
                    {"arg", <<"v">>}],
                   run_extract(O, Extractor2)),
  
      stop_pid(JsMgr),
      stop_pid(JsSup),
      application:stop(erlang_js).
  
  conflict_test_object() ->
      O0 = riak_object:new(<<"b">>,<<"k">>,<<"v">>),
      riak_object:set_contents(O0, [{dict:new(), <<"some">>},
                                    {dict:new(), <<"data">>}]).
      
  
  maybe_start_app(App) ->
      case application:start(App) of
          {error, {already_started, App}} ->
              ok;
          ok ->
              ok
      end.
  
  stop_pid(Pid) ->
      unlink(Pid),
      exit(Pid, kill).
  
  -endif. % TEST
      
  