Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

I create the class using John Hickey's excellent ClarionLive! Class Creator, part of the ClarionLive! Utilities:

Image RemovedImage Added

I've defined my baseline class that uses the CML conventions. It's included in CML as CML_BaseClass.inc and CML_BaseClass.clw.

...

At the risk of repeating myself even more than usual, one of the things I like best about test-driven development is it forces me to think about how I want to use my classes long before I think about how to write them. And that's almost always a more productive approach

I see CML_Data_ManyToManyLinks as my repository of information about whether any two records from each of two files linked. As with the original class that is the inspiration for this series, I'm going to use the arbitrary terms "left" and "right" to define those two records.

Let's say I have "left" file records A, B and C, and "right" file records X, Y and Z. 

A is linked to X and Z, and C is linked to Y and Z. B is linked to none of the right records. 

I'll need a way to store the links in CML_Data_ManyToManyLinks and a way to query CML_Data_ManyToManyLinks to find out if a link exists.

Now I can start writing some code. Here's my first test procedure's data section:

Code Block
ManytoMany                  CML_Data_ManyToManyLinks 
                            itemize(),pre()
LeftRecordA                     equate
LeftRecordB                     equate
LeftRecordC                     equate
RightRecordX                    equate
RightRecordY                    equate
RightRecordZ                    equate
                            end

And here's the code:

Code Block
    ManyToMany.AddLink(LeftRecordA,RightRecordX)
    ManyToMany.AddLink(LeftRecordA,RightRecordZ)
    ManyToMany.AddLink(LeftRecordC,RightRecordY)
    ManyToMany.AddLink(LeftRecordC,RightRecordZ)
    AssertThat(ManyToMany.HasLink(LeftRecordA,RightRecordX),IsEqualTo(true), 'Test 1 failed')
    AssertThat(ManyToMany.HasLink(LeftRecordA,RightRecordY),IsEqualTo(false),'Test 2 failed')
    AssertThat(ManyToMany.HasLink(LeftRecordA,RightRecordZ),IsEqualTo(true), 'Test 3 failed')
    AssertThat(ManyToMany.HasLink(LeftRecordB,RightRecordX),IsEqualTo(false),'Test 4 failed')
    AssertThat(ManyToMany.HasLink(LeftRecordB,RightRecordY),IsEqualTo(false),'Test 5 failed')
    AssertThat(ManyToMany.HasLink(LeftRecordB,RightRecordZ),IsEqualTo(false),'Test 6 failed')    
    AssertThat(ManyToMany.HasLink(LeftRecordC,RightRecordX),IsEqualTo(false),'Test 7 failed')
    AssertThat(ManyToMany.HasLink(LeftRecordC,RightRecordY),IsEqualTo(true), 'Test 8 failed')
    AssertThat(ManyToMany.HasLink(LeftRecordC,RightRecordZ),IsEqualTo(true), 'Test 9 failed')

Here's the most important bit: I haven't written the class code yet!

Step one, then, is to write the idealized unit test. 

I do have an empty class on hand, so I need to include that globally:

Image Added

When I compile I get a bunch of errors about the missing methods:

Image Added

The next step is to create stub methods so everything compiles:

Here's the class header:

Code Block
    include('CML_IncludeInAllClassHeaderFiles.inc'),once
CML_Data_ManyToManyLinks                        Class,Type,Module('CML_Data_ManyToManyLinks.CLW'),Link('CML_Data_ManyToManyLinks.CLW',_CML_Classes_LinkMode_),Dll(_CML_Classes_DllMode_)
Construct                                           Procedure()
Destruct                                            Procedure()
AddLink                                             procedure(long leftRecordID,long rightRecordID)
HasLink                                             procedure(long leftRecordID,long rightRecordID),bool
                                                End

And the methods:

Code Block
CML_Data_ManyToManyLinks.Construct              Procedure()
    code
    
CML_Data_ManyToManyLinks.Destruct               Procedure()
    code
    !dispose(self.Errors)
CML_Data_ManyToManyLinks.AddLink                procedure(long leftRecordID,long rightRecordID)
    code
    
CML_Data_ManyToManyLinks.HasLink                procedure(long leftRecordID,long rightRecordID)!,bool
    code
    return false

Now the unit test app compiles, but the test fails:

Image Added

All I need to do now is write the minimum code necessary to get the code to pass.

It isn't possible to declare an actual queue inside a class, so I've created a typed queue and a reference in the class:

Code Block
CML_Data_ManyToManyLinks_DataQ                  queue,TYPE
LeftRecordID                                        long
RightRecordID                                       long
                                                end
CML_Data_ManyToManyLinks                        Class,Type,Module('CML_Data_ManyToManyLinks.CLW'),Link('CML_Data_ManyToManyLinks.CLW',_CML_Classes_LinkMode_),Dll(_CML_Classes_DllMode_)
LinksQ                                              &CML_Data_ManyToManyLinks_DataQ
Construct                                           Procedure()
Destruct                                            Procedure()
AddLink                                             procedure(long leftRecordID,long rightRecordID)
HasLink                                             procedure(long leftRecordID,long rightRecordID),bool
                                                End



The constructor creates and instance of the queue, the destructor empties and disposes of the queue. The AddLink and HasLink methods are almost identical except that one is looking up a record and the other one is adding a record. 

Code Block
CML_Data_ManyToManyLinks.Construct              Procedure()
    code
    self.LinksQ &= new CML_Data_ManyToManyLinks_DataQ
    
CML_Data_ManyToManyLinks.Destruct               Procedure()
    code
    free(self.LinksQ)
    dispose(self.LinksQ)
CML_Data_ManyToManyLinks.AddLink                procedure(long leftRecordID,long rightRecordID)
    code
    clear(self.LinksQ)
    self.LinksQ.LeftRecordID = LeftRecordID
    self.LinksQ.RightRecordID = rightRecordID
    add(self.LinksQ,self.LinksQ.LeftRecordID,self.LinksQ.RightRecordID)
    
    
CML_Data_ManyToManyLinks.HasLink                procedure(long leftRecordID,long rightRecordID)!,bool
    code
    self.LinksQ.LeftRecordID = LeftRecordID
    self.LinksQ.RightRecordID = rightRecordID
    get(self.LinksQ,self.LinksQ.LeftRecordID,self.LinksQ.RightRecordID)
    if not errorcode() then return true.
    return false

Now my test succeeds:

Image Added

So far so good. But can I wire this into my UITest program?

That program used this queue declaration:

Code Block
CheckboxQueue                                   queue
Checked                                             bool
CheckedIcon                                         long
CheckedText                                         string(30)
                                                end

But I'll need another field now, because I need to be able to identify my "right" record in the CML_Data_ManyToManyLinks class:

Code Block
CheckboxQueue                                   queue
Checked                                             bool
CheckedIcon                                         long
CheckedText                                         string(30)
ID                                                  long
                                                end

Now I can declare an instance of that class:

Code Block
Links                                           CML_Data_ManyToManyLinks

and in the code initialize the queue of data and the linking class separately. If I were using this in a database setting, and using this code to display which classes a student takes, the first loop would be reading the list of potential classes and the second loop would be reading the records that indicate which classes the student actually takes (except that for purposes of demonstration I'm randomly selecting about half of them). 

Code Block
    loop x = 1 to 100
        clear(CheckboxQueue)
        !CheckboxQueue.FirstField = x * 10
        CheckboxQueue.CheckedText = 'String ' & x
        CheckboxQueue.ID = x
        add(CheckboxQueue)
    end
    loop x = 1 to 100
        if Random(1,2) = 1
            Links.AddLink(x)
        end
    end
 

But there's something wrong with my code. I only have one parameter to AddLink, not two. This is effectively the "right" side value; there really isn't any particular reason to store the "left" side ID because there is only one record on the left side. 

That doesn't mean there's anything fundamentally wrong with the class design; there might be a situation somewhere down the road where I really do want to keep links for multiple left records. But probably most uses of the class will only have one left record and multiple right records. 

This also leads me to want some slightly different method names:

  • IsLinkedTo(long rightRecordID)
  • IsLinkBetween(long leftRecordID,long rightRecordID)
  • SetLinkTo(long rightRecordID)
  • SetLinkBetween(long leftRecordID,long rightRecordID)

The naming isn't strictly consistent (IsLink vs IsLinked) but I think it's a bit better grammatically. 

Next time I'll finish wiring the Links object into my UITest application, which no doubt will lead to some further code changes.