There can never be enough CMS's in the world, but there aren't nearly enough written in Perl. I've always thought that Everydevel, the one that runs Perlmonks was one of the most interesting.

It got even more interesting when chromatic started to overhaul it. I continued some of this work, such as expanding the test suite and zapping any global variables I could find. Recently, nate encouraged me to fork the project. I set up a git repository for this purpose.

In this CMS every bit of content (and most of the stuff that isn't 'content') is a 'node'. Each node has a 'type' which is also a node, a 'nodetype'. These types could inherit one from the other. However, Everything implemented its own unique inheritance system and didn't use Perl's class system.

The disadvantages of this became apparent and one of the original developers, chromatic, changed it to make sure that a 'node' was blessed into an appropriate perl sub-class. It's what the troops (including me) had been asking for. But the old system had its advantages.

The major advantage of the old system was that new nodetypes could be created whenever the system administrator wanted and this could be done on a live system. chromatic wisely turned off this feature.

But I needed it and hacked a reimplementation using string evals. Of course, it means that when I create new nodetypes Apache had to be restarted so that the relevant packages can be created for all the apache children. This is a bit inconvenient, (ok, it's an ugly hack then, I admit it) but it works.

Now, though, we have data related to the creation of nodetype classes in two spots. One is in the database the other in .pm files in the file system. This would be OK, except that the engine is designed to be run on Apache and whilst static .pm files can be run and loaded at start up and shared amongst all the children dynamic data in the database causes problems.

This problem needs a resolution:

Step 1: Moose and Metadata

After some thought, I figured that "nodetype" nodes should be treated as meta-nodes, which contain class data. What perl object solution provides fantastic meta-class support? Moose, of course.

In fact, I'd been thinking of switching Everydevel to Moose for a while. Not because I need a way to create accessors (Class::Accessor does that fine), but because of its meta-class capabilities. Having easy access to meta-data should make various tasks, like interfacing with the database, easier.

But back to nodetype data. Nodetype data, which I've now classified as meta-data, is pulled in from the backend database, I figured I could store this as a class attribute, thanks to MooseX::ClassAttribute, which slots it into each class's meta class. That's great and it works, so long as we have one implementation of the CMS on the server. If we have more than one in different 'locations' or in different virtual hosts, then each db will overwrite the other's class data.

I've tried it and the results are as unpredictable as could be predicted.

It got me thinking whether what I really need are 'lexical classes'? After all, Moose allows the creation of on-the-fly anonymous classes that aren't truly lexical, but their package names are random and they are deleted when the meta class object goes out of scope. It could be interesting to play with that.

But it is probably over-complicating the problem.

I think the real problem is that the data are not what they seem. 'Nodetype' data were originally classes of which ordinary nodes were instances. But is it really class data?

The data include such things as:

I don't think that any of these are class data. They are specific to a specific implementation. For example, access data are specific to a frontend object, database table names are specific to a db schema and so on.

Step 2: Moose and Metadata to Make Other Classes

Better than to treat these data as metadata is to treat them as instance data for completely different classes. Classes that are assigned specific jobs. The meta data proffered by Moose::Meta::Class can be used to help construct these instances.

This will be better, because it will mean that I'll be able to accomplish another thing: instances won't have to carry around references to their type nodes, and other objects (e.g. a database handle) - which could create leaks. It should also allow more flexibility.

Replies are listed 'Best First'.
Re: Moose, Metadata and the Everything CMS
by ELISHEVA (Prior) on Feb 26, 2009 at 10:14 UTC

    I see this kind of thing a lot in my work - we often assume that "type" means class, but as a design progresses we realize maybe it isn't after all, or maybe it is some sort of hybrid, or ...

    In sorting out these sorts of problems, it is important to realize (as I think you have) that "meta" is relative. It is always meta to something else and we can have layers upon layers of meta. Just check the UML specs if you don't believe - if I recall there are 3 or 4 meta layers in that model alone, and that is just to model modelling!

    So instead of thinking of these problems in terms of things, instances, classes, and types, I prefer instead to model "sets" - sets of related data, methods, and actions. To this end I recommend reframing the issue in light of the following questions. My sense is that you have a pretty good handle on 1,2,3 but are working your way through 4 and 5.

    1. what data do I need to track about X?
    2. what calculated things do I need to make with my data about X?
    3. what operations or intentional side effects do I need to do using my base and calculated data?
    4. How are the instances of X related to each other? When viewing the entire set of things that I consider instances of X how do they differ from one another? Is there a common core of data, actions, and calculations shared by all? Do subsets vary in specific ways? Is this really one class for all X instances? Separate singleton classes for each X instance? Or something in between - a superclass and a handful of subclasses? For that matter, is each instance of X really one "thing"? Or is a set of connected things? Can I explain the differences between various instances of X in terms of X's parts? Or is there something about each X instance that makes it more than just the sum of its parts?
    5. And finally for a bit of intelligent crystal ball gazing: How stable is my current set of all X? Do I have enough information to be certain? What kind of objects are likely to be added to it (given scope, user community)? Why? How are they likely to be different from the X instances I already know about? Have I studied other applications, implementations, or standards documents to see what sort of X instances they come up with?

    Question 1 identifies a set of data members. Questions 2 and 3 identify a set of methods. Question 4 keeps one from jumping to either-or conclusions about class architecture. And 5 is meant to jog one loose and prevent the short-sighted-ness that creates the need for yet-another-foo-framework.

    Only after you have have a clear handle on these questions, can the questions about implementation be answered. (And if you have the answers expanding your post to include them will get better feedback).

    A "class" is just an object with its own special set of data and methods. Some of this "data" may be pretty funky (e.g. code references) but it is still data. Furthermore, its definition is surprisingly dependent on your chosen framework. If your answers to the above questions line up with the data and methods available for a class as defined by OOP framework Foo, then by all means use framework Foo and treat X (or instances of X) as a class within that framework. But if not, pay closer attention to questions 4 and 5 and feel free to be creative in how you use the resources of your chosen OOP framework.

    Best, beth

      Thank you for this great, thought-provoking reply. It has certainly got me thinking about how to group the data and operations that need to be performed on the data.

      In relation to specific issue of the data that have been classified class data, the data can be classified like this:

      • the name of the class into which the object data should be blessed.
      • data that allow a user object to perform an operation on another object(these operations are currently only insert, update, delete and execute)
      • data that determine whether additional operations should be performed on a data node when it is inserted or updated in the back end database.

      These data are hierarchical so that data related to objects in a sub-class may override the defaults set in the superclass. These data follow the class structure of the objects to which they relate.

      What your post made me realise is that data such as access data don't necessarily have to follow the class structure of objects themselves. So, for example, whilst objects of class Node have access data of type Access::Node. Its subclass Node::HTMLData doesn't have to inherit its access data from Access::Node (although it could), perhaps its access data could come from Access::HTMLObjects.

      I'm going to give this some further thought and tweak some of the code - (first step will be to move the access methods into roles, I think).

      Thanks for your help.

        What your post made me realise is that data such as access data don't necessarily have to follow the class structure of objects themselves. So, for example, whilst objects of class Node have access data of type Access::Node. Its subclass Node::HTMLData doesn't have to inherit its access data from Access::Node (although it could), perhaps its access data could come from Access::HTMLObjects.

        Perhaps you should look into Roles for this. They make it very easy to address cross-cutting concerns like this by using their more horizontal composition style. This approach also tends to help prevent the typical OO explosion of subclass trees where you need to subclass an entire section of your class hierarchy to just extend one node of it, sometimes called the Expression Problem.

        -stvn
Re: Moose, Metadata and the Everything CMS
by zby (Vicar) on Feb 26, 2009 at 08:50 UTC
    I am not too much versed in the everything engine internals - but what you write here has some similarity to the Magritte framework. There is a perl project inspired by this idea: Ernst.