Friday, October 21, 2011

Project HardBoil: Day 1

To make HardBoil work really, really well, in the ideal world, it would link to the Google spreadsheet for the data and then validate the spreadsheet and its contents against the developer's Salesforce org.

However, in the real world (and in the interest of actually using this code to meet an immediate need for my own, full-time employer), the ideal will take way too long with too much technical expertise that I just don't have at this time. Also, I don't want to waste time just yet with trying to figure out how to create a GUI that allows the user to login to Google and pick out the spreadsheet with the test data.

So, where does that leave me with a day of progress?

I've created code that begins to define the following classes:
  • HardBoiler
  • SObject
  • SObjectField
  • SObjectRecord

Forcing my way into my Google Account and hard-coding the authentication and spreadsheet selection, I was able to setup HardBoiler to be constructed with a parameter for the spreadsheet's Zend_Gdata_Spreadsheets_WorksheetFeed. This in theory allows HardBoiler to initialize with the right Salesforce records by iterating through each row in each worksheet.

Everything looked good until I got down to the field level for a specific record, where Google did something interesting that screwed up my original plans: Google treats my column header as the key in an array that represents a row of cells in the spreadsheet; but the key is the column-header in lower case with non-alphanumeric characters stripped out.

My original plan was to use the column header to hold the fields' API names, but now it looks like my immediate challenge will be to figure out a plan B for enabling the code to match cell values to fields.

Project HardBoil: Kickoff

So, I've decided to start a project called HardBoil, for lack of a wittier name. What's the goal of this project? To produce a web app that generates boilerplate code for Apex test classes. Specifically, it generates boilerplate code for Apex test classes to create test data to be used in individual unit tests.

What might that code look like? Take a look at the inner TestRecordSet class in this example test class. Some basic elements of the boilerplate code include...
  • List of test records.
  • Map of test records by ID.
  • Map of test records by record name.
  • Constructor that creates the test records and populates the maps.
  • Standard getter methods to get test records by index, ID or record name.

Concept

  1. Create a Google spreadsheet containing test records for different objects, with each object on a separate worksheet.
  2. Login to HardBoiled, point to the spreadsheet, and click a button to generate hundreds of reliable lines of code to setup records for staging best-in-class unit tests in Apex.

Why?

Well, after a few scrambles to fix broken Apex code in a production org, I realized that there is a lot of wisdom in the "Testing Best Practices" article in the Force.com Apex Code Developer's Guide, Version 23.0.

The trouble with implementing this wisdom consistently and repeatedly is that when you have to setup test records for a bazillion objects, the exercise becomes extremely tedious and painful... not to mention error-prone.

Imagine writing the same boilerplate code for 5 objects, with 3 records per object, with each record having 10 fields filled in. At a minimum, you're looking at writing 5 x 3 x 10 = 150 assignment statements. And then, what do you do about Lookup and Master-Detail relationships?

And you have to write this code all over again for every single test class you write? And how do users trust that you really understand what you're doing with the data?

Added Benefit

Who wants to create test data? Well, if your users really care about the system, then they would want to create the test data! This is a great collaborative exercise where admins, developers and users can all learn from each other by reconciling how the system was intended to be used and how the system is actually used.

Developers can setup the spreadsheets and then get functional users to fill in the test data! This frees up the developer's time to focus on the important part that only (s)he can do: actually writing the unit test methods.

Thursday, October 13, 2011

JavaScript Hack to Disable Fields for Internal WebCenter Users

In ApplyYourself, it may sometimes be desirable for some application fields to be editable by the applicant and then locked down for internal WebCenter users after the application is submitted. One example may be the term to which the applicant originally applied, before a decision or subsequent deferral occurs.

In order to accomplish this, the following JavaScript hack may be used.

<script type="text/javascript">
function isViewedInWebCenter() {
  return document.domain == "webcenter.applyyourself.com";
} // function isViewedInWebCenter()

if (isViewedInWebCenter()) {
  var inputElement =
      document.getElementById(inputElementId);
  inputElement.disabled = true;
} // if (isViewedInWebCenter())
</script>

Adding this bit of code to the bottom of a section's HTML should do the trick.

Best Practice for Automated Task Creation?

In retrospect, I think a best practice for automating task creation in Salesforce is to use workflow rules instead of Apex triggers. The primary reason is that the tasks are easier to manage and can be avoided during a mass update is the user leaves the "trigger workflow" checkbox unmarked in the import wizard.

Trimming Whitespace in XSLT

After some digging with Google and a hit on the W3C website, it appears that the String.trim() equivalent in XSLT is normalize-space().

"XQuery 1.0 and XPath 2.0 Functions and Operators (Second Edition)." W3C.

Thursday, October 6, 2011

What Programs and Products Mean in Higher Education

The word "program" is very interesting in its application to higher education. If you ask any higher education administrator or staff member whether his or her institution offers programs, 99% (if not 100%) of the time you'll get a quick and confident "yes". But dive a little deeper and start asking how programs fit into the scheme of CRM, especially within the Salesforce framework, and you may run into a lot of unanswered questions (depending on which institution you're at).

At the crux of the confusion: What exactly is a program?

Merriam-Webster provides one definition of the word "program" as "a plan or system under which action may be taken toward a goal"; Merriam-Webster also provides an alternative definition as simply "curriculum". Great, now what?

Translated into higher education lingo, a program may be defined as a curriculum of study under which a student may enroll in classes with the goal of earning a degree or certification. In the world of enrollment, a program boils down to just another tool to entice people to purchase the products sold by a school, college or university.

At this point, another question may arise: Aren't programs the same thing as products in higher education?

The answer is "no", unless someone can give a convincing argument to support a different position. Products in higher education are the courses offered by an institution. Some may argue that students buy degrees, not courses. "A student comes to us saying that he wants a Bachelor of Science degree with a major in Accounting; the student doesn't come to us saying that they just want to take ACCT 101 and ACCT 102." This argument sort of makes sense but misses the mark in describing the true business relationship between institution and student. In reality, students buy courses and get degrees when enough courses are purchased and completed at an established standard of achievement.

So, in short, courses are the products, not programs. Programs exist to get customers to buy more products, which in the case of higher education are courses.

Armed with these two answers to two high-level questions, the conversation can now move on to how to setup this business model in Salesforce for effective CRM.

Thursday, September 8, 2011

Apex Data Loader for Mac, Linux and Unix

Salesforce had the foresight to code the Apex Data Loader (ADL) in Java, which is a cross-platform tool that should work on any computer that has a JRE installed. This implies that the ADL should work on all OS's, not just on Windows.

Force 201 made a great post about running ADL with a GUI, but I just wanted to know how to run the ADL in batch mode from a command line (so that I can script the process).

It turns out to be as easy as the following:
  1. Download and install the Apex Data Loader on a Windows computer.
  2. Copy the files in "%ProgramFiles%\salesforce.com\Apex Data Loader apiVersion" to the desired directory on the target computer or server. Use binary mode for data transfer when applicable.
  3. Setup the batch mode configuration files as you normally would. This generally includes config.properties and process-conf.xml.
  4. Look at "\bin\encrypt.bat" to figure out how to create an encrypted password if you need one.
  5. Look at the last few lines of "\bin\process.bat" in the folder to figure out what syntax to use to run ADL via the command line.

For example, with ADL 22.0 on Linux, I can change to the base directory containing all of the ADL files and run this command:
java -cp DataLoader.jar -Dsalesforce.config.dir=conf com.salesforce.dataloader.process.ProcessRunner process.name=classMeetingExtractProcess

classMeetingExtractProcess is the id of one of my beans defined in process-conf.xml.