How to show a commandline/terminal menu

You might have seen a lot of command line tools offering a small menu with a number of options to choose from. Also you might have written some bash scripts that you want to make available in your terminal as a menu. Here you can find a good example to get started.

Sample


#!/bin/bash
show_menu(){
NORMAL=`echo "\033[m"`
MENU=`echo "\033[36m"` #Blue
NUMBER=`echo "\033[33m"` #yellow
FGRED=`echo "\033[41m"`
RED_TEXT=`echo "\033[31m"`
ENTER_LINE=`echo "\033[33m"`
echo -e "${MENU}*********************************************${NORMAL}"
echo -e "${MENU}**${NUMBER} 1)${MENU} Create a file ${NORMAL}"
echo -e "${MENU}**${NUMBER} 2)${MENU} Search for a file ${NORMAL}"
echo -e "${MENU}**${NUMBER} 3)${MENU} Delete file ${NORMAL}"
echo -e "${MENU}**${NUMBER} 4)${MENU} List all files ${NORMAL}"
echo -e "${MENU}*********************************************${NORMAL}"
echo -e "${ENTER_LINE}Please enter a menu option and enter or ${RED_TEXT}enter to exit. ${NORMAL}"
read opt
}
function option_picked() {
COLOR='\033[01;31m' # bold red
RESET='\033[00;00m' # normal white
MESSAGE=${@:-"${RESET}Error: No message passed"}
echo -e "${COLOR}${MESSAGE}${RESET}"
}
clear
show_menu
while [ opt != '' ]
do
if [[ $opt = "" ]]; then
exit;
else
case $opt in
1) clear;
option_picked "Option 1 Picked";
echo "Command 1 for option 1";
echo "Command 2 for option 1...";
show_menu;
;;
2) clear;
option_picked "Option 2 Picked";
echo "Command 1 for option 2";
echo "Command 2 for option 2...";
show_menu;
;;
3) clear;
option_picked "Option 3 Picked";
echo "Command 1 for option 3";
echo "Command 2 for option 3....";
show_menu;
;;
4) clear;
option_picked "Option 4 Picked";
echo "Command 1 for option 4";
echo "Command 2 for option 4....";
show_menu;
;;
x)exit;
;;
\n)exit;
;;
*)clear;
option_picked "Pick an option from the menu";
show_menu;
;;
esac
fi
done

This menu sample code is organized into three main chunks. "show_menu" function takes care of styling and displaying options. option_picked function is used to show the option selected in a different color. Finally there is a while loop that is used to show the options continuously til you exit by hitting just enter.

Use cases

For an example, in Salesforce context you can use this script as a starter to get started with Salesforce DX commands. Salesforce DX commands are notoriously long and difficult to remember. You can setup a bash script with different options and user friendly labels. Then using the sample menu you can invoke necessary sfdx commands.
As final step you can alias the path to the bash script in you ".bash_profile" file. This will let you call your utility script anytime from terminal without worrying about path.

alias heydx="sh /Users/myname/Documents/Scripts/dxUtility.sh"

Apex Batch class template

Batch classes are used to process very large volume of data without hitting Salesforce governor limits. Batch classes should implement "Database.Batchable" interface and it has three methods that must be implemented. 

Start

This method executes only once at the start of the batch. It queries all the records that needs to be processed and passes a Database.Querylocator or an iterable of objects to next execute method.

Execute

This method gets executed multiple times for one batch invocation. The total number of records that need to be processed from start method is divided into small chunks of batch size(default is 200) and for each chunk execute method is executed one after another. Since each of these chunks get separate execution context, you get separate governor limits for each chunk.

Finish

This method gets executed once at the end of the batch. ie after all individual execute chunks are complete. This is usually used for post processing activities like sending email notifying admin that batch executed successfully. 

Sample - Batch to update number of contacts to account

/*******************************************************************************************
* @Name UpdateContactNumberBatch
* @Author FirstName LastName <author@email.com>
* @Date 09/20/2017
* @Group Apex Batches
* @Description This batch class updates a custom field in account with the number of contact
* under it
*******************************************************************************************/
/* MODIFICATION LOG
* Version Developer Date Description
*-------------------------------------------------------------------------------------------
* 1.0 Firstname 09/20/2017 Initial Creation
*******************************************************************************************/
global class UpdateContactNumberBatch implements Database.Batchable<sObject> {
/**************************************************************************************
* @Description This method gets all the accounts that needs to be processed and returns
* a QueryLocator
* @Param bc - BatchableContext
* @Return Database.QueryLocator - QueryLocator to the queried records
**************************************************************************************/
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator([SELECT Id,Contact_Count__c FROM Account]);
}
/**************************************************************************************
* @Description This method executes aggregate query on contact object and updates
* corresponding account with number of contacts under it
* @Param bc - BatchableContext
* @Param accountList - Chunk of accounts that needs to be processed
* @Return NA
**************************************************************************************/
global void execute(Database.BatchableContext bc, List<Account> accountList){
//Getting all Ids from list;
Set<Id> allAccIdSet = new Map<Id, Account>(accountList).keyset();
List<Account> accUpdateList = new List<Account>();
//Aggregating contacts by accounts
for(AggregateResult aggResult: [SELECT AccountId, Count(Id)
FROM Contact
WHERE AccountId =: accountList
GROUP BY AccountId]) {
Account acc = new Account(Id = (Id)aggResult.get('AccountId'));
acc.Contact_Count__c = (Integer)aggResult.get('expr0');
accUpdateList.add(acc);
allAccIdSet.remove(acc.Id);
}
for(Id accId: allAccIdSet) {
accUpdateList.add(new Account(Id = accId, Contact_Count__c = 0));
}
update accUpdateList;
}
/**************************************************************************************
* @Description This method executes any post-processing operations
* @Param bc - BatchableContext
* @Return NA
**************************************************************************************/
global void finish(Database.BatchableContext bc){
// execute any post-processing operations
}
}

Common use cases for apex batch classes are below,


  1. Process bulk data that cannot be processed in single execution due to governor limits. For example making updates to more than 10k records.
  2. If regular execution is hitting heap limit, usually we need to go for batch/future class. Regular heap limit is 6MB. But in asynchronous execution methods like batch or future classes we get a 12MB heap limit
  3. Making thousands of callouts. Suppose you have to get some details about your accounts from an external system by hitting a webservice during night. You can make only 100 callouts in one transaction.  
  4. Send custom emails(with complex data from multiple objects which is not possible with workflow) to multiple people from apex. Keep batch size 10 as salesforce allows only 10 outbound emails in single transaction
  5. Make more than 10 Future callouts. Here also keep batch size as 10 because salesforce allows only 10 future callouts in one transaction.