Wiki
A wiki will happen here eventually. But not today.
I’m beginning to think that perhaps GentleCMS is badly named. It’s a content management system, yes, but it’s also a lot more. It would probably be more accurate to call it a content management framework.
I’ve pretty much finished the main stuff for the backend. There’s some polishing left to do still, but without a frontend in place, further work on the backend code is going to inevitably end up getting out of touch with reality. I really need to get the code to the point where frontend development is sane, and as quickly as possible.
I’ve almost got the second rewrite of the theming code done. (Seriously, I don’t think there will be even a single component that hasn’t been rewritten at least once when I finally release this thing.) I’m going to keep the internal templating code exceptionally simple for the time being because at this point, it’s looking highly likely that I’m going to end up writing a custom templating system, probably based on Kid. (I’ll release it as a separate project most likely.) It’s not necessary yet though, so in the meantime, I’m just going to stick with very, very simple Erb templates so that any work I do is eventually going to be reusable. In any case, GentleCMS doesn’t really care too much how the templates are implemented, and as an end user, you’ll be able to pick and choose without any fuss.
Originally, my intention had been for GentleCMS to be a Rails application which was able to host other applications. That plan had a lot of shortcomings, not the least of which was that I really didn’t think a Rails application was going to be a good way to distribute, deploy, or upgrade things. Gem-based installers like the one Typo and other projects have recently started using would alleviate some of the pain points, but upgrades would still be problematic.
But beyond that, GentleCMS is nothing like Rails. Rails’ sweet-spot (quickly producing small database-backed web apps) is that which you should probably never try to do on GentleCMS. So, aside from hype-factor, why on earth would I want to tie this thing into Rails? Rails isn’t a webserver, and really, a webserver is all GentleCMS needs, not a full-blown framework, because GentleCMS is already the framework.
Which brings me to the option I’ve actually decided to go with: hooking directly into Mongrel. This gives me a lot of extra options, like for instance, multithreading like Merb, as well as a lot more control. Plus the performance is a lot better.
Lately, this has become something of a design pattern for me: A base class describes a type of behavior, and rather than subclasses inheriting the base class’s behavior (which tend to just raise NotImplementedErrors), the subclasses override the base class’s methods to do their own thing. The catch, of course, is that some of the base class’s methods are not, in fact, just stubs. Some of them actually dispatch the message just received to each of the subclasses. So if you send GentleCMS::Cache the :clear message, that message will actually get relayed to GentleCMS::ResponseCache, GentleCMS::ResourceCache, and GentleCMS::RouteCache, thus clearing out all caching systems within GentleCMS. This allows for either selective or indiscriminate clearing of the cache. Very useful.
The problem is that my method for finding subclasses is slow, and I was hoping that you, Dear Readers, might have some suggestions for how I might improve the performance of this method:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Module #:nodoc: # Returns a list of modules and classes that descend from this module. def descendants descendant_modules = [] ObjectSpace.each_object do |object| next if !object.kind_of? Module next if object == self descendant_modules << object if object.ancestors.include? self end return descendant_modules end end |
I tend to cache the results of calling this method, so it doesn’t have a huge performance hit during normal operation, but startup times have become rather surprisingly long.
Update: I discovered that ObjectSpace.each_object could take an optional type parameter. The code runs much faster now:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Module #:nodoc: # Returns a list of modules and classes that descend from this module. def descendants descendant_modules = [] ObjectSpace.each_object(Module) do |object| next if object == self descendant_modules << object if object.ancestors.include? self end return descendant_modules end end |
Update: Cool, that took the worst-case startup times down from 12 seconds to a worst-case startup time of 3 seconds. Not bad, a 400% overall performance improvement from changing two lines of code. I can live with that.
Get me rewrite! Again.
I’m not certain exactly how many times I’ve rewritten the base stuff for GentleCMS now, I’m pretty sure it’s been at least 5 times now, and every time it gets better and cleaner and simpler and better specified. I just finished up writing the code for managing resource properties (arbitrary file metadata). This time around, I (re)wrote the whole thing as a fancy subclass of Hash. Complete with 100.0% C0 code coverage and a fairly respectable 1.37:1 spec:code ratio. I’ll probably begin writing the specs for the ResourceNode class (the most important class in GentleCMS) in the next couple days.
I’ve been making a few evolutionary improvements to the URI class lately. (Which, by the way, is now at 100.0% C0 code coverage and 1.63:1 spec:code ratio. Ultimately the goal is to have 100.0% C0 code coverage for every single piece of GentleCMS and at least a 1.0:1 spec:code ratio for every file.) The improvements were mostly related to escaping. I misread parts of the RFC related to escaping certain characters and I had to go back and improve the specs for that. I’m pretty sure there’s still some edge cases that need to be better specified in my RSpec code. Whenever there’s some section of the RFC that supplies an example, I’ve been adding it verbatim to the RSpec code with a comment to allow easy cross-referencing between the executable specification and the RFC. I almost wish RSpec had some functionality similar to RDoc that would merge the comments with the generated HTML specification somehow. The generated stuff tends to be fairly bland (though still somewhat useful), but it would be really cool if it could be fleshed out a bit.
Anyways, since the properties code was what I just finished up, I thought I’d explain a bit about how the feature works exactly. Properties in GentleCMS are loosly modeled on Subversion’s metadata system, which is really quite simple. Metadata in both systems is basically represented as a set of key-value string pairs, which is why it makes a lot of sense to code it up as a Hash subclass. Namespacing is dealt with by simply prefixing the key’s name with the namespace string followed by a colon. So for example, Subversion uses “svn:mime-type” to store the mime-type of a file, while GentleCMS uses “cms:mime-type”. There’s really nothing special about the way namespacing is done, it’s actually not much more than a style convention.
However, GentleCMS’s metadata system is significantly different in one respect from Subversion’s. GentleCMS allows properties to be auto-generated. GentleCMS has a special class called ResourceAdaptor. Subclasses of ResourceAdaptor are able to selectively alter the behavior and state of ResourceNodes depending on the ResourceNode’s state. For example, if you wanted to auto-detect the encoding of an XML file in order to eliminate many of the problems that crop up as a result of RFC 3023, you could write a ResourceAdaptor subclass that only accepts XML file ResourceNodes. The subclass would add a generated property to the ResourceNode, “cms:encoding” whose value was obtained by inspecting the ResourceNode’s content and determining the XML file’s encoding. GentleCMS knows what to do with the “cms:encoding” property and will automatically supply the correct HTTP headers when a representation of the resource is requested.