I can't find anyone writing about this, but this works when executed anonymously:
public class foo {
public void bar() {
system.debug('\n\n#### FOO BAR ####\n\n');
}
}
foo f = new foo();
f.bar();
That looks useful! Anyone taking advantage of this in clever ways already?
I want to use it to evolve Salesforce's SearchAndReplace example so I can write anonymous clean up code that takes full advantage of batching. It wouldn't let me implement Database.Batchable in my anonymous apex class so I tried this anonymous code which implements an interface and it's one method to approximate a delegate:
public class anonBatchUpdater implements BatchUtil.BatchUtilable {
public boolean HandleRecord(Sobject s, BatchUtil bu) {
// this is basically a delegate in my anonymous APEX where I can do things
// more complicated than allowed by SearchAndReplace(q,e,f,v) like:
// s.put('field1', s.get('field2') + s.get('field3'));
// for now let's just see if we get here...
bu.Log('Saw record ' + s.Id);
return false;
}
}
integer BATCH_SIZE = 100;
string SOQL = 'select company from lead limit 3';
Database.executeBatch(new BatchUtil(SOQL, new anonBatchUpdater()), BATCH_SIZE);
Backed by this real class:
// Allows for doing quick batch updates in Anonymous Apex
global class BatchUtil implements Database.Batchable<sObject>, Database.Stateful {
global final String Query;
global final BatchUtilable Updater;
private String RunningLog; // depends on Database.Stateful
global interface BatchUtilable {
boolean HandleRecord(Sobject s, BatchUtil bu);
}
global void Log(string log) {
RunningLog += log;
}
global BatchUtil(String q, BatchUtilable upd) {
Query = q;
Updater = upd;
RunningLog = 'Started BatchUtil...\n';
}
global Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List<sObject> scope) {
List<sObject> ToCommit = new List<Sobject>();
for(Sobject s : scope) {
if (updater.HandleRecord(s, this)) {
ToCommit.add(s);
}
}
if (ToCommit.size() > 0)
database.update(ToCommit, false);
// TODO: log saveresults
}
global void finish(Database.BatchableContext BC) {
system.debug(LoggingLevel.INFO, '\n\n\n\n################\nSTART BATCHUTIL LOG:\n\n' + runningLog + '\n\nEND BATCHUTIL LOG\n################\n');
}
}
My tests pass fine:
public class BatchUtilTest implements BatchUtil.BatchUtilable {
public boolean HandleRecord(Sobject s, BatchUtil bu) {
string log = '';
if (s.get('LastName') == 'testing1') {
s.put('Company', 'updated');
bu.Log('Found ' + s.get('LastName') + ' and updated company to: "' + s.get('Company') + '"\n');
return true; // we want to update this record
} else {
bu.Log('Ignoring ' + s.get('LastName'));
return false; // skip this record
}
}
@isTest
static void Test() {
Lead l1 = new Lead();
l1.LastName = 'testing1';
l1.Company = 'testing1';
insert l1; // pull immediately to get LastModifiedDate for testing later
l1 = [select LastName, Company, LastModifiedDate from Lead where Id = :l1.Id];
Lead l2 = new Lead();
l2.LastName = 'testing2';
l2.Company = 'testing2';
insert l2; // pull immediately to get LastModifiedDate for testing later
l2 = [select LastName, Company, LastModifiedDate from Lead where Id = :l2.Id];
Test.startTest();
Database.executeBatch(new BatchUtil('select lastname, company from lead', new BatchUtilTest()), 10);
Test.stopTest();
Lead l1refreshed = [select LastName, Company, LastModifiedDate from Lead where Id = :l1.Id];
system.assertEquals('updated', l1refreshed.Company);
system.assertNotEquals(l1.LastModifiedDate, l1refreshed.LastModifiedDate);
Lead l2refreshed = [select LastName, Company, LastModifiedDate from Lead where Id = :l2.Id];
system.assertEquals('testing2', l2refreshed.Company);
system.assertEquals(l2.LastModifiedDate, l2refreshed.LastModifiedDate);
}
}
And give output like this:
START BATCHUTIL LOG:
Started BatchUtil...
Found testing1 and updated company to: "updated"
Ignoring testing2
END BATCHUTIL LOG
But when I try from anonymous apex the job just sits in Apex Jobs with a Status of Queued and I finally dug this out of the logs:
FATAL_ERROR|Internal Salesforce.com Error
Have I pushed anonymous apex classes too far? Anything I can do to make my code work? Is there an idea or two on the ideaexchange that would make this code work? Anyone have a similar utility already?