-
Notifications
You must be signed in to change notification settings - Fork 1
Debugging & writing COS scripts
It should go without saying, but it's always best to develop and test new code on your test server before bringing over to a production machine.
I always recommend searching/using the below Google groups if you have questions about Caché/COS/CSP/Lightning:
With that said, when I start a new script, I usually create a "test" CSP file and put it in a /demos folder at the root of my publication. I like to start fresh scripts in this folder, outside of a defined Lightning "Section", because I can really focus on the code and see the errors that normally are hidden due to the way the DTI is setup to suppress errors.
Once I create a new csp page, I start simple. I focus on all the small bits and pieces that will eventually make up the larger part of my code. Even if the code isn't for a larger script/project, I still like to use these standalone csp pages to test even the smallest bit of code.
If I need a gStory or gSection object, I'll use the <csp:object ...> tag. For example:
<csp:object name="gStory" classname="dt.cms.schema.CMSStory" objid="30205958">
#(gStory)#
<csp:object name="gSection" classname="dt.cms.schema.Section" objid="50">
#(gSection)#Once I confident that my code is ready for the real world, I'll move it into real templates.
When I'm developing COS, I always put this code at the bottom of my test page:
<script language="cache" runat="server">
; Debug:
write !, "<pre>"
try {
set currIO = ##class(%SYS.NLS.Device).SetIO("HTML")
zwrite
}
catch {
write "ERROR: ", $ZERROR
}
if ($get(currIO) '= "") {
do ##class(%SYS.NLS.Device).SetIO(currIO)
}
write "</pre>"
</script>Without any other code on the page, this should be similar to the output you'll see:
%CSPsc=1
%request=<OBJECT REFERENCE>[1@%CSP.Request]
%response=<OBJECT REFERENCE>[2@%CSP.Response]
%session=<OBJECT REFERENCE>[3@%CSP.Session]
currIO="UTF8"
The above code is extremely useful for when it comes to catching leftover variables (e.g., variables you forgot to kill) and/or when there's an error in the code.
External links:
- Caché, Ensemble, DeepSee › Applying ..EscapeHTML to argumentless write OR zwrite?
- 2013.1: Caché ObjectScript Reference: ZWRITE: ZWRITE Without an Argument
- 2013.1: Caché ObjectScript Reference: WRITE: WRITE without an Argument
I love me some %SYSTEM.OBJ methods.
Here's the %SYSTEM.OBJ Class docs:
One extremely useful bit of code is the Dump() method.
Example:
<pre>
#[ do $system.OBJ.Dump(gStory) ]#
</pre>The above will dump everything you need to know about the gStory object to the CSP page. It's cool! Give it a try. :)
Below are a few examples of using how I use $system.OBJ and argumentless zwrites on our system.
Put this code into a CSP file:
<csp:if condition='$data(%request.Data("FOO", 1))'>
<hr>
<div class="wiffle">
<section>
<h1 class="head">FOO!</h1>
<csp:if condition=($isobject($get(gStory)))>
<h2><code>gStory</code></h2>
<pre>#[ do $system.OBJ.Dump(gStory) ]#</pre>
<h2><code>gStory.getStory()</code></h2>
<pre>#[ do $system.OBJ.Dump(gStory.getStory()) ]#</pre>
</csp:if>
<csp:if condition=($isobject($get(gSection)))>
<h2><code>gSection</code></h2>
<pre>#[ do $system.OBJ.Dump(gSection) ]#</pre>
</csp:if>
<h2><code>%request</code></h2>
<pre>#[ do $system.OBJ.Dump(%request) ]#</pre>
<h2><code>zwrite</code></h2>
<script language="cache" runat="server">
write !, "<pre>"
try {
set currIO = ##class(%SYS.NLS.Device).SetIO("HTML")
zwrite
}
catch {
write "ERROR: ", $ZERROR
}
if ($get(currIO) '= "") {
do ##class(%SYS.NLS.Device).SetIO(currIO)
}
write "</pre>"
</script>
</section>
</div> <!-- /.wiffle -->
</csp:if>On your primary Section template, call it like so:
<dti:file:include base="assets" file="/includes/FOO.csp" />Test the code by adding ?FOO to the url.
On a test page, using my argumentless zwrite, I was seeing this output:
%CSPsc=1
%ROWCOUNT=1
%ROWID=16108
%objcn=1
%objlasterror="0 ;�-%LoadData+13^dt.cms.seo.DefaultNaming.1:CMS"
%request=<OBJECT REFERENCE>[1@%CSP.Request]
%response=<OBJECT REFERENCE>[2@%CSP.Response]
%session=<OBJECT REFERENCE>[3@%CSP.Session]
currIO="UTF8"
gStory=<OBJECT REFERENCE>[4@dt.cms.schema.CMSStory]
See the problem? Check out the value of %objlasterror.
Yah, not very helpful.
Let's see if this helps:
<pre>#[ do $system.OBJ.Dump(%objlasterror) ]#</pre>Output:
'0 ; ± - %LoadData+13^dt.cms.seo.DefaultNaming.1:CMS' is not an oref value.
Not much more helpful, but at least it gave me a little more info.
Let's parse %objlasterror even further (obviously, you'll want to make sure %objlasterror is an object and exists before dumping it):
<pre>#[ do $System.OBJ.DisplayError(%objlasterror) ]#</pre>Output:
ERROR #5809: Object to Load not found
Ok, now we're getting somewhere.
Checking the docs:
2009.1: Caché Error Reference: Object Error Messages
Error code #5809 says:
Object to Load not found
Nothing I didn't already know.
Searching the Caché group for "5809" doesn't yield any helpful results either. Dang-it!
I've kinda hit a dead end here.
Going back to the original error message:
%objlasterror="0 ;�-%LoadData+13^dt.cms.seo.DefaultNaming.1:CMS"
Not knowing which bit of code was causing this error, I started commenting out blocks of code and testing one line at a time.
I soon discovered that the problem was originating from this code:
story.getLink(section, layout)
From there, in order to rule out all other code, I pulled the link code from getLink() method and tested it on a page all by itself:
set link = ##class(dt.cms.support.StoryLink).%New()
do link.init(gStory, gStory.homeSectionID, gStory.defaultFullLayout)
w link
Interesting! I still get the same %objlasterror error!
In Studio, looking at the init() Method in dt.cms.support.StoryLink.cls, I see references to ##class(dt.cms.seo.DefaultNaming).%New().
... looking at dt.cms.seo.DefaultNaming.cls I see (in the comments) a lot of talk about SEO path building and default setups.
From there, I decide to use DB Visualizer to look at the DefaultNaming table:
select * from dt_cms_seo.DefaultNaming;When executing the above query I get:
19:02:51 [SELECT - 0 row(s), 0.002 secs] Empty result set fetched
... 1 statement(s) executed, 0 row(s) affected, exec/fetch time: 0.002/0.000 sec [0 successful, 1 warnings, 0 errors]
This table has no data.
Then it clicks! We need to setup our SEO Default Naming!
The moral of the story is: I would have never been able to track down the problem without having used $system.OBJ.* and an argumentless zwrite! IMHO, they are invaluable tools for when it comes to debugging COS and CSP pages.