Iliad Framework, the users-dao intermezzo

In the previous post we build a login form. In that form we included a “Register” link and so we must provide a way to make our users entries persistent.

Using Smalltalk the image is already persistent, saved every time you need reloaded from the image file. To cut a long story short if you don’t need ACID transactions and a strong mechanism to backup and restore data you can use the image as your database.

This may look strange (and do look strange) to non-smalltalkers but this wonderful post explains very well why using your image as a persistence layer isn’t a crazy idea.

For our didactic purpose we can safely use the image.

We need to store users so we start by creating an PnUser class:

Object subclass: #PnUser
    instanceVariableNames: 'realname surname email md5pwd'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'!

As you see this is a really simple class. You can automatically generate the accessors method using the Pharo browser. You will obtain something like this:

!PnUser methodsFor: 'accessing'!
email
    ^ email

password:pwd
    md5pwd :=  (MD5 hashMessage:pwd) hex.

realname: anObject
    realname := anObject

email: anObject
    email := anObject

realname
    ^ realname

surname: anObject
    surname := anObject

surname
    ^ surname

email
    ^ email

email: anObject
    email := anObject

realname
    ^ realname

surname: anObject
    surname := anObject

surname
    ^ surname

realname: anObject
    realname := anObject

Instead of storing the password we store the MD5 hash code and so we have these methods:

!PnUser methodsFor: 'accessing'!
password:pwd
    md5pwd :=  (MD5 hashMessage:pwd) hex.

!PnUser methodsFor: 'accessing'!
hasPassword:aString
    ^ (MD5 hashMessage:aString) hex = md5pwd .

The MD5 class has all the code needed to compute the MD5 hash code of a string. Pharo has a lot of utilities to ease the life of developers.

Ok. Now we need a class to store users informations. We call it PnUserDAO and we will use a Dictionary to store users informations: the key will be the email and the value the PnUser object.

Let’s start with the class definition:

Object subclass: #PnUserDAO
    instanceVariableNames: 'usersDictionary'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog'!

When we initialize a DAO we create a new Dictionary where we will store the users:

!PnUserDAO methodsFor: 'initialization'!
initialize
    super initialize.
    usersDictionary := Dictionary new.

The first thing we need is a method to store a new user:

!PnUserDAO methodsFor: 'accessing'!
register:anUser
    usersDictionary at: anUser email put: anUser

Then we need a method to retrieve an user from the email:

!PnUserDAO methodsFor: 'accessing'!
userForEmail: anEmail
    ^ usersDictionary at: anEmail ifAbsent: nil.

We created a DAO and now we need to create a test case, just to see if everything is working. Before every test we create a new DAO with a test user and after the test we delete the DAO just created:

TestCase subclass: #PnUserDAOTest
    instanceVariableNames: 'dao'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'LeonardoBlog-Tests'!

!PnUserDAOTest methodsFor: 'running'!
setUp
    | testUser |
    super setUp.
    dao := PnUserDAO new.
    testUser := PnUser new
        surname: 'Test user';
        realname: 'Test name';
        password: 'test';
        email: 'test@test.eu'.
    dao register: testUser.

!PnUserDAOTest methodsFor: 'running'!
tearDown
    super tearDown.
    dao := nil.

Now we can test the register: method:

!PnUserDAOTest methodsFor: 'tests'!
testUserCreation
    |u|
    u := PnUser new email: 'leonardo@leo.it'; realname:'Leonardo'; surname:'Test'.
    dao register:u.
    self assert:(dao userForEmail: 'leonardo@leo.it') isNotNil.

The login method needs a way to check for an user and a password:

!PnUserDAO methodsFor: 'authentication'!
userForEmail: anEmail password:pwd
    |user|
    user := self userForEmail: anEmail.
    user ifNil: [ ^ nil ].
    (user hasPassword: pwd) ifTrue: [ ^ user ] ifFalse: [ ^ nil].

Let’s test it:

!PnUserDAOTest methodsFor: 'tests'!
testAuthentication
    self assert:(dao userForEmail:'test@test.eu' password:'test' ) isNotNil.
    self assert:(dao userForEmail:'test@test.eu' password:'testnot' ) isNil.

As we will offer an user deregistration procedure we need a unregister: method:

!PnUserDAO methodsFor: 'accessing'!
unregister: anEmail
    usersDictionary removeKey: anEmail ifAbsent: [ ].
    ^ self

We test it:

!PnUserDAOTest methodsFor: 'tests'!
testRemovingUnknownUser
    dao unregister: 'nonexitent'

!PnUserDAOTest methodsFor: 'tests'!
testRemovingKnownUser
    |u|
    u := PnUser new email: 'leonardo@leo.it'; realname:'Leonardo'; surname:'Test'.
    dao register:u.
    self assert:(dao userForEmail: 'leonardo@leo.it') isNotNil.
    dao unregister:'leonardo@leo.it'.
    self assert:(dao userForEmail: 'leonardo@leo.it') isNil.

Now we create a method to remove all known users:

!PnUserDAO methodsFor: 'util'!
deleteAllUsers
    usersDictionary removeAll

This is the test:

!PnUserDAOTest methodsFor: 'tests'!
testDeleteAllUsers
    self assert:(dao userForEmail:'test@test.eu' password:'test' ) isNotNil .
    dao deleteAllUsers .
    self assert:(dao userForEmail:'test@test.eu' password:'test' ) isNil .

Well… if everything is ok you have all units test working correctly and you can be happy!

PnUserDAO test cases

Iliad Framework, the users-dao intermezzo

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s