Synopse Open Source - Tag - BusinessRulesmORMot MVC / SOA / ORM and friends2024-02-02T17:08:25+00:00urn:md5:cc547126eb580a9adbec2349d7c65274DotclearMustache Logic-less templates for Delphi - part 3urn:md5:177e56c3d5bed092677caa99fb1f52322014-04-28T13:39:00+02:002014-04-28T15:55:54+02:00AB4327-GANDIOpen Source librariesblogBusinessRulesCrossPlatformDelphiDocumentationGoodPracticeHTMLinterfaceJavaScriptmORMotmultithreadMustacheMVCOpenSourceperformanceSOASourcesyntaxtemplatesUserInterfaceweb<p><em><a href="https://blog.synopse.info?post/post/2012/03/07/Interface-based-services">Mustache</a></em> is
a well-known <em>logic-less</em> template engine.<br />
There is plenty of Open Source implementations around (including in JavaScript,
which can be very convenient for AJAX applications on client side, for
instance).<br />
For <em>mORMot</em>, we created the first pure Delphi implementation of it,
with a perfect integration with other bricks of the framework.</p>
<p><img src="http://santateresaschool.org/v2/wp-content/uploads/2014/03/mustache-much-8.jpg" alt="" width="250" height="292/" /></p>
<p>In last part of this series of blog articles, we will introduce the
<em>Mustache</em> library included within <em>mORMot</em> source code
tree.<br />
You can <a href="https://blog.synopse.info?post/public/Documents/SynMustache.pdf">download this documentation
as one single pdf file</a>.</p> <p>Part of our <em>mORMot</em> framework, we implemented an optimized
<em>Mustache</em> template engine in the <code>SynMustache</code> unit:</p>
<ul>
<li>It is the first Delphi implementation of <em>Mustache</em>;</li>
<li>It has a separate parser and renderer (so you can compile your templates
ahead of time);</li>
<li>The parser features a shared cache of compiled templates;</li>
<li>It passes all official <a href="http://github.com/mustache/spec"><em>Mustache</em> specification tests</a> -
including all weird whitespace process;</li>
<li>External partials can be supplied as <code>TSynMustachePartials</code>
dictionaries;</li>
<li><code>{{.}}</code>, <code>{{-index}}</code> and <code>{{"some text}}</code>
pseudo-variables were added to the standard <em>Mustache</em> syntax;</li>
<li><code>{{#-first}}</code>, <code>{{#-last}}</code> and
<code>{{#-odd}}</code> pseudo-sections were added to the standard
<em>Mustache</em> syntax;</li>
<li>Internal partials can be defined via <code>{{<partial}}</code> - also a
nice addition to the standard <em>Mustache</em> syntax;</li>
<li>It allows the data context to be supplied as JSON or our <a href="https://blog.synopse.info?post/post/2014/02/25/TDocVariant-custom-variant-type"><em>TDocVariant</em> custom
type</a>;</li>
<li>Almost no memory allocation is performed during the rendering;</li>
<li>It is natively UTF-8, from the ground up, with optimized conversion of any
string data;</li>
<li>Performance has been tuned and grounded in <code>SynCommons</code>'s
optimized code;</li>
<li>Each parsed template is thread-safe and re-entrant;</li>
<li>It follows the <em><a href="https://blog.synopse.info?post/post/2011/11/27/SOLID-design-principles">Open/Close principle</a></em> so
that any aspect of the process can be customized and extended (e.g. for any
kind of data context);</li>
<li>It is perfectly integrated with the other bricks of our <em>mORMot</em>
framework, ready to implement dynamic web sites with true 10 design, and full
separation of concerns in the views written in <em>Mustache</em>, the
controllers being e.g. interface-based services;</li>
<li>API is flexible and easy to use.</li>
</ul>
<h3>Variables</h3>
<p>Now, let's see some code.</p>
<p>First, we define our needed variables:</p>
<pre>
<strong>var</strong> mustache: TSynMustache;
doc: <strong>variant</strong>;
</pre>
<p>In order to parse a template, you just need to call:</p>
<pre>
mustache := TSynMustache.Parse(
'Hello {{name}}'#13#10'You have just won {{value}} dollars!');
</pre>
<p>It will return a compiled instance of the template.<br />
The <code>Parse()</code> class method will use the shared cache, so you won't
need to release the <code>mustache</code> instance once you are done with it:
no need to write a <code>try ... finally mustache.Free; end</code> block.</p>
<p>You can use a <code>TDocVariant</code> to supply the context data (with
late-binding):</p>
<pre>
TDocVariant.New(doc);
doc.name := 'Chris';
doc.value := 10000;
</pre>
<p>As an alternative, you may have defined the context data as such:</p>
<pre>
doc := _ObjFast(['name','Chris','value',1000]);
</pre>
<p>Now you can render the template with this context:</p>
<pre>
html := mustache.Render(doc);
<em>// now html='Hello Chris'#13#10'You have just won 10000 dollars!'</em>
</pre>
<p>If you want to supply the context data as JSON, then render it, you may
write:</p>
<pre>
mustache := TSynMustache.Parse(
'Hello {{value.name}}'#13#10'You have just won {{value.value}} dollars!');
html := mustache.RenderJSON('{value:{name:"Chris",value:10000}}');
<em>// now html='Hello Chris'#13#10'You have just won 10000 dollars!'</em>
</pre>
<p>Note that here, the JSON is supplied with an extended syntax (i.e. field
names are unquoted), and that <code>TSynMustache</code> is able to identify a
dotted-named variable within the execution context.</p>
<p>As an alternative, you could use the following syntax to create the data
context as JSON, with a set of parameters, therefore easier to work with in
real code storing data in variables (for instance, any <code>string</code>
variable is quoted as expected by JSON, and converted into UTF-8):</p>
<pre>
mustache := TSynMustache.Parse(
'Hello {{name}}'#13#10'You have just won {{value}} dollars!');
html := mustache.RenderJSON('{name:?,value:?}',[],['Chris',10000]);
html='Hello Chris'#13#10'You have just won 10000 dollars!'
</pre>
<p>You can find in the <code>mORMot.pas</code> unit the
<code>ObjectToJSON()</code> function which is able to transform any
<code>TPersistent</code> instance into valid JSON content, ready to be supplied
to a <code>TSynMustache</code> compiled instance.<br />
If the object's published properties have some getter functions, they will be
called on the fly to process the data (e.g. returning 'FirstName Name' as
FullName by concatenating both sub-fields).</p>
<h3>Sections</h3>
<p>Sections are handled as expected:</p>
<pre>
mustache := TSynMustache.Parse('Shown.{{#person}}As {{name}}!{{/person}}end{{name}}');
html := mustache.RenderJSON('{person:{age:?,name:?}}',[10,'toto']);
<em>// now html='Shown.As toto!end'</em>
</pre>
<p>Note that the sections change the data context, so that within the
<code>#person</code> section, you can directly access to the data context
<code>person</code> member, i.e. writing directly <code>name</code></p>
<p>It supports also inverted sections:</p>
<pre>
mustache := TSynMustache.Parse('Shown.{{^person}}Never shown!{{/person}}end');
html := mustache.RenderJSON('{person:true}');
<em>// now html='Shown.end'</em>
</pre>
<p>To render a list of items, you can write for instance (using the
<code>.</code> pseudo-variable):</p>
<pre>
mustache := TSynMustache.Parse('{{#things}}{{.}}{{/things}}');
html := mustache.RenderJSON('{things:["one", "two", "three"]}');
<em>// now html='onetwothree'</em>
</pre>
<p>The <code>-index</code> pseudo-variable allows to numerate the list items,
when rendering:</p>
<pre>
mustache := TSynMustache.Parse(
'My favorite things:'#$A'{{#things}}{{-index}}. {{.}}'#$A'{{/things}}');
html := mustache.RenderJSON('{things:["Peanut butter", "Pen spinning", "Handstands"]}');
<em>// now html='My favorite things:'#$A'1. Peanut butter'#$A'2. Pen spinning'#$A+</em>
<em>// '3. Handstands'#$A,'-index pseudo variable'</em>
</pre>
<h3>Partials</h3>
<p>External partials (i.e. standard <em>Mustache</em> partials) can be defined
using <code>TSynMustachePartials</code>. You can define and maintain a list of
<code>TSynMustachePartials</code> instances, or you can use a one-time partial,
for a given rendering process, as such:</p>
<pre>
mustache := TSynMustache.Parse('{{>partial}}'#$A'3');
html := mustache.RenderJSON('{}',TSynMustachePartials.CreateOwned(['partial','1'#$A'2']));
<em>// now html='1'#$A'23','external partials'</em>
</pre>
<p>Here <code>TSynMustachePartials.CreateOwned()</code> expects the partials to
be supplied as name/value pairs.</p>
<p>Internal partials (one of the <code>SynMustache</code> extensions), can be
defined directly in the main template:</p>
<pre>
mustache := TSynMustache.Parse('{{<partial}}1'#$A'2{{name}}{{/partial}}{{>partial}}4');
html := mustache.RenderJSON('{name:3}');
<em>// now html='1'#$A'234','internal partials'</em>
</pre>
<h3>Internationalization</h3>
<p>You can define <code>{{"some text}}</code> pseudo-variables in your
templates, which text will be supplied to a callback, ready to be transformed
on the fly: it may be convenient for <em>i18n</em> of web applications.</p>
<p>By default, the text will be written directly to the output buffer, but you
can define a callback which may be used e.g. for text translation:</p>
<pre>
<strong>procedure</strong> TTestLowLevelTypes.MustacheTranslate(<strong>var</strong> English: <strong>string</strong>);
<strong>begin</strong>
<strong>if</strong> English='Hello' <strong>then</strong>
English := 'Bonjour' <strong>else</strong>
<strong>if</strong> English='You have just won' <strong>then</strong>
English := 'Vous venez de gagner';
<strong>end</strong>;
</pre>
<p>Of course, in a real application, you may assign one
<code>TLanguageFile.Translate(var English: string)</code> method, as defined in
the <code>mORMoti18n.pas</code> unit.</p>
<p>Then, you will be able to define your template as such:</p>
<pre>
mustache := TSynMustache.Parse(
'{{"Hello}} {{name}}'#13#10'{{"You have just won}} {{value}} {{"dollars}}!');
html := mustache.RenderJSON('{name:?,value:?}',[],['Chris',10000],<strong>nil</strong>,MustacheTranslate);
<em>// now html='Bonjour Chris'#$D#$A'Vous venez de gagner 10000 dollars!'</em>
</pre>
<p>All text has indeed been translated as expected.</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1720">welcome on our forum, as
usual</a>!</p>Mustache Logic-less templates for Delphi - part 2urn:md5:933cfcf13ca723e4ff09e08550ceccfb2014-04-28T13:38:00+02:002014-04-28T15:55:39+02:00AB4327-GANDIOpen Source librariesblogBusinessRulesCrossPlatformDelphiDocumentationGoodPracticeHTMLinterfaceJavaScriptmORMotmultithreadMustacheMVCOpenSourceperformanceSOASourcesyntaxtemplatesUserInterfaceweb<p><em><a href="http://mustache.github.io/">Mustache</a></em> is a well-known
<em>logic-less</em> template engine.<br />
There is plenty of Open Source implementations around (including in JavaScript,
which can be very convenient for AJAX applications on client side, for
instance).<br />
For <em>mORMot</em>, we created the first pure Delphi implementation of it,
with a perfect integration with other bricks of the framework.</p>
<p><img src="http://santateresaschool.org/v2/wp-content/uploads/2014/03/mustache-much-8.jpg" alt="" width="250" height="292/" /></p>
<p>In this second part of this series of blog articles, we will introduce the
<em>Mustache</em> syntax.<br />
You can <a href="https://blog.synopse.info?post/public/Documents/SynMustache.pdf">download this documentation
as one single pdf file</a>.</p> <p>The <em>Mustache</em> template logic-less language has five types of
tags:</p>
<ol>
<li>Variables;</li>
<li>Sections;</li>
<li>Inverted Sections;</li>
<li>Comments;</li>
<li>Partials.</li>
</ol>
<p>All those tags will be identified with mustaches, i.e.
<code>{{...}}</code>.<br />
Anything found in a template of this form is interpreted as a template
marker.<br />
All other text is considered formatting text and is output verbatim at template
expansion time.</p>
<table>
<tbody>
<tr>
<td><strong>Marker</strong></td>
<td><strong>Description</strong></td>
</tr>
<tr>
<td><code>{{variable}}</code></td>
<td>The <code>variable</code> name will be searched recursively within the
current context (possibly with dotted names), and, if found, will be written as
escaped HTML.<br />
If there is no such key, nothing will be rendered.</td>
</tr>
<tr>
<td><code>{{{variable}}}<br />
{{& variable}}</code></td>
<td>The <code>variable</code> name will be searched recursively within the
current context, and, if found, will be written directly, <em>without any HTML
escape</em>.<br />
If there is no such key, nothing will be rendered.</td>
</tr>
<tr>
<td><code>{{#section}}<br /></code>...<br />
<code>{{/section}}</code></td>
<td>Defines a block of text, aka <em>section</em>, which will be rendered
depending of the <code>section</code> variable value, as searched in the
current context:<br />
- If <code>section</code> equals <code>false</code> or is an <em>empty
list</em> <code>[]</code>, the whole block won't be rendered;<br />
- If <code>section</code> is non-<code>false</code> but not a list, it will be
used as the context for a single rendering of the block;<br />
- If <code>section</code> is a non-empty list, the text in the block will be
rendered once for each item in the list - the context of the block will be set
to the current item for each iteration.</td>
</tr>
<tr>
<td><code>{{^section}}<br /></code>...<br />
<code>{{/section}}</code></td>
<td>Defines a block of text, aka <em>inverted section</em>, which will be
rendered depending of the <code>section</code> variable <em>inverted</em>
value, as searched in the current context:<br />
- If <code>section</code> equals <code>false</code> or is an <em>empty
list</em>, the whole block <em>will</em> be rendered;<br />
- If <code>section</code> is non-<code>false</code> or a non-empty list, it
won't be rendered.</td>
</tr>
<tr>
<td><code>{{! comment}}</code></td>
<td>The comment text will just be ignored.</td>
</tr>
<tr>
<td><code>{{>partial}}</code></td>
<td>The <code>partial</code> name will be searched within the registered
<em>partials list</em>, then will be executed at run-time (so recursive
partials are possible), with the current execution context.</td>
</tr>
<tr>
<td><code>{{=...=}}</code></td>
<td>The delimiters (i.e. by default <code>{{ }}</code>) will be replaced by the
specified characters (may be convenient when double-braces may appear in the
text).</td>
</tr>
</tbody>
</table>
<p>In addition to those standard markers, the <em>mORMot</em> implementation of
<em>Mustache</em> features:</p>
<table>
<tbody>
<tr>
<td><strong>Marker</strong></td>
<td><strong>Description</strong></td>
</tr>
<tr>
<td><code>{{.}}</code></td>
<td>This pseudo-variable refers to the context object itself instead of one of
its members. This is particularly useful when iterating over lists.</td>
</tr>
<tr>
<td><code>{{-index}}</code></td>
<td>This pseudo-variable returns the current item number when iterating over
lists, starting counting at 1</td>
</tr>
<tr>
<td><code>{{#-first}}<br /></code>...<br />
<code>{{/-first}}</code></td>
<td>Defines a block of text (pseudo-section), which will be rendered - or
<em>not</em> rendered for inverted <code>{{^-first}}</code> - for the
<em>first</em> item when iterating over lists</td>
</tr>
<tr>
<td><code>{{#-last}}<br /></code>...<br />
<code>{{/-last}}</code></td>
<td>Defines a block of text (pseudo-section), which will be rendered - or
<em>not</em> rendered for inverted <code>{{^-last}}</code> - for the
<em>last</em> item when iterating over lists</td>
</tr>
<tr>
<td><code>{{#-odd}}<br /></code>...<br />
<code>{{/-odd}}</code></td>
<td>Defines a block of text (pseudo-section), which will be rendered - or
<em>not</em> rendered for inverted <code>{{^-odd}}</code> - for the
<em>odd</em> item number when iterating over lists: it can be very usefull e.g.
to display a list with alternating row colors</td>
</tr>
<tr>
<td><code>{{<partial}}<br /></code>...<br />
<code>{{/partial}}</code></td>
<td>Defines an in-lined <em>partial</em> - to be called later via
<code>{{>partial}}</code> - within the scope of the current template</td>
</tr>
<tr>
<td><code>{{"some text}}</code></td>
<td>This pseudo-variable will supply the given text to a callback, which will
for instance transform its content (e.g. translate it), before writing it to
the output</td>
</tr>
</tbody>
</table>
<p>This set of markers will allow to easily write any kind of content, without
any explicit logic nor nested code.<br />
As a major benefit, the template content could be edited and verified without
the need of any <em>Mustache</em> compiler, since all those
<code>{{...}}</code> markers will identify very clearly the resulting
layout.</p>
<h3>Variables</h3>
<p>A typical Mustache template:</p>
<pre>
Hello {{name}}
You have just won {{value}} dollars!
Well, {{taxed_value}} dollars, after taxes.
</pre>
<p>Given the following hash:</p>
<pre>
{
"name": "Chris",
"value": 10000,
"taxed_value": 6000
}
</pre>
<p>Will produce the following:</p>
<pre>
Hello Chris
You have just won 10000 dollars!
Well, 6000 dollars, after taxes.
</pre>
<p>You can note that <code>variable</code> tags are escaped for HTML by
default. This is a mandatory security feature. In fact, all web applications
which create HTML documents can be vulnerable to Cross-Site-Scripting (XSS)
attacks unless data inserted into a template is appropriately sanitized and/or
escaped. With Mustache, this is done by default. Of course, you can override it
and force to <em>not-escape</em> the value, using <code>variable or &
variable</code>.</p>
<p>For instance:</p>
<table>
<tbody>
<tr>
<td><strong>Template</strong></td>
<td><strong>Context</strong></td>
<td><strong>Output</strong></td>
</tr>
<tr>
<td><code>* {{name}}<br />
* {{age}}<br />
* {{company}}<br />
* {{{company}}}</code></td>
<td><code>{<br />
"name": "Chris",<br />
"company": "<b>GitHub</b>"<br />
}</code></td>
<td><code>* Chris<br />
*<br />
* &lt;b&gt;GitHub&lt;/b&gt;<br />
* <b>GitHub</b></code></td>
</tr>
</tbody>
</table>
<p>Variables resolve names within the current context with an optional dotted
syntax, for instance:</p>
<table>
<tbody>
<tr>
<td><strong>Template</strong></td>
<td><strong>Context</strong></td>
<td><strong>Output</strong></td>
</tr>
<tr>
<td><code>* {{people.name}}<br />
* {{people.age}}<br />
* {{people.company}}<br />
* {{{people.company}}}</code></td>
<td><code>{<br />
"name": "Chris",<br />
"company": "<b>GitHub</b>"<br />
}</code></td>
<td><code>* Chris<br />
*<br />
* &lt;b&gt;GitHub&lt;/b&gt;<br />
* <b>GitHub</b></code></td>
</tr>
</tbody>
</table>
<h3>Sections</h3>
<p><em>Sections</em> render blocks of text one or more times, depending on the
value of the key in the current context.</p>
<p>In our "wining template" above, what happen if we do want to hide the tax
details?<br />
In most script languages, we may write an <code>if ...</code> block within the
template. This is what <em>Mustache</em> avoids. So we define a section, which
will be rendered on need.</p>
<p>The template becomes:</p>
<pre>
Hello {{name}}
You have just won {{value}} dollars!
{{#in_ca}}
Well, {{taxed_value}} dollars, after taxes.
{{/in_ca}}
</pre>
<p>Here, we created a new section, named <code>in_ca</code>.</p>
<p>Given the hash value of <code>in_ca</code> (and its presence), the section
will be rendered, or not:</p>
<table>
<tbody>
<tr>
<td><strong>Context</strong></td>
<td><strong>Output</strong></td>
</tr>
<tr>
<td><code>{<br />
"name": "Chris",<br />
"value": 10000,<br />
"taxed_value": 6000,<br />
"in_ca": true<br />
}</code></td>
<td><code>Hello Chris<br />
You have just won 10000 dollars!<br />
Well, 6000 dollars, after taxes.</code></td>
</tr>
<tr>
<td><code>{<br />
"name": "Chris",<br />
"value": 10000,<br />
"taxed_value": 6000,<br />
"in_ca": false<br />
}</code></td>
<td><code>Hello Chris<br />
You have just won 10000 dollars!</code></td>
</tr>
<tr>
<td><code>{<br />
"name": "Chris",<br />
"value": 10000,<br />
"taxed_value": 6000<br />
}</code></td>
<td><code>Hello Chris<br />
You have just won 10000 dollars!</code></td>
</tr>
</tbody>
</table>
<p>Sections also change the context of its inner block. It means that the
section variable content becomes the top-most context which will be used to
identify any supplied variable key.</p>
<p>Therefore, the following context will be perfectly valid: we can define
<code>taxed_value</code> as a member of <code>in_ca</code>, and it will be
rendered directly, since it is part of the new context.</p>
<table>
<tbody>
<tr>
<td><strong>Context</strong></td>
<td><strong>Output</strong></td>
</tr>
<tr>
<td><code>{<br />
"name": "Chris",<br />
"value": 10000,<br />
"in_ca": {<br />
"taxed_value": 6000<br />
}<br />
}<br />
<br /></code></td>
<td><code>Hello Chris<br />
You have just won 10000 dollars!<br />
Well, 6000 dollars, after taxes.</code></td>
</tr>
<tr>
<td><code>{<br />
"name": "Chris",<br />
"value": 10000,<br />
"taxed_value": 6000<br />
}</code></td>
<td><code>Hello Chris<br />
You have just won 10000 dollars!</code></td>
</tr>
<tr>
<td><code>{<br />
"name": "Chris",<br />
"value": 10000,<br />
"taxed_value": 3000,<br />
"in_ca": {<br />
"taxed_value": 6000<br />
}<br />
}<br /></code></td>
<td><code>Hello Chris<br />
You have just won 10000 dollars!<br />
Well, 6000 dollars, after taxes.</code></td>
</tr>
</tbody>
</table>
<p>In the latest context above, there are two <code>taxed_value</code>
variables.<br />
The engine will use the one defined by the context in the <code>in_ca</code>
section, i.e. <code>in_ca.taxed_value</code>; the one defined at the root
context level (which equals 3000) is just ignored.</p>
<p>If the variable pointed by the section name is a list, the text in the block
will be rendered once for each item in the list.<br />
The context of the block will be set to the current item for each
iteration.</p>
<p>In this way we can loop over collections.<br />
<em>Mustache</em> allows any depth of nested loops (e.g. any level of
master/details information).</p>
<table>
<tbody>
<tr>
<td><strong>Template</strong></td>
<td><strong>Context</strong></td>
<td><strong>Output</strong></td>
</tr>
<tr>
<td><code>{{#repo}}<br />
<b>name</b><br />
{{/repo}}</code></td>
<td><code>{<br />
"repo": [<br />
{"name": "resque"},<br />
{"name": "hub"},<br />
{"name": "rip"} <br />
]<br />
}</code></td>
<td><code><b>resque</b><br />
<b>hub</b><br />
<b>rip</b></code></td>
</tr>
<tr>
<td><code>{{#repo}}<br />
<b>.</b><br />
{{/repo}}</code></td>
<td><code>{<br />
"repo":<br />
["resque", "hub", "rip"]<br />
}</code></td>
<td><code><b>resque</b><br />
<b>hub</b><br />
<b>rip</b></code></td>
</tr>
</tbody>
</table>
<p>The latest template makes use of the <code>.</code> pseudo-variable, which
allows to render the current item of the list.</p>
<h3>Inverted Sections</h3>
<p>An inverted section begins with a caret (<code>^</code>) and ends as a
standard (non-inverted) section.<br />
They may render text once, based on the <em>inverse</em> value of the key. That
is, the text block will be rendered if the key doesn't exist, is false, or is
an empty list.</p>
<p>Inverted sections are usually defined after a standard section, to render
some message in case no information will be written in the non-inverted
section:</p>
<table>
<tbody>
<tr>
<td><strong>Template</strong></td>
<td><strong>Context</strong></td>
<td><strong>Output</strong></td>
</tr>
<tr>
<td><code>{{#repo}}<br />
<b>.</b><br />
{{/repo}}<br />
{{^repo}}<br />
No repos <img src="https://blog.synopse.info?pf=sad.svg" alt=":(" class="smiley" /><br />
{{/repo}}</code></td>
<td><code>{<br />
"repo":<br />
[]<br />
}</code></td>
<td><code>No repos <img src="https://blog.synopse.info?pf=sad.svg" alt=":(" class="smiley" /></code></td>
</tr>
</tbody>
</table>
<h3>Partials</h3>
<p>Partials are some kind of external sub-templates which can be included
within a main template, for instance to follow the same rendering at several
places.<br />
Just like functions in code, they do ease template maintainability and spare
development time.</p>
<p>Partials are rendered at runtime (as opposed to compile time), so recursive
partials are possible. Just avoid infinite loops.<br />
They also inherit the calling context, so can easily be re-used within a list
section, or together with plain variables.</p>
<p>In practice, partials shall be supplied together with the data context -
they could be seen as "template context".</p>
<p>For example, this "main" template uses a <code>> user</code> partial:</p>
<pre>
<h2>Names</h2>
{{#names}}
{{> user}}
{{/names}}
</pre>
<p>With the following template registered as "user":</p>
<pre>
<strong>{{name}}</strong>
</pre>
<p>Can be thought of as a single, expanded template:</p>
<pre>
<h2>Names</h2>
{{#names}}
<strong>{{name}}</strong>
{{/names}}
</pre>
<p>In <em>mORMot</em>'s implementations, you can also create some
<em>internal</em> partials, defined as <code><partial ... /partial</code>
pseudo-sections.<br />
It may decrease the need of maintaining multiple template files, and refine the
rendering layout.</p>
<p>For instance, the previous template may be defined at once:</p>
<pre>
<h2>Names</h2>
{{#names}}
{{>user}}
{{/names}}
{{<user}}
<strong>{{name}}</strong>
{{/user}}
</pre>
<p>The same file will define both the partial and the main template. </p>
<p>Note that we defined the internal partial after the main template, but we
may have defined it anywhere in the main template logic: internal partials
definitions are ignored when rendering the main template, just like
comments.</p>
<p><a href="https://blog.synopse.info?post/post/2014/04/28/Mustache-Logic-less-templates-for-Delphi-part-3">Next article
will detail the <em>Mustache</em> engine as implemented in <em>mORMot</em>'s
source code tree</a>.<br />
Now, a bit of practice!</p>Mustache Logic-less templates for Delphi - part 1urn:md5:5e26b8841fcba48a85895f3f787cd0172014-04-28T13:37:00+02:002014-04-28T15:55:15+02:00AB4327-GANDIOpen Source librariesblogBusinessRulesCrossPlatformDelphiDocumentationGoodPracticeHTMLinterfaceJavaScriptmORMotmultithreadMustacheMVCOpenSourceperformanceSOASourcesyntaxtemplatesUserInterfaceweb<p><em><a href="http://mustache.github.io/">Mustache</a></em> is a well-known
<em>logic-less</em> template engine.<br />
There is plenty of Open Source implementations around (including in JavaScript,
which can be very convenient for AJAX applications on client side, for
instance).<br />
For <em>mORMot</em>, we created the first pure Delphi implementation of it,
with a perfect integration with other bricks of the framework.</p>
<p><img src="http://santateresaschool.org/v2/wp-content/uploads/2014/03/mustache-much-8.jpg" alt="" width="250" height="292/" /></p>
<p>In this first part of this series of blog articles, we will introduce the
<em>Mustache</em> design.<br />
You can <a href="https://blog.synopse.info?post/public/Documents/SynMustache.pdf">download this documentation
as one single pdf file</a>.</p> <p>Generally speaking, a Template system can be used to separate output
formatting specifications, which govern the appearance and location of output
text and data elements, from the executable logic which prepares the data and
makes decisions about what appears in the output.</p>
<p>Most template systems (e.g. PHP, smarty, Razor...) feature in fact a full
scripting engine within the template content.<br />
It allows powerful constructs like variable assignment or conditional
statements in the middle of the HTML content. It makes it easy to modify the
look of an application within the template system exclusively, without having
to modify any of the underlying "application logic". They do so, however, at
the cost of separation, turning the templates themselves into part of the
application logic.</p>
<p><em>Mustache</em> inherits from Google's <em>ctemplate</em> library, and is
used in many famous applications, including the "main" <a href="http://code.google.com/p/ctemplate">Google web search</a>, or the Twitter web
site.<br />
The <em>Mustache</em> template system leans strongly towards preserving the
separation of logic and presentation, therefore ensures a perfect <a href="https://blog.synopse.info?post/post/2014/04/18/Introducing-mORMot-s-architecture-and-design-principles">MVC
design</a>, and ready to consume SOA services.</p>
<p><em>Mustache</em> is intentionally constrained in the features it supports
and, as a result, applications tend to require quite a bit of code to
instantiate a template: all the application logic will be defined within the
<em>Controller</em> code, not in the <em>View</em> source.<br />
This may not be to everybody's tastes. However, while this design limits the
power of the template language, it does not limit the power or flexibility of
the template system. This system supports arbitrarily complex text
formatting.</p>
<p>Finally, <em>Mustache</em> is designed with an eye towards efficiency.
Template instantiation is very quick, with an eye towards minimizing both
memory use and memory fragmentation. As a result, it sounds like a perfect
template system for our <em>mORMot</em> framework.</p>
<h3>Mustache principles</h3>
<p>There are two main parts to the <em>Mustache</em> template system:</p>
<ol>
<li>Templates (which are plain text files);</li>
<li>Data dictionaries (aka <em>Context</em>).</li>
</ol>
<p>For instance, given the following template:</p>
<pre>
<h1>{{header}}</h1>
<br />{{#items}}
{{#first}}
<li><strong>{{name}}</strong></li>
{{/first}}
{{#link}}
<li><a href="{{url}}">{{name}}</a></li>
{{/link}}
{{/items}}
<br />{{#empty}}
<p>The list is empty.</p>
{{/empty}}
</pre>
<p>and the following data context:</p>
<pre>
{
"header": "Colors",
"items": [
{"name": "red", "first": <strong>true</strong>, "url": "#Red"},
{"name": "green", "link": <strong>true</strong>, "url": "#Green"},
{"name": "blue", "link": <strong>true</strong>, "url": "#Blue"}
],
"empty": <strong>true</strong>
}
</pre>
<p>The <em>Mustache</em> engine will render this data as such:</p>
<pre>
<h1>Colors</h1>
<li><strong>red</strong></li>
<li><a href="#Green">green</a></li>
<li><a href="#Blue">blue</a></li>
<p>The list is empty.</p>
</pre>
<p>In fact, you did not see any "<code>if</code>" nor "<em>for</em>" loop in
the template, but <em>Mustache</em> conventions make it easy to render the
supplied data as the expected HTML output. It is up to the MVC
<em>Controller</em> to render the data as expected by the template, e.g. for
formatting dates or currency values.</p>
<p><a href="https://blog.synopse.info?post/post/2014/04/28/Mustache-Logic-less-templates-for-Delphi-part-2">Next article
will detail the Mustache syntax itself</a>.<br />
Stay tuned!</p>One ORM to rule them allurn:md5:ad34e480226d50d8ea322ecd413a8bd82012-07-12T23:26:00+02:002013-01-03T17:40:19+01:00AB4327-GANDImORMot FrameworkauthenticationblogBusinessRulesCQRSDatabaseDelphiDomainDrivendynamic arrayEventSourcingGoodPracticeinterfacemORMotMSSQLOleDBOracleORMParsingperformanceRestsecuritysessionSOASourceSQLSQLite3<p>If you discovered the <em>mORMot</em> framework, you may have found out that
its implementation may sound restricted, in comparison to other ORMs, due to
its design. It would be easy to answer that "it is not a bug, it is a feature",
but I suspect it is worth a dedicated article.</p>
<p><img src="http://www.peakprosperity.com/sites/default/files/resize/remote/3d1165df85f3aa4d318902fc56d8f4f0-590x400.jpg" alt="" width="450" height="305" /></p>
<p>Some common (and founded) criticisms are the following (quoting from our
forum - see e.g. <a href="http://synopse.info/forum/viewtopic.php?pid=4676#p4676">this
question</a>):<br />
- "One of the things I don't like so much about your approach to the ORM is the
mis-use of existing Delphi constructs like "<code>index n</code>" attribute for
the maximum length of a string-property. Other ORMs solve this i.e. with
official <code>Class</code>-attributes";<br />
- "You have to inherit from <code>TSQLRecord</code>, and can't persist any
plain class";<br />
- "There is no way to easily map an existing complex database".</p>
<p>I understand very well those concerns.<br />
Our <em>mORMot</em> framework is not meant to fit any purpose, but it is worth
understanding why it has been implemented as such, and why it may be quite
unique within the family of ORMs - which almost all are following the
<em>Hibernate</em> way of doing.</p> <h3>Rude class definition</h3>
<p>Attributes do appear in Delphi 2010, and it is worth saying that FPC has an
alternative syntax. Older versions of Delphi (still very deployed) do not have
attributes available in the language, so it was not possible to be compatible
with Delphi 6 up to latest versions (as we wished for our units).</p>
<p>It is perfectly right to speak about 'mis-use of <code>index</code>' - but
this was the easiest and only way we found out to have such information, just
using RTTI. Since this parameter was ignored and not used for most classes, it
was re-used (also for dynamic array properties, to have faster lookup).<br />
There is another "mis-use" for the "<code>stored false</code>" property, which
is used to identify unique mandatory columns.</p>
<p>Using attributes is one of the most common way of describing tables in most
ORMs.<br />
On the other hand, some coders have a concern about such class
definitions.<br />
They are mixing DB and logic: you are somewhat polluting the business-level
class definition with DB-related stuff.</p>
<p>That is why other kind of ORMs provide a way of mapping classes to tables
using external files (some ORMs provide both ways of definition).<br />
And why those days, even code gurus identified the attributes overuse as a
potential weakness of code maintainability.<br />
Attributes do have a huge downsize, when you are dealing with a Client-Server
ORM, like ours: on the Client side, those attributes are pointless (client does
not need to know anything about the database), and you need to link to all the
DB plumbing code to your application. For <em>mORMot</em>, it was some kind of
strong argument.</p>
<p>For the very same reasons, the column definitions (uniqueness, indexes,
required) are managed in <em>mORMot</em> at two levels:<br />
- At <em>ORM level</em> for <em>DB related stuff</em> (like indexes, which is a
DB feature, not a business feature);<br />
- At <em>Model level</em> for <em>Business related stuff</em> (like uniqueness,
validators and filters).</p>
<p>When you take a look at the supplied validators and filters - see <a href="https://blog.synopse.info?post/post/2011/06/01/Business-rules%3A-validate-and-filter-data">this article</a>
- you'll find out that this is much powerful than the attributes available in
"classic" ORMs: how could you validate an entry to be an email, or to match a
pattern, or to ensure that it will be stored in uppercase within the DB?</p>
<p>Other question worth asking is about the security.<br />
If you access the data remotely, a global access to the DB is certainly not
enough. Our framework handle per-table CRUD level access for its ORM, above the
DB layer (and has also complete security attributes for services). It works
however the underneath DB grants are defined (even an DB with no user rights -
like in-memory or <em>SQLite3</em> is able to do it).</p>
<p>The <em>mORMot</em> point of view (which is not the only one), is to let the
DB persist the data, as safe and efficient as possible, but rely on higher
levels layers to implement the business logic. It will make it pretty
database-agnostic (you can even not use a SQL database at all), and will make
the framework code easier to debug and maintain, since we don't have to deal
with all the DB engine particularities. In short, this is the REST point of
view, and main cause of success: <a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> is
enough.</p>
<p>About the fact that you need to inherit from <code>TSQLRecord</code>, and
can't persist anything, our purpose was in fact very similar to the "Layer
Supertype" pattern of Domain-Driven-Design, as <a href="http://martinfowler.com/eaaCatalog/layerSupertype.html">explained by Martin
Fowler</a>:</p>
<p><em><q>It's not uncommon for all the objects in a layer to have methods you
don't want to have duplicated throughout the system. You can move all of this
behavior into a common Layer Supertype.</q></em></p>
<h3>Several ORMs at once</h3>
<p>To be clear, <em>mORMot</em> offers three kind of table definitions:<br />
- Via <code>TSQLRecord</code> / <code>TSQLRecordVirtual</code> "native ORM"
classes: data storage is using either fast in-memory lists via
<code>TSQLRestServerStaticInMemory</code>, either <em>SQLite3</em> tables (in
memory, on file, or virtual). In this case, we do not use <code>index</code>
for strings (column length is not used by any of those engines).<br />
- Via <code>TSQLRecordExternal</code> "external ORM-managed" classes: DB tables
are created by the ORM, via SQL - see <a href="https://blog.synopse.info?post/post/2011/08/07/SQLite3-powered%2C-not-SQLite3-limited">this article</a>.
These classes will allow creation of tables in any supported external database
engine (<em>SQlite3, Oracle, MS SQL, Jet</em>, whatever <em>OleDB</em>
provider). In this case, we use <code>index</code> for text column length. This
is the only needed parameter to be defined for such a basic implementation, in
regard to <code>TSQLRecord</code> kind of classes.<br />
- Via <code>TSQLRecordMappedAutoID</code> /
<code>TSQLRecordMappedForcedID</code> "external mapped" classes: DB tables are
not created by the ORM, but already existing in the DB, with sometimes a very
complex layout. This feature is not yet implemented, but on the road-map. For
this kind of classes we won't probably use attributes, nor even external files,
but we will rely on definition from code, either with a fluent definition,
either with dedicated classes (or interface).</p>
<p>The concern of not being able to persist any class (it needs to inherit from
<code>TSQLRecord</code>) does perfectly make sense.</p>
<p>On the other hand, from the implementation point of view, it is very
powerful, since you have a lot of methods included within this class
definition. It does also make sense to have a common ancestor able to identify
all three kind of <em>mORMot</em>'s table definitions: the same abstract
ancestor is used, and clients won't even need to know that they are implemented
in-memory, using a <em>SQLite3</em> engine, or even a MS SQL / Oracle database.
Another benefit of using a parent class is to enforce code safety using
Delphi's strong typing: you won't be able to pass a non-persistent type to
methods which expect one.</p>
<p>From the Domain-Driven / SOA point of view, it is now an established rule to
make a distinction between DTO (Data Transfer Objects) and Domain Values
(<em>Entity objects</em> or <em>Aggregates</em>). In most implementations,
persistence objects (aka ORM objects) should be either the aggregate roots
themselves (you do not store Entity objects and even worse DTOs), either
dedicated classes. Do not mix layers, unless you like your software to be a
maintenance nightmare!</p>
<p>Some <a href="http://martinfowler.com/eaaDev/EventSourcing.html">Event-Sourcing</a>
architectures even implement <em>several DB back-end at once</em>:<br />
- It will store the status on one DB (e.g. high-performance in-memory) for most
common requests to be immediate;<br />
- And store the modification events in another ACID DB (e.g. <em>SQLite3</em>,
<em>MS SQL</em> or <em>Oracle</em>);<br />
- And even sometimes fill some dedicated consolidation DBs for further
analysis.</p>
<p>AFAIK it could be possible to directly access ORM objects remotely (e.g. the
consolidation DB), mostly in a read-only way, for dedicated reporting, e.g.
from consolidated data - this is one potential <a href="http://martinfowler.com/bliki/CQRS.html">CQRS</a> implementation pattern with
<em>mORMot.</em> Thanks to the framework security, remote access will be safe:
your clients won't be able to change the consolidation DB content!</p>
<p>As can be easily guessed, such design models are far away from a basic ORM
built only for class persistence.</p>
<h3>The good ORM is the one you need</h3>
<p>Therefore, we may sum up some potential use of ORM, depending of your
intent:<br />
- If your understanding of ORM is just to persist some existing objects,
<em>mORMot</em> won't help you directly (but we identified that some users are
using the built-in JSON serialization feature of the framework to create their
own dedicated Client-Server ORM-like platform);<br />
- If you want to persist some data objects (not tied to complex business
logic), the framework will be a light and fast candidate, via <em>SQLite3,
Oracle, MS SQL </em>or even with no SQL engine, using
<code>TSQLRestServerStaticInMemory</code> class which is able to persist its
content with small files - see <a href="https://blog.synopse.info?post/post/2011/05/14/Virtual-Tables-in-the-SQLite3-framework">this
article</a>;<br />
- If you need (perhaps not now, but probably in the future) to create some kind
of scalable domain-driven architecture, you'll have all needed features at hand
with <em>mORMot</em>;<br />
- If your expectation is to map an existing complex DB, <em>mORMot</em> will
handle it soon (it is planned and prepared within the framework
architecture).</p>
<p>Therefore, <em>mORmot</em> is not just an ORM, nor just a "classic"
ORM.</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?pid=4676#p4676">welcome on our
forum</a>, as usual.</p>Domain-Driven designurn:md5:bc60cd09bdcbc5c491e33ecee885a7b72012-05-25T18:59:00+02:002012-05-26T19:39:38+02:00AB4327-GANDImORMot FrameworkblogBusinessRulesDatabaseDelphiDocumentationDomainDrivendynamic arrayGoodPracticei18ninterfaceJSONlogmORMotORMSOASourceSQLite3TestingWCF<p>With a <a href="https://blog.synopse.info?post/post/2012/04/20/WCF%2C-mORMot-and-Event-Sourcing">previous
article</a>, we introduced the concept of "Domain-Driven design" into our
framework presentation.</p>
<p>It's now time to detail a bit more this very nice software architecture
design, and how <em>mORMot</em> is able to achieve such an implementation
pattern.</p> <p>The somewhat "official" definition of Domain-driven design is supplied at
<a href="http://domaindrivendesign.org">http://domaindrivendesign.org</a>:</p>
<blockquote>
<p><em>Over the last decade or two, a philosophy has developed as an
undercurrent in the object community. The premise of domain-driven design is
two-fold:</em><br />
- <em>For most software projects, the primary focus should be on the domain and
domain logic;</em><br />
- <em>Complex domain designs should be based on a model.</em></p>
<p><em>Domain-driven design is not a technology or a methodology. It is a way
of thinking and a set of priorities, aimed at accelerating software projects
that have to deal with complicated domains.</em></p>
</blockquote>
<p>It can be implemented in a kind of <a href="https://blog.synopse.info?post/post/2010/08/19/How-to-implement-multi-tier-architecture-in-our-SQLite3-Framework">
Multi-Tier architecture</a>.<br />
In this case, we are talking about <em>N-Layered Domain-Oriented
Architecture</em>.<br />
It involves a common representation splitting the <em>Logic Tier</em> into two
layers, i.e. <em>Application layer</em> and <em>Domain Model layer</em>.</p>
<p>Of course, this particular layered architecture is customizable according to
the needs of each project.<br />
We simply propose following an architecture that serves as a baseline to be
modified or adapted by architects according to their needs and
requirements.</p>
<p>It could therefore be presented as in the following model:</p>
<table>
<tbody>
<tr>
<td><strong>Layer</strong></td>
<td><strong>Description</strong></td>
</tr>
<tr>
<td>Presentation</td>
<td>MVC UI generation and reporting</td>
</tr>
<tr>
<td>Application</td>
<td>Services and high-level adapters</td>
</tr>
<tr>
<td>Domain Model</td>
<td>Where business logic remains</td>
</tr>
<tr>
<td>Data persistence</td>
<td>ORM and external services</td>
</tr>
<tr>
<td>Cross-Cutting</td>
<td>Horizontal aspects shared by other layers</td>
</tr>
</tbody>
</table>
<p>In fact, this design matches perfectly the RESTful SOA approach of the
<em>Synopse mORMot framework</em>.</p>
<p>See the following diagram:</p>
<p><a href="https://blog.synopse.info?post/public/DomainDrivenArchitecture.png"><img src="https://blog.synopse.info?post/public/.DomainDrivenArchitecture_m.jpg" alt="" title="N-Layered Domain-Oriented Architecture of mORMot, mai 2012" /></a></p>
<p>In fact, since a good SOA architecture tends to ensure that services
comprise unassociated, loosely coupled units of functionality that have no
calls to each other embedded in them, we may define two levels of services,
implemented by two <code>interface</code> factories, using their own hosting
and communication:<br />
- One set of services at <em>Application layer</em>, to define the uncoupled
contracts available from Client applications;<br />
- One set of services at <em>Domain Model layer</em>, which will allow all
involved domains to communicate with each other, without exposing it to the
remote clients.</p>
<p>Therefore, those layers could be also implemented as such:</p>
<p><a href="https://blog.synopse.info?post/public/DomainDrivenArchitecture2.png"><img src="https://blog.synopse.info?post/public/.DomainDrivenArchitecture2_m.jpg" alt="" title="Alternate Domain-Oriented Architecture of mORMot, mai 2012" /></a></p>
<p>Due to the SOLID design of mORMot - see <a href="https://blog.synopse.info?post/post/2011/11/27/SOLID-design-principles">this article</a> - you can use as
many Client-Server services layers as needed in the same architecture (i.e. a
Server can be a Client of the same process or any other processes, locally or
remotely), in order to fit your project needs, and let it evolve from the
simpliest architecture (e.g. a stand-alone executable with <em>SQLite3</em>
included) to a full scalable Domain-Driven design working with a
<em>Oracle</em> database. </p>
<p>In order to provide the better scaling of the server side, cache can be
easily implemented at every level, and hosting can be tuned in order to provide
the best response time possible: one central server, several dedicated servers
for application, domain and persistence layers...</p>
<p>With <em>mORMot</em>, your software solution will never be stucked in a
dead-end.<br />
You'll be able to always adapt to your customers need, and maximize your
ROI.</p>
<p>The <a href="http://synopse.info/fossil/wiki?name=SQLite3+Framework">main
presentation page of our framework</a> has been updated, and provide a clear
view of this N-Layered architecture.</p>
<p>Feedback is welcome <a href="http://synopse.info/forum">on our
forum</a>.<br />
More to come, including updated documentation!</p>The mORMot attitudeurn:md5:bcf447b193be88b26f13f8ccb9fe6f252012-04-25T06:27:00+02:002012-05-15T21:45:06+02:00AB4327-GANDImORMot FrameworkauthenticationblogBusinessRulesDatabaseDelphiDocumentationEventSourcingGoodPracticehttp.sysi18ninterfaceJSONlogmORMotMSSQLOleDBOracleORMPDFRADRestsessionSOASourceSQLSQLite3UserInterfaceWCF<p>In a discussion <a href="https://forums.embarcadero.com//thread.jspa?messageID=452287#452287">with Henrick
Hellström</a>, in Embarcadero forums, I wrote some high-level information about
<em>mORMot</em>.</p>
<p>It was clear to me that our little <em>mORMot</em> is now far away from
a simple Client-Server solution.</p>
<p>The Henrick point was that with <a href="http://www.realthinclient.com/">Real Thin Client</a> (RTC), you are able
to write any Client-Server solution, even a RESTful / JSON based one.</p>
<p>He is of course right, but it made clear to me all the work done in
<em>mORMot</em> since its beginning.<br />
From a Client-Server ORM, it is now a complete SOA framework, ready to serve
<em>Domain-Driven-Design</em> solutions.</p> <p>In <em>mORMot</em>, you can also have complete control over all of the
aspects. Even routing can be customized, the same for data access (you are not
stuck to <em>SQLite3</em> for instance), or JSON serialization. <em>mORMot</em>
can serve directly web content, dynamic or static, if needed. So you are not
stucked to one implementation pattern, from a black box. It is worth
mentioning. This is the same for RTC: both provide source code and try to
follow the <a href="https://blog.synopse.info?post/post/2011/11/27/SOLID-design-principles">Open/Close
principle</a>.<br />
About openness, the fact that <em>mORMot</em> is true <em>Open Source</em>
(with 3 licenses), allows it to be modified, even forked if needed - whereas
you can't be sure it would be possible with RTC more conventional proprietary
approach (remember the great <a href="http://edn.embarcadero.com/article/33058">Bold project</a>?).</p>
<p>My remark was not about "philosophy" of both projects, it is about
scope.</p>
<p>RTC is about the communication layer, whereas <em>mORMot</em> is now a
<a href="http://en.wikipedia.org/wiki/Domain-driven_design">Domain-Driven
framework</a> (DDD) - i.e. it proposes a proven (and adaptive) model embracing
communication, application, domain, persistence layers, with all cross-layers
features (like security, cache, session, data marshaling, logging,
<em>i18n</em>, business rules, reporting).<br />
I'll add some information about DDD in the framework documentation, in the next
days.</p>
<p>RTC-based projects are, AFAIK procedural-oriented (i.e. by "function"),
whereas <em>mORMot</em> has a true OOP model, involving classes and
interfaces.<br />
It is one drawback of the <a href="https://blog.synopse.info?post/post/2011/07/02/Is-Object-Relational-Mapping-the-Paradise-of-Computer-Science">
RAD approach</a>: when you use components and events, you are tied to this
procedural orientation in your project architecture, even if the RTC
implementation itself uses classes. Of course, you can map the RTC events
within your own classes, but you'll have to write a lot of code by hand, and
you'll soon reach the limits of RAD.</p>
<p>My point was that you will have to reinvent the wheel if you start from a
scratch RTC code - and this is not what the OP wanted in the original forum
thread: "I love not writing code. especially json streaming code".</p>
<p>IMHO, if we may compare to the <em>Micro$oft</em> product line, RTC is some
kind of light-weight and powerful ISS in managed code (including nice features
like scripting).</p>
<p>Whereas <a href="https://blog.synopse.info?post/post/2012/04/20/WCF%2C-mORMot-and-Event-Sourcing"><em>mORMot</em> is now a
whole SOA framework,</a> containing features from ISS, WCF, WPF, WSA, Entity
Framework and DB access.<br />
Of course, it is less complete than .Net based products, but it offers most of
the needed SOA features.<br />
And it is much lighter, also faster, with all the source code provided, with no
runtime to install, no marketing or commercial patterns to follow.</p>
<p>There is a gap between providing a Client-Server communication layer (like
IIS or any RESTful server), and proposing all necessary bricks to build a whole
<a href="http://en.wikipedia.org/wiki/Service-oriented_architecture">Service
Oriented Architecture</a>.</p>
<p>Feedback and comments are <a href="http://synopse.info/forum/viewtopic.php?id=695">welcome on our forum</a>.</p>Our mORMot won't hibernate this winter, thanks to FireMonkeyurn:md5:4b1e8a46cc307e0a0e1700b1fdb4f3872011-08-08T06:41:00+02:002011-08-09T08:37:44+02:00AB4327-GANDImORMot Framework64bitasmBusinessRulesCrossPlatformDatabaseDelphiFireMonkeyFreePascaliOSLinuxMaxOSXmORMotORMSourceSQLite3Unicode<p>Everybody is buzzing about <em>FireMonkey</em>...</p>
<p><img src="http://blogs.embarcadero.com/files/2011/08/firemonkey-medium_4234-300x93.png" /></p>
<p>Our little <em>mORMot</em> will like <em>FireMonkey</em>!<br />
Here is why...</p> <h3>What is FireMonkey?</h3>
<p><em>FireMonkey</em> is a scalable vector graphics based GUI framework
exploiting GPU capabilities for hardware accelerated cross platform GUI’s,
running on Windows, Mac OS X and iOS.</p>
<p>It's based on previous work of Eugene A. Kryukov, who <a href="http://www.ksdev.com/">has been hired by Embarcadero last year</a>.</p>
<p>This previous framework was named <em>DXScene</em>, and you can still browse
a lot of pictures, even on iOS support <a href="http://ksdev.blogspot.com/">on
Eugene's blog</a>.</p>
<h3>What's up, doc?</h3>
<p><em>FireMonkey</em> is the main feature of XE2, but not the only one.</p>
<p>From the compiler point of view, the main features are 64 bit compilation,
and OS X cross-compilation and cross-debugging. You'll need to buy a Mac (or
try some hack to <a href="http://www.redmondpie.com/how-to-install-os-x-snow-leopard-in-vmware-windows-7-9140301/">
install a virtual Mac on your PC</a>) in order to test it, but it's
very promising.</p>
<p>In comparison, the <em>DataSnap</em> enhancements included in XE2 are not so
groovy: client proxy generation – for Android, .NET, Objective-C, Blackberry,
iOS etc.. Should not be so difficult to implement, in a RESTful / JSON
world...</p>
<p><em>LiveBindings</em> – a way to create ‘live” relationships not just
between objects but between individual properties of objects - is a tempting
technology. Sounds more compatible with the n-Tier model than the previous RAD
approach.</p>
<h3>Our mORMot won't hibernate this winter</h3>
<p>Thanks to <em>FireMonkey</em>, our <em>mORMot / SQLite3</em> framework will
be able to stay awake during this winter.<br />
<img src="https://blog.synopse.info?post/public/mORMot.png" alt="" title="mORMot, août 2011" /></p>
<p>Here is why:</p>
<ul>
<li>Delphi XE2 still lacks of a standard ORM, and our framework is just there,
with some unique features (like its Client-Server, SOA architecture);</li>
<li>Its n-Tier design makes it easy to change the presentation layer: the
RESTful server can be accessed, through JSON, by light clients running on OS X
or iOS - our <em>mORMot</em> is just waiting for <em>FireMonkey</em> to
appear at its burrow's entrance - it is already <em>LiveBindings</em>
friendly;</li>
<li>Our kernel database is <em>SQLite3</em>, which is already part of both
<a href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/sqlite3.1.html">
OS X</a> and <a href="http://developer.apple.com/technologies/ios/data-management.html">iOS</a>;</li>
<li>Our code was <a href="https://blog.synopse.info?post/post/2010/08/10/Writing-Delphi-code-for-64-bits-compiler">designed to be 64
bit ready</a>, and since the optimized <em>asm</em> functions have their "pure
pascal" version, it will be able to run on ARM - even low-level performance
tricks (<code>PCardinal(...)^=..</code>) will work as expected, since iOS uses
<a href="http://en.wikipedia.org/wiki/Endianness">little-endian</a> ARM mode,
just as x86/x64 CPUs (as Android BTW);</li>
<li>Most of the framework low-level stuff is concentrated in the <em><a href="http://synopse.info/fossil/finfo?name=SynCommons.pas">SynCommons</a></em> unit
- we tried to avoid code duplication where possible, and there is already a
full coverage of regression tests, ready to be run on new Operating Systems /
CPUs;</li>
<li>We don't rely on third-party components, and we manage the full source code
of all our used units;</li>
<li>Some Windows-specific units (like <em><a href="https://blog.synopse.info?post/post/2010/03/20/GIF%2C-TIF%2C-PNG-and-JPG-pictures-TGraphic-read/write-via-GDI">
SynGdiPlus</a></em> or such) are already simple and abstract enough to be
implemented easily on another platform - and some specific classes (like the
<a href="https://blog.synopse.info?post/post/2011/03/11/HTTP-server-using-fast-http.sys-kernel-mode-server"><em>http.sys</em>
kernel server</a>) have their own "generic" version (using plain sockets
or <em>Fastcgi</em>).</li>
</ul>
<p>So we are waiting for the fire to be released... monkeys and marmots on your
mark!</p>
<h3>FPC at the end of the tunnel</h3>
<p>For its ARM back-end, it sounds like if Delphi will use FPC / Lazarus on the
OS X site, within XCode, to create iOS applications. Even if I'm quite sure
Embarcadero will get rid of the FPC compatibility as soon as possible (i.e.
when their ARM compiler back-end will be available), there was certainly some
internal debate in the San Francisco's building to embed or not FPC to Delphi
XE2. I'm quite happy that <a href="https://blog.synopse.info?post/post/2010/08/14/FPC-and-Delphi%3A-toward-a-%22fratricidal-war%22">the war I
spoke about one year ago</a> did not occur.</p>
<p>I don't know if it comes from open-mindedness or from pragmatism,
but it surely put some lights on <a href="http://www.freepascal.org/">Free
Pascal</a>, which deserves it.</p>
<p>And since it will most probably use either <a href="http://developer.apple.com/carbon/">Carbon on MacOS X</a> (this is the
main reason why Delphi XE2 is not able to produce 64 bit applications yet),
or <a href="http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/URL_iPhone_OS_Overview/_index.html#//apple_ref/doc/uid/TP40007592-CH1-SW3">the
C APIs of iOS</a>, you will be able to use the same standard "Delphi" syntax
for all platform, and won't have to use <a href="http://wiki.freepascal.org/FPC_PasCocoa"><em>Objective
Pascal</em> paradigm</a>.</p>
<p>Feedback and comments <a href="http://synopse.info/forum/viewtopic.php?pid=2423#p2423">are welcome on our
forum</a>.</p>Is Object-Relational Mapping the Paradise of Computer Science?urn:md5:3c5ef51e7b9ace6cc08bc88742a7f8db2011-07-02T11:32:00+02:002011-07-02T10:53:51+02:00AB4327-GANDImORMot FrameworkblogBusinessRulesDatabaseDelphiGoodPracticeJSONmORMotOleDBORMRestRTTISQLSQLite3TDynArrayVirtualTable<p>There is a well known syndrome around, against ORM.</p>
<p>Do you remember <a href="http://blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Computer+Science.aspx">The
Vietnam of Computer Science</a> article?</p>
<p>It is worth reading... and commenting.<br />
Sounds a bit outdated by now. <em>Tempus fugit!</em></p> <h3>Vietnam</h3>
Here are the "solutions" the above article concluded with:
<p><em>1. Developers simply give up on objects entirely</em></p>
<p>Not an argument (by the way it's strange to start with this as a
"solution")... <img src="https://blog.synopse.info?pf=wink.svg" alt=";)" class="smiley" /></p>
<p><em>2. Wholehearted acceptance.</em></p>
<p>This is exactly what a well-design ORM should do: transparency.</p>
<p>For our framework, I found out that SQLite3 was a very good way of some kind
of "native" object persistence.<br />
Thanks to unique features of Delphi RTTI, you can persistent dynamic arrays of
records in native binary format in our ORM (as BLOB), and even access from it
from SQL if needed.</p>
<p><em>3. Manual mapping.</em></p>
<p>AFAIK, most ORM allow this. Some via external configuration files, some from
code.</p>
<p>I'm currently <a href="http://blog.synopse.info/post/2011/06/09/Close-future-of-the-framework%3A-database-agnosticism">
adding this for our mORMot framework</a>, for any external database, via direct
OleDB access.</p>
<p><em>4. Acceptance of O/R-M limitations.</em></p>
<p>I agree with that.</p>
<p>That's why I wanted our ORM to have access to SQL when needed, inside our
RESTful model.<br />
We rely on SQLite3 and its custom functions and virtual table mechanism to have
the full ORM power at hand, even in SQL!</p>
<p><em>5. Integration of relational concepts into the languages.</em></p>
<p>This is the LINQ approach.</p>
<p>We <a href="http://synopse.info/forum/viewtopic.php?id=202">tried to
avoid</a> mixing syntax in high-level code by simply letting the WHERE (and
optionally the SELECT) clause available from the ORM caller.<br />
This is quite powerful:</p>
<pre>
aPerson := TSQLPerson.CreateAndFillPrepare(RestClient,
'Points > :(%): AND Points < :(%): AND Active <> 0', [100,200],
'Name,FirstName');
while aPerson.FillOne do
DoSomeThingWith(aPerson);
aPerson.Free;
</pre>
<p>Note that the above code will only retrieve the needed columns
(Name,FirstName), so it fixes one issue quoted by this article.</p>
<p><em>6. Integration of relational concepts into frameworks, i.e. developers
take a different view of "objects" that is more relational in nature.</em></p>
<p>This is an issue of ORM. You should know how to best create your
objects.</p>
<h3>Another solution: highway to paradise?</h3>
<p>In fact, a solution with potential ORM problems is the <strong>SOA
approach</strong>.</p>
<p>IMHO objects are just a way of accessing, via a RESTful front-end, to
services.<br />
What is great with ORM is that the same object definition can be used on both
Client and Server side.</p>
<p>In fact, you may have several kind of objects, all accessible via a RESTful
ORM:<br />
- Low-level DB objects, mapping the database, either local (SQLite3) or remote
(via OleDB);<br />
- High-level DB objects, with high-type properties (like dynamic arrays), which
are created from low-level DB objects, and optionally stored locally;<br />
- In-memory objects, i.e. short life objects, never stored in the DB, but
accessible from Client and Server;<br />
- Services are viewed as objects methods.</p>
<p>So you may have some hidden objects in the Client or the Server side.</p>
<p>If you <a href="http://synopse.info/forum/viewtopic.php?id=55">download the
documentation</a> of our framework, take a look at the SAD document.</p>
<p>There are some pages highlighting all those aspects, in the first part of
this document:<br />
- MVC and Multi-Tier architecture;<br />
- Why an ORM (with advanced RTTI) - why it is not a DB;<br />
- Why a Client/Server ORM;<br />
- Writing RESTful Services;<br />
- ORM and SQL (via SQLite3 virtual tables, custom functions to access
BLOB).</p>
<h3>Conclusion</h3>
<p>My point is that mixing ORM and RESTful is a good way.</p>
<p>You can do wonders or monsters with ORM, as with any other computer
scheme.</p>
<p>ORM is not evil nor good by itself. It's not Vietnam nor Paradise. Can be
both, depending on what you do with it.</p>
<p>Feedback <a href="http://synopse.info/forum/viewtopic.php?pid=2245">welcome on our
forum</a>.</p>Synopse SQLite3 Framework 1.13urn:md5:f53aa2714400a9c738c59ed88c9cd50b2011-06-05T17:45:00+02:002020-07-03T09:29:59+02:00AB4327-GANDImORMot FrameworkauthenticationBatchBusinessRulesDatabaseDelphiFTS3http.sysRTREESourceSQLite3VirtualTable<p>This is a major step for the framework.</p>
<p>Among a lot of new features and bug fixes:</p>
<ul>
<li><a href="https://blog.synopse.info?post/post/2011/03/12/TDynArray-and-Record-compare/load/save-using-fast-RTTI">Dynamic
arrays and Record handling</a>, including low-level access and high-level
handling for the ORM part of the framework (using <code>TDynArray</code>
wrapper);</li>
<li>Business rules now handled via <a href="https://blog.synopse.info?post/post/2011/06/01/Business-rules%3A-validate-and-filter-data">filtering and
validating classes</a> for both our ORM and the <a href="https://blog.synopse.info?post/category/Open-Source-Projects/Synopse-BigTable">Synopse Big Table</a>;</li>
<li>New <a href="https://blog.synopse.info?post/post/2011/04/14/Enhanced-logging-in-SynCommons">full-featured
logging classes</a>, with exception handling, stack trace and JSON
serialization;</li>
<li>Custom <a href="https://blog.synopse.info?post/post/2011/06/03/Custom-SQL-functions">SQL functions</a>
and <code>TSQLRecordRTree</code> to implement <a href="https://blog.synopse.info?post/post/2011/06/02/TSQLRecordRTree-to-implement-R-Tree-virtual-tables">R-Tree
virtual tables</a>;</li>
<li><a href="https://blog.synopse.info?post/post/2011/06/03/BATCH-sequences-for-adding/updating/deleting-records">Batch
sequences</a> for adding/updating/deleting multiple records;</li>
<li>Secure <a href="https://blog.synopse.info?post/post/2011/05/24/How-to-implement-RESTful-authentication">RESTful
authentication</a> via per-user light sessions, and per-query signature;</li>
<li>Define <a href="https://blog.synopse.info?post/post/2011/05/14/Virtual-Tables-in-the-SQLite3-framework">custom Virtual
Tables</a> (i.e. access any kind of data from SQL) in Delphi code;</li>
<li>New HTTP server <a href="https://blog.synopse.info?post/post/2011/03/11/HTTP-server-using-fast-http.sys-kernel-mode-server">using
fast http.sys kernel-mode server</a> and zip / SynLZ compression;</li>
<li><a href="https://blog.synopse.info?post/post/2011/03/05/Open-Source-SynTaskDialog-unit-for-XP%2CVista%2CSeven"><em>SynTaskDialog</em>
unit</a> native under Vista,Seven with emulation code under XP/2000 ;</li>
<li><a href="http://synopse.info/forum/viewtopic.php?id=280">User Interface
automated Generation</a> using standard VCL or proprietary TMS components;</li>
<li><a href="http://synopse.info/forum/viewtopic.php?id=311">Documentation
completed</a> - more than 600 pages by now, including general User Guide and
exhaustive Technical Reference;</li>
<li>Updated <em>SQLite3</em> database engine to version 3.7.6.3;</li>
<li>Numerous bug fixes, speed and feature enhancements (including in the PDF
and other UI-related units).</li>
</ul>
<p>Open Source project, for Delphi 6 up to XE, licensed under a <a href="http://synopse.info/forum/viewtopic.php?id=27">MPL/LGPL/GPL
tri-license</a>.</p> <p>Here are the main modifications, grouped by unit:</p>
<h3>SynCommons</h3>
<p>- unit now compiles and works with Delphi 5 compiler<br />
- new low-level RTTI functions for handling record types: RecordEquals,
RecordSave, RecordSaveLength, RecordLoad, RecordClear and RecordCopy<br />
- new TDynArray object, which is a wrapper around any dynamic array: you can
now access to the dynamic array using TList-like properties and methods, e.g.
Count, Add, Insert, Delete, Clear, IndexOf, Find, Sort and some new methods
like LoadFromStream, SaveToStream, LoadFrom and SaveTo which allow fast binary
serialization of any dynamic array, even containing strings or records; a
CreateOrderedIndex method is also available to create individual index
according to the dynamic array content; and any dynamic array can be serialized
as UTF-8 JSON via TTextWriter.AddDynArrayJSON and TDynArray.LoadFromJSON
methods<br />
- introducing direct content filtering and validation using
TSynFilterOrValidate dedicated classes, for both TSQLRecord and
SynBigTable<br />
- filtering is handled via some TSynFilter classes - TSynFilterUpperCase,
TSynFilterUpperCaseU, TSynFilterLowerCase, TSynFilterLowerCaseU and
TSynFilterTrim e.g.<br />
- validation is handled via some TSynValidate classes - TSynValidateRest,
TSynValidateIPAddress, TSynValidateEmail, TSynValidatePattern,
TSynValidatePatternI, TSynValidateText, TSynValidatePassWord e.g.<br />
- dedicated TSynTableFieldProperties.Validate method for validation (e.g. a
TSynValidateTableUniqueField instance is created if tfoUnique is in
Options)<br />
- dedicated TSynTableFieldProperties.Filter method for filtering (using common
TSynFilter classes, working at UTF-8 Text content)<br />
- faster implementation of Move() for Delphi versions with no FastCode
inside<br />
- new ConvertCaseUTF8(), UpperCaseU(), LowerCaseU(), Int64ToUInt32(),
GetCardinalDef(), IsValidEmail, IsValidIP4Address(), PatchCodePtrUInt(),
GetCaptionFromClass(), GetDisplayNameFromClass(), DateTimeToIso8601Text()
StrUInt32(), StringBufferToUtf8(), IsZero(), AddPrefixToCSV(), IntToString(),
RawUTF8DynArrayEquals(), FromVarString(), GetBitCSV(), SetBitCSV() procedures
or functions (with associated tests)<br />
- new grep-like IsMatch() function for basic pattern matching<br />
- new BinToBase64, Base64ToBin and IsBase64 *fast* conversion functions (with
optimized assembler version, using CPU pipelining and lookup table)<br />
- introducing the GarbageCollector TObjectList for handling a global garbage
collector for instances which must live during the whole executable process
(used e.g. to avoid a memory leak for "class var" or such variables)<br />
- new TSynLog class to handle enhanced logging to any application, with
exception handling (+stack trace) and customer-side performance profiling<br />
- new TSynMapFile class to retrieve debugging information from .map file (and
able to save and read smaller .mab files) - used by TSynLog if available<br />
- new TSynTestsLogged test suit class, with automated test case logging<br />
- Windows version detection enhanced, now retrieving TWindowsVersion
enumerate<br />
- great performance improvement in TSynTableFieldProperties for update
process<br />
- added TMemoryMap and TSynMemoryStreamMapped to handle memory-mapped
files<br />
- added TMemoryMapText class to fast handle big UTF-8 text files (like
logs)<br />
- now TTextWriter can have a custom internal buffer size (default 1024
bytes)<br />
- now TFileBufferWriter and TFileVersion are regular classes, not an
object/record any more (this was incoherent since Delphi 2010)<br />
- new TFileBufferReader.OpenFrom(Stream) and ReadRawUTF8 methods<br />
- now TSynCache will use faster TDynArrayHashed for its content hashing<br />
- new Escape: TTextWriterKind optional parameter for TTextWriter.Add()<br />
- new SynLZ related compression functions: FileSynLZ/FileUnSynLZ and
StreamSynLZ/StreamUnSynLZ<br />
- source can now be parsed per all Delphi IDE pre-parser (dual declaration as
record or object because of Delphi 2010 and up was not understood)<br />
- fixed issue in TSynTable.Data() method: ID was not set as expected<br />
- fixed issue in TSynTableFieldProperties: wrong constraint evaluation and
index refresh at records update<br />
- fixed issue in ToVarUInt32Length/ToVarUInt32LengthWithData</p>
<h3>SynCrtSock</h3>
<p>- code modifications to compile with Delphi 5 compiler<br />
- new THttpApiServer class, using fast http.sys kernel-mode server for better
performance and less resource usage<br />
- DOS / TCP SYN Flood detection time enhanced to 5 seconds<br />
- fixed HTTP client stream layout (to be more RFC compliant)<br />
- new generic compression handling mechanism: can handle gzip, deflate or
custom synlz / synlzo algorithms via THttpSocketCompress functions<br />
- new THttpServerGeneric.Request virtual abstract method prototype<br />
- new TWinINet class, using WinINet API (very slow, do not use)<br />
- new TWinHTTP class, using WinHTTP API (faster than THttpClientSocket): this
is the class to be used</p>
<h3>SynCrypto</h3>
<p>- code modifications to compile with Delphi 5 compiler</p>
<h3>SynGdiPlus</h3>
<p>- code modifications to compile with Delphi 5 compiler<br />
- handle TCanvas.DrawCurve() method in TMetaFile enumeration<br />
- suppress GDI+ library back thread which may hang up application when using
this unit in a DLL - manual hook and unhook is done at statup/shutdown see
http://mikevdm.com/BlogEntry/Key/GdiplusShutdown-Hangs-Mysteriously</p>
<h3>SynLZ</h3>
<p>- code modifications to compile with Delphi 5 compiler<br />
- comment refactoring (mostly for inclusion in SynProject documentation)<br />
- new CompressSynLZ function, for THttpSocket.RegisterCompress - this function
will return 'synlzo' as "ACCEPT-ENCODING:" HTTP header parameter</p>
<h3>SynLZO</h3>
<p>- code modifications to compile with Delphi 5 compiler<br />
- comment refactoring (mostly for inclusion in SynProject documentation)<br />
- new CompressSynLZO function, for THttpSocket.RegisterCompress - those
functions will return 'synlzo' as ACCEPT-ENCODING: HTTP header parameter</p>
<h3>SynPdf</h3>
<p>- code modifications to compile with Delphi 5 compiler<br />
- added horizontal scaling for GDI enumeration in case of text kerning (could
occur for small fonts)<br />
- fixed "Save when closing with Acrobat Reader X" - thanks to Ondrej<br />
- fixed clipping problems and vertical font positioning issue in GDI
enumeration - thanks to Ondrej for those corrections!</p>
<h3>SynZip</h3>
<p>- code modifications to compile with Delphi 5 compiler<br />
- new CompressGZip and CompressDeflate functions, for
THttpSocket.RegisterCompress<br />
- now handle Unicode file names UTF-8 encoded inside .Zip archive<br />
- new TZipWrite.CreateFrom constructor, to add some new content to an existing
.Zip archive<br />
- EventArchiveZip function can be used as a TSynLogArchiveEvent handler to
compress old .log files into a .zip standard archive</p>
<h3>SynZipFiles</h3>
<p>- code modifications to compile with Delphi 5 compiler<br />
- handle Unicode versions of Delphi (Delphi 2009/2010/XE)<br />
- now officialy handle UTF-8 encoded file names inside .Zip archive</p>
<h3>SQLite3Commons</h3>
<p>- the ORM will now include all published properties of the parents, up to
TSQLRecord, to the database fields (it was only using the published properties
at the topmost class level)<br />
- dynamic arrays can now be specified for TSQLRecord published properties: a
new sftBlobDynArray field kind has been added - will be stored as BLOB in the
database (following the TDynArray.SaveTo binary stream layout), and will be
transmitted as Base64 encoded in the JSON stream - we implemented a
sftBlobRecord field kind, but Delphi RTTI is not generated for published
properties of records: so our code is disabled (see PUBLISHRECORD conditional)
<img src="https://blog.synopse.info?pf=sad.svg" alt=":(" class="smiley" /> - but you can use dynamic arrays of records<br />
- TPersistent can be now be specified for TSQLRecord published properties: a
new sftObject field kind has been added - will be stored as TEXT in the
database (following the ObjectToJSON serialization format) - TStrings or
TRawUTF8List will be stored as a JSON array of string, and TCollection as a
JSON array of objects, other TPersistent classes will have their published
properties serialized as a JSON object<br />
- introducing direct content filtering and validation using
TSynFilterOrValidate dedicated classes<br />
- filtering is handled directly in the new TSQLRecord.Filter virtual method, or
via some TSynFilter classes - TSynFilterUpperCase, TSynFilterUpperCaseU,
TSynFilterLowerCase, TSynFilterLowerCaseU and TSynFilterTrim e.g.<br />
- validation is handled in the new TSQLRecord.Validate virtual method, or via
some TSynValidate classes - TSynValidateRest, TSynValidateIPAddress,
TSynValidateEmail, TSynValidatePattern, TSynValidatePatternI, TSynValidateText,
TSynValidatePassWord e.g.<br />
- introducing TSQLRecordRTree to implement R-Tree virtual tables - and new
TSQLRecordVirtual parent table for all virtual tables like TSQLRecordFTS*<br />
- new TSQLRestClientURI methods to implement BATCH sequences to speed up
database modifications: after a call to BatchStart, database modification
statements are added to the sequence via BatchAdd/BatchUpdate/BatchDelete, then
all statments are sent as once to the remote server via BatchSend - this is
MUCH faster than individual calls to Add/Update/Delete in case of a slow remote
connection (typically HTTP over Internet)<br />
- introducing TSQLVirtualTableModule / TSQLVirtualTable /
TSQLVirtualTableCursor classes for a generic Virtual table mechanism (used e.g.
by TSQLVirtualTableModuleDB in the SQLite3 unit)<br />
- new TSQLRecordVirtualTableAutoID and TSQLRecordVirtualTableForcedID classes,
used to access any TSQLVirtualTable in our ORM<br />
- security and per-user access rights is now implemented in the framework core
using per-User authentication via in-memory sessions (stored as TAuthSession),
with group-defined associated security parameters (via TSQLAuthUser and
TSQLAuthGroup tables), and RESTful Query Authentication via URI signature;
should avoid most MITM and replay attacks<br />
- new TJSONSerializer class and ObjectToJSON/JSONToObject method (handles also
dynamic arrays following the TTextWriter.AddDynArrayJSON format, i.e. plain
JSON array for common types aka '[1,2,3]', but Base64 encoded stream aka
'["\uFFF0base64encodedbinary"]' for other arrays) and corresponding
UrlDecodeObject() function (to be called by RESTful Services implementation on
Server side)<br />
- wider usage of TSQLRecordProperties, for faster RTTI access, via the new
class function TSQLRecord.RecordProps: TSQLRecordProperties: only virtual class
function or procedure are now defined in TSQLRecord<br />
- enhanced TPropInfo.GetLongStrValue/SetLongStrValue methods, now converting
RawUnicode, WinAnsiString, TSQLRawBlob and AnsiString properties<br />
- now ensure that no published property named ID or RowID was defined (this
unique primary key field must be handled directly by TSQLRecord)<br />
- MAX_SQLFIELDS default is still 64, but can now be set to any value (64, 128,
192 and 256 have optimized fast code) so that you can have any number of fields
in a Table<br />
- MAX_SQLTABLES default is now 256, i.e. you can have up to 256 tables in a
TSQLModel instance (you can set any other value, on need)<br />
- TSQLModel implementation speed up, in case of a huge number of registered
TSQLRecord in the database Model (since MAX_SQLTABLES=256 by default)<br />
- enhanced TSQLRecordMany.DestGetJoinedTable method to handle custom
fields<br />
- TSQLRecordMany.DestGetJoined* methods now accept generic TSQLRest class<br />
- new aCustomFieldsCSV parameter for FillPrepare / CreateAndFillPrepare methods
of TSQLRecord, to retrieve only neeeded fields: be aware that not specified
fields will be left untouched, so a later Update() call may corrupt the row
data - this optional parameter is about to save bandwidth when retrieving
records field in a loop<br />
- TSQLRestServerStaticInMemory can now store its content into UTF-8 JSON or an
optimized (SynLZ) compressed binary format - associated TPropInfo
GetBinary/SetBinary and TSQLRecord GetBinaryValues/SetBinaryValues
methods<br />
- the generic TVarData type is now used as a standard way of fast values
communication: only handled VType are varNull, varInt64, varDouble, varString
(mapping a constant PUTF8Char), and varAny (BLOB with size = VLongs[0]) - used
e.g. by SQLite3 unit (VarDataToContext/VarDataFromValue)<br />
- new TSQLRest.Retrieve(aPublishedRecord, aValue: TSQLRecord) and
TSQLRecord.Create(aClient: TSQLRest; aPublishedRecord: TSQLRecord..)
methods<br />
- ExecuteList defined in TSQLRest, so now available also in
TSQLRestServer<br />
- added a magic pattern check to ignore broadcasted WM_COPYDATA message<br />
- fixed issue in serializing sftCurrency type in TSQLRecord.GetJSONValue</p>
<h3>SQLite3</h3>
<p>- update SQLite3 engine to version 3.7.6.3<br />
- added sqlite3InternalFreeObject(), sqlite3_malloc/realloc/free(),
sqlite3_memory_used/highwater(), sqlite3_set_authorizer() and
sqlite3_update/commit/rollback_hook() functions<br />
- introducing TSQLVirtualTableModuleSQLite3 / TSQLVirtualTableModuleServerDB
classes, and associated low-level sqlite3 functions and memory structures, in
order to implement Virtual Table modules in pure Delphi code via common
TSQLVirtualTable classes as defined in SQLite3Commons<br />
- new IntegerDynArrayContains(), RawUTF8DynArrayContainsCase/NoCase() and
Byte/Word/Cardinal/Int64/CurrencyDynArrayContains(BlobField,I64) SQL functions,
able to fast search data within T*DynArray and TRawUTF8DynArray published
properties BLOB (Currency mapped as PInt64)<br />
- new TSQLDataBaseSQLFunction and TSQLDataBase.RegisterSQLFunction method, to
implement custom SQL functions with any kind of BLOB data<br />
- regression test now exclude unfixed order for select (may vary, just like
happened for 3.7.6 when e.g. indexes started to be used)<br />
- added regression tests for sftBlobDynArray and sftObject kind of
records<br />
- TSQLRestServerDB now uses faster TDynArrayHashed for its internal prepared
statement cache<br />
- fixed issue in TSQLRestClientDB.URI: wrong InternalState returned<br />
- fastest internal cdecl qsort() function (optimized for PAnsiChar)</p>
<h3>SQLite3HttpClient</h3>
<p>- now can compress its content using deflate or faster SynLZ algorithm: by
default, the SynLZ algorithm will be used between a Delphi Client and Server
over HTTP/1.1 - there will be no speed penalty on the server side, whereas
deflate would use much more CPU<br />
- can make TCP/IP stream not HTTP compliant (against antivirus slowdown)<br />
- new TSQLite3HttpClientWinINet class, using WinINet API (very slow)<br />
- new TSQLite3HttpClientWinHTTP class, using WinHTTP API (fast and stable):
this class should be considered to be used instead of TSQLite3HttpClient for
any HTTP/1.1 client connection over a network - it is therefore the default
TSQLite3HttpClient class since this 1.13 revision</p>
<h3>SQLite3HttpServer</h3>
<p>- can now use fast http.sys kernel-mode server (THttpApiServer) if
available, and our pure Delphi THttpServer on default<br />
- now can compress its content using deflate or faster SynLZ algorithm<br />
- by default, will handle SynLZ compression for TSQLite3HttpClient<br />
- can make TCP/IP stream not HTTP compliant (for security visions)<br />
- TSQLite3HttpServer.Request now uses the new THttpServerGeneric.Request
virtual abstract method prototype to handle THttpApiServer</p>
<h3>SQLite3i18n</h3>
<p>- fix URW699 issue when compiling with Delphi 6</p>
<h3>SQLite3ToolBar</h3>
<p>- now uses TSQLRecord.RecordProps instead of lowest level RTTI calls<br />
- by default, will use only VCL components to create the Ribbon; can use
proprietary TMS component pack if USETMSPACK global conditional is defined</p>
<h3>SQLite3UI</h3>
<p>- now compiles with Delphi 6<br />
- new TSQLTableToGrid.SelectedRecordCreate method<br />
- added generic TSynLabeledEdit to handle Integer, Int64, Currency and double
kind of values</p>
<h3>SQLite3UIEdit</h3>
<p>- new OnComponentValidate property to allow custom field content
validation<br />
- now handle TSQLRecord automated filtering (using TSynFilter classes) and
validation (using TSynValidate classes)<br />
- unique field validation is now in TSQLRecord.Validate (better multi-tier
architecture)<br />
- hanle sftTimeLog and sftDateTime with a TDateTimePicker<br />
- handle sftInteger (including Int64 fields), sftCurrency and sftFloat with a
TSynLabeledEdit field<br />
- now use TMS component pack only if USETMSPACK global conditional is defined:
by default, will use only VCL components (i.e. TSynButton) for the form</p>
<h3>SQLite3UILogin</h3>
<p>- now use TMS component pack only if USETMSPACK global conditional is
defined<br />
- introducing new TSynButton, mapping either to the default TBitButton, either
to TAdvGlowButton (if USETMSPACK conditional is defined)<br />
- introducing a new TTaskDialog record/object, to access the new Vista/Seven
TaskDialog, with a fallback dialog written in Delphi under XP<br />
- use a best available font (Calibri or Tahoma), and useful bitmaps</p>
<h3>SQLite3UIOptions</h3>
<p>- Delphi 2009/2010/XE compatibility fixes</p>
<p>Full <a href="http://synopse.info/files/SynopseSQLite3.zip">source code
available</a> to download, or from our <a href="http://synopse.info/fossil">Source Code repository</a>.</p>
<p>Feedback and comments are <a href="http://synopse.info/forum/viewtopic.php?id=333">welcome on our forum</a>.</p>Business rules: validate and filter dataurn:md5:7fe035644f92dd8c8e6a71a3be02ee172011-06-01T08:19:00+02:002020-07-03T09:29:59+02:00AB4327-GANDImORMot FrameworkblogBusinessRulesDatabaseDelphiGoodPracticeORMSourceSQLite3UserInterface<p>According to the <a href="http://en.wikipedia.org/wiki/Multitier_architecture">n-Tier architecture</a>,
data filtering and validation should be implemented in the business logic, not
in the User Interface.</p>
<p>If you were used to develop RAD database application using Delphi, you may
have to change a bit your habits here. Data filtering and validation should be
implemented not in the User Interface, but in pure Delphi code.</p>
<p>Data filtering is a process run on the User entry: for instance, it will
trim left or right spaces, make the text uppercase...<br />
Data validating is performed before saving the data to the database: for
instance, an email address is checked for consistency, a field value to be
unique...</p>
<p>Some try to implement this <a href="http://www.remobjects.com/tv/da.aspx?video=da-06-scripting">using an external
scripting engine</a>, in a procedure/event mode. Back to the 80th...<br />
In our ORM framework, filtering and validation can be performed by creating
some Delphi classes, which may be used on both Server and Client side.</p> <p>In order to make this easy, a dedicated set of classes are available in the
<code>SynCommons.pas</code> unit, and allow to define both filtering and
navigation.</p>
<p>They all will be children of any of two common ancestors,
named <code>TSynValidate</code> and <code>TSynFilter</code> classes:</p>
<ul>
<li><code>TSQLRecord</code> field content validation is handled in the new
<code>TSQLRecord.Validate</code> virtual method, or via some
<code>TSQLValidate</code> classes;</li>
<li><code>TSQLRecord</code> field content filtering is handled in the new
<code>TSQLRecord.Filter</code> virtual method, or via some
<code>TSQLFilter</code> classes.</li>
</ul>
<p>Some "standard" classes are already defined in the <code>SynCommons</code>
and <code>SQLite3Commons</code> unit, to be used for most common usage:</p>
<p><a href="https://blog.synopse.info?post/public/ValidateFilter.png"><img src="https://blog.synopse.info?post/public/.ValidateFilter_m.jpg" alt="" title="Default filters and Validation classes hierarchy, juin 2011" /></a></p>
<p>You have powerful validation classes for IP Address, Email (with TLD+domain
name), simple <em>regex</em> pattern, textual validation, strong password
validation...</p>
<p>Note that some database-related filtering are existing, like
<code>TSynValidateUniqueField</code> which derivates from
<code>TSynValidateRest</code>. Thanks to this kind of data-driven classes, you
can filter or validate data using all methods of the ORM, in pure Delphi code,
with no SQL code to write by hand.</p>
<p>Of course, the <code>SQLite3UIEdit</code> unit now handles
<code>TSQLRecord</code> automated filtering (using <code>TSQLFilter</code>
classes) and validation (using one of the <code>TSQLValidate</code>
classes).</p>
<p>The unique field validation is now in <code>TSQLRecord. Validate</code> and
not in <code>SQLite3UIEdit</code> itself (to have a better multi-tier
architecture).</p>
<p>To initialize it, you can add some filters/validators to your
<code>TSQLModel</code> creation function:</p>
<pre>
<strong>function</strong> CreateFileModel(Owner: TSQLRest): TSQLModel;
<strong>var</strong> Classes: <strong>array</strong>[0..high(FileTabs)] <strong>of</strong> TSQLRecordClass;
i: integer;
<strong>begin</strong>
<strong>for</strong> i := 0 <strong>to</strong> high(FileTabs) <strong>do</strong>
Classes[i] := FileTabs[i].Table;
result := TSQLModel.Create(Classes,'synfile');
result.Owner := Owner;
result.SetActions(TypeInfo(TFileAction));
result.SetEvents(TypeInfo(TFileEvent));
TSQLFile.AddFilterOrValidate('Name',TSQLFilterLowerCase);
TSQLUser.AddFilterOrValidate('Email',TSQLValidateEmail);
<strong>end</strong>;
</pre>
<p>Calling <code>AddFilterOrValidate</code> method is enough to declare
the validation for the whole framework.</p>
<p>Feedback and comments are <a href="http://synopse.info/forum/viewtopic.php?id=241">welcome in our forum</a>.</p>