Archive for the tag 'Magritte'

A Smalltalk ActiveRecord using Magritte, Seaside, and Glorp

I’ve been working on a side project that’s given me reason to want to use Glorp with Seaside. Having just mapped the sample blog written from my screencast into Glorp manually, by writing Glorp descriptors, I decided that I wanted something simpler, something more like Ruby on Rails, automatic persistence, with almost no configuration.

Having used Magritte for a while to describe my Seaside UI’s, I decided that those same descriptions contained all the necessary meta data to write Glorp descriptions from. Unlike the ActiveRecord in Rails, or the one Alan Knight is working on, I’m not using the database as the source of my metadata, I’m using the objects themselves with meta data from Magritte instead.

I sat down and started hacking out my own ActiveRecord implementation, which is really just a small framework that glues these three existing frameworks together for me and makes using Seaside against a Postgres database easy for me. Needless to say, knowledge of Magritte is a prerequisite for using this code.

I just open sourced the code I’ve been using on SqueakSource, just add this repository to Monticello to get a copy

MCHttpRepository
    location: 'http://www.squeaksource.com/MagritteGlorp'
    user: ''
    password: ''

This is a first cut Alpha release, I make no guarantees, only the brave need attempt using it. To use it, simply requires the following.

subclass MGActiveRecord, this will be your root class, all your biz classes can descend from this.

MGActiveRecord subclass: #SBActiveRecord
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'SeasideBlog-Glorp'

subclass MGDescriptorSystem and override #rootClass, returning the class above, like so…

rootClass
	^SBActiveRecord

and on the class side, override #defaultLogin

defaultLogin
    ^(Login new)
        database: PostgreSQLPlatform new;
        username: 'xxxx';
        password: 'xxxx';
        connectString: '127.0.0.1_yourDatabaseName'

and optionally #initializeDatabase: if you want to insert some test data on creation of the schema…

initializeDatabase: aSession
    aSession inUnitOfWorkDo: [aSession register: SBPost testPost]

Now, describe your classes with Magritte, here’s an example…

descriptionCategories
    ^ (MAMultipleOptionDescription selector: #categories label: 'Categories' priority: 1000)
        options: [SBCategory findAll execute] dynamicallyRefreshed ;
        classes:{SBCategory};
        reference: SBCategory description;
        componentClass: MACheckboxGroupComponent;
        yourself

[SBCategory findAll execute] dynamicallyRefreshed is a shortcut for (MADynamicObject on:[SBCategory findAll execute]) that I implemented, since Magritte descriptions are cached, I do this to ensure each time a UI is rendered, a fresh query is done against the database.

You must manually create your database in Postgres. Once created, to create your schema, simply call #createSchema on your MGDescriptorSystem subclass like so…

SBGlorpDescriptions createSchema.

It will use your default connection to infer and create the schema necessary to support your object model. I’ve only used this on two schemas so far, but it seems to work OK, though I’m sure there must be bugs.

Now, to use this all in Seaside, subclass MGGlorpSession and override #glorpDescriptionClass like so…

glorpDescriptionClass
    ^SBGlorpDescriptions

Once done, from Seaside, I can execute queries like so…

blogPosts
    "Grab published blog posts from database and return them in reverse order"

    ^(SBPost findAll)
        limit: self numberOfPostsToShow;
        where: [:each | each isPublished];
        orderBy: [:each | each timestamp descending];
        execute

And have the full power of Glorp available from two class methods, #find and #findAll which return Glorp queries wrapped in a decorator that allows you to call execute on them for the current Seaside context. All classes are commented. If anyone is brave enough to use this, I’d appreciate any feedback on any trouble you run into, or just general discussion about the approach. As I said before, I make no guarantees, but I’m using this code myself, and so far, it seems OK.

UPDATE: The tests included in the package are there to demonstrate a missing feature, automatic inheritance mapping. They do not need to be ran and have nothing to do with the base package. If createSchema works, you’re done, just start using it.

Making a Connection Pool for Glorp in Seaside

First let me say…. Glorp rocks! Kudos to Alan Knight for this framework. I’m really liking it, having written two home brew O/R frameworks in the past (mostly to learn how, and in less capable languages than Smalltalk), I can appreciate the flexibility of its design. This is going on my list of programs to read thoroughly from time to time. It’s a very well written and very nice example of a well written OO system that anyone could learn a lot from. I’d add both Seaside and Magritte to that list as well. Reading great code is a lost art too few programmers do these days.

Glorp really gives me that object oriented feel and allows me to at least pretend I’m working with an object database, while keeping all the benefits of a relational database like constraints, indexing, and random queries. It’s far more capable than I thought it’d be and totally pluggable if you need to add any capabilities. It’ll do things Rails couldn’t dream of as far as mapping and querying goes, and it does it in native Smalltalk syntax.

OK, enough of the Glorp envy. I’ve been working to get Glorp, Seaside, and Magritte all tied together so I can have a full stack framework to work with that allows me to work in Smalltalk at every level. I’ve used many languages and nothing comes close to the productivity I feel in Smalltalk, so naturally, I’m looking forward to finally using it from top to bottom, html, biz objects, and sql queries, all in Smalltalk.

While working on an implementation of a sort of ActiveRecord, but using Magritte for the meta data instead of the database, I quickly found I needed to mary one Glorp session to one Seaside session to keep everything simple and intuitive. It’s a good match, however, I don’t want to keep a connection to PostgreSQL, they’re too valuable a resource to keep sitting idle and unused for 10 minutes while a session times out.

I played around a bit and found, at least so far, that within Glorp, the Squeak and Postgres adapters don’t really maintain any state and can be swapped in and out of an existing GlorpSession. I decided that I’d write a connection pool for Glorp’s SqueakDatabaseAccessor allowing me to tie a PostgreSQL connection to a request allowing much more scalability in the web scenario without risking running out of connections during peak loads. So, after a bit of playing around, I came up with a class called MGConnectionPool. I’m using the class side for all this code, taking advantage of the simple fact that in Smalltalk a class “is” a singleton ensuring there’s only one instance of the pool in the image.

MGConnectionPool class>>initialize
    lock := Monitor new.
    connections := Dictionary new.

MGConnectionPool class>>poolTimeout
    ^30 seconds

MGConnectionPool class>>withUser: aUser password: aPassword
    server: aServer database: aDatabase in: aBlock

    | connection result expired |
    "Grab a connection from the pool util you find one that
    isn't expired, logout the expired ones"
    expired := true.
    [expired]
        whileTrue: [connection := self
                        getConnectionUser: aUser
                        password: aPassword
                        server: aServer
                        database: aDatabase.
            expired := DateAndTime now - connection value > self poolTimeout.
            expired ifTrue: [connection key logout]].

    “pass the connection through the block, which will be the page
    request, and ensure it’s returned to the pool when done”
    [result := aBlock value: connection key]
        ensure: [self
                returnConnection: connection
                forKey: (self
                        makeKeyUser: aUser
                        password: aPassword
                        server: aServer
                        database: aDatabase)].
    ^result

MGConnectionPool class>>getConnectionUser: aUser password: aPassword
    server: aServer database: aDatabase 

    | key matchingConnections |
    ^ lock
        critical: [key := self
                        makeKeyUser: aUser
                        password: aPassword
                        server: aServer
                        database: aDatabase.
            matchingConnections := connections
                        at: key
                        ifAbsentPut: [OrderedCollection new].
            matchingConnections
                ifEmpty: [matchingConnections add: (self
                            newLoginForUser: aUser
                            password: aPassword
                            server: aServer
                            database: aDatabase)
                            -> DateAndTime now].
            matchingConnections removeFirst]

MGConnectionPool class>>makeKeyUser: aUser password: aPassword
    server: aServer database: aDatabase 

    ^aUser , ‘~’ , aPassword , ‘~’ , aServer , ‘~’ , aDatabase

MGConnectionPool class>>newLoginForUser: aUser password: aPassword
    server: aServer database: aDatabase 

    ^ (SqueakDatabaseAccessor forLogin:
        (Login new database: PostgreSQLPlatform new;
             username: aUser;
             password: aPassword;
             connectString: aServer , ‘_’ , aDatabase))
         login;
         yourself

MGConnectionPool class>>returnConnection: aConnection forKey: aKey
    lock
        critical: [aConnection value: DateAndTime now.
            (connections at: aKey)
                add: aConnection]

This allows me to create a subclass of a Seaside session and glue Glorp and Seaside together like this.

MGGlorpSession>>commit: aBlock
	^database inUnitOfWorkDo: aBlock

MGGlorpSession>>execute: aQuery
	^database execute: aQuery

MGGlorpSession>>register: anObject
	^database register: anObject

MGGlorpSession>>ensureGlorpSessionOn: aDbAccessor
	database
		ifNil: [database := GlorpSession new
						 system: (SBGlorpDescriptions
                            forPlatform: aDbAccessor
                                currentLogin database);
						 accessor: aDbAccessor;
						 yourself]

MGGlorpSession>>responseForRequest: aRequest
	^ MGConnectionPool
		withUser: (self application preferenceAt: #glorpUserName)
		password: (self application preferenceAt: #glorpPassword)
		server: (self application preferenceAt: #glorpServer)
		database: (self application preferenceAt: #glorpDatabase)
		in: [:dbAccessor |
			self ensureGlorpSessionOn: dbAccessor.
			database accessor: dbAccessor.
			super responseForRequest: aRequest]

Reading in the authentication from the current application config. Now I can feel safe using Glorp from Seaside, and from within any Seaside component, run Glorp queries with ease in a scalable manner. The code is totally generic and can reused for every future application by simply subclassing. Programming is getting more fun by the day; it’s going to be a good year for Seaside. All the necessary frameworks exist to build a truly awesome and “fully” object oriented web stack that doesn’t drag you down into the request response cycle inherent to other frameworks, even Rails. Glorp + PostgreSQL + Seaside + Magritte + Scriptaculous + Albatross + a little glue == Slick totally object oriented full stack Ajax web framework of the future, today!

05 Dec 2006 > Squeak Image Updated

Just a quick notification that I’ve updated my squeak image. I do this occasionally to keep my base up to date with the latest and greatest of the frameworks I use.

In this update I loaded Albatross, a great new testing framework for Seaside that lets you write SUnit Tests against Seaside apps like this little test for the blog I built in a previous post…

testAddPost
    | body |
    body := Random sentenceMaxWords: 200.
    browser
        click: 'New Post';
        enter: Random sentence for: 'Title:';
        enter: body for: 'Body:';
        click: 'Save'.
    self assert: (browser textExists: body)

This test automates the browser just like Ruby’s Watir or Selenium so all your Ajax code will be tested, but IMHO, has a much nicer test API, thanks to Smalltalk.

I also fixed a bug in the refactoring browser in my image that was causing a DNU on renaming a method. I fixed up my two refactoring additions to the browser for creating accessors with Magritte descriptors on the class side. The collaboration accessor now produces simpler and more useable code, I removed some unnecessary experimental code I was generating and forgot to take out. Made a couple small changes to my garbageCollect method to be compatible with 3.9, and finally, I updated all libraries to the current versions I’m using in my own development and production images.

I’m still using a 3.8.1 image, but I’ll probably be upgrading to 3.9 soon, I’ve got a working image that contains my enhancements, which took a bit of hacking and merging to get going.

I use a custom port of Monticello and rely heavily on its dependency mechanism to properly load dependent packages. The default mechanism doesn’t work correctly IMHO, and I haven’t yet had the time to tackle learning Monticello configurations, nor have I found any good documentation on it. The version of Monticello in 3.9 is different from 3.8, and not as good IMHO. I can no longer see my dependencies because the packages no long have tree controls listing the dependencies. The tool bar is messed up and gets huge when you maximize a window, a side effect of some changes in ToolBuilder I think, but I could be wrong.

The KeyBinder can no longer be configured to use right click to edit key bindings because right clicking now brings up some other menu globally. Now I have to deal with the KeyBinder popping up every time I accidentally hover over it, an annoyance but something I can live with.

I hacked through a broken installer and got Andreas’s Win32 Native fonts working, which to me, is the one thing that really makes Squeak feel right and usable. A couple hacks to SystemWindow to get the border width down to a reasonable 2px and a few more to fix those annoying floating corner grips which get out of whack when you mess with the border width.

All in all, I’m a bit annoyed by 3.9, but I want to be able to use Traits, Göran’s namespaces, and Pragmas, so I’m going to have to bit the bullet and upgrade soon. When I do, and have a good working image done my style, I’ll post it here.

Screencast: How to Build a Blog in 15 Minutes with Seaside

OK, I’ll make this short and sweet. Here’s a screencast of me building a super simple blog in Seaside in 15 minutes, similar to the Ruby demo screencast.

I use two frameworks here, Seaside, and Magritte, with a few custom extensions to my code browser to generate accessors with Magritte descriptions for me.

I tossed in a few required fields, and one business rule about duplicate post titles, to demo Magritte’s abilities. I used the image for persistence, something I think greatly enhances development speed and enables rapid prototyping in a pure OO fashion; something you can’t do if you’re futzing around with database mappings and schemas.

This is my first time using screencast software, so there’s no sound, and everything is real time, I didn’t edit or speedup the video. I used a freeware capture program called CamStudio, it’s very simple and doesn’t allow editing. In the future, I think I’m going to need to find some better screencast software, the whole experience was a bit painful, but this will do for now.

If anyone knows any better free screencast software or formats to use, drop me a line.

UPDATE: The screencast is now in Quicktime and only 7 megs thanks to a reader Manuel Blanc re-encoding it for me.

UPDATE: In the video, I use a method called #contains: on the BlogPost collection, this method comes from the Refactoring Browser and is for compatibility with Visual Works. It may not be in your image, the equivalent Squeak method is #anySatisify: which #contains: simply delegates to.

UPDATE: For anyone who’d like an explanation of what I’m doing, Kevin Kleinfelter was kind enough to transcribe all the steps as if I’d done a voice over.

Featured Resources

When we try to build blogs, we also might consider making our own website some day. It’s a piece of cake building one for you with so many website templates available online. Neither is it an uphill task to get a cheap website hosting for your site. Different packages exist from good companies such as ipowerweb, that will provide you with all the necessary facilities for hosting your website. You can easily browse through names such as anhosting that will have the right solution for your needs.

« Previous PageNext Page »