There are ways to surface the namespace itself from within Apex (doesn't help us in JavaScript, yet).
Deriving the namespace prefix itself:
String rawPrefix = MyClass.class.getName().substringBefore('MyClass').substringBefore('.');
//this gives '' in any development org
//and gives 'ns' in the packaging org
Getting a single token which can be used to qualify Apex Classes:
String dotPrefix = MyClass.class.getName().substringBefore('MyClass');
//this gives '' in any development org
//and gives 'ns.' in the packaging org
Getting a single token to qualify Salesforce Objects:
String barPrefix = SObjectType.MyObject__c.Name.substringBefore('MyObject__c');
//this gives '' in any development org
//and gives 'ns__' in the packaging org
As long as there's a Class or SObject in the package, this gets a handle on the namespace prefix. But doesn't help for Custom Button code which only has formula context. I tried a similar approach:
var ns = "{!$ObjectType.MyObject__c.Name}";
// I had thought the above would give MyObject__c in any development org
// and would give ns__MyObject__c in the packaging org
// and SUBSTR() and LEFT() and ISBLANK() etc etc etc could be used to deal with the prefix
var result = sforce.apex.execute(
ns + "WebServiceClass",
"someWebServiceMethod",
{"arg1": "foo", "arg2": "bar"}
);
However $ObjectType
is unavailable in Formula context much like $Page
and $Resource
, grrr!
But Hierarchy Custom Settings are available in formula context. So in combination, one could use the Apex code above to derive the namespace prefix. Then use a Post Install Script to automatically populate an org-wide Custom Setting instance to make it accessible in formula context.
public void onInstall(InstallContext context) {
//fetch custom setting or create it for the first time
NamespaceSettings__c ns = NamespaceSettings__c.getOrgDefaults();
if (setting == null) ns = new NamespaceSettings__c()
//ns
ns.RawPrefix__c = MyClass.class.getName().substringBefore('MyClass').substringBefore('.'),
//ns.
ns.DotPrefix__c = MyClass.class.getName().substringBefore('MyClass'),
//ns__
ns.BarPrefix__c = MyObject.Tax__c.Name.substringBefore('MyObject')
//write the values away
upsert ns;
}
Then the webservice invocation can look more like this:
var result = sforce.apex.execute(
"{!$Setup.NamespaceSettings__c.DotPrefix__c}WebServiceClass",
"someWebServiceMethod",
{"arg1": "foo", "arg2": "bar"}
);
Which meets the requirements of: all code namespace agnostic, no precompile, and no manual steps after install. Having shared this would be grateful for any improvements or to hear lessons learned etc.