Upgraded to Mephisto Edge: various plugin fixes and updates
Plugin: Mephisto Paged Article Lists - now with will_paginate
Rails core kicked pagination as a build-in feature on their way to Rails 2.0. Pagination is now a regular citizen in plugin territory and accordingly Mephisto needs a pagination plugin, too, for its search results and admin pages. Rick opted for will_paginate - which I think is a reasonable solution (and a pretty cool one, too, by the way).
So, among the first things that totally blew up when I dauntlessly installed my plugins in Mephisto Edge was of course ... my Mephisto Paged Article List plugin.
(In my website stats I notice quite some visitors from Google who are on the hunt for search terms like "NameError (uninitialized constant ActionController::Pagination)". Obviously, that's the error message that Rails chokes with when you try to use that old Pagination class in Rails Edge.)
Well ... to me pagination of article lists (section lists etc.) is an essential feature of a blogging engine and so I sat down and did my homework. A new version of the plugin is available in my Subversion repository: Mephisto Paged Article List. I'm going to put some notes together in the next few days. This much in advance: for popular demand it supports paged tag pages. :)
Fixing Mephisto Comment Notification and Mephisto Post Ping
Two other plugins that crashed on the floor when I installed them on Mephisto Edge. These were:
But both had a soft landing were fixed easily. Mephisto Comment Notification runs into Rails Edge's changed handling of ActiveRecord Observers:
# this doesn't work anymore: # ActionController::Base.send(:observer, :comment_notifier_observer) # but this will: ActiveRecord::Base.observers << :comment_notifier_observer
Mephisto Post Ping crashes into a plugin load order issue: Mephisto Post Ping requires the Article model ... which (now) relies on the newly added Permalink_fu plugin ... which is not loaded yet.
Of course one quick solution is to simply rename the plugin folder (e.g. to "post_ping") to "fix" the load order. Another solution seems to be to delay the initialization by changing the
init.rb to something like this:
# init.rb config.to_prepare do require 'post_ping/plugin' ActiveRecord::Base.observers << :article_ping_observer end
For my own reference I filed away two mini-patches here:
Feedburner count plugin
While I were at it I thought I'd also fix my Feedburner stats display. I had gotten annoyed of that clumpsy Feedburner count widget a while ago and wanted to display that count in a more lightweight and unintrusive way. So I just pulled the info from Feedburner's "Awareness" api.
I'm suspect that nobody even noticed it, but these stats were cached by Mephistos glorious page cache. Thus the counter was never really up to date and it displayed different numbers on different pages. *ahem*
This stuff definitely is no big deal. Tt is (finally) a quite good example for my favorite RESTful dynamic web apps approach though that I can incorporate into my own blog. (And hey! There's a bit more about REST than having a CRUDlike interface to ressources. By the way, I'm totally curious what Roy T. Fielding will have to say in his talk The Rest of REST at RailsConf Europe 2007! That sounds like a subtly ironic session title ;-) )
Mephisto Inverse Captcha (anti-spam) - now a plugin
One other thing that had been lingering on my todo list for a while was: enhance that Mephisto Inverse Captcha anti-spam stuff and re-allow users to leave their email address. It's a pretty tiny, yet highly efficient, neat little trick that spares me all the time that I'd spend on sorting out comment spam otherwise (and tell you what, I hate, hate, hate to sort out spam).
So I thought I'd revamp this thing as a plugin instead of those two awkward patches.
For aesthetical reasons I kind of extracted something that I've called "sneaky" parameters. I.e.: parameters that masquerade themselves behind a different name. When the Controller loads up they "unsneak" and take notice of any suspicious values in strawman parameters with their original names (that only superstupid spam bots would send, i.e. 99,9% of all bots).
As you see that's just what that Inverse Captcha stuff did but in a more generalized way. I'm sure it will be quite hard to find a different usage for it than this specific plugin though ... or can you think of any?
The plugin is in my repository already, too. Have a look: Mephisto Inverse Captcha Plugin
I probably should put some more instructive notes together on how to install and use it etc.
So all in all that's been quite some stuff. I guess I have learned one thing or another about Rails plugin development and about some of the reloading issues that may occur in development mode.
Like with many tips-and-tricks-type information most of these techniques are pretty obvious once you've figured it out. But you can spend millions of hours to figure out to do something actually pretty simple.
E.g., do you know how to really unload and reload a plugin class or module that's in your plugins and modifies an app's controller or model? I believe that this will do this dirty job:
name = 'mephisto_comic_collection' Dependencies.load_once_paths.delete lib_path Dependencies.require_or_load name, name.camelize
It probably would make a nice ressource to collect some plugin development techniques. Actually there doesn't seem to be that much on the web. Any suggestions on this?
Oh, by the way. Rick's extremely handy and elegant Module#include_into thingy comes with a quite important limitation: you probably shouldn't use it in modules that get unloaded by Rails' Dependencies and reloaded during the next request. That's because:
(@@class_mixins[klass] ||= ) << self @@class_mixins[klass].uniq!
... this will hold a reference on the old (i.e. unloaded) module and the newly loaded module instances. Thus, the same module will be mixed into your class twice, thrice ... (and even more interesting if you modify it between the reloads: then you have the old and the new versions!). That might or might not work as expected. It also might lead to some very funny results ;)
But we can easily register the module's classname instead of the module itself and then
eval it to re-retrieve the module later on. Probably not the most elegant way, but it does the job. Do you see any problems with this? Or probably a more elegant way that doesn't use an
So much for this.
Now, what have you been doing the last couple of days? ;-))