<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-5095070319827679950</atom:id><lastBuildDate>Tue, 10 Sep 2024 16:04:13 +0000</lastBuildDate><category>delphi</category><category>ajax</category><category>blogging</category><category>css</category><category>eco</category><category>intraweb</category><category>ocl</category><category>variants</category><title>Delphi: All Play and no Work</title><description>Education is an admirable thing, but it is well to remember from time to time that nothing that is worth&lt;br&gt; knowing can be taught. (Oscar Wilde)&lt;br&gt;&#xa;&lt;br&gt; &#xa;Some irrelevant musings about Delphi programming and &lt;a href=&quot;http://www2.blogger.com/profile/11477827111055279514&quot;&gt;my life&lt;/a&gt;.</description><link>http://robertcram.blogspot.com/</link><managingEditor>noreply@blogger.com (Robert Cram)</managingEditor><generator>Blogger</generator><openSearch:totalResults>4</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5095070319827679950.post-9195548153405700078</guid><pubDate>Thu, 17 Sep 2009 12:00:00 +0000</pubDate><atom:updated>2009-09-18T00:12:43.955+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">delphi</category><category domain="http://www.blogger.com/atom/ns#">variants</category><title>Beware of the Past</title><description>&lt;div class=&quot;blog&quot;&gt;&lt;br /&gt;
&lt;div class=&quot;kop&quot;&gt;Undiscovered things&lt;/div&gt;A few days ago, Jolyon Smith warned Delphi users to &lt;a href=&quot;http://www.deltics.co.nz/blog/?p=507&quot;&gt;Mind the Gap(s)&lt;/a&gt;. The warning is about how assigning specific ordinal values to enum members defeats the RTTI system. He writes:&lt;br /&gt;
&lt;blockquote&gt;&lt;i&gt;This is what I love about Delphi. After almost 15 years of Delphi’ing there’s still new things to learn, and I don’t just mean new features in the latest releases. I mean, basic, fundamental things that have been there for years, just undiscovered (by me).&lt;/i&gt;&lt;/blockquote&gt;I fully agree, and today it was my turn to find such an &quot;undiscovered (by me)&quot; thing in Delphi.&lt;br /&gt;
&lt;br /&gt;
I received a bugreport about a simple edit screen not opening due to an EVariantInvalidArgError exception. Strange, because the exception seemed to be in straightforward code that has been in production for twelve odd years, and hasn&#39;t been changed lately (sound familiar?). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;kop&quot;&gt;Examining the code&lt;/div&gt;Leaving out the code that checks for invalid inputs, the remaining code basically boils down to this:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;sourcecode&quot;&gt;&lt;code class=&quot;keyword&quot;&gt;var&lt;/code&gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;EditText: &lt;code class=&quot;keyword&quot;&gt;string&lt;/code&gt;;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;StoredValue: Variant;&lt;br /&gt;
&lt;code class=&quot;keyword&quot;&gt;begin&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&lt;code class=&quot;comment&quot;&gt;// example user input from the edit screen&lt;/code&gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;EditText := &lt;code class=&quot;quote&quot;&gt;&#39;16-9-2009&#39;&lt;/code&gt;;&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;...&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&lt;code class=&quot;comment&quot;&gt;  // store the date value when the edit screen closes&lt;/code&gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;StoredValue := StrToDate(EditText);&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;...&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&lt;code class=&quot;comment&quot;&gt;// convert the stored value to string when the edit screen opens&lt;/code&gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;EditText := VarToStr(StoredValue); &lt;code style=&quot;color: red;&quot;&gt;&amp;lt;= EVariantInvalidArgError reported ???&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
Not much too worry about codewise it seems. And I couldn&#39;t reproduce the error using my development data. Still, the error was readily reproducible in the production environment, so I looked at the production database record to see what value was causing the trouble. It turned out to be &#39;01-01-0010&#39;. Clearly a typo by a user that wanted to write &#39;01-01-2010&#39;. A typo maybe, but a valid date nevertheless. And besides, if there really was anything fishy about that date, I would expect the exception to be raised when trying to store the value in the variant, not when converting the variant back to a string. What was happening?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;kop&quot;&gt;Use the source, Luke&lt;/div&gt;Well, if it isn&#39;t me, it must be Delphi. So I looked through the code in the variants unit and after a while it started to make sense. It turns out that Delphi uses an OS function (&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms221699.aspx&quot;&gt;VarBstrFromDate&lt;/a&gt;) to cast the variant date value to a string. And this OS function only accepts dates from 01-01-100 onward, returning a E_INVALIDARG for earlier dates. So, although we can set a variant to a date before 01-01-100 and get the value from the variant as a date, we&#39;ll get an exception as soon as any implicit or explicit cast to string is performed (e.g. viewing the variant in the debugger or, the case at hand, using the VarToStr function).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;kop&quot;&gt;Feel Good Bugs&lt;/div&gt;Being a responsible Delphi citizen, I submitted the bug to Quality Central (&lt;a href=&quot;http://qc.embarcadero.com/wc/qcmain.aspx?d=77783&quot;&gt;#77783&lt;/a&gt;). But as this bug has &quot;extreme corner case&quot; written all over it, I don&#39;t think it will be fixed anytime soon. So I rewrote my code to only accept dates from 01-01-100 onwards, and treat all dates before that as invalid. It was a simple change because the application had centralized input checking. Another bug bites the dust!&lt;br /&gt;
&lt;br /&gt;
I realize I just love this part of my work. Admittedly, there have been bugs in the past that I loved a lot less. But this was one of the &quot;feel good&quot; bugs. A bug that is brought to your attention by strange, apparently unexplainable symptoms. A bug that&amp;nbsp; takes some effort (but no longer than say a few hours) to localize (and is preferably localized in some else&#39;s code), and finally a bug that is easy to repair or workaround. &lt;br /&gt;
&lt;br /&gt;
Can&#39;t wait for the next one.&lt;/div&gt;</description><link>http://robertcram.blogspot.com/2009/09/beware-of-past.html</link><author>noreply@blogger.com (Robert Cram)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5095070319827679950.post-3920299844933555085</guid><pubDate>Sun, 04 Mar 2007 13:14:00 +0000</pubDate><atom:updated>2007-11-04T16:09:44.396+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ajax</category><category domain="http://www.blogger.com/atom/ns#">delphi</category><category domain="http://www.blogger.com/atom/ns#">intraweb</category><title>Ending a Server Session when a Browser Window closes</title><description>&lt;div class=&quot;blog&quot;&gt;&lt;div class=&quot;kop&quot;&gt;The infamous &quot;Log Off&quot; problem&lt;/div&gt;I&#39;m working on a webapplication using &lt;a href=&quot;http://www.codegear.com/Default.aspx?tabid=122&quot; target=&quot;_blank&quot;&gt;Delphi 2006&lt;/a&gt; and Intraweb 8. Basic stuff: the user logs on, does some database work, and then logs off. Or rather: that&#39;s how I would like it to work...&lt;br /&gt;&lt;br /&gt;The reality is, alas, that users rarely use the provided &quot;log off&quot; button, but close the browser window instead. And that poses a bit of a problem: the server doesn&#39;t know the session has ended, and can&#39;t release its resources until some preconfigured time-out has occurred (half an hour in my case). Wouldn&#39;t it be great if the server could detect the closing of the browser and not keep those valuable resources tied up for an unnecessary amount of time? Well, it turns out it can, sort of.&lt;br /&gt;&lt;br /&gt;Searching the net I found a number of solutions to the &quot;Log Off&quot; problem. All solutions use the unload event of the body tag. The problem is that the unload event not only fires when the browser closes, but also when a user navigates to a different page. Although some clever &quot;hacks&quot; exist to distinguish between closing and navigating, I haven&#39;t found one that works reliably on IE and Firefox.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;kop&quot;&gt;An alternative solution: Getting help from the Server&lt;/div&gt;Given the fact that it&#39;s a bit problematic, from within the browser itself, to differentiate between the closing of a browser window and navigating to another page, couldn&#39;t we somehow get the server to help? &lt;br /&gt;&lt;br /&gt;The idea is simple enough: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;In the browser &quot;unload&quot; event we tell the server to set the session time-out to one minute.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;When the server receives a page request we reset the session time-out to its default value&lt;/li&gt;&lt;/ul&gt;This will ensure that the session time-out remains set to its default value as long as the user keeps visiting pages on our server: an &quot;unload&quot; event (setting the time-out to one minute) will be followed by a page request (resetting the session time-out to its default value). However, when the user closes the browser window, or navigates to some other site, the session will time-out in one minute. In these cases the &quot;unload&quot; event is not followed by a page request.&lt;br /&gt;&lt;br /&gt;Now all we need is a way to call the server from within the browser unload event. Obviously some asynchronous javascript and xml could come in handy (yep, you&#39;ve got it: AJAX). Well, enough talking already, let&#39;s get down to some coding.  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;kop&quot;&gt;Implementing the Browser Close Detection&lt;/div&gt;The implementation I&#39;m giving is for Delphi 2006 and Intraweb 8, but the same solution could also be implemented for other versions or web frameworks.&lt;br /&gt;&lt;br /&gt;First let&#39;s examine the javascript that&#39;s needed to call the server from within the browser unload event. It&#39;s fairly straightforward. First we create a &lt;a href=&quot;http://en.wikipedia.org/wiki/XMLHttpRequest&quot; target=&quot;_blank&quot;&gt;xmlhttp request object&lt;/a&gt;, and then we call the special url &quot;endsession&quot; on the server providing the intraweb sessionid &quot;GAppID&quot; as the post data. We use a POST rather than a GET command because Internet Explorer caches GET commands.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;&lt;code class=&quot;keyword&quot;&gt;function&lt;/code&gt; unload(){&lt;br /&gt;&amp;nbsp;&amp;nbsp;if(window.XMLHttpRequest){&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var xmlhttp = new XMLHttpRequest();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}else{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var xmlhttp = new ActiveXObject(&lt;code class=&quot;quote&quot;&gt;&amp;quot;Msxml2.XMLHTTP&amp;quot;&lt;/code&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;if(xmlhttp != null){&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data = &lt;code class=&quot;quote&quot;&gt;&amp;quot;GAppID=&amp;quot;&lt;/code&gt;+GAppID;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;url = GURLBase+&lt;code class=&quot;quote&quot;&gt;&amp;quot;/endsession&amp;quot;&lt;/code&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;xmlhttp.open(&lt;code class=&quot;quote&quot;&gt;&amp;quot;POST&amp;quot;&lt;/code&gt;, url, false);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;xmlhttp.send(data);&lt;br /&gt;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;}&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The javascript must be part of all the pages of our webapplication. A simple way of achieving this in Intraweb is to use a base form, and derive all our other pages from this base form. In the constructor code of the base form, we add the javascript, and an initialization command that links the script to the beforeunload event of the page. We&#39;re using the &quot;onbeforeunload&quot; event instead of the &quot;onunload&quot; event because we can&#39;t be sure the &quot;onunload&quot; event will give us enough time to fullfil the server request before the browser closes. In contrast, the &quot;onbeforeunload&quot; event will actually wait for the result of our server request before continuing.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;constructor&lt;/code&gt; TFRMIWBase.Create(AOwner: TComponent);&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;begin&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;inherited&lt;/code&gt; Create(AOwner);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;comment&quot;&gt;// add javascript to the form (body left out for brevity)&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Javascript.Add(&lt;code class=&quot;quote&quot;&gt;&#39;function unload(){&#39;&lt;/code&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;javascript.Add(&amp;lt;rest of body&amp;gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;Javascript.Add(&lt;code class=&quot;quote&quot;&gt;&#39;}&#39;&lt;/code&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;comment&quot;&gt;// link the javascript to the onbeforeunload browser event&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;AddToInitProc(&lt;code class=&quot;quote&quot;&gt;&#39;window.onbeforeunload = unload;&#39;&lt;/code&gt;);&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;On the server side, we have to intercept the &quot;endsession&quot; url, and set the session time-out to 1 minute. In Intraweb we can achieve this by catching the url in the OnBeforeDispatch event of the servercontroller:&lt;br /&gt; &lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;&lt;code class=&quot;keyword&quot;&gt;procedure&lt;/code&gt; TIWServerController.IWServerControllerBaseBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; &lt;code class=&quot;keyword&quot;&gt;var&lt;/code&gt; Handled: Boolean);&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;var&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Session: TIWApplication;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;begin&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;if&lt;/code&gt; Pos(&lt;code class=&quot;quote&quot;&gt;&#39;/endsession&#39;&lt;/code&gt;, Lowercase(Request.PathInfo)) &gt; 0 &lt;code class=&quot;keyword&quot;&gt;then&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;begin&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Session := GSessions.LookupAndLock(Request.ContentFields.Values[&lt;code class=&quot;quote&quot;&gt;&#39;GAppID&#39;&lt;/code&gt;]);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;if&lt;/code&gt; Assigned(Session) &lt;code class=&quot;keyword&quot;&gt;then&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;begin&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;try&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Session.SessionTimeOut := 1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Response.StatusCode := 200;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Response.ContentType := &lt;code class=&quot;quote&quot;&gt;&#39;text/xml&#39;&lt;/code&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Response.Content := &lt;code class=&quot;quote&quot;&gt;&#39;&amp;lt;xml/&amp;gt;&#39;&lt;/code&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Response.SendResponse;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Handled := true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;finally&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Session.Unlock;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;As expected, the &quot;endsession&quot; url will be called when the browser window is closed, but also when the user navigates to a new page. So it&#39;s important to reset the session time-out to its default value when a page is requested. In Intraweb we can use the OnAfterRender() event of the servercontroller:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;procedure&lt;/code&gt; TIWServerController.IWServerControllerBaseAfterRender(ASession: TIWApplication; AForm: TIWBaseForm);&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;begin&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;comment&quot;&gt;// set the current session timeout to the default servercontroller timeout&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbspASession.SessionTimeOut := SessionTimeout;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&lt;/div&gt;  &lt;br /&gt;&lt;br /&gt;&lt;/div&gt;</description><link>http://robertcram.blogspot.com/2007/03/detectbrowserclose.html</link><author>noreply@blogger.com (Robert Cram)</author><thr:total>7</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5095070319827679950.post-2489984891404581097</guid><pubDate>Sat, 10 Feb 2007 18:04:00 +0000</pubDate><atom:updated>2007-02-12T10:15:02.627+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">delphi</category><category domain="http://www.blogger.com/atom/ns#">eco</category><category domain="http://www.blogger.com/atom/ns#">ocl</category><title>Adding a custom Ocl Operation to ECO</title><description>&lt;div class=&quot;blog&quot;&gt;&lt;div class=&quot;kop&quot;&gt;Some Background Info&lt;/div&gt;To get myself acquainted with &lt;a href=&quot;http://en.wikipedia.org/wiki/Enterprise_Core_Objects&quot; target=&quot;_blank&quot;&gt;ECO&lt;/a&gt; and ASP.NET, I started with a simple (but long time overdue) pet project: a web based bug reporting utility. Noting fancy, mind, just a way for my users to report bugs or feature requests, and a way for myself to mark submitted reports as &quot;opened&quot;, &quot;closed&quot; or, as is often the case, &quot;needs more info&quot;. I&#39;ll be blogging about my first experiences with ECO at a later date.&lt;br /&gt;&lt;br /&gt;In the model for my bug reporting tool, I have a &quot;Session&quot; class, a transient singleton with an association &quot;CurrentUser&quot; to a &quot;User&quot; class. That makes it possible to refer to the current user with the Ocl expression: &quot;Session.AllInstances-&gt;first.CurrentUser&quot;.&lt;br /&gt;&lt;br /&gt;Soon I realised that I was using this Ocl expression almost everywhere. Especially in the state diagram for reports, where I wanted to express transition guards like &quot;a submitted report can only be deleted by the submitter&quot; (Session.AllInstances-&gt;first.CurrentUser = User), or &quot;only an administrator can mark a submitted project as open&quot; (Session.AllInstances-&gt;first.CurrentUser.IsAdministrator). This certainly had the duplicate code smell all over it.&lt;br /&gt;&lt;br /&gt;Asking about this in the &lt;a href=&quot;http://groups.google.com/groups?group=borland.public.delphi.modeldrivenarchitecture.eco&quot; target=&quot;_blank&quot;&gt;ECO newsgroup&lt;/a&gt;, Oleg Zhukov suggested to define a custom Ocl operation (Thanks Oleg!). &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;kop&quot;&gt;Defining the custom operation&lt;/div&gt;Defining a custom Ocl operation is not as complicated as it might seem (although it can take some time finding the relevant documentation on the net). It involves overriding two methods from the base class &quot;OclOperationBase&quot;, and installing the derived operation into the oclservice. Let&#39;s start with the interface: &lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;&lt;code class=&quot;keyword&quot;&gt;uses&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Borland.Eco.Ocl.Support;&lt;br /&gt;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;type&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;OclCurrentUser = &lt;code class=&quot;keyword&quot;&gt;class&lt;/code&gt;(OclOperationBase)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;strict protected&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;procedure&lt;/code&gt; Init; &lt;code class=&quot;keyword&quot;&gt;override&lt;/code&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;public&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;procedure&lt;/code&gt; Evaluate(Parameters: IOclOperationParameters); &lt;code class=&quot;keyword&quot;&gt;override&lt;/code&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Not much to see there, so on to the implementation. In the overridden Init method, we can define the number of parameters our custom Ocl operation will take and set their types. An Ocl operation always needs at least one parameter. This parameter represents the context of the Ocl operation. In this case we want to be able to use the &quot;CurrentUser&quot; operation with any type of context.&lt;br /&gt;&lt;br /&gt;To complete the initialisation we call the InternalInit method, providing a name, the input parameter array, and the return type for our custom Ocl operation. In most of the examples that are floating round the net the return type is a predefined type like Support.StringType or Support.IntegerType. In our case however, we need to return a user object (&quot;user&quot; being a class in the model). We get the Ocl type for our user class by using the GetClassifierByType() and GetOclTypeFor() functions.&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;&lt;code class=&quot;keyword&quot;&gt;uses&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Borland.Eco.UmlRt;&lt;br /&gt;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;procedure&lt;/code&gt; OclCurrentUser.Init;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;var&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;OCLParameters: &lt;code class=&quot;keyword&quot;&gt;array&lt;/code&gt; &lt;code class=&quot;keyword&quot;&gt;of&lt;/code&gt; IOclType;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Classifier: IClassifier;&lt;br /&gt;&amp;nbsp;&amp;nbsp;OclType: IOclType;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;begin&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;SetLength(OclParameters, 1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;OclParameters[0] := Support.AnyType;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Classifier := Support.Model.GetClassifierByType(Typeof(User));&lt;br /&gt;&amp;nbsp;&amp;nbsp;OclType := Support.GetOclTypeFor(Classifier);&lt;br /&gt;&amp;nbsp;&amp;nbsp;InternalInit(&lt;code class=&quot;quote&quot;&gt;&#39;currentuser&#39;&lt;/code&gt;, OclParameters, OclType);&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;That&#39;s the initialisation done. Now let&#39;s get on with the evaluation. We already know the Ocl expression to return the current user, so we can just call the Ocl service to evaluate this expression.&lt;br /&gt;&lt;br /&gt;The last task is setting the result parameter. We have two options: SetOwnedElement() and SetReferenceElement(). It&#39;s my feeling that SetOwnedElement() should be used for returning objects created by the operation itself, so that in our case SetReferenceElement() is the way to go. However, both options seem to work without problems (and documentation is lacking), so perhaps someone can shed some light on this?&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;&lt;code class=&quot;keyword&quot;&gt;uses&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Borland.Eco.ObjectRepresentation,&lt;br /&gt;&amp;nbsp;&amp;nbsp;Borland.Eco.Services;&lt;br /&gt;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;procedure&lt;/code&gt; OclCurrentUser.Evaluate(Parameters: IOclOperationParameters);&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;var&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;OclExpression: &lt;code class=&quot;keyword&quot;&gt;string&lt;/code&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;OclService: IOclService;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Element: IElement;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;begin&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;OclExpression := &lt;code class=&quot;quote&quot;&gt;&#39;Session.AllInstances-&amp;gt;first.currentuser&#39;&lt;/code&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;OclService := Support.OclService;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Element := OclService.Evaluate(OclExpression);&lt;br /&gt;&amp;nbsp;&amp;nbsp;Parameters.Result.SetReferenceElement(Element);&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;kop&quot;&gt;Installing the custom operation in run- and designtime&lt;/div&gt;All the above takes care of defining our custom operation. Installing it into the OclService is a breeze. Just add one line of code to the constructor of the EcoSpace:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;&lt;code class=&quot;keyword&quot;&gt;constructor&lt;/code&gt; TBugReportsEcoSpace.Create;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;begin&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;keyword&quot;&gt;inherited&lt;/code&gt; Create;&lt;br /&gt;&amp;nbsp;&amp;nbsp;InitializeComponent;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;code class=&quot;comment&quot;&gt;// TODO: Add any constructor code here&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;OclService.InstallOperation(OclCurrentUser.Create);&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Are we all done now? Well, almost. Although the custom operation is now known in runtime, and functions as expected, the operation is not yet known in design time. This has as a side effect that when we try to validate a model using the custom operation, we&#39;ll get an error: &quot;undefined operation: CurrentUser&quot;. Luckily that&#39;s easily solved. By adding an attribute to our EcoSpace we can make the Ocl operation known at design time:&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;[EcoOclOperation(typeof(OclCurrentUser), true)]&lt;br /&gt;TBugReportsEcoSpace = &lt;code class=&quot;keyword&quot;&gt;class&lt;/code&gt;(Borland.Eco.Handles.DefaultEcoSpace)&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;private&lt;/code&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Side note: in &lt;a href=&quot;http://homepages.borland.com/ecoteam/pmwiki/pmwiki.php?n=Eco.DesignTimeOclOperations&quot; target=&quot;_blank&quot;&gt;this article&lt;/a&gt; about the same subject, Jesper Hogstrom remarks &quot;Due to a very small. Minor... Miniscule... let&#39;s call it oversight, the attribute cannot be applied twice&quot;, and provides an alternative solution if you want to add more than one design time Ocl operation. It seems however that in the meantime this oversight has been fixed.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;kop&quot;&gt;Calling the custom operation&lt;/div&gt;When calling the custom operation, there are two &quot;gotchas&quot; to be beware of. The first is not forgetting the parentheses (i.e. calls must be in the form &quot;CurrentUser()&quot;), and the second is that an Ocl operation always needs a context. Although (for this operation) it doesn&#39;t matter what the context is, a context must be provided. When using the custom operation in a state diagram, the context is understood to be the class we&#39;re designing the state diagram for, so we can use &quot;CurrentUser()&quot; on its own. When evaluating the custom operation in code, we have to provide the context ourselves. &lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;&lt;code class=&quot;comment&quot;&gt;// context can be anything, but must be provided&lt;/code&gt;&lt;br /&gt;  Element := OclService.Evaluate(&lt;code class=&quot;quote&quot;&gt;&#39;42.CurrentUser()&#39;&lt;/code&gt;);&lt;br /&gt;  Currentuser := User(Element.AsObject);&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;</description><link>http://robertcram.blogspot.com/2007/02/customocl.html</link><author>noreply@blogger.com (Robert Cram)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5095070319827679950.post-3803531699066978114</guid><pubDate>Sun, 04 Feb 2007 08:22:00 +0000</pubDate><atom:updated>2007-02-10T10:11:38.728+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">blogging</category><category domain="http://www.blogger.com/atom/ns#">css</category><category domain="http://www.blogger.com/atom/ns#">delphi</category><title>Hello World</title><description>&lt;div class=&quot;blog&quot;&gt;Before even thinking about starting a Blog, I wanted to make sure I could easily post code snippets, without endlessly hassling with html markup. A mere copy and paste won&#39;t preserve formatting, so I needed a simple tool to convert source code to html, preferably giving me the possibility of doing some syntax highlighting.&lt;br /&gt;&lt;br /&gt;The BDS 2006 &lt;a href=&quot;http://cc.borland.com/Item.aspx?ID=23971&quot;&gt;community welcome page v2&lt;/a&gt; (provided by Daniel Wischnewski) has a handy utility, using a CDN web service provided by &lt;a href=&quot;http://blogs.codegear.com/johnk/&quot;&gt;John Kaster&lt;/a&gt;, called YAPP - Yet another pretty printer. This service allows you to format source code to HTML, directly from within the Delphi IDE. It tags reserved words, comments, quotes etc. with classnames, so you can use a stylesheet to do syntax highlighting. Exactly what I needed. Thanks guys!&lt;br /&gt;&lt;br /&gt;To illustrate the point, here is the classic and unavoidable &quot;Hello World&quot; example (in Delphi):&lt;br /&gt;&lt;br /&gt;&lt;div class=&quot;sourcecode&quot;&gt;&lt;code class=&quot;keyword&quot;&gt;uses&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Dialogs;&lt;br /&gt; &lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;procedure&lt;/code&gt; HelloWorld;&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;begin&lt;/code&gt;&lt;br /&gt;&lt;code class=&quot;comment&quot;&gt;&amp;nbsp;&amp;nbsp;// superfluous comment&lt;/code&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;ShowMessage(&lt;code class=&quot;quote&quot;&gt;&#39;Hello World&#39;&lt;/code&gt;);&lt;br /&gt;&lt;code class=&quot;keyword&quot;&gt;end&lt;/code&gt;;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;It seems I&#39;ve overcome my first Blogging hurdle. Ready to go on to even greater and more meaningful things. I&#39;ll keep you posted.&lt;br /&gt; &lt;br /&gt;O, and if you&#39;re wondering where to put your syntax highlighting styles: log in to blogger, and navigate to menu option &quot;customize&quot;, tabsheet &quot;template&quot;, menu option &quot;edit html&quot;. Have fun!&lt;/div&gt;</description><link>http://robertcram.blogspot.com/2007/02/hello-world.html</link><author>noreply@blogger.com (Robert Cram)</author><thr:total>0</thr:total></item></channel></rss>