<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Danilo Gurovich &#187; code</title>
	<atom:link href="http://gurovich.com/site/category/enterprise-development/code/feed/" rel="self" type="application/rss+xml" />
	<link>http://gurovich.com/site</link>
	<description>Strategic eCommerce Technology and Architecture</description>
	<lastBuildDate>Mon, 25 Sep 2023 14:32:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Using TDD to create a Python Django SSN validation</title>
		<link>http://gurovich.com/site/2010/03/30/ssn_validation_with_django/</link>
		<comments>http://gurovich.com/site/2010/03/30/ssn_validation_with_django/#comments</comments>
		<pubDate>Tue, 30 Mar 2010 19:44:59 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[Enterprise Development]]></category>
		<category><![CDATA[django advanced validation]]></category>
		<category><![CDATA[Django SSN Validation]]></category>
		<category><![CDATA[Django TDD]]></category>
		<category><![CDATA[frozensete]]></category>
		<category><![CDATA[Python TDD]]></category>
		<category><![CDATA[SSN Validation]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=1299</guid>
		<description><![CDATA[The Problem to Solve Many web applications collect financial information to submit as leads for loans, credit card applications, bank information or other purposes. To ensure that the submitted SSN is valid, a validation algorithm must be implemented against the submitted value. Many websites in their infancy check only for length and null, but there [...]]]></description>
			<content:encoded><![CDATA[<h2>The Problem to Solve</h2>
<p>Many web applications collect financial information to submit as leads for loans, credit card applications, bank information or other purposes.  To ensure that the submitted SSN is valid, a validation algorithm must be implemented against the submitted value.  Many websites in their infancy check only for length and null, but there is actually a formula that is published by the government to further qualify the SSN in the form field.  By validating this SSN further, the quality of the generated lead is increased, which will have direct impact on the value of the data.</p>
<p>The algorithm we&#8217;ll construct is based on the following formulaic instruction:</p>
<blockquote><p><em>&#8220;A valid SSN is determined by the following rules according to the website </em><a title="SSA validation utilities" href="http://www.ssa.gov/employer/ssnweb.htm" target="_blank"><em>http://www.ssa.gov/employer/ssnweb.htm</em></a></p>
<p><em>Area-Group-Serial (AAA-GG-SSSS) The Social Security number consists of nine (9) digits. The first three (3) digits denote the area (or State) where the application for an original Social Security number was filed.</em></p>
<p><em>Within each area, the group number (middle two (2) digits) range from 01 to 99 but are not assigned in consecutive order. For administrative reasons, group numbers issued first consist of the ODD numbers from 01 through 09 and then EVEN numbers from 10 through 98, within each area number allocated to a State. After all numbers in group 98 of a particular area have been issued, the EVEN Groups 02 through 08 are used, followed by ODD Groups 11 through 99.</em></p>
<p><em>Within each group, the serial numbers (last four (4) digits) run consecutively from 0001 through 9999.&#8221;</em></p></blockquote>
<p>So, for reference, just in case:  The first three digits are the &#8220;Area&#8221; reference.  The second two digits are the &#8220;Group&#8221; reference, and the final four digits are the &#8220;Serial Number&#8221; value.</p>
<p>We&#8217;ll create a validator that checks to ensure that the social security number is the right length (stripped of dashes and white spaces), is all numbers, and finally implements the Social Security Administration&#8217;s formula for the Area/Group validation and their list of invalid SSNs.<span id="more-1299"></span></p>
<h2>What is needed</h2>
<p>The data for Area/Group validation and invalid SSN&#8217;s is quite static. ?For performance reasons, it is not necessary or reasonable to create a database to manage this data when a simple list can be used. ?In this particular implementation, we&#8217;ll <em>hard code</em> these values into a <code>dictionary</code> constant and create a <code>frozenset</code> to make the lookup and validation of the SSN Area even faster.  The list of invalid SSNs will also be a frozenset.</p>
<h3>Why frozenset</h3>
<p>I use a Python <code>set</code> because I&#8217;m just testing &#8220;membership&#8221; in the group.  This gives me optimization.  Further I decided to use <code>frozenset</code> because it&#8217;s API is limited further, not allowing updates, ordering or indexing.  I want to use these values as a constant that is loaded into the VM at run-time and does not depend on any database lookup for performance reasons.</p>
<h3>Project Structure</h3>
<p>The file structure is a typical Django Project with an &#8220;application&#8221; subdirectory.  The application subdirectory is called <code>finvs</code>, short for &#8220;financial validators&#8221;.  By using the <code>manage.py startapp finvs</code>, you&#8217;ll get the basic structure of the <code>finvs</code> automatically, and then you can add the <code>utils</code> subdirectory to abstract your algorithms and validation objects away from the actual application.</p>
<p>Note that a <code>tests.py</code> file has been created automatically.  When running <code>manage.py test {app}</code> the tests that are located in the tests.py of each {app} directory &#8212; in this case <code>finvs</code>, will be run.  Once everything is set up, we will explain how to use the tests to develop the actual implementation used in the <code>validation_formulas.py</code> file without ever having to start the Django server.  This means that you won&#8217;t have to develop a lot of front end code or do anything that you don&#8217;t need to do.  You can develop in an independent manner and work on the code that is a priority.</p>
<pre>project_root
        |-finvs
                |-utils
                        |-__init__.py
                        |-ssn_constants.py
                        |-validation_formulas.py
                |-__init__.py
                |-models.py
                |-tests.py
                |-views.py
        |-__init__.py
        |-manage.py
        |-settings.py
        |-urls.py</pre>
<p>For the purposes of this demonstration, the only files we&#8217;ll be using in this structure are <code>finvs/utils/ssn_constants.py</code> to hold the data in a &#8220;persistence layer&#8221;, <code>finvs/utils/validation_formulas.py</code> to contain the actual implementation method(s) of the validation, and <code>finvs/tests.py</code> to test and develop the code before I actually implement it. </p>
<h2>The &#8220;Persistence Layer&#8221;</h2>
<p>As described above, having all the information in a database requires too much overhead and is a drag on performance of the validation.  Having to suffer through a lookup to a database and the maintenance of this data involves more work and runtime cost than is necessary for what is very static data that rarely changes. </p>
<p>Since there are only a few hundred records to deal with, there is very little memory overhead taken up holding these values as static members.  I therefore created a file in the <code>utils</code> directory that contains the data I will use to validate SSNs against (<code>ssn_constants.py</code>).  The code I inserted is enumerated in its entirety below:</p>
<pre>finvs/utils/ssn_constants.py</pre>
<pre># a dictionary of area/group field values.
#The values represent the "high value" to be
# used with the SSN validation algorithm.
SSN_HIGH_VALUES ={
"001":"04","002":"02","001":"04","002":"02","003":"02","004":"06","005":"06","006":"06",
"007":"06","008":"90","009":"88","010":"90","011":"88","012":"88","013":"88","014":"88",
"015":"88","016":"88","017":"88","018":"88","019":"88","020":"88","021":"88","022":"88",
"023":"88","024":"88","025":"88","026":"88","001":"04","002":"02","003":"02","004":"06",
"005":"06","006":"06","007":"06","008":"90","009":"88","010":"90","011":"88","012":"88",
"013":"88","014":"88","015":"88","016":"88","017":"88","018":"88","019":"88","020":"88",
"021":"88","022":"88","023":"88","024":"88","025":"88","026":"88","027":"88","028":"88",
"029":"88","030":"88","031":"88","032":"88","033":"88","034":"88","035":"72","036":"70",
"037":"70","038":"70","039":"70","040":"08","041":"08","042":"08","043":"08","044":"08",
"045":"08","046":"08","047":"08","048":"08","049":"08","050":"96","051":"96","052":"96",
"053":"96","054":"96","055":"94","056":"94","057":"94","058":"94","059":"94","060":"94",
"061":"94","062":"94","063":"94","064":"94","065":"94","066":"94","067":"94","068":"94",
"069":"94","070":"94","071":"94","072":"94","073":"94","074":"94","075":"94","076":"94",
"077":"94","078":"94","079":"94","080":"94","081":"94","082":"94","083":"94","084":"94",
"085":"94","086":"94","087":"94","088":"94","089":"94","090":"94","091":"94","092":"94",
"093":"94","094":"94","095":"94","096":"94","097":"94","098":"94","099":"94","100":"94",
"101":"94","102":"94","103":"94","104":"94","105":"94","106":"94","107":"94","108":"94",
"109":"94","110":"94","111":"94","112":"94","113":"94","114":"94","115":"94","116":"94",
"117":"94","118":"94","119":"94","120":"94","121":"94","122":"94","123":"94","124":"94",
"125":"94","126":"94","127":"94","128":"94","129":"94","130":"94","131":"94","132":"94",
"133":"94","134":"94","135":"17","136":"17","137":"17","138":"17","139":"17","140":"17",
"141":"15","142":"15","143":"15","144":"15","145":"15","146":"15","147":"15","148":"15",
"149":"15","150":"15","151":"15","152":"15","153":"15","154":"15","155":"15","156":"15",
"157":"15","158":"15","159":"82","160":"82","161":"82","162":"82","163":"82","164":"82",
"165":"82","166":"82","167":"82","168":"82","169":"82","170":"82","171":"82","172":"82",
"173":"82","174":"82","175":"82","176":"82","177":"82","178":"82","179":"82","180":"82",
"181":"82","182":"82","183":"82","184":"82","185":"82","186":"82","187":"82","188":"82",
"189":"82","190":"82","191":"82","192":"82","193":"82","194":"82","195":"82","196":"82",
"197":"82","198":"82","199":"82","200":"82","201":"82","202":"82","203":"82","204":"82",
"205":"82","206":"82","207":"80","208":"80","209":"80","210":"80","211":"80","212":"75",
"213":"75","214":"75","215":"75","216":"73","217":"73","218":"73","219":"73","220":"73",
"221":"04","222":"02","223":"99","224":"99","225":"99","226":"99","227":"99","228":"99",
"229":"99","230":"99","231":"99","232":"53","233":"51","234":"51","235":"51","236":"51",
"237":"99","238":"99","239":"99","240":"99","241":"99","242":"99","243":"99","244":"99",
"245":"99","246":"99","247":"99","248":"99","249":"99","250":"99","251":"99","252":"99",
"253":"99","254":"99","255":"99","256":"99","257":"99","258":"99","259":"99","260":"99",
"261":"99","262":"99","263":"99","264":"99","265":"99","266":"99","267":"99","268":"11",
"269":"11","270":"11","271":"11","272":"11","273":"11","274":"11","275":"11","276":"11",
"277":"11","278":"11","279":"11","280":"11","281":"11","282":"11","283":"11","284":"11",
"285":"11","286":"11","287":"11","288":"11","289":"11","290":"11","291":"11","292":"11",
"293":"11","294":"11","295":"11","296":"08","297":"08","298":"08","299":"08","300":"08",
"301":"08","302":"08","303":"31","304":"29","305":"29","306":"29","307":"29","308":"29",
"309":"29","310":"29","311":"29","312":"29","313":"29","314":"29","315":"29","316":"29",
"317":"29","318":"04","319":"04","320":"04","321":"04","322":"04","323":"04","324":"04",
"325":"04","326":"04","327":"04","328":"04","329":"04","330":"04","331":"04","332":"04",
"333":"04","334":"04","335":"04","336":"04","337":"04","338":"04","339":"04","340":"04",
"341":"04","342":"04","343":"04","344":"04","345":"04","346":"04","347":"04","348":"04",
"349":"04","350":"04","351":"04","352":"04","353":"04","354":"04","355":"04","356":"04",
"357":"04","358":"04","359":"04","360":"04","361":"04","362":"33","363":"33","364":"33",
"365":"33","366":"33","367":"33","368":"31","369":"31","370":"31","371":"31","372":"31",
"373":"31","374":"31","375":"31","376":"31","377":"31","378":"31","379":"31","380":"31",
"381":"31","382":"31","383":"31","384":"31","385":"31","386":"31","387":"27","388":"27",
"389":"27","390":"27","391":"27","392":"27","393":"27","394":"27","395":"25","396":"25",
"397":"25","398":"25","399":"25","400":"65","401":"65","402":"65","403":"65","404":"65",
"405":"63","406":"63","407":"63","408":"99","409":"99","410":"99","411":"99","412":"99",
"413":"99","414":"99","415":"99","416":"59","417":"59","418":"59","419":"59","420":"59",
"421":"59","422":"59","423":"59","424":"57","425":"99","426":"99","427":"97","428":"97",
"429":"99","430":"99","431":"99","432":"99","433":"99","434":"99","435":"99","436":"99",
"437":"99","438":"99","439":"99","440":"21","441":"21","442":"21","443":"21","444":"21",
"445":"21","446":"21","447":"19","448":"19","449":"99","450":"99","451":"99","452":"99",
"453":"99","454":"99","455":"99","456":"99","457":"99","458":"99","459":"99","460":"99",
"461":"99","462":"99","463":"99","464":"99","465":"99","466":"99","467":"99","468":"47",
"469":"47","470":"47","471":"47","472":"47","473":"47","474":"47","475":"47","476":"47",
"477":"45","478":"37","479":"35","480":"35","481":"35","482":"35","483":"35","484":"35",
"485":"35","486":"23","487":"23","488":"23","489":"23","490":"23","491":"23","492":"23",
"493":"23","494":"23","495":"23","496":"23","497":"23","498":"21","499":"21","500":"21",
"501":"31","502":"31","503":"39","504":"37","505":"51","506":"49","507":"49","508":"49",
"509":"25","510":"25","511":"25","512":"25","513":"25","514":"25","515":"25","516":"43",
"517":"41","518":"73","519":"73","520":"51","521":"99","522":"99","523":"99","524":"99",
"525":"99","526":"99","527":"99","528":"99","529":"99","530":"99","531":"59","532":"59",
"533":"57","534":"57","535":"57","536":"57","537":"57","538":"57","539":"57","540":"71",
"541":"71","542":"69","543":"69","544":"69","545":"99","546":"99","547":"99","548":"99",
"549":"99","550":"99","551":"99","552":"99","553":"99","554":"99","555":"99","556":"99",
"557":"99","558":"99","559":"99","560":"99","561":"99","562":"99","563":"99","564":"99",
"565":"99","566":"99","567":"99","568":"99","569":"99","570":"99","571":"99","572":"99",
"573":"99","574":"45","575":"99","576":"99","577":"41","578":"41","579":"41","580":"37",
"581":"99","582":"99","583":"99","584":"99","585":"99","586":"57","587":"97","589":"99",
"590":"99","591":"99","592":"99","593":"99","594":"99","595":"99","596":"80","597":"80",
"598":"80","599":"80","600":"99","601":"99","602":"57","603":"57","604":"55","605":"55",
"606":"55","607":"55","608":"55","609":"55","610":"55","611":"55","612":"55","613":"55",
"614":"55","615":"55","616":"55","617":"55","618":"55","619":"55","620":"55","621":"55",
"622":"55","623":"55","624":"55","625":"55","626":"55","627":"02","628":"02","629":"02",
"630":"02","631":"02","632":"02","633":"02","634":"02","635":"02","636":"02","637":"02",
"638":"02","639":"02","640":"02","641":"02","642":"98","643":"98","644":"98","645":"98",
"646":"86","647":"86","648":"40","649":"38","650":"40","651":"38","652":"38","653":"38",
"654":"22","655":"22","656":"22","657":"22","658":"20","659":"12","660":"12","661":"12",
"662":"12","663":"10","664":"10","665":"10","667":"30","668":"28","669":"28","670":"28",
"671":"28","672":"28","673":"28","674":"28","675":"28","676":"10","677":"10","678":"10",
"679":"09","680":"76","681":"10","682":"09","683":"09","684":"09","685":"09","686":"09",
"687":"09","688":"09","689":"09","690":"09","691":"03","692":"03","693":"03","694":"03",
"695":"03","696":"03","697":"03","698":"03","699":"01","700":"18","701":"18","702":"18",
"703":"18","704":"18","705":"18","706":"18","707":"18","708":"18","709":"18","710":"18",
"711":"18","712":"18","713":"18","714":"18","715":"18","716":"18","717":"18","718":"18",
"719":"18","720":"18","721":"18","722":"18","723":"18","724":"28","725":"18","726":"18",
"727":"10","728":"14","729":"07","730":"05","731":"05","732":"05","733":"05","750":"05",
"751":"03","756":"01","757":"01","758":"01","759":"01","760":"01","761":"01","762":"01",
"764":"62","765":"62","766":"48","767":"48","768":"48","769":"48","770":"48","771":"46",
"772":"46"
}

# frozenset makes for quick access to the key fields of the SSN high values
# the key fields are the 'areas'
VALID_SSN_AREAS = frozenset(SSN_HIGH_VALUES.keys())

# a frozenset of invalid ssn numbers
INVALID_SSN_NUMBERS = frozenset([
"002281852","042103580","062360749","078051120",
"095073645","128036045","135016629","141186941",
"165167999","165187999","165207999","165227999",
"165247999","189092294","212097694","212099999",
"306302348","308125070","468288779","549241889",
"987654320","987654321","987654322","987654323",
"987654324","987654325","987654326","987654327",
"987654328","987654329"])</pre>
<h2>The Test Harness</h2>
<p>Description of the Test</p>
<p>There are a few parts to the test.  I built it in a few steps.  first, the <code>setUp</code> method creates a &#8220;Good&#8221; SSN, &#8220;Good&#8221; Group and &#8220;Good&#8221; High Group values.  The &#8220;High Group&#8221; is that value that is highest allowed for the particular &#8220;Area&#8221; value.  The first method, <code>testSSNValidation(self)</code> is a test of the implementation with a known good serial number.  This is the first method that I wrote, because this is where I &#8220;worked out&#8221; the algorithm that I wanted to use in the implementation. </p>
<p>Once I got the &#8220;right answer&#8221;, I could create another test method to throw a whole bunch of numbers at it to ensure that it is working right under all circumstances and border conditions.  Note the extensive documentation &#8212; you may be passing this code off to another team in the future, and it&#8217;s important to leave a good trail.  Validation this complicated is not <i>self documenting</i>.  Walking through the method, I first created the return value that I wanted to use and made it <code>False</code> so that if any validation failed it would fall through and just return.  Once the default was set, I stripped out any hyphens and whitespace, I checked the value for length, then check to ensure that the Area (first three digits) is valid.  If the number passes all these tests, we can then go into the <I>private method</i> that I created to implement the &#8220;high values&#8221; formula <code>__isGroupValid(self,group,groupHighestValue)</code>:</p>
<p><sup><em>Note: this is a pretty complex formula so I&#8217;m not going to get into the gritty details &#8212; see the SSA site for more info&#8230;</em></sup></p>
<pre>finvs/tests.py</pre>
<pre>"""
This file tests the finvs application
"""
import unittest
from django.test import TestCase
from finvs.utils.validation_formulas import ValidationFormulas
from finvs.utils.ssn_constants import SSN_HIGH_VALUES
from finvs.utils.ssn_constants import INVALID_SSN_NUMBERS
from finvs.utils.ssn_constants import VALID_SSN_AREAS

class ValidatorsTestCase(unittest.TestCase):

    GOOD_SSN = ' '
    GOOD_GRP = 0
    GOOD_HIGH_GRP_VAL = 0

    def setUp(self):
        self.GOOD_SSN = '526-33-4563'
        self.GOOD_GRP = 33
        self.GOOD_HIGH_GRP_VAL = 99

   def testSSNValidation(self):
        """Tests the algorithm in the validator against
            known high values for the first three digits

           Validate a Social Security Number (SSN). A valid SSN
            is determined by the following rules according to the
            website http://www.ssa.gov/employer/ssnweb.htm

           Area-Group-Serial (AAA-GG-SSSS) The Social Security
            number consists of nine (9) digits. The first three
            (3) digits denote the area (or State) where the application
            for an original Social Security number was filed.

           Within each area, the group number (middle two (2) digits)
            range from 01 to 99 but are not assigned in consecutive
            order. For administrative reasons, group numbers issued
            first consist of the ODD numbers from 01 through 09 and
            then EVEN numbers from 10 through 98, within each area
            number allocated to a State. After all numbers in group
            98 of a particular area have been issued, the EVEN Groups
            02 through 08 are used, followed by ODD Groups 11 through
            99.

           Within each group, the serial numbers (last four (4) digits)
            run consecutively from 0001 through 9999.
           """
    def testSSNAlgorithm(self):
        bool=False
        ssn_v=self.GOOD_SSN.translate(None,'-')
        ssn_v=ssn_v.strip()
        #print("SSN: %s" % (ssn_v))
        if (len(ssn_v)==9 and ssn_v.isdigit()):
            #print("SSN passed for length and digits: %s" % (ssn_v))
            key_v = ssn_v[3:5]
            if ssn_v[0:3] in VALID_SSN_AREAS:
                hg_v = SSN_HIGH_VALUES[ssn_v[0:3]]
                #check for valid group for Area ID in private method
                bool = self.__isGroupValid(int(key_v), int(hg_v))
            #print("VALID_GROUP: %s" % (bool))
        self.assertTrue(bool)

    def testIsValidSSN(self):
        validator = ValidationFormulas()
        self.assertTrue(validator.isValidSSN(self.GOOD_SSN))
        long_ssn = "   %s   " % (self.GOOD_SSN)
        self.assertTrue(validator.isValidSSN(long_ssn))
        stripped_ssn = self.GOOD_SSN.translate(None,'-')
        self.assertTrue(validator.isValidSSN(stripped_ssn))
        for bad_ssn in INVALID_SSN_NUMBERS:
            self.assertFalse(validator.isValidSSN(bad_ssn))
        bad_ssn = "742-51-1234"
        self.assertFalse(validator.isValidSSN(bad_ssn))

    def testGroupValidAlgorithm(self):
        self.assertTrue(self.__isGroupValid(self.GOOD_GRP,self.GOOD_HIGH_GRP_VAL))

    def testShowSSNAreas(self):
        self.assertEqual(len(SSN_HIGH_VALUES.keys()), len(VALID_SSN_AREAS))

    #-----------------Private Functions-------------------------------------------

def __isSSNGroupValid(self,group,groupHighestValue):

        """Within each area, the group number (middle two (2) digits)
            range from 01 to 99 but are not assigned in consecutive
            order. For administrative reasons, group numbers issued
            first consist of the ODD numbers from 01 through 09 and
            then EVEN numbers from 10 through 98, within each area
            number allocated to a State. After all numbers in group
            98 of a particular area have been issued, the EVEN Groups
            02 through 08 are used, followed by ODD Groups 11 through
            99.
            """
        validGroup = False
        moduloGroupNumber = group % 2
        moduloGroupHighest = groupHighestValue % 2

        if ((moduloGroupHighest == 1) &#038; (groupHighestValue >= 1) &#038; (groupHighestValue <= 9)):             if ((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= groupHighestValue)):                 validGroup = True          elif ((moduloGroupHighest == 0) &#038; (groupHighestValue >= 10) &#038; (groupHighestValue <= 98)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10) &#038; (group <= groupHighestValue))):                 validGroup = True          elif ((moduloGroupHighest == 0) &#038; (groupHighestValue >= 2) &#038; (groupHighestValue <= 8)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10)
            &#038; (group <= 98))             | ((moduloGroupNumber == 0) &#038; (group >= 2)
            &#038; (group <= groupHighestValue))):                 validGroup = True          elif ((moduloGroupHighest == 1) &#038; (groupHighestValue >= 11) &#038; (groupHighestValue <= 99)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10)
            &#038; (group <= 98))             | ((moduloGroupNumber == 0) &#038; (group >= 2)
            &#038; (group <= 8))             | ((moduloGroupNumber == 1) &#038; (group >= 11)
            &#038; (group <= groupHighestValue))):                 validGroup = True          return validGroup</pre>
<p>The next methods test and retest the algorithms, ensuring that bad numbers, numbers with white space and invalid numbers also return <code>False</code> for their tests.  More comprehensive tests can and should be written before this code goes into production, but there is now the basis for an implementation and transfer of our "test" code to <code>validation_formulas.py</code> </p>
<h2>The Implementation</h2>
<p>The implementation is almost "cut and paste" from the test at this point.  The private method is a complete clone, and the public validation method just changes a few things around to get the right information.  This particular implementation is somewhat basic, since it only returns <code>True</code> or <code>False<code> with the number submitted -- it doesn't tell the requestor exactly what happened, just that the number submitted is good or bad.  More sophistication can be added by returning pointers towards messages, then assigning a pointer for each test that fails.  A test with no failure could return a "zero", and after that messages could pile up.  </p>
<p>This is a good strategy for any sophisticated validation system, since there are few hard-core validations that need to have a binary response -- it is more important to have some "why" answers with a failed validation, and in some cases, validations may not "fail" in a particular manner.  One example of a "gray area" in validation would be in profanity filters.  Some lexicon may be quite profane to a particular user, where another may not.  In these particular cases, a "score" might be returned for verification instead of a binary.  Another case would be in Address fields -- ones that don't have a number followed by string of letters may not be a real address, but there are outliers that need to be covered...</p>
<pre>validation_formulas.py</pre>
<pre>#!/usr/bin/env python
# encoding: utf-8
"""
validation_formulas.py
Created by Danilo Gurovich on 2010-03-19.
"""
from finvs.utils.ssn_constants import *

class ValidationFormulas:

    #Validates Social Security Number
    def isValidSSN(self,ssn_str):
        """Validate a Social Security Number (SSN). A valid SSN
            is determined by the following rules according to the
            website http://www.ssa.gov/employer/ssnweb.htm

           Area-Group-Serial (AAA-GG-SSSS) The Social Security
            number consists of nine (9) digits. The first three
            (3) digits denote the area (or State) where the application
            for an original Social Security number was filed.

           Within each area, the group number (middle two (2) digits)
            range from 01 to 99 but are not assigned in consecutive
            order. For administrative reasons, group numbers issued
            first consist of the ODD numbers from 01 through 09 and
            then EVEN numbers from 10 through 98, within each area
            number allocated to a State. After all numbers in group
            98 of a particular area have been issued, the EVEN Groups
            02 through 08 are used, followed by ODD Groups 11 through
            99.

           Within each group, the serial numbers (last four (4) digits)
            run consecutively from 0001 through 9999.
           """
        isValid=False
        #strip all the dashes and whitespace
        ssn_v=ssn_str.translate(None,'-')
        ssn_v=ssn_v.strip()
        #Check length and ensure that it's a digit
        if (len(ssn_v)==9 and ssn_v.isdigit()):
            #print("SSN passed for length and digits: %s" % (ssn_v))
            #Check to ensure that it's not an invalid number
            if(ssn_v not in INVALID_SSN_NUMBERS):
                key_v = ssn_v[3:5]
                if ssn_v[0:3] in VALID_SSN_AREAS:
                    hg_v = SSN_HIGH_VALUES[ssn_v[0:3]]
                    #check for valid group for Area ID in private method
                    isValid = self.__isSSNGroupValid(int(key_v), int(hg_v))

        return isValid

    #---------- Private Functions -------------------------------------------------------

def __isSSNGroupValid(self,group,groupHighestValue):

        """Within each area, the group number (middle two (2) digits)
            range from 01 to 99 but are not assigned in consecutive
            order. For administrative reasons, group numbers issued
            first consist of the ODD numbers from 01 through 09 and
            then EVEN numbers from 10 through 98, within each area
            number allocated to a State. After all numbers in group
            98 of a particular area have been issued, the EVEN Groups
            02 through 08 are used, followed by ODD Groups 11 through
            99.
            """
        validGroup = False
        moduloGroupNumber = group % 2
        moduloGroupHighest = groupHighestValue % 2

        if ((moduloGroupHighest == 1) &#038; (groupHighestValue >= 1) &#038; (groupHighestValue <= 9)):             if ((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= groupHighestValue)):                 validGroup = True          elif ((moduloGroupHighest == 0) &#038; (groupHighestValue >= 10) &#038; (groupHighestValue <= 98)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10) &#038; (group <= groupHighestValue))):                 validGroup = True          elif ((moduloGroupHighest == 0) &#038; (groupHighestValue >= 2) &#038; (groupHighestValue <= 8)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10)
            &#038; (group <= 98))             | ((moduloGroupNumber == 0) &#038; (group >= 2)
            &#038; (group <= groupHighestValue))):                 validGroup = True          elif ((moduloGroupHighest == 1) &#038; (groupHighestValue >= 11) &#038; (groupHighestValue <= 99)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10)
            &#038; (group <= 98))             | ((moduloGroupNumber == 0) &#038; (group >= 2)
            &#038; (group <= 8))             | ((moduloGroupNumber == 1) &#038; (group >= 11)
            &#038; (group <= groupHighestValue))):                 validGroup = True          return validGroup</pre>
<h2>Why is this Cool?</h2>
<p>This whole way of developing code is cool because I don't need to fire up the server and drill through a bunch of pages to find out if I had "done it right".  In fact, I haven't even wrote the service that I want to connect to, and I haven't even filled out the model in the application that I'm working in.  I'm just doing one piece at a time, then building the model and service once I have everything ironed out.  I'll have super-duper test coverage without writing my code twice, and really have confidence in the entire back-end as I build the view and controller modules out, buying plenty of time to optimize, refactor and work everything into some nice design patterns (<em>Strategy</em>, anyone?)</p>
]]></content:encoded>
			<wfw:commentRss>http://gurovich.com/site/2010/03/30/ssn_validation_with_django/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Test Driven Development with Django Unit Testing</title>
		<link>http://gurovich.com/site/2010/03/20/test-driven-development-with-django-unit-testing/</link>
		<comments>http://gurovich.com/site/2010/03/20/test-driven-development-with-django-unit-testing/#comments</comments>
		<pubDate>Sat, 20 Mar 2010 16:56:20 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[Best Practices]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[Enterprise Development]]></category>
		<category><![CDATA[Django TDD]]></category>
		<category><![CDATA[Django Test Driven Development]]></category>
		<category><![CDATA[Django Unit Testing]]></category>
		<category><![CDATA[Django unittest]]></category>
		<category><![CDATA[python unit testing]]></category>
		<category><![CDATA[TDD python]]></category>
		<category><![CDATA[unittest python]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=1266</guid>
		<description><![CDATA[Django has wonderful unit testing functionality built into the framework. It is often ignored because Django gives so much functionality &#8220;out of the box&#8221;, but this doesn&#8217;t mean that one shouldn&#8217;t do &#8220;their part&#8221; when extending the framework to build your own applications. Tests are a great way to &#8220;figure out&#8221; what needs to be [...]]]></description>
			<content:encoded><![CDATA[<p>Django has wonderful unit testing functionality built into the framework. It is often ignored because Django gives so much functionality &#8220;out of the box&#8221;, but this doesn&#8217;t mean that one shouldn&#8217;t do &#8220;their part&#8221; when extending the framework to build your own applications.  Tests are a great way to &#8220;figure out&#8221; what needs to be done, then actually taking what you&#8217;ve learned to implement in the application proper.</p>
<p>My attempt here is to show how I used TDD in an Django application.  We&#8217;ll create a basic project with a single application and use unit testing to figure out an algorithm an implement it in the application&#8217;s class.<span id="more-1266"></span></p>
<h3>Why Test at All?</h3>
<p>I&#8217;ve spoken to many groups about Test Driven Development (TDD).  Many experienced engineers don&#8217;t do it because they haven&#8217;t been indoctrinated into the practice early, and they are established in an organization where they maintain and extend code that they might have been writing for months or even years.  The code has been fully tested and it is solid.  Why write unit tests?</p>
<p>Newer developers don&#8217;t know how to start with tests.  They write code, then write tests, then get frustrated because they are continuously writing code twice.  Corners get cut, and at best you get the &#8220;bare minimum&#8221; of coverage, which is basically what the boss sees and reviews.</p>
<p>Both of these scenarios are unfortunate, because there are a few things that TDD gives gives all engineers and the organizations that they work for:</p>
<ul>
<li>More efficient QA. QA engineers and testers don&#8217;t have to test for functionality, and they get better quality code.?What does this mean? It means that they get to do the part of their job that is more meaningful to the company as a whole. If the basic code and functionality are automatically tested, QA can then find the border conditions and those evil underlying non-obvious bugs that can kill an application, and if large enough, kill a company&#8217;s profits.</li>
<li>Better and more cohesive organizations. Test Driven Development builds confidence throughout the entire organization. Engineers working often in geographically dispersed groups need to &#8220;trust&#8221; each other, their skills and abilities. If everyone in the organization is using TDD, these groups will know that the quality coming from others will work. This not only creates trust amongst engineers, but throughout the organization, from Marketing and Business resources all the way through QA.</li>
<li>Professionalism. Being a &#8220;Pro&#8221; doesn&#8217;t mean generating thousands of lines of code.?It means generating efficient, simple to maintain, easily configurable code that &#8220;just works&#8221; every time. It not only has to work, it has to be tested and documented.</li>
<li>Peer pressure to &#8220;do the right thing&#8221;. If the senior members and thought leaders of an engineering group are all using TDD, documenting their code and communicating effectively, this will spread throughout the organization and foment a best practices environment.</li>
</ul>
<p>Test Driven Development with high unit test coverage creates efficient, spirited and profitable software. The balance of this post will explain just how I use TDD to implement a complex Bank Routing Number (ABN) validation class. I am assuming that the reader is familiar with Django, and has a comfortable understanding with basic Django and Python development principles:</p>
<h3>Create the Project and Application</h3>
<p>My application&#8217;s name is</p>
<pre>valid_lib</pre>
<p>It is destined to be a Service Platform that allows all kinds of complex financial form data to be validated through a ReSTful interface. At the moment I&#8217;m not worried about the Service, the Interfaces or even the Business Model. Currently, I want to get all the validation algorithms and calculations in place. The rest of the stuff is pretty easy.  At the command line, in my Django development directory, I type:</p>
<pre>django-admin.py startproject valid_lib</pre>
<p>To start my project.  I <code>cd</code> into the <code>valid_lib</code> directory and then type:</p>
<pre>django-admin.py startapp finvs</pre>
<p>To create my application and it&#8217;s directory.  At this point I add the <code>valid_lib.finvs</code> to my <code>INSTALLED_APPS</code> in the the <code>settings.py</code> file, and while I&#8217;m there I add my database information, even though I really won&#8217;t be using it yet. I also add another folder in the <code>finvs</code> directory that I name <code>util</code>.  Run a <code>manage.py syncdb</code> against the application&#8217;s root and you should be good to go.  Everything should now look like fig 1:</p>
<div id="attachment_1268" class="wp-caption aligncenter" style="width: 204px"><a href="http://gurovich.com/site/wp-content/uploads/2010/03/fig1.png"><img class="size-full wp-image-1268 " title="Initial TDD validation app structure" src="http://gurovich.com/site/wp-content/uploads/2010/03/fig1.png" alt="" width="194" height="222" /></a><p class="wp-caption-text">fig 1: Initial TDD validation app structure</p></div>
<p><em>(I&#8217;m writing this on my MacBook Pro and using Textmate, but you should be getting similar results however you&#8217;re doing it)</em></p>
<p>Notice that there is a <code>test.py </code> file in the<code> finvs </code>directory.  This is where we&#8217;re going to start.  Django&#8217;s default unit testing fixture looks for this file and will execute tests within it.  We&#8217;ll open that file up and begin writing our code there.</p>
<h3>ABN Validation</h3>
<p>ABA (American Banker&#8217;s Association) routing numbers (ABNs) are used to identify financial institutions when making transactions. This number is usually required when setting up a bank account for direct deposits, automated payments, etc. Or when making payments by &#8220;paperless&#8221; check over the phone or online.</p>
<p>These routing numbers include a checksum digit which can be used to help verify if a given number is valid or not.</p>
<p>The algorithm checks the first second and third number and adds them together, then iterates through every three numbers (total of 9) to return a value. If the resulting sum is an even multiple (think <code>modulus</code> here&#8230;) of ten (but not zero), the ABA routing number is good. ?We&#8217;ll now write a test with a valid ABN to see if we can get things to work correctly.  In the <code>tests.py</code> file, write:</p>
<pre>
"""
This file tests the finvs application
"""
import unittest
from django.test import TestCase

#Here are some bank numbers that we can run tests against...
SHORT_BAD_ABN_NUM = 1000012
LONG_BAD_ABN_NUM = 255073345999
BAD_ABN_NUM = 255073545
GOOD_ABN_NUM = 255073345

class ValidatorsTestCase(unittest.TestCase):

    def setUp(self):
        self.value = CITY_CODE_FRAUD

    def testAbnAlgorythm(self):
        """Tests the algorythm with a known good bank number.
           This test is here to create the algorythm to be used
             in the validator implementation.  When it's right,
             cut and paste it into the validator class.

           The algorithm checks the first second and third number
            and adds them together, then iterates through every
            three numbers (total of 9) to return a value.

           If the resulting sum is an even multiple of ten
            (but not zero), the ABA routing number is good.
           """
        n=0
        bank_str = str(GOOD_ABN_NUM)
        num_length = len(bank_str)
        if (num_length ==9):
            for j in range(0,num_length,3):
                t = int(bank_str[j]) #this is the first digit
                ti = int(bank_str[j + 1]) #this is the second digit
                tii = int(bank_str[j + 2]) #this is the third digit
                n += (t * 3)+ (ti * 7)+ tii #add them up
            #check for zero and modulus of 10
            print((n != 0) &#038; ((n % 10) == 0))  #we'll take this line out when we get it right.
            self.assertTrue((n != 0) &#038; ((n % 10) == 0))
</pre>
<p>Now, at the application root, run:</p>
<pre>python manage.py test finvs.ValidatorsTestCase</pre>
<p>You should get something very similar to this:</p>
<pre>
$ python manage.py test finvs.ValidatorsTestCase
Creating test database...
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Installing index for auth.Permission model
Installing index for auth.Message model
True
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Destroying test database...
</pre>
<p>The <b><code>True</code></b> that you see in the output is your print statement. Notice that the system also creates and destroys a test database.  This is extremely cool functionality that can be used with Django&#8217;s Fixtures, but we&#8217;re not going to use it here for this code set.  We haven&#8217;t written a line of production code, but our test passes, and if you look at the test, <i>it is the basis for the actual implementation</i>!</p>
<h3>Implement It!</h3>
<p>We&#8217;re really close, but we&#8217;ll need to make a few changes before we implement it in our &#8220;production&#8221; code.  Seriously, we&#8217;re really close!</p>
<p>In the <code>utils</code> directory, create the file <code>validation_formulas.py</code>  This is where we&#8217;ll add our ABN validation class.  In my test code I created a check to ensure that the number sent in had exactly 9 digits.  I did this because I didn&#8217;t want to have a <code>try / except</code> block, and to keep it clean. Feel free to add this if needed.  The other change is that we actually need to return something &#8212; True or False in this case.  The test code&#8217;s <code>def</code> doesn&#8217;t return anything, it just makes an assertion.  To validate something, we need to insert a parameter into the method and then return a true/false.  We also need to give it a name.  In the <code>validation_formulas.py</code> file, you can now insert:</p>
<pre>
class ValidationFormulas:

    def isBankRoutingNumber(self,bankNumber):
        """Parses the bank routing number for purposes of finding a fraudulent entry.
            the algorithm check the first second and third number and adds them together,
            then iterates through every three numbers (total of 9) to return a value.

           If the resulting sum is an even multiple of ten (but not zero), the ABA
            routing number is good.

           param -- bankNumber the 9 digit bank number;

           return boolean true if number is valid, false if not. false is the default
           """
        n=0
        retbool=False
        bank_str = str(bankNumber)
        num_length = len(bank_str)
        if (num_length ==9):
            for j in range(0,num_length,3):
                t = int(bank_str[j])
                ti = int(bank_str[j + 1])
                tii = int(bank_str[j + 2])
                n += (t * 3)+ (ti * 7)+ tii
            retbool = (n != 0) &#038; ((n % 10) == 0)
        #return the value
        return retbool
</pre>
<p>Notice the added documentation.  Some of it is cut and paste from the Test, the rest is information that you&#8217;ll want to convey to anyone that is using this code in the future, because hopefully at some point the company you work for will realize just how brilliant you are and promote you to CTO, where you won&#8217;t get to write code anymore.  You sure as heck don&#8217;t want to leave the poor schlep that is hired to take your place in the dark!</p>
<p>Also notice that I have a default return of <code>False</code>  some applications may need a different default return, or this return may be handled in a manager class that registers the default returns in some kind of persistence layer that is more dynamic.  Your mileage may vary.</p>
<h3>Wrapping it Up</h3>
<p>There it is.  You&#8217;ve written a test first.  You&#8217;ve used the implementation of the test and created a sensible implementation.  If there are other ways of refining your code or cleaning things up, you can always go back and make more changes.  But your&#8217;e not done.  You need to write more tests to actually <b>verify</b> that this algorithm works, that it shows false for bad routing numbers, and that it an spot when the length of the number is incorrect without throwing any index out of bounds exceptions or the like.  you can then add more test methods to your <code>ValidatorsTestCase</code> class.  I&#8217;ve also leveraged the <code>setUp</code> method that is provided with the framework to generate my constants for me. The code will then look like this:</p>
<pre>
"""
This file tests the finvs application
"""
import unittest
from django.test import TestCase
from valid_lib.finvs.utils.validation_formulas import ValidationFormulas

class ValidatorsTestCase(unittest.TestCase):

    def setUp(self):
        self.SHORT_BAD_ABN_NUM = 1000012
        self.LONG_BAD_ABN_NUM = 255073345999
        self.BAD_ABN_NUM = 255073545
        self.GOOD_ABN_NUM = 255073345

    def testAbnAlgorythm(self):
        """Tests the algorythm with a known good bank number.
           This test is here to create the algorythm to be used
             in the validator implementation.  When it's right,
             cut and paste it into the validator class.

           The algorithm checks the first second and third number
            and adds them together, then iterates through every
            three numbers (total of 9) to return a value.

           If the resulting sum is an even multiple of ten
            (but not zero), the ABA routing number is good.
           """
        n=0
        bank_str = str(self.GOOD_ABN_NUM)
        num_length = len(bank_str)
        if (num_length ==9):
            for j in range(0,num_length,3):
                t = int(bank_str[j])
                ti = int(bank_str[j + 1])
                tii = int(bank_str[j + 2])
                n += (t * 3)+ (ti * 7)+ tii
            self.assertTrue((n != 0) &#038; ((n % 10) == 0))

    def testBankRouter(self):
        """Tests the algorythm in the validator against
              known good and bad bank numbers.
           """
        bank_num = self.SHORT_BAD_ABN_NUM
        formt = ValidationFormulas()
        bool = formt.isBankRoutingNumber(bank_num)
        self.assertFalse(bool)

        bank_num = self.LONG_BAD_ABN_NUM
        formt = ValidationFormulas()
        bool = formt.isBankRoutingNumber(bank_num)
        self.assertFalse(bool)

        bank_num = self.BAD_ABN_NUM
        formt = ValidationFormulas()
        bool = formt.isBankRoutingNumber(bank_num)
        self.assertFalse(bool)

        bank_num = self.GOOD_ABN_NUM
        formt = ValidationFormulas()
        bool = formt.isBankRoutingNumber(bank_num)
        self.assertTrue(bool)</b>
</pre>
<p>I&#8217;ve added an import for the &#8220;production&#8221; class that i&#8217;m testing (<code>ValidationFormulas</code>).  Finally, I&#8217;ve added a new method that implements new tests against the actual methods in this class to check my work.  Running the tests now reveal:</p>
<pre>
Creating test database...
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Installing index for auth.Permission model
Installing index for auth.Message model
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
Destroying test database...
</pre>
<p> If someone in the future messes with the &#8220;production&#8221; code implementation and things get out of whack, It will break here instead of in front of the user.  By combining this with a continuous-build environments, a lot of mistakes can be caught extremely fast, sometimes within seconds of a source control check in!</p>
<p>Enjoy.</p>
]]></content:encoded>
			<wfw:commentRss>http://gurovich.com/site/2010/03/20/test-driven-development-with-django-unit-testing/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Looking at Pinax &#8211; a Review</title>
		<link>http://gurovich.com/site/2010/03/05/looking-at-pinax-a-review/</link>
		<comments>http://gurovich.com/site/2010/03/05/looking-at-pinax-a-review/#comments</comments>
		<pubDate>Fri, 05 Mar 2010 17:59:27 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Pinax]]></category>
		<category><![CDATA[Pinax Review]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=1239</guid>
		<description><![CDATA[Django has become a friend of mine over the last year.  I had previous experience with Ruby on Rails, and was always disappointed when it came to deploying and productizing the application, and the actual framework APIs and implementations underwent too many changes over the last few years to give me the confidence I needed [...]]]></description>
			<content:encoded><![CDATA[<p>Django has become a friend of mine over the last year.  I had previous experience with Ruby on Rails, and was always disappointed when it came to deploying and productizing the application, and the actual framework APIs and implementations underwent too many changes over the last few years to give me the confidence I needed to go into production with it.  Python has been around forever;  it plays nice with Apache through the mod_wsgi and mod_python libraries, and with Django a RoR-like development framework appeared.  It&#8217;s not as quite as &#8220;fun&#8221; to use as Ruby on Rails, but it more than makes up for it when I step in front of an executive committee that is extremely concerned about risk, long-term maintenance and stability of both the platform and community.</p>
<p>I&#8217;ve been dancing around Pinax for a few months.  Their philosophy that &#8220;there are a few different categories of web applications and they share similar basic functionality &#8211; which shouldn&#8217;t have to be written from scratch every time something needs to get done,&#8221; is congruent with my beliefs. While writing greenfield applications is a source of delight for competent engineers, it is preferable to build on a solid foundation of &#8220;super-libraries&#8221; and add the functionality that is needed for your particular requirements.<span id="more-1239"></span>Pinax offers this and so much more.  The developers of Pinax have created a set of &#8220;standard&#8221; platforms that may be used as an extendable base for anyone&#8217;s further development. The applications that they have created are:</p>
<ul>
<li><strong>sample_group_project:</strong> This project demonstrates group functionality with a barebones group?containing no extra content apps as well as two additional group types,?tribes and projects, which show different membership approaches and?content apps.</li>
</ul>
<ul>
<li><strong>intranet_project:</strong> This project demonstrates a closed site requiring an invitation to join and?not exposing any information publicly. It provides a top-level task tracking?system, wiki and bookmarks. It is intended to be the starting point of sites?like intranets.</li>
</ul>
<ul>
<li><strong>social_project:</strong> This project demonstrates a social networking site. It provides profiles,?friends, photos, blogs, tribes, wikis, tweets, bookmarks, swaps,?locations and user-to-user messaging.?<em>In 0.5 this was called &#8220;complete_project&#8221;.</em></li>
</ul>
<ul>
<li><strong>cms_project_holidayhouse:</strong> A very simple <a title="CMS" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');" href="http://en.wikipedia.org/wiki/Content_management_system">CMS</a> that lets you set up templates and then edit content, including images, right in the frontend of the site.?<em>The sample media, templates and content including in the project demonstrate?a basic site for holiday house rentals.</em></li>
</ul>
<ul>
<li><strong>code_project:</strong> This project demonstrates group functionality and the tasks, wiki and topics?apps. It is intended to be the starting point for things like code project?management where each code project gets its own wiki, task tracking system?and threaded discussions.</li>
</ul>
<ul>
<li><strong>private_beta_project:</strong> This project demonstrates the use of a waiting list and signup codes for?sites in private beta. Otherwise it is the same as basic_project.</li>
</ul>
<ul>
<li><strong>cms_project_company:</strong> A very simple CMS that lets you set up templates and then edit content,?including images, right in the frontend of the site.<em> The sample media, templates and content including in the project demonstrate?a basic company website.</em></li>
</ul>
<ul>
<li><strong>basic_project:</strong> This project comes with the bare minimum set of applications and templates?to get you started. It includes no extra tabs, only the profile and notices?tabs are included by default. From here you can add any extra functionality?and applications that you would like.</li>
<p><em>source &#8211; Pinax documentation</em></ul>
<p>These projects are all quite well developed even if they are not at a &#8220;1.0&#8243; release.  They all contain registration, email confirmation functionality, profile functionality and other basic &#8220;member&#8221; web application attributes.  I spent yesterday looking through the documentation and various on-line tutorials and deployed the sample_group_project on my Ubuntu 9.1-powered laptop.</p>
<p>I found that I had to jump between a couple of tutorials and once needed to completely wipe and rebuild to get it right, but the results were quite spectacular once I got it right.  Out of the box, user profiles, photo sharing, blogs, tweets, groups, Yahoo map locations, bookmarks and a &#8220;merchandise swapping and trading&#8221; application appear with no additional coding.  This application also enjoys Django&#8217;s dynamite Admin modules.</p>
<p>In a future post I&#8217;ll follow through and show what worked for me and endeavor to explain how easy it can be once you have everything in place.  As I develop the application, I&#8217;ll further post what I did to develop, deploy and extend the application for further use.</p>
<h3>Is it Ready for the Enterprise?</h3>
<p>I&#8217;m not sure if I can completely answer this question.  From an engineering/architecture point of view, it&#8217;s spot on.  For application maturity and production, that&#8217;s another matter.  Are there products using Pinax already?  What size are they?  Have they found, fixed and published bugs?</p>
<p>Quality Assurance in open-source projects can be in-bred amongst the developers and engineers on a project.  While they &#8220;know&#8221; the product the best and work to ensure functionality and eventual viability, they also make decisions on what needs to be fixed and &#8220;what can wait&#8221;.  In a strong enterprise environment, QA stands alone as a benevolent antagonist against development; business requirements will expose different issues and eventually make the product stronger.  If, as a technology leader, you are considering the use of Pinax, it would be prudent to add extra Quality Assurance time to the planned schedule to ensure future stability and a great customer experience.</p>
]]></content:encoded>
			<wfw:commentRss>http://gurovich.com/site/2010/03/05/looking-at-pinax-a-review/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Simple &#8220;Restlet&#8221; Demo application</title>
		<link>http://gurovich.com/site/2009/11/23/a-simple-restlet-demo-application/</link>
		<comments>http://gurovich.com/site/2009/11/23/a-simple-restlet-demo-application/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 01:59:00 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[Java Rest]]></category>
		<category><![CDATA[Restlet]]></category>
		<category><![CDATA[Restlet Demo]]></category>

		<guid isPermaLink="false">http://danilogurovich.wordpress.com/?p=106</guid>
		<description><![CDATA[I&#8217;m assuming that if you&#8217;re here you already know the basics of REpresentational State Transfer.  I&#8217;m also going to assume that you&#8217;ve looked at all the goodies at the Restlet community site.  Further, I&#8217;m thinking that you want a quick start guide to show you how to implement it.  The following tutorial shows how I [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m assuming that if you&#8217;re here you already know the basics of <strong>RE</strong>presentational <strong>S</strong>tate<strong> T</strong>ransfer.  I&#8217;m also going to assume that you&#8217;ve looked at all the goodies at the <a title="Restlet home pages" href="http://www.restlet.org" target="_blank">Restlet</a> community site.  Further, I&#8217;m thinking that you want a quick start guide to show you how to implement it.  The following tutorial shows how I &#8220;found a home&#8221; with Restlet, what worked for me, and hopefully something that will work for you, too.</p>
<p>The Restlet API is a Java framework for the REST architectural style. The <a href="http://www.restlet.org/documentation/1.1/nre/" target="_blank">Noelios Restlet Engine (NRE)</a> is available as the reference implementation, as well as <a href="http://www.restlet.org/documentation/1.1/ext/" target="_blank">several extensions</a> to the API and to NRE. <span id="more-106"></span></p>
<p><strong>Why is it cool?</strong></p>
<p>To Quote Jérôme Louvel, founder and lead developer of Restlet:</p>
<address>&#8220;After several iterations, it became clear that it would be beneficial for developers to separate the Restlet project into two parts. The first part is a generic set of classes, called the Restlet API, including a few helper classes and a mechanism to register a Restlet implementation. The second part is a reference implementation, called the Noelios Restlet Engine, including several server and client connectors (HTTP, JDBC, SMTP, etc.), a set of representations (based on strings, files, XML documents, templates, streams, etc.) and a Directory able to serve static files from a tree of directories with automatic content negotiation based on file extensions. Since the first public release in November 2005, the project keeps maturing thanks to the help of an active community of users and developers.&#8221;</address>
<p>It&#8217;s cool because it delivers on this statement.  I love that fact that you can send a request to a Restlet engine, and it will return back this amazing variety of Representations based upon it.  The first exposure to Restlet for me came when I put forth a design for a beacon tracking system that could be triggered via HTML pages, server-side calls, log-file digestion and anything else I could think of.  The lead developer turned me on to Restlet.</p>
<p>I later found ways to hook it up to a JBoss-Rules system, a Low-Latency Data Store for delivering customer demographic information, and a Affiliate Network lead quality validation system.  Each application had completely different requirements except that they needed to be a service.  I decided on REST instead of  SOAP, and the &#8220;rest&#8221; is in the implementations.  It subscribes to all my basic requirements for an application:</p>
<address> Any application that represents good design gives those that interact<br />
with it the feeling that &#8220;<strong>it just works&#8221;</strong>.</address>
<p>Once you work with Restlet a bit, get an application running, manipulate what you&#8217;ve built into other things, and get real expertise around it, you&#8217;ll find, like I have, that <em>it just works</em>.</p>
<h3>The Jars you need for this application.</h3>
<p>To run this application, you&#8217;ll need the following jar files:</p>
<ul>
<li>com.noelios.restlet.ext.servlet_2.5.jar</li>
<li>com.noelios.restlet.jar</li>
<li>org.restlet.jar</li>
<li>xalan-2.7.0.jar</li>
<li>xrcesImpl.jar</li>
</ul>
<div>This is the complete listing of libraries.</div>
<h3>Running Restlet from Tomcat &#8212; Configure the web.xml file.</h3>
<p>You&#8217;ll want to run Restlet from a WAR file in most production cases, but you really don&#8217;t have to.  I like to use it because it creates homogeneous environments for my ITOPs people to work with; nice, similar stacks for maintenance purposes, so we&#8217;ll do it this way here. It is my belief that productizing applications with little or no Network Configurations is key to long-term viability. The following is the web.xml file used in the web application that we&#8217;re deploying our service through:</p>
<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"&gt;
    &lt;display-name&gt;first steps servlet&lt;/display-name&gt;
    &lt;!-- Application class name --&gt;
    &lt;context-param&gt;
        &lt;param-name&gt;org.restlet.application&lt;/param-name&gt;
        &lt;param-value&gt;
            <strong>com.dg.demo.restlet.application.DemoApplication</strong>
        &lt;/param-value&gt;
    &lt;/context-param&gt;

    &lt;!-- Restlet adapter --&gt;
    &lt;servlet&gt;
        &lt;servlet-name&gt;ServerServlet&lt;/servlet-name&gt;
        &lt;servlet-class&gt;
            com.noelios.restlet.ext.servlet.ServerServlet
        &lt;/servlet-class&gt;
    &lt;/servlet&gt;

    &lt;!-- Catch all requests --&gt;
    &lt;servlet-mapping&gt;
        &lt;servlet-name&gt;ServerServlet&lt;/servlet-name&gt;
        &lt;url-pattern&gt;/demo/*&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;
&lt;/web-app&gt;</pre>
<p>Note that com.dg.demo.restlet.application.DemoApplication will be the Application class file that you will write.  ServerServlet comes with the libraries.  Note that the &#8220;servlet-mapping&#8221; that I&#8217;m using here is &#8220;/demo/*&#8221;.  This means that all URLs directed to the Restlet must have &#8220;/demo/&#8221; as their root, (i.e. http://localhost:8080/demo/&#8230;).</p>
<p><strong>The Application Class</strong></p>
<p>The Application Class is the &#8220;manager&#8221; for the Restlet Application. This class will find the proper resource with respect to the URL that is presented to it.  In the case of this application, we&#8217;ll be mapping incoming URLs to:</p>
<ul>
<li>a &#8220;System up&#8221; Resource that shows that the Restlet is actually running and mapping correctly.</li>
<li>a &#8220;Mapping&#8221; Resource that will allow for a list of cities and states to be shown based upon given inputs.</li>
<li>an &#8220;XML&#8221; Resource that shows mappings of Cities and States as an XML Representation.</li>
</ul>
<p>Here is the application Class:</p>
<pre>package com.dg.demo.restlet.application;

import com.dg.demo.restlet.resource.CityXMLResource;
import com.dg.demo.restlet.resource.MapResource;
import com.dg.demo.restlet.resource.SystemResource;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Restlet;
import org.restlet.Router;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.StringRepresentation;

/**
 * The Application Root Class.  Maps all of the resources.
 */
public class DemoApplication extends Application {

    /**
     * Creates a new DemoApplication object.
     */
    public DemoApplication() {
        //empty
    }

    /**
     * Public Constructor to create an instance of DemoApplication.
     *
     * @param parentContext - the org.restlet.Context instance
     */
    public DemoApplication(Context parentContext) {
        super(parentContext);
    }

    /**
     * The Restlet instance that will call the correct resource
     * depending up on URL mapped to it.
     *
     * @return -- The resource Restlet mapped to the URL.
     */
    @Override
    public Restlet createRoot() {
        Router router = new Router(getContext());

        <strong>router.attach("/sys", SystemResource.class);
        router.attach("/{key}/maps", MapResource.class);
        router.attach("/xml", CityXMLResource.class);</strong>

        <strong>Restlet mainpage</strong> = new Restlet() {
            @Override
            public void handle(Request request, Response response) {
                StringBuilder stringBuilder = new StringBuilder();

                stringBuilder.append("&lt;html&gt;");
                stringBuilder.append("&lt;head&gt;&lt;title&gt;Sample Application " +
                        "Servlet Page&lt;/title&gt;&lt;/head&gt;");
                stringBuilder.append("&lt;body bgcolor=white&gt;");
                stringBuilder.append("&lt;table border=\"0\"&gt;");
                stringBuilder.append("&lt;tr&gt;");
                stringBuilder.append("&lt;td&gt;");
                stringBuilder.append("&lt;h3&gt;available REST calls&lt;/h3&gt;");
                stringBuilder.append("&lt;ol&gt;&lt;li&gt;/sys --&gt; returns system " +
                        "up and date string&lt;/li&gt;");
                stringBuilder.append("&lt;li&gt;/all/maps --&gt; returns a list" +
                        " of all the cities and states&lt;/li&gt;");
                stringBuilder.append("&lt;li&gt;/{key}/maps --&gt; returns a list " +
                        "of cities for a particular state (CA,IL,TX)&lt;br/&gt;");
                stringBuilder.append("using one of the keys from the \"all\" " +
                        "call&lt;br/&gt; pasted in place as the {key}.&lt;/li&gt;");
                stringBuilder.append("&lt;li&gt;/xml --&gt; POST or GET URL or web " +
                        "form elements as XML to this&lt;/li&gt;");
                stringBuilder.append("&lt;/ol&gt;");
                stringBuilder.append("&lt;/td&gt;");
                stringBuilder.append("&lt;/tr&gt;");
                stringBuilder.append("&lt;/table&gt;");
                stringBuilder.append("&lt;/body&gt;");
                stringBuilder.append("&lt;/html&gt;");

                response.setEntity(new StringRepresentation(
                        stringBuilder.toString(),
                        MediaType.TEXT_HTML));
            }
        };
        <strong>router.attach("", mainpage);</strong>
        return router;
    }
}</pre>
<p>With this Application I tried to show a &#8220;default&#8221;, &#8220;servlet-like&#8221; page returned by a Restlet created as an anonymous inner class &#8212; you can do that with all your URL requests if you like, but creating different Resource implementations is much easier to manage and write tests for. It is also very important to set the Context of the application in the Constructor (calling the superclass).  I forgot to do this and lost about an hour before I finally found it.</p>
<p>Once the Resources are mapped, You&#8217;re ready to rock and roll.  We&#8217;ll create a simple Model, and then get into the Resource Classes.</p>
<h3>A Simple Model to Work From:</h3>
<p>We need something to do with our application, so I decided to create a simple &#8220;persistence layer&#8221; that has a map of cities and states.  I could have done more and it could have been more complex, but I just wanted something that you can bang against and get an idea about how it all works.  I created an implementation class that contains a map of cities and states extending java.util.AbstractMap.  I then exposed various structures of this mapping through an interface class that the application will use to return information by implementing an Interface against it, obfuscating anything that I didn&#8217;t want to expose:</p>
<pre>package com.dg.demo.model;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A simple interface to return a map of
 * values for the demonstration.
 */
public interface MappedPersistence {

    // returns a formatted string of the cities
    // mapped to a particular state.
    String getCommonValues(String state);

    // returns a set of cities, which are
    // the map keys.
    Set&lt;String&gt; keys();

    // returns a map of cities &lt;K&gt; with states&lt;V&gt;
    // as the value.
    Map&lt;String, String&gt; proxy();

    // returns a set of states available.
    Set&lt;String&gt; valueSet();

    //returns a List of cities.
    List&lt;String&gt; mappedKeys(String s);

}</pre>
<p>The implementation of the Interface above is MappedCityPersistence:</p>
<pre>package com.dg.demo.model.impl;

import com.dg.demo.model.MappedPersistence;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Implementation of MappedPersistence Interface.
 * Extends Abstract Map, but obfuscates the
 * mappings via the MappedPersistence Interface.
 */
public class MappedCityPersistence
        extends AbstractMap&lt;String, String&gt;
        implements MappedPersistence {

    private Map&lt;String, String&gt; proxyMap;

    public MappedCityPersistence() {
        proxyMap = new HashMap&lt;String, String&gt;();
        loadProxy();
    }
    // this class must be implemented...
    public Set&lt;Entry&lt;String, String&gt;&gt; entrySet() {
        return proxyMap.entrySet();
    }
    //the values for persistence.
    private void loadProxy() {
        proxyMap.put("Los Angeles", "CA");
        proxyMap.put("Santa Barbara", "CA");
        proxyMap.put("San Francisco", "CA");
        proxyMap.put("San Diego", "CA");
        proxyMap.put("Dallas", "TX");
        proxyMap.put("Houston", "TX");
        proxyMap.put("Austin", "TX");
        proxyMap.put("El Paso", "TX");
        proxyMap.put("Chicago", "IL");
        proxyMap.put("Joliet", "IL");
        proxyMap.put("Naperville", "IL");
        proxyMap.put("Oak Park", "IL");
    }

    @Override
    public String toString() {
        StringBuilder builder =
                new StringBuilder("City/State values:\n");
        for (String s : proxyMap.keySet()) {
            builder.append(s).append(" =&gt; ")
                    .append(proxyMap.get(s)).append("\n");
        }
        return builder.toString();
    }

    public String getCommonValues(String value) {
        StringBuilder builder =
                new StringBuilder("Cities located in "
                        + value + ":\n");
        for (String s : proxyMap.keySet()) {
            if (proxyMap.get(s).equals(value)) {
                builder.append(s).append("\n");
            }
        }
        return builder.toString();
    }

    public Set&lt;String&gt; keys() {
        return proxyMap.keySet();
    }

    public Map&lt;String, String&gt; proxy() {
        return this.proxyMap;
    }

    public Set&lt;String&gt; valueSet() {
        Set&lt;String&gt; quickAndDirtySet = new HashSet&lt;String&gt;();
        for (String s : proxyMap.values()) {
            quickAndDirtySet.add(s);
        }
        return quickAndDirtySet;
    }

    public List&lt;String&gt; mappedKeys(String s) {
        List&lt;String&gt; quickAndDirtyList = new ArrayList&lt;String&gt;();
        for (String key : proxyMap.keySet()) {
            if (proxyMap.get(key).equals(s)) {
                quickAndDirtyList.add(key);
            }
        }
        return quickAndDirtyList;
    }

    public static void main(String[] args) {
        MappedCityPersistence p = new MappedCityPersistence();
        System.out.println(p.getCommonValues("TX"));
    }
}</pre>
<p>Note the extra methods that are now obfuscated by the Mapped Persistence Interface.</p>
<h3>Building the Resources that are Mapped to URLs</h3>
<p>The first, and easiest to implement of the Resources is the SystemResource that I created to show whether or not I got the mappings right and if the system is running or not.  It returns a basic &#8220;text&#8221; string that says the system is running and what time the JVM says it is:</p>
<p><strong>SystemResource.java</strong></p>
<pre>package com.dg.demo.restlet.resource;
import org.restlet.Context;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.Representation;
import org.restlet.resource.Resource;
import org.restlet.resource.StringRepresentation;
import org.restlet.resource.Variant;

import java.util.Calendar;

/**
 * Simple String retrieval to determine whether the system is up
 * and running.  it adds a timestamp for varification that the
 * message isn't cached and allows for identification of
 * the time zone.
 */
public class SystemResource extends Resource {

    /**
     * Creates a new MapResource object.
     *
     * @param context  -- Restlet Context Instance.
     * @param request  -- Restlet Request Instance.
     * @param response -- Restlet Response Instance.
     */
    public SystemResource(Context context,
                          Request request,
                          Response response) {
        super(context, request, response);
        // Here we add the representation variants exposed
        getVariants().add(new Variant(MediaType.TEXT_PLAIN));
    }

    /**
     * Do not allow post to this resource.
     *
     * @return -- always false for this resource.
     */
    public boolean allowPost() {
        return false;
    }

    /**
     * default method for "get" from Client Application.
     *  Returns wheter or not the system is available,
     * and the current time.
     *
     * @param variant -- Available variants.
     *
     * @return -- A String representing whether or
     *            not the demo system is available, etc.
     */
    public Representation represent(Variant variant) {
        //LQSCache.getInstance();
        <strong>String message = "hello!, the Demo " +
                "Service is available." +
                " Time of request is:"
                + Calendar.getInstance()
                .getTime().toString();</strong>

        return new StringRepresentation(message,
                MediaType.TEXT_PLAIN);
    }
}</pre>
<p>The mapping resource shows all of the cities and states mapped, and also shows the cities mapped to a particular state when that state is input within the url (i.e. http://localhost:8080/demo/all/maps &#8212; to show all cities and their city mappings, ./demo/TX/maps to get all cities mapped to Texas):</p>
<p><span><strong>MapResource.java</strong></span></p>
<pre>package com.dg.demo.restlet.resource;

import com.dg.demo.model.MappedPersistence;
import com.dg.demo.model.impl.MappedCityPersistence;
import org.restlet.Context;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.Representation;
import org.restlet.resource.Resource;
import org.restlet.resource.StringRepresentation;
import org.restlet.resource.Variant;

/**
 * Utility Resource to view the various strings
 * mapped in the Persistence.
 */
public class MapResource extends Resource {

    /**
     * The map requested by the client application
     */
    String mapName;

    /**
     * Creates a new MapResource object.
     *
     * @param context  -- The restlet Context instance.
     * @param request  -- The restlet REquest instance.
     * @param response -- The restlet Response instance.
     */
    public MapResource(Context context,
                       Request request,
                       Response response) {
        super(context, request, response);
        this.mapName = (String) request.getAttributes().get("key");

        // Here we add the representation variants exposed
        getVariants().add(new Variant(MediaType.TEXT_PLAIN));
    }

    /**
     * Do not allow post to this resource.
     *
     * @return -- always false for this resource.
     */
    @Override
    public boolean allowPost() {
        return false;
    }

    /**
     * Collect the values for a particular mapping and return
     * a formatted string.  By inputting "all" into the request,
     * it returns a list of the mappings available. When this
     * service returns, substituting "all" for one of the
     * returned values will return the values for that mapping key
     *
     * @param variant -- The variant describing the return mappings.
     *
     * @return -- a formatted string with the map values as a list.
     */
    @Override
    public Representation represent(Variant variant) {
        //UDCAPICache.getInstance();
        MappedPersistence persistence = new MappedCityPersistence();

        //LQSCache.getInstance();
        String message;
        //String mapName = (String) getRequest().getAttributes().get("key");

        if (mapName.equals("all")) {
            message = persistence.toString();
        } else if (persistence.valueSet().contains(mapName)) {
            message = persistence.getCommonValues(mapName);
        } else {
            message = "NO VALUES FOUND FOR: " + mapName;
        }
        return new StringRepresentation(message, MediaType.TEXT_PLAIN);
    }
}</pre>
<p>The XML resource returns a list of cities that are mapped to each state as an xml representation.  It allows a post or get, via the &#8220;acceptRepresentation()&#8221; (POST) and &#8220;represent()&#8221; (GET) methods.  I&#8217;ve put both of these in <strong>bold</strong>, along with other things that seemed important to me.  Each calls the same private method that creates the XML representation:</p>
<div><strong>CityXMLResource.java</strong></div>
<pre>package com.dg.demo.restlet.resource;

import com.dg.demo.model.MappedPersistence;
import com.dg.demo.model.impl.MappedCityPersistence;
import org.restlet.Context;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.DomRepresentation;
import org.restlet.resource.Representation;
import org.restlet.resource.Resource;
import org.restlet.resource.Variant;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.io.IOException;

public class CityXMLResource extends Resource {

    /**
     * @param context  -- Restlet Context Instance.
     * @param request  -- Restlet Request Instance.
     * @param response -- Restlet Response Instance.
     */
    public CityXMLResource(Context context,
                           Request request,
                           Response response) {
        super(context, request, response);
        getVariants().add(
                new Variant(MediaType.TEXT_PLAIN));
        getVariants().add(
                new Variant(MediaType.APPLICATION_XML));
        //handle(request,response);
    }

    /**
     * Flag to allow POST via web form.
     *
     * @return true if POST is allowed, false if not.
     */
    <strong>public boolean allowPost()</strong> {
        return true;
    }

    /**
     * Represent the requested object in the requested format.
     *
     * @param variant -- the requested variant or default.
     *
     * @return an XML representation or an error string.
     */
    <strong>public Representation represent</strong>(Variant variant) {
        DomRepresentation representation = null;
        try {
            representation = getFraudXMLRepresentation();
        } catch (IOException e) {
            e.printStackTrace();
            getResponse().setEntity("ERROR " + e.getMessage(),
                    MediaType.TEXT_PLAIN);
        }
        return representation;
    }

    /**
     * Handle a POST Http request.
     *
     * @param entity -- the declared entity
     */
    <strong>public void acceptRepresentation</strong>(Representation entity) {
        System.out.println("USING POST");

        try {
            if (entity.getMediaType()
                    .equals(MediaType.APPLICATION_WWW_FORM, true)) {
                DomRepresentation representation =
                        getFraudXMLRepresentation();
                // Returns the XML representation of this document.
                getResponse().setEntity(representation);
            }
        } catch (IOException e) {
            e.printStackTrace();
            getResponse().setEntity("ERROR " + e.getMessage(),
                    MediaType.TEXT_PLAIN);
        }
    }

    /**
     * Creates the XML that will be returned to the requesting client.
     *
     * @return - The formatted XML Document
     */
    private DomRepresentation getFraudXMLRepresentation() throws IOException {

        DomRepresentation representation =
                new DomRepresentation(MediaType.TEXT_XML);

        MappedPersistence persistence =
                new MappedCityPersistence();

        // Generate a DOM document representing the list of frauds generated..
        Document d = representation.getDocument();

        //The Root Element, with it's corresponding attribute for the total score.
        Element root = d.createElement("citystatemappings");
        root.setAttribute("numberofstates",
                String.valueOf(persistence.valueSet().size()));
        d.appendChild(root);

        //If there are no frauds, then we skip this and just return the root.
        for (String state : persistence.valueSet()) {
            Element stateElement = d.createElement("state");
            stateElement.setAttribute("name", state);

            for (String city : persistence.mappedKeys(state)) {
                Element cityElement = d.createElement("city");
                cityElement.appendChild(d.createTextNode(city));

                stateElement.appendChild(cityElement);
            }
            //tie the frauds to the root.
            root.appendChild(stateElement);
        }
        d.normalizeDocument();
        return representation;
    }
}</pre>
<p>Compile these classes into your web application with your lib jars, and make sure the &#8220;web.xml&#8221; file is configured as shown earlier in this text.  Create a .war file or run from your IDE.  You should be able to use the URLS that have been configured into the DemoApplication class file to view data in different formats.  If your application is configured to run on http://localhost:8080&#8230;</p>
<ul>
<li>http://localhost:8080/demo &#8212; returns the Representation defined in the &#8220;anonymous inner class&#8221; of the DemoApplication.</li>
</ul>
<ul>
<li>http://localhost:8080/demo/sys   &#8212; returns the &#8220;system up&#8221; resource and it&#8217;s information, something like:</li>
</ul>
<pre style="padding-left: 30px;">hello!, the Demo Service is available. Time of request is:Tue Sep 23 17:53:42 PDT 2008</pre>
<ul>
<li>http://localhost:8080/demo/all/maps &#8212; returns a list of all the states mapped into the persistence layer:</li>
</ul>
<pre style="padding-left: 30px;">City/State values:
Austin =&gt; TX
Chicago =&gt; IL
El Paso =&gt; TX
Los Angeles =&gt; CA
Naperville =&gt; IL
Houston =&gt; TX
San Diego =&gt; CA
Santa Barbara =&gt; CA
Dallas =&gt; TX
Oak Park =&gt; IL
Joliet =&gt; IL
San Francisco =&gt; CA</pre>
<ul>
<li>http://localhost:8080/TX/maps &#8212; returns a list of cities mapped to TX:</li>
</ul>
<pre style="padding-left: 30px;">Cities located in TX:
Austin
El Paso
Houston
Dallas</pre>
<p>http://localhost:8080/demo/xml &#8212; Finally, you want to get an xml representation of all this (view source):</p>
<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;citystatemappings numberofstates="3"&gt;
    &lt;state name="CA"&gt;
        &lt;city&gt;Los Angeles&lt;/city&gt;
        &lt;city&gt;San Diego&lt;/city&gt;
        &lt;city&gt;Santa Barbara&lt;/city&gt;
        &lt;city&gt;San Francisco&lt;/city&gt;
    &lt;/state&gt;
    &lt;state name="TX"&gt;
        &lt;city&gt;Austin&lt;/city&gt;
        &lt;city&gt;El Paso&lt;/city&gt;
        &lt;city&gt;Houston&lt;/city&gt;
        &lt;city&gt;Dallas&lt;/city&gt;
    &lt;/state&gt;
    &lt;state name="IL"&gt;
        &lt;city&gt;Chicago&lt;/city&gt;
        &lt;city&gt;Naperville&lt;/city&gt;
        &lt;city&gt;Oak Park&lt;/city&gt;
        &lt;city&gt;Joliet&lt;/city&gt;
    &lt;/state&gt;
&lt;/citystatemappings&gt;</pre>
<p>From here you&#8217;re on your own.  You should be able to extrapolate the tasks that you might need for your projects;  there are other representations that you can work with, including some very interesting uses of the java.util.nio libraries that allow for blazing fast file retrieval and delivery.  You can even send Java Objects around.  It&#8217;s worth looking through the<a title="Restlet API Documentation" href="http://www.restlet.org/documentation/1.1/api/" target="_blank"> Restlet API Docs at this point</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://gurovich.com/site/2009/11/23/a-simple-restlet-demo-application/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Using Object-Oriented JavaScript to Create Multi-Use Tabs</title>
		<link>http://gurovich.com/site/2008/08/10/using-object-oriented-javascript-to-create-multi-use-tabs/</link>
		<comments>http://gurovich.com/site/2008/08/10/using-object-oriented-javascript-to-create-multi-use-tabs/#comments</comments>
		<pubDate>Sun, 10 Aug 2008 22:16:51 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[JavaScript Objects]]></category>
		<category><![CDATA[Object Oriented JavaScript]]></category>
		<category><![CDATA[Tabs in JavaScript]]></category>

		<guid isPermaLink="false">http://danilogurovich.wordpress.com/?p=79</guid>
		<description><![CDATA[Taking JavaScript More Seriously As I migrated from basic HTML to JSPs and finally complex Server-Side Java, I became less and less respectful of client-side languages, and most of my colleagues were pretty much in lockstep with me. While working in the view layer, adding JavaScript was a tedious necessity, and most programmers I worked [...]]]></description>
			<content:encoded><![CDATA[<h2>Taking JavaScript More Seriously</h2>
<p>As I migrated from basic HTML to JSPs and finally complex Server-Side Java, I became less and less respectful of client-side languages, and most of my colleagues were pretty much in lockstep with me. While working in the view layer, adding JavaScript was a tedious necessity, and most programmers I worked with built well-made, but <em>structural,</em> scripts to handle the needs of the page.  They knew what they wanted it to accomplish and they just wrote it to &#8220;do that thing&#8221;.  It &#8220;wasn&#8217;t worth&#8221; digging deep into it, because it is just &#8220;throw away&#8221; code that the view used; <em>not really worthy of Server-Side Programmers&#8217; attention</em>.<span id="more-79"></span></p>
<p>A new interest in JavaScript came onto my radar screen when, as a Principal Engineer at E*******k, I was presented with a problem that involved portal pages and their downloaded page weights. there were also many problems with any changes to the page breaking the very large (600 lines at one point) JavaScript that controlled much of the view layer. </p>
<p>So I looked into it.<!--more--></p>
<p>What I found was a gigantic script that controlled all sorts of things, looping through the Document Object Model, backwards and forwards; doing some <em>innerHTML</em> calls, and lots of &#8220;if this is happening&#8211; do this, disable this, and if anything ends in this, make it appear&#8221; kind of things.  It worked quite well, but nobody would dare change it, make changes to the basic UI or even allow it to be <em>touched</em>, because it was so complex and undocumented.  Resources were unavailble to keep up with it.</p>
<p>Being a Principal Engineer gave me the chance to take time and solve problems instead of doing hard core, heads-down coding every day.  I decided to take some time and crack this nut.  I started digging into a few articles about Object Oriented JavaScript and did a few exercises to really try and understand how it could work for me.  I discovered that with the page I was looking at, Tabs controlled a lot about what happened on it.  Depending upon which tab was clicked, different values would appear in the box underneath it &#8212; form fields would change, be disabled, etc.</p>
<p>Each Tab triggered a different &#8220;Mode&#8221; on the page.  So the Page has <em>Modes</em>&#8230; Well that would make a nice Object. The Mode has various attributes?  Now we&#8217;re getting somewhere!  Eventually this 600 line behemoth, tipping the scales at 86K, become a 64 line midget that, condensed, popped through the pipe at 16k.  Nice win.</p>
<p>What I&#8217;m demonstrating here isn&#8217;t this particular code. It <em>is</em> the basis for it, and not only that, I think its better than what I first wrote.  I&#8217;ve tested it on Mac Safari/Firefox &#8212; it should work on IE but I wasn&#8217;t able to test, so you might need to tweak.  So much for the disclaimer.</p>
<p><!--more--></p>
<h2>Creating a JavaScript Model to Manipulate Tabs on a Page</h2>
<p><em>code reference - </em><a title="Zipped content for initial tab functionality" href="http://www.gurovich.com/version_1_tabs.zip" target="_blank"><em>version_1_tabs.zip</em></a></p>
<p>Let&#8217;s start with the Initial Requirement.  Let&#8217;s say you have a requirement that orders 5 tabs that change back and forth with the least amount of scripting possible, and at some later date you might have to do more with it, such as add more tabs or add functionality to each tab.  To make it more complex, the Tab has images as background, so you need to manipulate these as well.</p>
<p>All the images will be manipulated through CSS, so we&#8217;re not going to clutter up <em>our beautiful HTML</em> markup with it.  Further, we&#8217;re going to not bother with &#8220;<em>document.write</em>&#8220;, because it can get kinda cluttered in the JavaScript code, I&#8217;m still not sure about performance hits using it, and you don&#8217;t get any Search Engine love with that, either.  Here below is the original HTML mark-up for the initial requirement (<em>tabpage.html</em>):</p>
<pre>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Object Oriented Tabs&lt;/title&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
    &lt;style type="text/css"&gt;
        @import url( "tabMode.css" );
    &lt;/style&gt;

&lt;/head&gt;
&lt;body&gt;
&lt;table class="tabToggle" cellspacing="0"&gt;
    &lt;tr&gt;
        &lt;td&gt;&lt;span class="selectstring"&gt;Select Category&lt;/span&gt;&lt;/td&gt;
        &lt;td id="dramaTab"&gt;
            &lt;div&gt;
               &lt;a class="tab_0" href="#"
                           onclick="changeMode(DRAMA_MODE);return false;"&gt;
                &lt;span&gt;Drama&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="comedyTab"&gt;
            &lt;div&gt;
               &lt;a class="tab_1" href="#"
                           onclick="changeMode(COMEDY_MODE);return false;"&gt;
                &lt;span&gt;Comedy&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="thrillerTab"&gt;
            &lt;div&gt;
               &lt;a class="tab_2" href="#"
                           onclick="changeMode(THRILLER_MODE);return false;"&gt;
                &lt;span&gt;Thriller&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="mysteryTab"&gt;
            &lt;div&gt;
               &lt;a class="tab_3" href="#"
                           onclick="changeMode(MYSTERY_MODE);return false;"&gt;
                &lt;span&gt;Mystery&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="fantasyTab"&gt;
            &lt;div&gt;
               &lt;a class="tab_4" href="#"
                           onclick="changeMode(FANTASY_MODE);return false;"&gt;
                &lt;span&gt;Fantasy&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;
    &lt;script type="text/javascript" src="tabtoggle.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>You will notice that for each href, there is an &#8220;<em>onclick</em>&#8221; value that is mapped to a <em>const</em> value (shown in the JavaScript file) in JavaScript.  For those of you that only code in IE, which doesn&#8217;t recognize <em>const</em>, it is exactly what you think.  an immutable value that is set and may not be changed.  Why is it cool?  because the browser maps it into memory, and it becomes a reference instead of a variable &#8212; <em>faster, like a static final value in Java</em>.  The other cool thing about it is that you can put any strings behind it, and if you&#8217;ve written your code properly, you only make a change in one place and can easily adapt your code to various conditions.  Easy to change, faster, and you&#8217;re not doing cut and paste everywhere.  Here is the JavaScript that manipulates the page (tabtoggle.js), with the constant values:</p>
<pre>/*------------- CONSTANTS -----------------------*/
//Mode names and values
const DRAMA_MODE = "drama";
const COMEDY_MODE = "comedy";
const THRILLER_MODE = "thriller";
const MYSTERY_MODE = "mystery";
const FANTASY_MODE = "fantasy";
//initial modes
const INIT_MODE = DRAMA_MODE;
// The array of Tabs -- establishes the order
// and values used. Pretty much the whole enchilada right here.
var TAB_ARRAY = [DRAMA_MODE,COMEDY_MODE,THRILLER_MODE,MYSTERY_MODE, FANTASY_MODE];

/*-------------MODEL--------------------------*/
/* This section could be separated from the
above as a "standard" library and used against
any set of tabs or arrays with an identical
structure.*/

//Local constants specific to the model.
const TAB_ID_SUFFIX = "Tab";
const EMPTY_STR = "";
// private Tab class values for CSS
const LAST_TAB_CLASS_VALUE = "last ";
const NON_SLCT_TAB_CLASS_VALUE = "engine";
const SLCT_TAB_CLASS_VALUE = " selected";

/**
 * The Class defining the Mode of the Object
 * @param name - The name of the Tab to that
 * will receive the selected state.
 */
function TabMode(name) {
    //public accessors
    this.name = tabModes(name);
    this.modeId = name + TAB_ID_SUFFIX;

    //change all the tabs, privately.
    function tabModes(name) {
        for (var i = 0; i &lt; TAB_ARRAY.length; i++) {
            var tid = TAB_ARRAY[i];
            var tab = new Tab(tid, (name == tid), (i == (TAB_ARRAY.length - 1)));
            document.getElementById(tab.tabId).className = tab.classVal;
        }
        return name;
    }
}

/**
 * A Tab object with needed attributes.
 * @param id -- mapped to the "id" attribute of the HTML.
 * @param selected -- boolean as to whether or not it
 * is in a "selected" state.
 * @param isLast -- boolean as to whether or not it
 * is the last tab of the series.
 */
function Tab(id, selected, isLast) {
    //accessors
    this.tabId = id + TAB_ID_SUFFIX;
    this.classVal = selectCheck();

    function selectCheck() {
        var cval = NON_SLCT_TAB_CLASS_VALUE;
        if (selected) {
            cval += SLCT_TAB_CLASS_VALUE;
        }
        if (isLast) {
            cval = LAST_TAB_CLASS_VALUE + cval;
        }
        return cval;
    }
}

/*------------- page-accessible methods -----------------------*/

//initialize mode object
var mode;

/**
 * "Public" function to change the name of the tab.
 * Accepts any tabname value mapped to the above constants.
 * @param tabname - a legal value within the TAB_ARRAY.
 */
function changeMode(tabname) {
    mode = new TabMode(tabname);
}

/**
 * Load up the initial mode.  Initializes what is
 * Shown on the page first.
 */
changeMode(INIT_MODE);</pre>
<p>Ok, it&#8217;s more than 64 lines.  Production versions of this might have the comments stripped and some compression. It currently weighs in at 4k. The constants at the top of the file are:</p>
<pre>//Mode names and values
const DRAMA_MODE = "drama";
const COMEDY_MODE = "comedy";
const THRILLER_MODE = "thriller";
const MYSTERY_MODE = "mystery";
const FANTASY_MODE = "fantasy";</pre>
<p>These are mapped to the tabs, but the tabs all have a &#8220;<em>Tab</em>&#8221; string-value appended to it in the &#8220;<em>id</em>&#8221; attribute.  We&#8217;ll use this to manipulate the view elements (usually in CSS) by the &#8220;getElementByID&#8221; method.  There are two objects that are created in the JavaScript file; one is Tab, and the other is TabMode. </p>
<h3><strong>What I&#8217;m doing here &#8212; the basics of declaring Objects in JavaScript</strong></h3>
<p>To declare an Object in JavaScript, create a function, and for best practices-sake, use an initial capital letter, similar to a Java Class Declaration.  Since we&#8217;re not dealing with a pure object-oriented language, we&#8217;ll need to declare our Object as a function:</p>
<pre>function Tab(id, selected, isLast) {</pre>
<p>and</p>
<pre>function TabMode(name) {</pre>
<p>There&#8217;s no constructor method in the class.  The function declaration contains the attributes needed to construct the Object.  <em>Tab</em> take three attributes &#8212; the id, a boolean for &#8220;selected&#8221;, and a boolean for &#8220;isLast&#8221; to delinieate whether or not it is the last Tab in the sequence.  You can use an Object quite easily, so let&#8217;s create some basic <em>FooObject</em> and demonstrate a few things.  Here&#8217;s an Object assigned to a variable:</p>
<pre><em>var foo = new FooObject("funky","fruity");</em></pre>
<p>This object instance can now be accessed by using <em>foo</em>.  What are &#8220;funky&#8221; and &#8220;fruity&#8221;?  How do we associate these to <em>FooObject</em>?</p>
<p>They have been used in the &#8220;constructor&#8221; to the Object.  The inner methods of the Object will use this information to populate accessors and can even be injected into private functions.  Let&#8217;s take a look at the actual FooObject declaration, and create some basic accessors for it:</p>
<pre>function FooObject(smell, taste){
          this.smell = smell;
          this.taste = taste;
}</pre>
<p>So, using the foo variable from the earlier listing, <em>foo.smell</em> would return &#8220;funky&#8221;, and <em>foo.taste</em> returns &#8220;fruity&#8221;.  Go ahead and try it, it&#8217;s just that easy. </p>
<h3>The Tab Object</h3>
<p>Now back to the <em>tabtoggle.js</em> code. Besides accessors, I added some private functions to return some values.  The reason I made them private as I didn&#8217;t want some <em>hackmeister</em> coming in behind me and using these functions outside of the Object, creeping everything out on my page.  Here is the &#8220;Tab&#8221; Object code snip for discussion purposes:</p>
<pre>function Tab(id, selected, isLast) {
    //accessors
    this.tabId = id + TAB_ID_SUFFIX;
    this.classVal = selectCheck();

    function selectCheck() {
        var cval = NON_SLCT_TAB_CLASS_VALUE;
        if (selected) {
            cval += SLCT_TAB_CLASS_VALUE;
        }
        if (isLast) {
            cval = LAST_TAB_CLASS_VALUE + cval;
        }
        return cval;
    }
}</pre>
<p>There are two accessors in the Tab Object, created from the three parameters and some of the &#8220;constants&#8221; that were declared outside of the class and available to the entire Document. The first accessor is the &#8220;id&#8221; used for the tab, and the second is used to build a CSS class value to alter the corresponding Tab&#8217;s view.  </p>
<p>This second accessor, &#8220;classVal&#8221;, is interesting because it is mapped to an inner function that determines its actual value.  This function could be mapped externally to the Tab, and I&#8217;ve done this before, but after some deliberation, I decided to keep it private because I didn&#8217;t want to make it available outside of the Object.</p>
<h3>The TabMode Object</h3>
<p>The TabMode Object defines the Mode of the each tab.  It takes a &#8220;name&#8221; parameter that iterates through the TAB_ARRAY (Ordered Array of all Tabs) and creates Tab Objects and sets their class value through the <em>document.getElementById() </em>method.  I &#8220;cheated&#8221; a little here by calling this method while I set the Tab instances-  mainly to ensure that the iteration was complete (it would throw errors at this point if there is a failure).  I highlighted in red the real work that&#8217;s getting done.</p>
<pre>/**
 * The Class defining the Mode of he Object
 * @param name - The name of the Tab to that
 * will receive the selected state.
 */
function TabMode(name) {
    //public accessors
    this.name = <span style="color:#ff0000;">t</span><span style="color:#ff0000;">abModes(name)</span>;
    this.modeId = name + TAB_ID_SUFFIX;

    //change all the tabs, privately.
    function tabModes(name) {
        for (var i = 0; i &lt; TAB_ARRAY.length; i++) {
            <span style="color:#ff0000;">var tid = TAB_ARRAY[i];
            var tab = new Tab(tid, (name == tid), (i == (TAB_ARRAY.length - 1)));
            document.getElementById(tab.tabId).className = tab.classVal;</span>
        }
        return name;
    }
}</pre>
<p>Everything will work now.  Go ahead and open up the HTML page and click on the  tabs.  We have a very nice and snappy set of tabs with only a little bit of code.  We can change the code in different ways to make it more useful.  In the next two sections, I&#8217;ll demonstrate how to use the basic JavaScript file that we&#8217;ve already defined against a completely different set of tabs with minimal code change, and then in the last section I&#8217;ll add more functionality to show/hide different information boxes when a particular tab is clicked.</p>
<p><!--more--></p>
<h2>Using the Same Basic Code to Populate Different Tabbed Pages</h2>
<p><em>code reference &#8211; <a title="Zipped content for extended tab functionality" href="http://www.gurovich.com/version_2_tabs.zip" target="_blank">version_2_tabs.zip</a></em></p>
<p>We&#8217;re now going to take the code that we just wrote, create another HTML page with the same functionality but completely different tabs using the same structure, only changing the &#8220;name&#8221; constants and the &#8220;array&#8221; that they are located in.</p>
<p>The only big change is to separate &#8220;Page Attributes&#8221; that define a particular page for the &#8220;Model&#8221; part of the JavaScript Code.  There is no real dependency between them, and as long as there are &#8220;names&#8221; and an &#8220;array&#8221; to traverse, the tabs will pretty much work as designed against anything you want.  This allows for any number of different tabs or tab names to be used &#8212; you just attach a new file with the constants you want to use for the page, and plug in the same JavaScript Toggle Model.  For this particular exercise, we created a file that only holds the constants that we want to use for the first &#8220;tabpage1.html&#8221;.</p>
<pre>const DRAMA_MODE = "drama";
const COMEDY_MODE = "comedy";
const THRILLER_MODE = "thriller";
const MYSTERY_MODE = "mystery";
const FANTASY_MODE = "fantasy";
//initial modes
const INIT_MODE = DRAMA_MODE;
// The array of Tabs -- establishes the order
// and values used. Pretty much the whole enchilada right here.
var TAB_ARRAY = [DRAMA_MODE,COMEDY_MODE,THRILLER_MODE,
                 MYSTERY_MODE, FANTASY_MODE];

// private Tab class values for CSS
const LAST_TAB_CLASS_VALUE = "last ";
const NON_SLCT_TAB_CLASS_VALUE = "engine";
const SLCT_TAB_CLASS_VALUE = " selected";</pre>
<p>No structural code changes were made.  I just &#8220;cut&#8221; the script from the first section in two, putting the constants that are mutable away from all of the code that doesn&#8217;t need to change.  If we want to create a different page, we&#8217;ll be able to use the exact same &#8220;model&#8221; code and only change the code in the listing above to suit our needs.  The Model part of the code is still located in the &#8220;tabtoggle.js&#8221; file. </p>
<p>Since there&#8217;s no change, I&#8217;ve truncated the listing below only to provide clarity as to where the &#8220;split&#8221; occurs.  refer to the <a title="Zipped content for extended tab functionality" href="http://www.gurovich.com/version_2_tabs.zip" target="_blank">version_2_tabs.zip</a> for more granularity.</p>
<pre>/*-------------MODEL--------------------------*/

//Local constants specific to the model.
const TAB_ID_SUFFIX = "Tab";
const EMPTY_STR = "";

function TabMode(name) {
  ... (no changes to code -- see details in zipped download)
}

function Tab(id, selected, isLast) {

... (no changes to code -- see details in zipped download)}

/*-------- page-accessible methods -----------*/

//initialize mode object
var mode;

function changeMode(tabname) {
    mode = new TabMode(tabname);
}

changeMode(INIT_MODE);</pre>
<p>Again, no changes.  We&#8217;ve left the &#8220;model&#8221; part of the code at the bottom of the page to load just before the &lt;/body&gt; tag, and the &#8220;Page Attributes&#8221; are coded to load in the &lt;head&gt; of the document.  Other than that, the HTML is unchanged in the &#8220;tabpage1.html&#8221;.  The tabs will work and fire identically as they did before.</p>
<p>But now, we get to do something cool.  We create a new HTML file, &#8220;tabpage2.html&#8221;, and put completely different tab names in them.  Not only are they different names, they are even more of them!  The underlying model code will handle them perfectly, as all it cares about is the array mapped to the constant names.  The array provides the order and a looping mechanism, and the constants provide a way to change things without making changes all over the place.</p>
<p>The new HTML code contains different cartoon shows or characters mapped to the same html pattern as &#8220;tabpage1.html&#8221;.  Here is the new table with the tabs.  Changes are in <span style="color:#ff0000;">red</span>:</p>
<pre>&lt;/head&gt;
&lt;body&gt;
&lt;table class="tabToggle" cellspacing="0"&gt;
    &lt;tr&gt;
       <span style="color:#ff0000;"><strong> &lt;td&gt;&lt;span class="selectstring"&gt;Select Category&lt;/span&gt;&lt;/td&gt;
        &lt;td id="bullwinkleTab"&gt;
            &lt;div&gt;&lt;a class="tab_0" href="#" onclick="changeMode(BULLWINKLE_MODE);return false;"&gt;
                &lt;span&gt;Bullwinkle&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="tomandjerryTab"&gt;
            &lt;div&gt;&lt;a class="tab_1" href="#" onclick="changeMode(TOM_AND_JERRY_MODE);return false;"&gt;
                &lt;span&gt;Tom and Jerry&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="simpsonsTab"&gt;
            &lt;div&gt;&lt;a class="tab_2" href="#" onclick="changeMode(SIMPSONS_MODE);return false;"&gt;
                &lt;span&gt;Simpsons&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="futuramaTab"&gt;
            &lt;div&gt;&lt;a class="tab_3" href="#" onclick="changeMode(FUTURAMA_MODE);return false;"&gt;
                &lt;span&gt;Futurama&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="bugsbunnyTab"&gt;
            &lt;div&gt;&lt;a class="tab_4" href="#" onclick="changeMode(BUGSBUNNY_MODE);return false;"&gt;
                &lt;span&gt;Bugs Bunny&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="mickeymouseTab"&gt;
            &lt;div&gt;&lt;a class="tab_5" href="#" onclick="changeMode(MICKEYMOUSE_MODE);return false;"&gt;
                &lt;span&gt;Mickey Mouse&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="barneybearTab"&gt;
            &lt;div&gt;&lt;a class="tab_6" href="#" onclick="changeMode(BARNEYBEAR_MODE);return false;"&gt;
                &lt;span&gt;Barney Bear&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
        &lt;td id="ScoobyDooTab"&gt;
            &lt;div&gt;&lt;a class="tab_7" href="#" onclick="changeMode(SCOOBYDOO_MODE);return false;"&gt;
                &lt;span&gt;Scooby Doo&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;</strong></span>
    &lt;/tr&gt;
&lt;/table&gt;
&lt;script type="text/javascript" src="tabtoggle.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Note the script reference at the bottom of the page.  It only contains the &#8220;Model&#8221; code.  The new &#8220;Page Attributes&#8221; are shown below, contained in the file tabpage2.js.  New code is in <span style="color:#ff0000;">red</span>.  Although there is some &#8220;cut and paste&#8221; code in here, it&#8217;s because we might want to change the class names of the tabs to vary the view, and this would be the best place to keep it.:</p>
<pre>/*
 * Copyright (c) 2008, Your Corporation. All Rights Reserved.
 */
/*------------- CONSTANTS -----------------------*/
//Mode names and values
<strong><span style="color:#ff0000;">const BULLWINKLE_MODE = "bullwinkle";
const TOM_AND_JERRY_MODE = "tomandjerry";
const SIMPSONS_MODE = "simpsons";
const FUTURAMA_MODE = "futurama";
const BUGSBUNNY_MODE = "bugsbunny";
const MICKEYMOUSE_MODE = "mickeymouse";
const BARNEYBEAR_MODE = "barneybear";
const SCOOBYDOO_MODE = "ScoobyDoo";</span></strong>
//initial modes
const INIT_MODE = <strong><span style="color:#ff0000;">BULLWINKLE_MODE</span></strong>;
// The array of Tabs -- establishes the order
// and values used. Pretty much the whole enchilada right here.
var TAB_ARRAY = <strong><span style="color:#ff0000;">[BULLWINKLE_MODE,
    TOM_AND_JERRY_MODE,SIMPSONS_MODE,FUTURAMA_MODE,
    BUGSBUNNY_MODE,MICKEYMOUSE_MODE,BARNEYBEAR_MODE,
    SCOOBYDOO_MODE];</span></strong>

// private Tab class values for CSS
const LAST_TAB_CLASS_VALUE = "last ";
const NON_SLCT_TAB_CLASS_VALUE = "engine";
const SLCT_TAB_CLASS_VALUE = " selected";</pre>
<p>When you load the the &#8220;tabpage2.html&#8221; in the browser, the new tabs will appear, with new names, more of them, but with exactly the same functionality. The underlying model works just as it did with the tabs on the other page, with only the inputs changed. You could put the inputs on the page or load it into a file that is picked up by the page as we did here  I believe either is fine &#8211; your decision where to put them is based upon your requirements and judgement.  No wrong answers!</p>
<h2><!--more-->Adding A Content Box Below the Tabs</h2>
<p><em>code reference &#8212; <a title="Zipped content for tabbed content and boxes" href="http://www.gurovich.com/version_3_tabs.zip" target="_blank">version_3_tabs.zip</a></em></p>
<p>Finally, we get a requirement to load a different but corresponding content box to the tabs we have created.  Let&#8217;s also say that our requirement states that only some of the tabs will have variable content boxes, other functionality may be required elsewhere.  We want to add a c<em>ontent show-hide</em> feature to the boxes, yet make sure that any tabbed page using our code doesn&#8217;t throw errors, <em>as we just hate that</em>.   The style attribute we will chose is &#8220;<em>visiblity</em>&#8220;, and we&#8217;ll use the &#8220;<em>none</em>&#8221; and &#8220;<em>inline</em>&#8221; values &#8212; since &#8220;<em>none</em>&#8221; will take the object out of formatting and not affect the flow of the page. &#8220;<em>inline</em>&#8221; will put the box in the flow. To change the style attribute trigger, we&#8217;ll use:</p>
<pre>document.getElementById("foo").style.display = ...</pre>
<p>It is necessary to loop through all of the tabs whenever the page mode is changed to make sure that the right one is &#8220;turned on&#8221; and the others are invisible.  Arriving at this functionality is actually quite easy.  We&#8217;ll just add this to the existing private function in the <em>TabMode</em> Object.</p>
<p>A new HTML page is similar to the others we&#8217;ve created, with the changes added in the listing below (changes in <span style="color:#ff0000;">red</span>, surrounded by existing code for reference).  The &#8220;outer&#8221; <em>div</em> for each box is what controls the visibility, the &#8220;inner&#8221; <em>div</em> with the <em>class</em> attribute &#8220;<em>underbox</em>&#8221; is for decorating the view, and has the <em>class</em> value mapped into the css file to control it.  Each &#8220;<em>id</em>&#8221; attribute in the &#8220;outer&#8221; box maps to a tab &#8220;<em>name</em>&#8221; attribute, the &#8220;<em>Box</em>&#8221; is the suffix for this <em>id</em> and is used within the JavaScript code, e.g. dramaBox, comedyBox, etc.</p>
<pre>       &lt;td id="fantasyTab"&gt;
            &lt;div&gt;&lt;a class="tab_4" href="#" onclick="changeMode(FANTASY_MODE);return false;"&gt;
                &lt;span&gt;Fantasy&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;
<strong><span style="color:#ff0000;">&lt;!-- Add a Box! --&gt;
&lt;div id="dramaBox"&gt;
    &lt;div class="underbox"&gt;
        &lt;h2&gt;Drama&lt;/h2&gt;
        Lorem ipsum dolor sit amet,...
        zzril delenit augue duis dolore te feugait
        nulla facilisi.
    &lt;/div&gt;
&lt;/div&gt;
&lt;div id="comedyBox"&gt;
    &lt;div class="underbox"&gt;
        &lt;h2&gt;Comedy&lt;/h2&gt;
        Darkness washed over the Dude, darker than a steer's
        tukus on a moonless night.
    &lt;/div&gt;
&lt;/div&gt;
&lt;div id="thrillerBox"&gt;
    &lt;div class="underbox"&gt;
        &lt;h2&gt;Thriller&lt;/h2&gt;
        ... So I made a decision, and it
        was... wrong. It was a bad call, Ripley. It was a bad call..
    &lt;/div&gt;
&lt;/div&gt;
&lt;div id="mysteryBox"&gt;
    &lt;div class="underbox"&gt;
        &lt;h2&gt;Mystery&lt;/h2&gt;
        Some text here
    &lt;/div&gt;
&lt;/div&gt;
&lt;div id="fantasyBox"&gt;
    &lt;div class="underbox"&gt;
        &lt;h2&gt;Fantasy&lt;/h2&gt;
        Some text here...
    &lt;/div&gt;
&lt;/div&gt;</span></strong>
&lt;script type="text/javascript" src="tabtoggle.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Here is the code for the enhanced tabs in the file<em> tabtoggle.js</em>. The only change needed is to add a few constants and a small conditional routine in the <em>TabMode</em> Object.  It will actually detect if the boxes are present to toggle their visibility, otherwise this functionality is ignored. The changed code is in <span style="color:#ff0000;">red</span>.</p>
<pre><span style="color:#ff0000;"><strong>const BOX_ID_SUFFIX = "Box";</strong></span>
//Display box values
<strong><span style="color:#ff0000;">const YES_DISPLAY = "inline";
const NO_DISPLAY = "none";</span></strong>

/**
 * The Class defining the Mode of he Object. Alters the tab Selected,
 * and also changes which box should be displayed under it, IF a box exists.
 * @param name - The name of the Tab to that
 * will receive the selected state.
 */
function TabMode(name) {
    //public accessors
    this.name = tabModes(name);
    this.modeId = name + TAB_ID_SUFFIX;

    //change all the tabs, privately.
    function tabModes(name) {
        for (var i = 0; i &lt; TAB_ARRAY.length; i++) {
            var tid = TAB_ARRAY[i];
            var tab = new Tab(tid, (name == tid), (i == (TAB_ARRAY.length - 1)));
            document.getElementById(tab.tabId).className = tab.classVal;
        }
        //This changes out the boxes, if they exist...
        <strong><span style="color:#ff0000;">if (document.getElementById(name + BOX_ID_SUFFIX) != null) {
            for (var i = 0; i &lt; TAB_ARRAY.length; i++) {
                var tid = TAB_ARRAY[i];
                if (tid == name) {
      document.getElementById(tid+ BOX_ID_SUFFIX).style.display = YES_DISPLAY;
                } else {
      document.getElementById(tid+ BOX_ID_SUFFIX).style.display = NO_DISPLAY;
                }
            }</span></strong>
        }
        return name;
    }
}</pre>
<p>The code above will now allow the choosing of each tab and the display of different boxes below.  Not only that, but when applied to our second, &#8220;cartoon tabs&#8221;, page, no errors will be thrown since there is a &#8220;check&#8221; as to whether the boxes exist first.  If you want to add boxes to the other page, you just map them to their tab names and use the &#8220;Box&#8221; instead of &#8220;Tab&#8221; suffix!</p>
<h2>Conclusions:</h2>
<p>Using the JavaScript Prototype element can extend the above code in even more directions.  Want to have a form connected to each tab?  Do they have different elements?  You can enable and disable them by extending the above classes to do even more.  A &#8220;TabbedForm&#8221; class could contain the form elements and keep track of what to enable and disable, different submission urls or even more.  You don&#8217;t have to alter the base class, you just extend it using the Prototype Element.</p>
<p>One of the truly wonderful things about JavaScript is the fact that there are many ways to the same result.  What I find interesting is how people can come up with ideas to make their scripts smaller, more re-usable and extendable.  Creating Objects in JavaScript allow for power that many mark-up engineers don&#8217;t know about to this day;  a lot of server-side programmers, myself included, begin to take this client side language seriously and realize the power, and fun, of writing this type of code.</p>
]]></content:encoded>
			<wfw:commentRss>http://gurovich.com/site/2008/08/10/using-object-oriented-javascript-to-create-multi-use-tabs/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Using a prototype to extend your JavaScript methods.</title>
		<link>http://gurovich.com/site/2008/03/27/using-a-prototype-to-extend-your-javascript-methods/</link>
		<comments>http://gurovich.com/site/2008/03/27/using-a-prototype-to-extend-your-javascript-methods/#comments</comments>
		<pubDate>Thu, 27 Mar 2008 18:31:40 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[display]]></category>
		<category><![CDATA[extending Javascript]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[javascript methods]]></category>
		<category><![CDATA[prototype]]></category>
		<category><![CDATA[style]]></category>

		<guid isPermaLink="false">http://danilogurovich.wordpress.com/?p=43</guid>
		<description><![CDATA[The Problem: Calling a function many times throughout a particular JavaScript file or block. JavaScript is often used to manipulate a variety of HTML objects on a page, i.e. to &#8220;show&#8221; or &#8220;hide&#8221; them, or perform some type of toggled functionality on a variety of buttons, divs, etc. It is not uncommon to see code [...]]]></description>
			<content:encoded><![CDATA[<h2><span style="font-style:italic;font-family:Times;"><span style="font-weight:normal;">The Problem:  Calling a function many times throughout a particular JavaScript file or block.</span></span></h2>
<div>JavaScript is often used to manipulate a variety of HTML objects on a page, i.e. to &#8220;show&#8221; or &#8220;hide&#8221; them, or perform some type of toggled functionality on a variety of buttons, divs, etc.  It is not uncommon to see code like this littered in a variety of places throughout a typical large Script block:</div>
<div><code>if(foo == bar){</code></div>
<div><code>document.getElementById("blah").style.visibility="block";</code></div>
<div><code>document.getElementById("blue).style.visibility="none";</code></div>
<div><code>document.getElementById("red").style.visibility="block";</code></div>
<div><code>document.getElementById("black").style.visibility="none";</code></div>
<div><code>}</code></div>
<div>Implementing the above in numerous places throughout said block of Script with a couple of hundred lines can eat up a lot of space, <span style="font-style:italic;">be harder than all get out to maintain</span>, and if the guy that wrote it gets hit by a bus, you can often watch the next engineer in line cry for days in front of their screen just trying to figure out what the original coder was thinking when they wrote it.</div>
<p><span id="more-57"></span></p>
<div>This usually happens when your team begins to look at performance and realizes that 80% of what happens to the user is on the Client-side.  A typical scenario involves JavaScript files that have been developed and built over time with very little attention paid to maintainability or even long life.</div>
<div>While standard libraries are solving many of these problems, they may be too bulky, or in most cases, invoking them would involve a total rewrite of the scripts and pages involved.  <span style="font-weight:bold;">As a Best Practice</span>, it&#8217;s probably better to &#8220;evolve&#8221; your way to more standards-based solutions; creating your own methods is a good way to simplify what you currently have and allows your team to evaluate how far you <span style="font-style:italic;">really</span> need to go with any changes.</div>
<div>Cleaning up what is currently there is the first step.   Wouldn&#8217;t it be great that once you had a &#8220;handle to an element&#8221;, you could just write &#8220;changeVisiblilty(&#8220;block&#8221;) instead of having to iterate out &#8220;<span style="font-style:italic;">document.Element.style.display=&#8217;block&#8217;;</span> &#8221; time and time again?</div>
<div>Of course with the example code snip at the top, you could create arrays and iterate through them based upon their common modes &#8212; possibly create different versions of JavaScript objects and vary their values that way, but somewhere along the line the &#8220;<span style="font-style:italic;">document.Element.style.visiblity= &#8216;block&#8217;;</span> &#8221; gets written out, usually quite a bit.</div>
<div><span style="font-family:Times;font-size:24px;font-style:italic;">Using the &#8220;prototype&#8221; keyword to extend functionality of JavaScript objects.</span></div>
<div>Prototype (not to be confused with the &#8220;Prototype Library&#8221;) was introduced to JavaScript beginning with the 1.1 release.  It is a &#8220;prebuilt&#8221; object, and it is used to extend existing Objects with custom properties and methods, similar to extending Classes in Java.  It is probably one of the most powerful objects that you can work with when refactoring existing legacy code into more manageable chunks.</div>
<div>The prototype keyword will allow the addition of custom properties and methods to all the existing objects of a particular JavaScript Object (String, Array, Element or your own Custom class).  To implement this, just append &#8220;.prototype.&#8221; to the Object that you wish to extend, then name the method/function that you&#8217;ll be making available to all of these objects:</div>
<div><code> /* Extends Element functionality by switching the display to an inserted variable */</code></div>
<div><code>Element.prototype.changeStyleDisplay = function(strVal) {</code></div>
<div><code> this.style.display = strVal;</code></div>
<div><code>}</code></div>
<p>Now you have a new method &#8220;changeStyleDisplay(&#8216;value&#8217;)&#8221; available to you on the Element object only.  It can be used thus:</p>
<div><code>document.getElementById("foo").changeStyleDisiplay("none");</code></div>
<p>Which changes the &#8220;display&#8221; attribute value to &#8220;none&#8221;. This <a title="Doc containing HTML file with JavaScript prototype implementation." href="http://danilogurovich.files.wordpress.com/2008/03/basicprototype.doc">Doc containing HTML file with JavaScript prototype implementation, </a>contains a test that I created to toggle different visibility modes with two different method extensions of the Element Object.  Feel free to copy, hack, change, use, and enjoy.</p>
<p><em>(note that is probably doesn&#8217;t work in IE &#8212; Will be testing to find out if newer versions will work.  As Usual, MS just goes their own way&#8230;.)</em></p>
]]></content:encoded>
			<wfw:commentRss>http://gurovich.com/site/2008/03/27/using-a-prototype-to-extend-your-javascript-methods/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ruby on Rails:  Create a Select Drop Down from an Array</title>
		<link>http://gurovich.com/site/2008/01/13/ruby-on-rails-create-a-select-drop-down-from-an-array/</link>
		<comments>http://gurovich.com/site/2008/01/13/ruby-on-rails-create-a-select-drop-down-from-an-array/#comments</comments>
		<pubDate>Mon, 14 Jan 2008 02:49:14 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[array]]></category>
		<category><![CDATA[array object]]></category>
		<category><![CDATA[collection_select]]></category>
		<category><![CDATA[drop down]]></category>
		<category><![CDATA[Ruby LabelValue]]></category>
		<category><![CDATA[Ruby LabelValueBean.]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[select box]]></category>

		<guid isPermaLink="false">http://danilogurovich.wordpress.com/2008/01/13/ruby-on-rails-create-a-select-drop-down-from-an-array/</guid>
		<description><![CDATA[Problem: You wish to populate a &#8220;collection_select&#8221; box with an Array. I recently had an issue where I wanted to create a select box populated from an array of continuous years. I wanted to use thecollection_select method so it would have a minimal &#8220;code footprint&#8221; and play nice with the form that I had built. [...]]]></description>
			<content:encoded><![CDATA[<div style="font-size:18px;font-style:italic;"> 			Problem: You wish to populate a &#8220;collection_select&#8221; box with an Array.</div>
<p>I recently had an issue where I wanted to create a select box populated from an array of continuous years. I wanted to use the<code>collection_select</code> method so it would have a minimal &#8220;code footprint&#8221; and play nice with the form that I had built. The application that I&#8217;m working on is in Rails2 and I didn&#8217;t want to divert heavily or make to many changes, and after some fiddling I came up with a nice solution: In the <code>environment.rb</code> file I created an Array constant to contain the year range that I wanted to use:</p>
<div style="font-size:10px;font-family:lucida,courier,monospace;">     #Constant Values<br />
YEARS_ARRAY = Array.new(89) {|i| 1920+i}</div>
<p>This code is a shortcut to create an array of values between 1920 and 2008 (88 years). It creates a new array with a size of 88, and then uses the <code>i</code> value to populate each year incrementally. I was pleased to find a method like this!</p>
<p>The form declaration contained the object that I wanted to populate with the form values using the Rails2 syntax:</p>
<div style="font-size:10px;font-family:lucida,courier,monospace;">    &lt;table class=&#8221;new&#8221;&gt;<br />
&lt;% form_for(@movie_poster) do |f| %&gt;<br />
&nbsp;&lt;tr&gt;<br />
&lt;th&gt;Title:&lt;/th&gt;<br />
&nbsp;&nbsp;&lt;td&gt;&lt;%= f.text_field :title %&gt;&lt;/td&gt;<br />
&nbsp;&lt;/tr&gt;<br />
&#8230;..<br />
&lt;% end %&gt;<br />
&lt;/table&gt;</div>
<p><span style="font-size:10px;font-style:italic;">*note the newer Rails2 syntax for the form&#8230;</span></p>
<p>I will populate my select box with an Array of Objects. I need to create my Object first to populate my Labels and Values, and I used an &#8220;old trick&#8221; from Struts by creating a Ruby <code>LabelValue</code> object that mimics Struts <code>LabelValueBean</code> utility POJO. The object has a label and value <code>accessor</code>, which works extremely well for any set of arrays since any kind of mapping scheme would consume duplicate keys and often select boxes, radio buttons, etc may need some duplication. Here is my <code>label_value.rb</code> class, located in my model directory:</p>
<div style="font-size:10px;font-family:lucida,courier,monospace;"># Creates a Label and value for select boxes and<br />
# forms without clearly delinieated# objects, such as Arrays.<br />
class LabelValue<br />
# name the accessors. Label and Value<br />
attr_accessor :label, :value<br />
end</div>
<p>Now that I have a model to work with and an Array Constant to use, I created the &#8220;helper&#8221; to use with the form tag in the <code>application_helper.rb</code> file, so I might use it throughout the application&#8217;s forms. There are two methods, one public, and one private. Note the &#8220;Select year&#8221; prompt:</p>
<div style="font-size:10px;font-family:lucida,courier,monospace;"># selection for a year value<br />
# &#8216;f&#8217; represents the passed in form value<br />
def year_select(f)<br />
f.collection_select(:year,year_lookup,:label,:value,{:prompt=&gt;&#8221;Select year&#8221;})<br />
end<br />
#  &#8212;&#8212;&#8212;&#8212; PRIVATE METHODS &#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>private</p>
<p>def year_lookup<br />
#create  an emptycollection to hold the LabelValue Objects<br />
years = Array.new()#populate the ArrayYEARS_ARRAY.each do |yr| y = LabelValue.new<br />
y.label = yr<br />
y.value = yr<br />
years.push(y)<br />
end<br />
years<br />
end</p></div>
<p>Finally, we need to call the _helper method from the form:</p>
<div style="font-size:10px;font-family:lucida,courier,monospace;">&lt;tr&gt;<br />
&lt;th&gt;Grade:&lt;/th&gt;<br />
&lt;td&gt;&lt;%= grade_select(f) %&gt;&lt;/td&gt;<br />
&lt;/tr&gt;</div>
<p>The resulting code renders a select box:</p>
<div style="font-size:10px;font-family:lucida,courier,monospace;">      &lt;select id=&#8221;movie_poster_year&#8221; name=&#8221;movie_poster[year]&#8220;&gt;<br />
&lt;option value=&#8221;"&gt;Select year&lt;/option&gt;<br />
&lt;option value=&#8221;1920&#8243;&gt;1920&lt;/option&gt;<br />
&lt;option value=&#8221;1921&#8243;&gt;1921&lt;/option&gt;<br />
&lt;option value=&#8221;1922&#8243;&gt;1922&lt;/option&gt;<br />
&lt;option value=&#8221;1923&#8243;&gt;1923&lt;/option&gt;<br />
&lt;option value=&#8221;1924&#8243;&gt;1924&lt;/option&gt;</p>
<p># continues&#8230;</p>
<p>&lt;option value=&#8221;2005&#8243;&gt;2005&lt;/option&gt;<br />
&lt;option value=&#8221;2006&#8243;&gt;2006&lt;/option&gt;<br />
&lt;option value=&#8221;2007&#8243;&gt;2007&lt;/option&gt;<br />
&lt;option value=&#8221;2007&#8243;&gt;2007&lt;/option&gt;<br />
&lt;/select&gt;</p></div>
<p>The nice thing is reusability of the <code>LabelValue</code> class, the <code>YEAR</code> Constant and the <code>collection_select</code> itelf. Everything can be accessed as it is needed, whether you want to use the Constant Array for something else, the <code>LabelValue</code> object for a different array or tuple, or the actual rendered select box in different views.</p>
<h5> 			UPDATE, 1/15:</h5>
<p><i>See the comments below for a nice alternative and discussion!</i></p>
]]></content:encoded>
			<wfw:commentRss>http://gurovich.com/site/2008/01/13/ruby-on-rails-create-a-select-drop-down-from-an-array/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Struts vs. Cocoon:  Why Cocoon lost the battle.</title>
		<link>http://gurovich.com/site/2007/11/09/struts-vs-cocoon-why-cocoon-lost-the-battle/</link>
		<comments>http://gurovich.com/site/2007/11/09/struts-vs-cocoon-why-cocoon-lost-the-battle/#comments</comments>
		<pubDate>Fri, 09 Nov 2007 20:30:47 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[Cocoon]]></category>
		<category><![CDATA[Cocoon vs Struts]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Struts]]></category>
		<category><![CDATA[Struts2]]></category>

		<guid isPermaLink="false">http://danilogurovich.wordpress.com/2007/11/09/struts-vs-cocoon-why-cocoon-lost-the-battle/</guid>
		<description><![CDATA[Cocoon has been around for years with an active development community. I tried to work with it in 2002 using the books and resources available at the time, but I was using Windows at that point and the books really focused more on a Unix/Linux deployment, and at the time I didn&#8217;t have the chops [...]]]></description>
			<content:encoded><![CDATA[<p>Cocoon has been around for years with an active development community.  I tried to work with it in 2002 using the books and resources available at the time, but  I was using Windows at that point and the books really focused more on a Unix/Linux deployment, and at the time I didn&#8217;t have the chops to really transfer this knowledge cleanly and was really never able to set up a coherent development environment for it.  Struts, on the other hand, had the resources available to get up and running.  Although the documentation wasn&#8217;t that great, I could &#8220;get home&#8221; on most issues, and when the level of frustration on completing some of the things that I needed to get done was at a boiling point, I did something about it and wrote a book with George Franciscus to share what I had learned with the larger community.  I wasn&#8217;t alone with this either; many great books on the subject came out.  As the framework diversified and solved many problems, so did the available web and print documentation from many wonderful people.</p>
<p>Step forward to today.  Struts 1 is ubiquitous; nearly every Java Web Application Programmer has had some experience with Struts, building upon the base and working to create other Frameworks as well extend Struts1 into Struts2. Lately, I&#8217;ve been working with projects that consume a great deal of XML services and need to be available to many different viewing platforms in a coherent manner.  Not only that, but there is a tremendous amount of conditional logic that must be matched depending upon input.  Cocoon handles these things very well &#8212; it&#8217;s dispatch system allows for various URL matching which gives it&#8217;s configurations a very similar feel to a lightweight Business Rule engine.  I decided to give it another shot and go through the tutorials and documentation &#8212; trying to evaluate whether or not it could fit into the way that my company does business.</p>
<p>I proceeded to find tutorials on the Cocoon Site, DeveloperWorks and other places.  What I found was at best adequate, but at worst quite disappointing.   The Cocoon Community insists that you download their source code and use their scripts to<span id="more-40"></span>build it.  Further, they don&#8217;t publish ANYWHERE what or how a file structure should be, and the various examples I found showed them to have no &#8220;best practices&#8221; within the community, let alone publish what these are.  Further, all of the examples I found used Eclipse as the IDE and gave no information as to what to do in case you happen to use another favorite platform, say, IntelliJ.  The best example I found used Maven/Jetty/Eclipse.  It incorporated Spring and the latest build of Cocoon extremely well.  Unfortunately if you&#8217;re not a Maven/Jetty/Eclipse house, it leaves no idea as to how to build your own system (YES, I DID use mvn idea:idea, still didn&#8217;t get what I wanted, in fact the mvn war:exploded didn&#8217;t help either).</p>
<p>I looked around for examples of how to build Cocoon from a root folder.  I want to build it from the ground up, placing files, etc where they should be &#8220;in my opinion&#8221; so that I can integrate them with the way we do things around here (we still use Ant, for instance, and have our own way of creating projects for Idea that works for integrating builds with our platforms).  Nothing exists, nowhere.  The Cocoon mailing list goes so far as to say that they specifically WONT do this because they want people to get a deep dive into the entire project and all the available blocks.  You can build a basic app from source at the Cocoon Site, but the war tasks to me were inadequate and I didn&#8217;t see the cool Spring Integration that the one really useful example offered.  10 days go by and still I&#8217;ve really never had a good debugger going in my IntelliJ with Cocoon, and the real, under-the-hood stuff that I seek just isn&#8217;t forthcoming.</p>
<p>I&#8217;ve even seen talk on the Cocoon Mailing lists that they plan to abandon Java and build the whole thing in Perl.  Super, although I don&#8217;t know if this is real or just a few persons blowing off some steam.</p>
<p>Contrast this with Struts and Struts2.  There are very clear road maps for both.  Although I don&#8217;t agree with a lot of the integrations (<em>especially JSF!!</em>), Struts is, and continues to be, very easy to pick up, understand, extend and integrate with existing processes, software and builds.  Although the Struts2 tutorials at this point are limited, and the number of books on Struts2 can be counted by a five-year-old, everything looks to be good going forward.  I admire the WebWork integration and the way that the configurations have been cleaned up.  I will definitely keep it in my Toolkit.</p>
<p>What do I think is better?  Well, for it&#8217;s ability to match URLS, pass parameters, allow for non-reboot redeployments and presentation-agnostic capabilities, I would really feel that Cocoon, in a purist sense, is a superior platform.  I can easily see complex integrations with A/B testing, Rule Engines and various view platforms working much easier in the Cocoon environment over Struts.  I also believe that Cocoon offers some superior capabilities in the consumption of Web Services and other XML sources.  I believe that REST is a better proposition with Cocoon.  But I can&#8217;t recommend it for use in an ongoing Enterprise Environment because:</p>
<ul>
<li>There is too little documentation, and this documentation shrinks with newer releases.</li>
<li>The Cocoon community is too fragmented and insulated.</li>
<li>The Documentation is all over the place.</li>
<li>There are no published &#8220;Best Practices&#8221;.</li>
<li>It doesn&#8217;t easily integrate with disparate IDEs</li>
<li>Nobody seems to want to share information to outsiders.</li>
</ul>
<p>The Struts Community seems to be the opposite; Books are in several languages, plugins for the framework ship with most IDEs out of the box, and the community shares lovingly with anyone that wants to learn it.  There is great documentation and many, many different answers for the same questions, allowing development organizations to pick and choose what&#8217;s right for them.</p>
<p>Cocoon is insular.  There are many different plug-in &#8220;blocks&#8221; with solutions for problems, but the basics just aren&#8217;t covered well, and the &#8220;you must do it this way with these tools&#8221; attitude doesn&#8217;t play nice with organizations looking at their framework for long-term use.  Resourcing is a big deal, and when I saw that my book had been published in India, I knew that Struts had won hands down over Cocoon, which really is, in my opinion, a better, but less organization-friendly, platform.</p>
<p>If I can finally figure out how to build Cocoon with Spring from a basic root folder without all the baggage that the Cocoon Developers&#8217; Group insists on everyone using, I&#8217;ll share it because it might be worthwhile. If you don&#8217;t see it here after awhile, you know that I&#8217;ve moved on and found further investigation a waste of my time.</p>
]]></content:encoded>
			<wfw:commentRss>http://gurovich.com/site/2007/11/09/struts-vs-cocoon-why-cocoon-lost-the-battle/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Creating a Java TesterTimer for Performance Testing.</title>
		<link>http://gurovich.com/site/2007/09/06/creating-a-java-testertimer-for-performance-testing/</link>
		<comments>http://gurovich.com/site/2007/09/06/creating-a-java-testertimer-for-performance-testing/#comments</comments>
		<pubDate>Fri, 07 Sep 2007 01:36:30 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://danilogurovich.wordpress.com/2007/09/06/creating-a-java-testertimer-for-performance-testing/</guid>
		<description><![CDATA[I often find myself writing lightweight performance tests as part of a suite of the unit tests, to make sure that my code hasn&#8217;t been written in a way to destroy the performance of an application. I also create performance tests as a standalone Application to test (for instance) how many HttpServletRequest/Response calls can be [...]]]></description>
			<content:encoded><![CDATA[<p>I often find myself writing lightweight performance tests as part of a suite of the unit tests, to make sure that my code hasn&#8217;t been written in a way to destroy the performance of an application.  I also create performance tests as a standalone Application to test (for instance) how many HttpServletRequest/Response calls can be made over a certain period of time.</p>
<p>My requirements are normally simple, and rarely have a strong need to be COMPLETELY perfect; i.e. the actual calls to and from the timer can cause a slight time loss in the actual application, but frankly I&#8217;m willing to put up with this in most circumstances.  The &#8220;TesterTimer&#8221; class featured below is pretty damned efficient, especially if you never call the &#8220;elapsed(id)&#8221; method, since the start and stop happen pretty much outside the application being tested.   Here is the fully commented code with a demonstration &#8220;main&#8221; class:</p>
<p><span id="more-37"></span>import java.util.Hashtable;</p>
<p>/**<br />
* TestTimer Class, with methods to calculate the time to run<br />
* particular tests.  Multiple events can be mapped at once<br />
* to a hashtable. Each event is retrieved by the id passed in<br />
* to each available static method.<br />
*<br />
* @author dan@gurovich.com<br />
* @version 1.0<br />
* @since Sep 6, 2007<br />
*/<br />
public class TesterTimer {</p>
<p>//~ Static fields/initializers ***********************************</p>
<p>/**<br />
* Hashtable holding various timer events, mapped to ids.<br />
*/<br />
private static final Hashtable startTime = new Hashtable()</p>
<p>//~ Methods ******************************************************</p>
<p>/**<br />
* Stops the Timer, and removes the time.<br />
* Returns the elapsed time.<br />
*<br />
* @param id &#8212; The id of the timer.<br />
*<br />
* @return long value of the elapsed time in milliseconds.<br />
*/<br />
public static long elapsed(String id) {|<br />
return System.currentTimeMillis()<br />
- ((Long) startTime.get(id)).longValue();<br />
}<br />
/**<br />
* Returns a formatted Elapsed time string.<br />
*<br />
* @param time &#8212; long value passed in,<br />
* presumed to be milliseconds.<br />
*<br />
* @return String formatted similar to &#8221; 2h :1m :53s :443ms &#8220;.<br />
*/<br />
public static String returnFormattedTime(long time) {</p>
<p>//the seconds<br />
long seconds = time / 1000;</p>
<p>//the hours<br />
long hours = seconds / 3600;</p>
<p>//seconds adjusted after hours<br />
seconds = seconds % 3600;</p>
<p>//the minutes<br />
long minutes = seconds / 60;</p>
<p>//seconds adjusted after minutes<br />
seconds = seconds % 60;</p>
<p>//milliseconds left over.<br />
long millis = time % 1000;</p>
<p>// Print out the results<br />
return new StringBuffer().append(hours).append(&#8220;h :&#8221;)<br />
.append(minutes).append(&#8220;m :&#8221;).append(<br />
seconds).append(&#8220;s :&#8221;).append(millis).append(&#8220;ms&#8221;)<br />
.toString();<br />
}</p>
<p>/**<br />
* Starts the timer for a particular id, adds it to a Hashtable.<br />
*<br />
* @param id &#8212; the id of the timer for the Hashtable.<br />
*/<br />
public static void start(String id) {<br />
startTime.put(id, new Long(System.currentTimeMillis()));<br />
}<br />
/**<br />
* Stops the Timer, and removes the time.  Returns the elapsed time.<br />
*<br />
* @param id &#8212; The id of the timer.<br />
*<br />
* @return long value of the elapsed time in milliseconds.<br />
*/<br />
public static long stop(String id) {<br />
return System.currentTimeMillis()<br />
- ((Long) startTime.remove(id)).longValue();<br />
}<br />
/**<br />
* Main method to do some math and take up some time.<br />
*<br />
* @param args &#8212; no args digested.<br />
*/<br />
public static void main(String[] args) {<br />
TesterTimer.start(&#8220;hello&#8221;);</p>
<p>for (int h = 0; h &lt; 20; h++) {<br />
for (int i = 0; i &lt; 32000000; i++) {<br />
int j = i / 25;<br />
}</p>
<p>if ((h % 4) == 0) {<br />
System.out.println(<br />
TesterTimer.returnFormattedTime(<br />
TesterTimer.elapsed(&#8220;hello&#8221;)));<br />
}<br />
h++;<br />
}</p>
<p>long t = TesterTimer.stop(&#8220;hello&#8221;);<br />
System.out.println(&#8220;Timer: &#8221; + t);<br />
System.out.println(&#8220;FMT Timer: &#8221; + TesterTimer.returnFormattedTime(t))<br />
}<br />
}</p>
<p>* TestTimer Class, with methods to calculate the time to run<br />
* particular tests.  Multiple events can be mapped at once<br />
* to a hashtable. Each event is retrieved by the id passed in<br />
* to each available static method.<br />
*<br />
* @author dan@gurovich.com<br />
* @version 1.0<br />
* @since Sep 6, 2007<br />
*/<br />
public class TesterTimer {<br />
//~ Static fields/initializers ***********************************</p>
<p>/**<br />
* Hashtable holding various timer events, mapped to ids.<br />
*/<br />
private static final Hashtable startTime = new Hashtable();</p>
<p>//~ Methods ******************************************************</p>
<p>/**<br />
* Stops the Timer, and removes the time.<br />
* Returns the elapsed time.<br />
*<br />
* @param id &#8212; The id of the timer.<br />
*<br />
* @return long value of the elapsed time in milliseconds.<br />
*/<br />
public static long elapsed(String id) {<br />
return System.currentTimeMillis()<br />
- ((Long) startTime.get(id)).longValue();<br />
}</p>
<p>/**<br />
* Returns a formatted Elapsed time string.<br />
*<br />
* @param time &#8212; long value passed in,<br />
* presumed to be milliseconds.<br />
*<br />
* @return String formatted similar to &#8221; 2h :1m :53s :443ms &#8220;.<br />
*/<br />
public static String returnFormattedTime(long time) {<br />
//the seconds<br />
long seconds = time / 1000;<br />
//the hours<br />
long hours = seconds / 3600;<br />
//seconds adjusted after hours<br />
seconds = seconds % 3600;<br />
//the minutes<br />
long minutes = seconds / 60;<br />
//seconds adjusted after minutes<br />
seconds = seconds % 60;<br />
//milliseconds left over.<br />
long millis = time % 1000;</p>
<p>// Print out the results<br />
return new StringBuffer().append(hours).append(&#8220;h :&#8221;)<br />
.append(minutes).append(&#8220;m :&#8221;).append(<br />
seconds).append(&#8220;s :&#8221;).append(millis).append(&#8220;ms&#8221;)<br />
.toString();<br />
}</p>
<p>/**<br />
* Starts the timer for a particular id, adds it to a Hashtable.<br />
*<br />
* @param id &#8212; the id of the timer for the Hashtable.<br />
*/<br />
public static void start(String id) {<br />
startTime.put(id, new Long(System.currentTimeMillis()));<br />
}</p>
<p>/**<br />
* Stops the Timer, and removes the time.<br />
* Returns the elapsed time.<br />
*<br />
* @param id &#8212; The id of the timer.<br />
*<br />
* @return long value of the elapsed time in milliseconds.<br />
*/<br />
public static long stop(String id) {<br />
return System.currentTimeMillis()<br />
- ((Long) startTime.remove(id)).longValue();<br />
}</p>
<p>/**<br />
* Main method to do some math and take up some time.<br />
*<br />
* @param args &#8212; no args digested.<br />
*/<br />
public static void main(String[] args) {<br />
TesterTimer.start(&#8220;hello&#8221;);</p>
<p>for (int h = 0; h &lt; 20; h++) {<br />
for (int i = 0; i &lt; 32000000; i++) {<br />
int j = i / 25;<br />
}<br />
int foo = h % 4;<br />
if (foo == 0) {<br />
System.out.println(<br />
TesterTimer.returnFormattedTime(<br />
TesterTimer.elapsed(&#8220;hello&#8221;)));<br />
}<br />
h++;<br />
}<br />
long t = TesterTimer.stop(&#8220;hello&#8221;);<br />
System.out.println(&#8220;Timer: &#8221; + t);<br />
System.out.println(<br />
&#8220;FMT Timer: &#8221; + TesterTimer.returnFormattedTime(t));<br />
}<br />
}</p>
<p>The timer has three static working methods &#8211; start(id), elapsed(id), and stop(id).  The start(id) method is void and starts the timer for a particular event mapped to an id.  The id is a key within a Hashtable that records a starting time.  When elapsed(id) is called, the value returned is a long primitive which is the start time subtracted from the current time, returning a milliseconds value.  With elapsed(id) the key isn&#8217;t removed so one may keep the timer running.  stop() returns the same value as elapsed(id), but stop(id) removes the key value mapped to the id, thereby &#8220;stopping&#8221; the timer completely.</p>
]]></content:encoded>
			<wfw:commentRss>http://gurovich.com/site/2007/09/06/creating-a-java-testertimer-for-performance-testing/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Adding a DRL FileType with Syntax Highlighting to IntelliJ</title>
		<link>http://gurovich.com/site/2007/05/31/adding-a-drl-filetype-with-syntax-highlighting-to-intellij/</link>
		<comments>http://gurovich.com/site/2007/05/31/adding-a-drl-filetype-with-syntax-highlighting-to-intellij/#comments</comments>
		<pubDate>Thu, 31 May 2007 18:31:00 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://danilogurovich.wordpress.com/2007/05/31/adding-a-drl-filetype-with-syntax-highlighting-to-intellij/</guid>
		<description><![CDATA[IntelliJ&#8217;s support of JBoss-rules in no way compares to Eclipse, but even that isn&#8217;t enough to get me off my butt and move from my fave. That said, I created a semi-literate FileType for the drl files so that IntelliJ will recognize them and you will get a decent amount of syntax highlighting. Is it [...]]]></description>
			<content:encoded><![CDATA[<p>IntelliJ&#8217;s support of JBoss-rules in no way compares to Eclipse, but even that isn&#8217;t enough to get me off my butt and move from my fave.  That said, I created a semi-literate FileType for the drl files so that IntelliJ will recognize them and you will get a decent amount of syntax highlighting.  Is it perfect?</p>
<p>No.</p>
<p>But it will probably get you home.  Here&#8217;s what you do. Create a new filetype and make sure that it includes ?*.drl files.  Once that is created, it makes a filetype with the name you gave it in the</p>
<p align="center"><em>%User%/Library/Preferences/IntellijIDEA%your-release-version-here%/filetypes/%name_of_file.xml%</em></p>
<p>directory (at least that&#8217;s what it looks like on my Mac &#8211; I haven&#8217;t touched a windows box in years, but it should have some parallels.</p>
<p>Open the XML file and replace anything between the &lt;filetype&gt; nodes with:</p>
<p><span id="more-27"></span></p>
<blockquote><p>&lt;highlighting&gt;<br />
&lt;options&gt;<br />
&lt;option name=&#8221;LINE_COMMENT&#8221; value=&#8221;#&#8221; /&gt;<br />
&lt;option name=&#8221;COMMENT_START&#8221; value=&#8221;/*&#8221; /&gt;<br />
&lt;option name=&#8221;COMMENT_END&#8221; value=&#8221;*/&#8221; /&gt;<br />
&lt;option name=&#8221;HEX_PREFIX&#8221; value=&#8221;" /&gt;<br />
&lt;option name=&#8221;NUM_POSTFIXES&#8221; value=&#8221;" /&gt;<br />
&lt;option name=&#8221;HAS_BRACES&#8221; value=&#8221;true&#8221; /&gt;<br />
&lt;option name=&#8221;HAS_BRACKETS&#8221; value=&#8221;true&#8221; /&gt;<br />
&lt;option name=&#8221;HAS_PARENS&#8221; value=&#8221;true&#8221; /&gt;<br />
&lt;/options&gt;<br />
&lt;keywords ignore_case=&#8221;false&#8221;&gt;<br />
&lt;keyword name=&#8221;agenda-group&#8221; /&gt;<br />
&lt;keyword name=&#8221;assert&#8221; /&gt;<br />
&lt;keyword name=&#8221;auto-focus&#8221; /&gt;<br />
&lt;keyword name=&#8221;duration&#8221; /&gt;<br />
&lt;keyword name=&#8221;eval&#8221; /&gt;<br />
&lt;keyword name=&#8221;function&#8221; /&gt;<br />
&lt;keyword name=&#8221;import&#8221; /&gt;<br />
&lt;keyword name=&#8221;modify&#8221; /&gt;<br />
&lt;keyword name=&#8221;new&#8221; /&gt;<br />
&lt;keyword name=&#8221;no-loop&#8221; /&gt;<br />
&lt;keyword name=&#8221;package&#8221; /&gt;<br />
&lt;keyword name=&#8221;query&#8221; /&gt;<br />
&lt;keyword name=&#8221;retract&#8221; /&gt;<br />
&lt;keyword name=&#8221;return&#8221; /&gt;<br />
&lt;keyword name=&#8221;rule&#8221; /&gt;<br />
&lt;keyword name=&#8221;salience&#8221; /&gt;<br />
&lt;/keywords&gt;<br />
&lt;keywords2&gt;<br />
&lt;keyword name=&#8221;end&#8221; /&gt;<br />
&lt;keyword name=&#8221;exists&#8221; /&gt;<br />
&lt;keyword name=&#8221;false&#8221; /&gt;<br />
&lt;keyword name=&#8221;not&#8221; /&gt;<br />
&lt;keyword name=&#8221;or&#8221; /&gt;<br />
&lt;keyword name=&#8221;then&#8221; /&gt;<br />
&lt;keyword name=&#8221;true&#8221; /&gt;<br />
&lt;keyword name=&#8221;when&#8221; /&gt;<br />
&lt;/keywords2&gt;<br />
&lt;keywords3&gt;<br />
&lt;keyword name=&#8221;!=&#8221; /&gt;<br />
&lt;keyword name=&#8221;*&#8221; /&gt;<br />
&lt;keyword name=&#8221;-&gt;&#8221; /&gt;<br />
&lt;keyword name=&#8221;:&#8221; /&gt;<br />
&lt;keyword name=&#8221;&lt;&#8221; /&gt;<br />
&lt;keyword name=&#8221;&lt;=&#8221; /&gt;<br />
&lt;keyword name=&#8221;=&#8221; /&gt;<br />
&lt;keyword name=&#8221;==&#8221; /&gt;<br />
&lt;keyword name=&#8221;&gt;&#8221; /&gt;<br />
&lt;keyword name=&#8221;&gt;=&#8221; /&gt;<br />
&lt;keyword name=&#8221;contains&#8221; /&gt;<br />
&lt;keyword name=&#8221;excludes&#8221; /&gt;<br />
&lt;keyword name=&#8221;matches&#8221; /&gt;<br />
&lt;/keywords3&gt;<br />
&lt;keywords4&gt;<br />
&lt;keyword name=&#8221;Double&#8221; /&gt;<br />
&lt;keyword name=&#8221;Integer&#8221; /&gt;<br />
&lt;keyword name=&#8221;String&#8221; /&gt;<br />
&lt;/keywords4&gt;<br />
&lt;/highlighting&gt;</p></blockquote>
<p>This should get you home.  If you have a better one or more highlighting ideas, feel free to drop me note.</p>
]]></content:encoded>
			<wfw:commentRss>http://gurovich.com/site/2007/05/31/adding-a-drl-filetype-with-syntax-highlighting-to-intellij/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
