<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" /> <meta name="generator" content="AsciiDoc 8.6.8" /> <title>Listing of doc/example-scripts/ruby-mixins.tcl</title> <style type="text/css"> /* Shared CSS for AsciiDoc xhtml11 and html5 backends */ /* Default font. */ body { font-family: Georgia,serif; } /* Title font. */ h1, h2, h3, h4, h5, h6, div.title, caption.title, thead, p.table.header, #toctitle, #author, #revnumber, #revdate, #revremark, #footer { font-family: Arial,Helvetica,sans-serif; } body { margin: 1em 5% 1em 5%; } a { color: blue; text-decoration: underline; } a:visited { color: fuchsia; } em { font-style: italic; color: navy; } strong { font-weight: bold; color: #083194; } h1, h2, h3, h4, h5, h6 { color: #527bbd; margin-top: 1.2em; margin-bottom: 0.5em; line-height: 1.3; } h1, h2, h3 { border-bottom: 2px solid silver; } h2 { padding-top: 0.5em; } h3 { float: left; } h3 + * { clear: left; } h5 { font-size: 1.0em; } div.sectionbody { margin-left: 0; } hr { border: 1px solid silver; } p { margin-top: 0.5em; margin-bottom: 0.5em; } ul, ol, li > p { margin-top: 0; } ul > li { color: #aaa; } ul > li > * { color: black; } .monospaced, code, pre { font-family: "Courier New", Courier, monospace; font-size: inherit; color: navy; padding: 0; margin: 0; } #author { color: #527bbd; font-weight: bold; font-size: 1.1em; } #email { } #revnumber, #revdate, #revremark { } #footer { font-size: small; border-top: 2px solid silver; padding-top: 0.5em; margin-top: 4.0em; } #footer-text { float: left; padding-bottom: 0.5em; } #footer-badges { float: right; padding-bottom: 0.5em; } #preamble { margin-top: 1.5em; margin-bottom: 1.5em; } div.imageblock, div.exampleblock, div.verseblock, div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, div.admonitionblock { margin-top: 1.0em; margin-bottom: 1.5em; } div.admonitionblock { margin-top: 2.0em; margin-bottom: 2.0em; margin-right: 10%; color: #606060; } div.content { /* Block element content. */ padding: 0; } /* Block element titles. */ div.title, caption.title { color: #527bbd; font-weight: bold; text-align: left; margin-top: 1.0em; margin-bottom: 0.5em; } div.title + * { margin-top: 0; } td div.title:first-child { margin-top: 0.0em; } div.content div.title:first-child { margin-top: 0.0em; } div.content + div.title { margin-top: 0.0em; } div.sidebarblock > div.content { background: #ffffee; border: 1px solid #dddddd; border-left: 4px solid #f0f0f0; padding: 0.5em; } div.listingblock > div.content { border: 1px solid #dddddd; border-left: 5px solid #f0f0f0; background: #f8f8f8; padding: 0.5em; } div.quoteblock, div.verseblock { padding-left: 1.0em; margin-left: 1.0em; margin-right: 10%; border-left: 5px solid #f0f0f0; color: #888; } div.quoteblock > div.attribution { padding-top: 0.5em; text-align: right; } div.verseblock > pre.content { font-family: inherit; font-size: inherit; } div.verseblock > div.attribution { padding-top: 0.75em; text-align: left; } /* DEPRECATED: Pre version 8.2.7 verse style literal block. */ div.verseblock + div.attribution { text-align: left; } div.admonitionblock .icon { vertical-align: top; font-size: 1.1em; font-weight: bold; text-decoration: underline; color: #527bbd; padding-right: 0.5em; } div.admonitionblock td.content { padding-left: 0.5em; border-left: 3px solid #dddddd; } div.exampleblock > div.content { border-left: 3px solid #dddddd; padding-left: 0.5em; } div.imageblock div.content { padding-left: 0; } span.image img { border-style: none; } a.image:visited { color: white; } dl { margin-top: 0.8em; margin-bottom: 0.8em; } dt { margin-top: 0.5em; margin-bottom: 0; font-style: normal; color: navy; } dd > *:first-child { margin-top: 0.1em; } ul, ol { list-style-position: outside; } ol.arabic { list-style-type: decimal; } ol.loweralpha { list-style-type: lower-alpha; } ol.upperalpha { list-style-type: upper-alpha; } ol.lowerroman { list-style-type: lower-roman; } ol.upperroman { list-style-type: upper-roman; } div.compact ul, div.compact ol, div.compact p, div.compact p, div.compact div, div.compact div { margin-top: 0.1em; margin-bottom: 0.1em; } tfoot { font-weight: bold; } td > div.verse { white-space: pre; } div.hdlist { margin-top: 0.8em; margin-bottom: 0.8em; } div.hdlist tr { padding-bottom: 15px; } dt.hdlist1.strong, td.hdlist1.strong { font-weight: bold; } td.hdlist1 { vertical-align: top; font-style: normal; padding-right: 0.8em; color: navy; } td.hdlist2 { vertical-align: top; } div.hdlist.compact tr { margin: 0; padding-bottom: 0; } .comment { background: yellow; } .footnote, .footnoteref { font-size: 0.8em; } span.footnote, span.footnoteref { vertical-align: super; } #footnotes { margin: 20px 0 20px 0; padding: 7px 0 0 0; } #footnotes div.footnote { margin: 0 0 5px 0; } #footnotes hr { border: none; border-top: 1px solid silver; height: 1px; text-align: left; margin-left: 0; width: 20%; min-width: 100px; } div.colist td { padding-right: 0.5em; padding-bottom: 0.3em; vertical-align: top; } div.colist td img { margin-top: 0.3em; } @media print { #footer-badges { display: none; } } #toc { margin-bottom: 2.5em; } #toctitle { color: #527bbd; font-size: 1.1em; font-weight: bold; margin-top: 1.0em; margin-bottom: 0.1em; } div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { margin-top: 0; margin-bottom: 0; } div.toclevel2 { margin-left: 2em; font-size: 0.9em; } div.toclevel3 { margin-left: 4em; font-size: 0.9em; } div.toclevel4 { margin-left: 6em; font-size: 0.9em; } span.aqua { color: aqua; } span.black { color: black; } span.blue { color: blue; } span.fuchsia { color: fuchsia; } span.gray { color: gray; } span.green { color: green; } span.lime { color: lime; } span.maroon { color: maroon; } span.navy { color: navy; } span.olive { color: olive; } span.purple { color: purple; } span.red { color: red; } span.silver { color: silver; } span.teal { color: teal; } span.white { color: white; } span.yellow { color: yellow; } span.aqua-background { background: aqua; } span.black-background { background: black; } span.blue-background { background: blue; } span.fuchsia-background { background: fuchsia; } span.gray-background { background: gray; } span.green-background { background: green; } span.lime-background { background: lime; } span.maroon-background { background: maroon; } span.navy-background { background: navy; } span.olive-background { background: olive; } span.purple-background { background: purple; } span.red-background { background: red; } span.silver-background { background: silver; } span.teal-background { background: teal; } span.white-background { background: white; } span.yellow-background { background: yellow; } span.big { font-size: 2em; } span.small { font-size: 0.6em; } span.underline { text-decoration: underline; } span.overline { text-decoration: overline; } span.line-through { text-decoration: line-through; } div.unbreakable { page-break-inside: avoid; } /* * xhtml11 specific * * */ div.tableblock { margin-top: 1.0em; margin-bottom: 1.5em; } div.tableblock > table { border: 3px solid #527bbd; } thead, p.table.header { font-weight: bold; color: #527bbd; } p.table { margin-top: 0; } /* Because the table frame attribute is overriden by CSS in most browsers. */ div.tableblock > table[frame="void"] { border-style: none; } div.tableblock > table[frame="hsides"] { border-left-style: none; border-right-style: none; } div.tableblock > table[frame="vsides"] { border-top-style: none; border-bottom-style: none; } /* * html5 specific * * */ table.tableblock { margin-top: 1.0em; margin-bottom: 1.5em; } thead, p.tableblock.header { font-weight: bold; color: #527bbd; } p.tableblock { margin-top: 0; } table.tableblock { border-width: 3px; border-spacing: 0px; border-style: solid; border-color: #527bbd; border-collapse: collapse; } th.tableblock, td.tableblock { border-width: 1px; padding: 4px; border-style: solid; border-color: #527bbd; } table.tableblock.frame-topbot { border-left-style: hidden; border-right-style: hidden; } table.tableblock.frame-sides { border-top-style: hidden; border-bottom-style: hidden; } table.tableblock.frame-none { border-style: hidden; } th.tableblock.halign-left, td.tableblock.halign-left { text-align: left; } th.tableblock.halign-center, td.tableblock.halign-center { text-align: center; } th.tableblock.halign-right, td.tableblock.halign-right { text-align: right; } th.tableblock.valign-top, td.tableblock.valign-top { vertical-align: top; } th.tableblock.valign-middle, td.tableblock.valign-middle { vertical-align: middle; } th.tableblock.valign-bottom, td.tableblock.valign-bottom { vertical-align: bottom; } /* * manpage specific * * */ body.manpage h1 { padding-top: 0.5em; padding-bottom: 0.5em; border-top: 2px solid silver; border-bottom: 2px solid silver; } body.manpage h2 { border-style: none; } body.manpage div.sectionbody { margin-left: 3em; } @media print { body.manpage div#toc { display: none; } } </style> <script type="text/javascript"> /*<+'])'); // Function that scans the DOM tree for header elements (the DOM2 // nodeIterator API would be a better technique but not supported by all // browsers). var iterate = function (el) { for (var i = el.firstChild; i != null; i = i.nextSibling) { if (i.nodeType == 1 /* Node.ELEMENT_NODE */) { var mo = re.exec(i.tagName); if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") { result[result.length] = new TocEntry(i, getText(i), mo[1]-1); } iterate(i); } } } iterate(el); return result; } var toc = document.getElementById("toc"); if (!toc) { return; } // Delete existing TOC entries in case we're reloading the TOC. var tocEntriesToRemove = []; var i; for (i = 0; i < toc.childNodes.length; i++) { var entry = toc.childNodes[i]; if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") && entry.getAttribute("class").match(/^toclevel/)) tocEntriesToRemove.push(entry); } for (i = 0; i < tocEntriesToRemove.length; i++) { toc.removeChild(tocEntriesToRemove[i]); } // Rebuild TOC entries. var entries = tocEntries(document.getElementById("content"), toclevels); for (var i = 0; i < entries.length; ++i) { var entry = entries[i]; if (entry.element.id == "") entry.element.id = "_toc_" + i; var a = document.createElement("a"); a.href = "#" + entry.element.id; a.appendChild(document.createTextNode(entry.text)); var div = document.createElement("div"); div.appendChild(a); div.className = "toclevel" + entry.toclevel; toc.appendChild(div); } if (entries.length == 0) toc.parentNode.removeChild(toc); }, ///////////////////////////////////////////////////////////////////// // Footnotes generator ///////////////////////////////////////////////////////////////////// /* Based on footnote generation code from: * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html */ footnotes: function () { // Delete existing footnote entries in case we're reloading the footnodes. var i; var noteholder = document.getElementById("footnotes"); if (!noteholder) { return; } var entriesToRemove = []; for (i = 0; i < noteholder.childNodes.length; i++) { var entry = noteholder.childNodes[i]; if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote") entriesToRemove.push(entry); } for (i = 0; i < entriesToRemove.length; i++) { noteholder.removeChild(entriesToRemove[i]); } // Rebuild footnote entries. var cont = document.getElementById("content"); var spans = cont.getElementsByTagName("span"); var refs = {}; var n = 0; for (i=0; i<spans.length; i++) { if (spans[i].className == "footnote") { n++; var note = spans[i].getAttribute("data-note"); if (!note) { // Use [\s\S] in place of . so multi-line matches work. // Because JavaScript has no s (dotall) regex flag. note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1]; spans[i].innerHTML = "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n + "' title='View footnote' class='footnote'>" + n + "</a>]"; spans[i].setAttribute("data-note", note); } noteholder.innerHTML += "<div class='footnote' id='_footnote_" + n + "'>" + "<a href='#_footnoteref_" + n + "' title='Return to text'>" + n + "</a>. " + note + "</div>"; var id =spans[i].getAttribute("id"); if (id != null) refs["#"+id] = n; } } if (n == 0) noteholder.parentNode.removeChild(noteholder); else { // Process footnoterefs. for (i=0; i<spans.length; i++) { if (spans[i].className == "footnoteref") { var href = spans[i].getElementsByTagName("a")[0].getAttribute("href"); href = href.match(/#.*/)[0]; // Because IE return full URL. n = refs[href]; spans[i].innerHTML = "[<a href='#_footnote_" + n + "' title='View footnote' class='footnote'>" + n + "</a>]"; } } } }, install: function(toclevels) { var timerId; function reinstall() { asciidoc.footnotes(); if (toclevels) { asciidoc.toc(toclevels); } } function reinstallAndRemoveTimer() { clearInterval(timerId); reinstall(); } timerId = setInterval(reinstall, 500); if (document.addEventListener) document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false); else window.onload = reinstallAndRemoveTimer; } } asciidoc.install(); /*]]>*/ </script> </head> <body class="article"> <div id="header"> <h1>Listing of doc/example-scripts/ruby-mixins.tcl</h1> </div> <div id="content"> <div class="sect1"> <h2 id="_design_study_to_show_the_differences_between_decorator_mixin_classes_and_ruby_8217_s_mixin_modules">Design study to show the differences between decorator mixin classes and Ruby’s mixin modules</h2> <div class="sectionbody"> <div class="paragraph"><p>This example shows that the dynamic class structure of NX (and XOTcl) is able to support <em>Ruby style mixins</em> (called modules) and <em>decorator style mixins</em> (named after the design pattern Decorator) in the same script.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>nx::test <span class='nx-keyword'>configure</span> -count 1</pre></div></div> <div class="paragraph"><p>One important difference between mixin classes in NX and Ruby’s mixins is the precedence order. While in NX, mixins are decorators (the mixins have higher precedence than the intrinsic classes, therefore a mixin class can overload the methods of the current class and its subclasses), the mixins of Ruby have a lower precedence (they extend the <em>base behavior</em>; although Ruby’s modules are not full classes, they are folded into the <em>intrinsic class hierarchy</em>). Therefore, a Ruby style mixin can be refined by the class, into which it is mixed in (or by a subclass). Decorator style mixins modify the behavior of a full intrinsic class tree, while Ruby style mixins are compositional units for a single class.</p></div> <div class="paragraph"><p>To show the differences, we define the method <code>module</code>, which behaves somewhat similar to Ruby’s <code>module</code> command. This method adds the provided class to the precedence order after the current class. The easiest way to achieve this is via multiple inheritance (i.e. via the <code>superclass</code> relationship).</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'><span class='nx-keyword'>package</span> req nx <span class='nx-keyword'>nx::Class</span> <span class='nx-keyword'>eval</span> { <span class='nx-keyword'>:protected</span> <span class='nx-keyword'>method</span> module {name<span class='nx-keyword'>:class</span>} { nsf::relation [<span class='nx-keyword'>self</span>] <span class='nx-keyword'>superclass</span> [<span class='nx-keyword'>concat</span> <span class='nx-variable'>$name</span> [<span class='nx-keyword'>:info</span> <span class='nx-keyword'>superclass</span>]] } }</pre></div></div> <div class="paragraph"><p>For illustration of the behavior of <code>module</code> we define a class <code>Enumerable</code> somewhat inspired by the Ruby module with the same name. We define here just the methods <code>map</code>, <code>each</code>, <code>count</code>, and <code>count_if</code>.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'><span class='nx-keyword'>nx::Class</span> <span class='nx-keyword'>create</span> Enumerable { <span class='nx-keyword'>:property</span> members:0..n <span class='nx-comment'># The method 'each' applies the provided block on every element of </span> <span class='nx-comment'># 'members' </span> <span class='nx-keyword'>:public</span> <span class='nx-keyword'>method</span> each {var block} { <span class='nx-keyword'>foreach</span> member <span class='nx-variable'>${</span><span class='nx-variable'>:members}</span> { <span class='nx-keyword'>uplevel</span> [<span class='nx-keyword'>list</span> <span class='nx-keyword'>set</span> <span class='nx-variable'>$var</span> <span class='nx-variable'>$member</span>] <span class='nx-keyword'>uplevel</span> <span class='nx-variable'>$block</span> } } <span class='nx-comment'># The method 'map' applies the provided block on every element of </span> <span class='nx-comment'># 'members' and returns a list, where every element is the mapped </span> <span class='nx-comment'># result of the source. </span> <span class='nx-keyword'>:public</span> <span class='nx-keyword'>method</span> map {var block} { <span class='nx-keyword'>set</span> result [<span class='nx-keyword'>list</span>] :each <span class='nx-variable'>$var</span> { <span class='nx-keyword'>uplevel</span> [<span class='nx-keyword'>list</span> <span class='nx-keyword'>set</span> <span class='nx-variable'>$var</span> [<span class='nx-keyword'>set</span> <span class='nx-variable'>$var</span>]] <span class='nx-keyword'>lappend</span> result [<span class='nx-keyword'>uplevel</span> <span class='nx-variable'>$block</span>] } <span class='nx-keyword'>return</span> <span class='nx-variable'>$result</span> } <span class='nx-comment'># The method 'count' returns the number of elements. </span> <span class='nx-keyword'>:public</span> <span class='nx-keyword'>method</span> count {} { <span class='nx-keyword'>return</span> [<span class='nx-keyword'>llength</span> <span class='nx-variable'>${</span><span class='nx-variable'>:members}</span>] } <span class='nx-comment'># The method 'count_if' returns the number of elements for which </span> <span class='nx-comment'># the provided expression is true. </span> <span class='nx-keyword'>:public</span> <span class='nx-keyword'>method</span> count_if {var <span class='nx-keyword'>expr</span>} { <span class='nx-keyword'>set</span> result 0 :each <span class='nx-variable'>$var</span> { <span class='nx-keyword'>incr</span> result [<span class='nx-keyword'>expr</span> <span class='nx-variable'>$expr</span>] } <span class='nx-keyword'>return</span> <span class='nx-variable'>$result</span> } }</pre></div></div> <div class="paragraph"><p>After having defined the class <code>Enumerable</code>, we define a class <code>Group</code> using <code>Enumerable</code> as a Ruby style mixin. This makes essentially <code>Group</code> a subclass of <code>Enumerable</code>, but with the only difference that <code>Group</code> might have other superclasses as well.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'><span class='nx-keyword'>nx::Class</span> <span class='nx-keyword'>create</span> Group { <span class='nx-comment'># </span> <span class='nx-comment'># Include the "module" Enumerable </span> <span class='nx-comment'># </span> :module Enumerable }</pre></div></div> <div class="paragraph"><p>Define now a group <code>g1</code> with the three provided members.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>% Group <span class='nx-keyword'>create</span> g1 -members {mini trix trax} ::g1</pre></div></div> <div class="paragraph"><p>Since the definition of <code>Group</code> includes the module <code>Enumerable</code>, this class is listed in the precedence order after the class <code>Group</code>:</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>% g1 <span class='nx-keyword'>info</span> precedence ::Group ::Enumerable ::nx::Object</pre></div></div> <div class="paragraph"><p>Certainly, we can call the methods of <code>Enumerable</code> as usual:</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>% g1 count 3 % g1 map x {<span class='nx-keyword'>list</span> pre-<span class='nx-variable'>$x</span>-post} pre-mini-post pre-trix-post pre-trax-post % g1 count_if x {[<span class='nx-keyword'>string</span> match tr*x <span class='nx-variable'>$x</span>] > 0} 2</pre></div></div> <div class="paragraph"><p>To show the difference between a <code>module</code> and a <code>decorator mixin</code> we define a class named <code>Mix</code> with the single method <code>count</code>, which wraps the result of the underlaying <code>count</code> method between the <code>alpha</code> and <code>omega</code>.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'><span class='nx-keyword'>nx::Class</span> <span class='nx-keyword'>create</span> Mix { <span class='nx-keyword'>:public</span> <span class='nx-keyword'>method</span> count {} { <span class='nx-keyword'>return</span> [<span class='nx-keyword'>list</span> alpha [<span class='nx-keyword'>next</span>] omega] } }</pre></div></div> <div class="paragraph"><p>When the mixin class is added to <code>g1</code>, it is added to the front of the precedence list. A decorator is able to modify the behavior of all of the methods of the class, where it is mixed into.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>% g1 <span class='nx-keyword'>object</span> <span class='nx-keyword'>mixin</span> Mix ::Mix % g1 <span class='nx-keyword'>info</span> precedence ::Mix ::Group ::Enumerable ::nx::Object % g1 count alpha 3 omega</pre></div></div> <div class="paragraph"><p>For the time being, remove the mixin class again.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>% g1 <span class='nx-keyword'>object</span> <span class='nx-keyword'>mixin</span> <span class='nx-string'>""</span> % g1 <span class='nx-keyword'>info</span> precedence ::Group ::Enumerable ::nx::Object</pre></div></div> <div class="paragraph"><p>An important difference between NX/XOTcl style mixins (decorators) and Ruby style modules is that the decorator will have always a higher precedence than the intrinsic classes, while the <code>module</code> is folded into the precedence path.</p></div> <div class="paragraph"><p>Define a class <code>ATeam</code> that uses <code>Enumerable</code> in the style of a Ruby module. The class might refine some of the provided methods. We refined the method <code>each</code>, which is used as well by the other methods. In general, by defining <code>each</code> one can define very different kind of enumerators (for lists, databases, etc.).</p></div> <div class="paragraph"><p>Since <code>Enumerable</code> is a module, the definition of <code>each</code> in the class <code>ATeam</code> has a higher precedence than the definition in the class <code>Enumerable</code>. If <code>Enumerable</code> would be a decorator style mixin class, it would not e possible to refine the definition in the class <code>ATeam</code>, but maybe via another mixin class.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'><span class='nx-keyword'>nx::Class</span> <span class='nx-keyword'>create</span> ATeam { <span class='nx-comment'># </span> <span class='nx-comment'># Include the "module" Enumerable </span> <span class='nx-comment'># </span> :module Enumerable <span class='nx-comment'># </span> <span class='nx-comment'># Overload "each" </span> <span class='nx-comment'># </span> <span class='nx-keyword'>:public</span> <span class='nx-keyword'>method</span> each {var block} { <span class='nx-keyword'>foreach</span> member <span class='nx-variable'>${</span><span class='nx-variable'>:members}</span> { <span class='nx-keyword'>uplevel</span> [<span class='nx-keyword'>list</span> <span class='nx-keyword'>set</span> <span class='nx-variable'>$var</span> <span class='nx-variable'>$member</span>-[<span class='nx-keyword'>string</span> length <span class='nx-variable'>$member</span>]] <span class='nx-keyword'>uplevel</span> <span class='nx-variable'>$block</span> } } <span class='nx-comment'># </span> <span class='nx-comment'># Use "map", which uses the "each" method defined in this class. </span> <span class='nx-comment'># </span> <span class='nx-keyword'>:public</span> <span class='nx-keyword'>method</span> foo {} { <span class='nx-keyword'>return</span> [:map x {<span class='nx-keyword'>string</span> totitle <span class='nx-variable'>$x</span>}] } }</pre></div></div> <div class="paragraph"><p>Define now a team <code>t1</code> with the three provided members.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>% ATeam <span class='nx-keyword'>create</span> t1 -members {arthur bill chuck} ::t1</pre></div></div> <div class="paragraph"><p>As above, the precedence of <code>ATeam</code> is higher than the precedence of <code>Enumerable</code>. Therefore, the object <code>t1</code> uses the method <code>each</code> specialized in class <code>ATeam</code>:</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>% t1 <span class='nx-keyword'>info</span> precedence ::ATeam ::Enumerable ::nx::Object % t1 foo Arthur-6 Bill-4 Chuck-5</pre></div></div> <div class="paragraph"><p>The class <code>ATeam</code> can be specialized further by a class <code>SpecialForce</code>:</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'><span class='nx-keyword'>nx::Class</span> <span class='nx-keyword'>create</span> SpecialForce -superclass ATeam { <span class='nx-comment'># ... </span>}</pre></div></div> <div class="paragraph"><p>Define a special force <code>s1</code> with the four provided members.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>% SpecialForce <span class='nx-keyword'>create</span> s1 -members {Donald Micky Daniel Gustav} ::s1</pre></div></div> <div class="paragraph"><p>As above, the precedence of <code>Enumerable</code> is lower then the precedence of <code>ATeam</code> and <code>Enumerable</code>. Therefore <code>ATeam</code> can refine the behavior of <code>Enumerable</code>, the class <code>SpecialForce</code> can refine the behavior of <code>ATeam</code>.</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>% s1 <span class='nx-keyword'>info</span> precedence ::SpecialForce ::ATeam ::Enumerable ::nx::Object % s1 foo Donald-6 Micky-5 Daniel-6 Gustav-6</pre></div></div> <div class="paragraph"><p>Let us look again on decorator style mixin classes. If we add a per-class mixin to <code>ATeam</code>, the mixin class has highest precedence, and decorates the instances of <code>ATeam</code> as well the instances of its specializations (like e.g. <code>SpecialForce</code>).</p></div> <div class="listingblock"> <div class="content"><style type='text/css'> .nx {color: #000000; font-weight: normal; font-style: normal; padding-left: 10px} table.nx {border-collapse: collapse; border-spacing: 3px;} .nx-linenr {border-right: 1px solid #DDDDDD;padding-right: 5px; color: #2B547D;font-style: italic;} .nx-string {color: #779977; font-weight: normal; font-style: italic;} .nx-comment {color: #717ab3; font-weight: normal; font-style: italic;} .nx-keyword {color: #7f0055; font-weight: normal; font-style: normal;} .nx-placeholder {color: #AF663F; font-weight: normal; font-style: italic;} .nx-variable {color: #AF663F; font-weight: normal; font-style: normal;} </style> <pre class='nx'>% ATeam <span class='nx-keyword'>mixin</span> Mix ::Mix % s1 <span class='nx-keyword'>info</span> precedence ::Mix ::SpecialForce ::ATeam ::Enumerable ::nx::Object % s1 count alpha 4 omega</pre></div></div> <div class="paragraph"><p>This example showed that NX/XOTcl dynamic class structure is able to support Ruby-style mixins, and decorator style mixins in the same script.</p></div> </div> </div> </div> <div id="footnotes"><hr /></div> <div id="footer"> <div id="footer-text"> Last updated 2013-06-07 00:16:54 CEST </div> </div> </body> </html>