Mephisto Theme Gallery

What started as a 5 minutes hack a few weeks ago is now a reality. I’ve put together a new gallery, and unlike many other galleries, you get to see all the existing themes for Mephisto as they would appear in a real Mephisto installation, because that’s where they all run. All sites show the same content, which makes it really easy to find out how each one treats sections, static pages, comments, sidebars, etc.

To the best of my knowledge, this is the most extensive collection of Mephisto designs to date! It contains 34 templates (and would contain more if it was not for the fact that the license did not allow for free redistribution).

Some may require extra Mephisto plugins such as the ERB, Erubis or HAML renderers. Speaking of HAML, I’m going to be releasing a new template within a few days to demonstrate how you can use the HAML renderer for Mephisto.

I’m also officially introducing a new Mephisto theme: Cutline, very accurately translated from the original Cutline Template by Chris Pearson. This is a gorgeous design, with a clean layout, plenty of white space and very precise typography.

I’ll be posting some more details in the following days about why that 5 minutes hack turned out into a much bigger project, but I think the result is quite impressive, and a hats off to Rick for creating an impressive blog engine.

Simplicity is the Ultimate Sophistication

As Saint Exupery said,

A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away.

And Joshua Porter adds:

Simplicity is treading a line: knowing what to keep and what to throw away. It comes across as magic when it works, because none of the complexity is transferred to users, only simplicity.

Nothing to add, really (emphasis mine)

(from Five Principles to Design By)

after_method

It started innocently enough. I was writing a Rails Plugin, and needed to call a Class Method on a Model. No problemo, right? In init.rb just call the method (To register a renderer in this case).


  Site.register_template_handler(".erubis", ErubisTemplate)

The method goes on and adds some date to a Class Variable. I try it and it worked…

… For the first request only :( After that, the data is gone from the class! What gives???

Well, there is a very convenient mechanism that reloads all your models, views and controllers so you don’t have to restart your app every time you make a change. Except here. Once the class gets unloaded, BAM! There goes the Class Variable and my carefully registered renderer.

First Solution

The code responsible for unloading and reloading all the models, views, and controllers is Dispatcher.reset_application!, so why not piggy back on reset_application! ? So using alias_method_chain, after a few iteration, here’s what I came up with:


  class << Dispatcher
    def register_erubis_template
      Site.register_template_handler(".erubis", ErubisTemplate)
    end
    def reset_application_with_erubis_registration!
      returning reset_application_without_erubis_registration! do
        register_erubis_template
      end
    end
    alias_method_chain :reset_application!, :erubis_registration
  end
  Dispatcher.register_erubis_template

Since reset_application! is not called till the second request, I had to also call the registration method once, which is why I created a register_erubis_template method.

It now worked. That was nice. But, then I started implementing a second plugin, which needed the same code. Not very DRY.

So I wrote down what ideally I wanted to end up with:


after_reset_application {
  Site.register_template_handler(".erubis", ErubisTemplate)
}

So next time I have the same problem, I can just reuse the same after_reset_application call with any other code I need to run after all the classes get unloaded.

Lots of aborted attempts

Foolishly, I thought it would be quite easy to transform what I had into something reusable. A little dose of Monkey Patching here and there, a bit of Meta Programming, and there you have it. Well, I did not keep all the many iterations I tried, here are just a few that did not work for one reason or an other (I did not try to find out why, just moved on to the next attempt)


  Dispatcher.alias_method_chain :reset_application!, #{feature}
  Dispatcher.class_eval do
    def reset_application_with_#{feature}!
      returning reset_application_without_#{feature}! do
        register_#{feature}
      end
    end
  end

def after_reset_application(feature, &block)
  patch = <<-end_eval
    class << Dispatcher
      def register_#{feature}
        proc
      end
      def reset_application_with_#{feature}!
        returning reset_application_without_#{feature}! do
          register_#{feature}
        end
      end
      alias_method_chain :reset_application!, #{feature}
    end
  end_eval
  eval patch#, &block, __FILE__, __LINE__
  Dispatcher.send "register_#{feature}"
end

Use the singleton-class, Luke! Use the singleton-class!

Then I retrieved a post from Ola Bini that I had read some months back: The ruby singleton class and I started to make some progress after I understood that to define methods, I needed to do it on the singleton class, not the class itself, because even though it did not fail, it did not accomplish much. And finally arrived at:

To retrieve that singleton class:


class << Dispatcher; self end

So now, I finally arrived at this code:


require 'dispatcher'

def after_reset_application(feature, &block)
  class << Dispatcher; self end.class_eval do
    define_method("register_#{feature}", &block)
    define_method("reset_application_with_#{feature}!") {
      returning Dispatcher.send("reset_application_without_#{feature}!") do
        Dispatcher.send("register_#{feature}")
      end
    }
    alias_method_chain :reset_application!, "#{feature}"
  end
  Dispatcher.send("register_#{feature}")
end

after_reset_application("erubis_registration") {
  Site.register_template_handler(".erubis", ErubisTemplate)
}

The key reasons why this works is that it is adding the methods to the singleton class, not the class itself. It is using send to be able to build the method names, and using alias_method_chain to be able to monkey patch reset_application! as many time as needed. I had to add a parameter to after_reset_application for the same reason, so it can be called more than once. And it uses define_method because you can give it a block, plus you can define the method name from variables.

All good, except that it is still not very DRY. What if I want to use the same technique on a different class? A different method?

The final version (for now?)

This one was easy, just add a few parameters, deal safely with ?, ! and = and there you go


def after_method(klass, target, feature, &block)
  # Strip out punctuation on predicates or bang methods since
  # e.g. target?_without_feature is not a valid method name.
  aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
  class << klass; self end.class_eval do
    define_method("register_#{feature}", &block)
    define_method("#{aliased_target}_with_#{feature}#{punctuation}") {
      returning klass.send("#{aliased_target}_without_#{feature}#{punctuation}") do
        klass.send("register_#{feature}")
      end
    }
    alias_method_chain target, "#{feature}"
  end
  klass.send("register_#{feature}")
end

Then, to implement after_reset_application:


require 'dispatcher'

def after_reset_application(feature, &block)
  after_method(Dispatcher, :reset_application!, feature, &block)
end

And to use:


after_reset_application("erubis_registration") {
  Site.register_template_handler(".erubis", ErubisTemplate)
}

Phew! The block gets called once the first time, and every time the classes need to be reloaded.

Charlie Rose talks with Bill Gates

56 minutes, not one boring moment! Charming, insightful, fascinating, I’m just so impressed by those 2…

And I’m glad I took the time to watch them and so should you if you didn’t already

(via Search Engine Journal, from PBS on 11/23/06, courtesy of Google Video)

in News | 46 Words

Out with Dream Host, in with Rails Playground

Well, well, well, almost a year after signing up with Dream Host, after going through many turbulent times, trying to fix things up myself, and again, surviving an unannounced, and not so welcome upgrade, after hoping for mongrel support, it really came down to a severe case of overselling. Performance had become abysmal, database connections hard to come by, site was down for longer and longer periods of time. Sometimes several hours without much I could do to revive it.

Don’t get me wrong, not all overselling is bad as long as it is managed properly. In my case, it came down to cpu and resource usage (database, memory). I mean, a rails application like typo is no small app. It routinely had 3 fcgi processes, each 70Mb plus, which on a 4 Gb machine is over 5% of memory, ouch! A good thing that not everyone there runs typo! But I don’t think that was the problem though. On average, the load of the machine was between 10 to 20, which is high but could be sustainable if it wasn’t for the fact that cpu usage seems to be above 90% all the time. This does not give much for a little boost when needed. And on that front, Dream Host needs to make some improvements quickly!

To be honest, this does not seem to be a big issue to run something like wordpress and my other sites on the same machine seem to be doing just fine and I don’t plan to move then anywhere anytime soon. I mean, it is hard to beat the amazing storage space and bandwidth allotment.

So with all that in mind, it was time for a new host! I knew what I wanted:

  • mongrel support
  • ssh support
  • no need for extravagant bandwidth
  • no setup fee, reasonable month to month cost

To find the best contenders, I used Google and the Hosting page on rubyonrails.org.

The best fit: Rails Playground. This post is hosted there, and so far, so good. For $12/month, that looks like a very competitive plan for a rails app. Granted I can only run one app with 2 mongrels, but that will do great for now.

The transition went really smoothly, all things considered.

First thing was to export the database, and I hit a first snag trying to get the data. I could not get a database connection to dump the data, fortunately, I was able to do the export using phpMyAdmin.

On the Rails Playground side, it came down to setting up a database, importing the data, setting up Capistrano, and putting out a request for mongrel setup. Less than a couple hours works and the request for mongrel setup was completed overnight.

Then the only thing left was to switch dns (so the old site was still up, at least as much up as it can be while the dns change propagates), and that’s where the only hitch happened. I ran into a perl error on the Dream Host control panel. Nothing their support couldn’t solve, and an hour later the whois database was updated, and dns servers all over started to pick up the change, and Voila! Rails Playground, here I come!

I now have the distinct (ahem… cough) advantage of having the 2 biggest processes running on this new machine but shush, don’t tell anyone…

And what a difference this makes. Average cpu usage seems to be below 5% and so far, not dreaded 500 error page :)

Note: if you read this via RSS, this may get delayed because as I write this, FeedBurner is still polling the old site. Hopefully this will update by tomorrow.

Bookmark It! Rails plugin for adding bookmarklets.

A while back I wrote BookmarkIt! , a social bookmarllet plugin for Typo, and it worked for a while, but the Typo plugin API changed, and changed again, and again, and keeping up was no fun. So since then, I’ve already changed my approach for my TextLinkAds plugin and instead wrote a bona fide rails plugin, and since Typo 4.0.3 broke the plugin one more time, it was time to take the same route.
Morevover, the previous version did not allow for putting the Bookmkarlets inline with the post and this new version addresses that also. It is now a real Rails plugin, useable in any Rails app.

How to Get the plugin

script/plugin install http://svn.nanorails.com/plugins/bookmarkit/

or to use svn:externals to get updates

script/plugin install -x http://svn.nanorails.com/plugins/bookmarkit/

The install will automatically copy the config file, the images, the css and the partial in their final location. If you delete these files, you can reinstall with:

script/plugin install --force http://svn.nanorails.com/plugins/bookmarkit/

How to configure the plugin

To configure, edit the file config/bookmarkit.yml to change the title and select which bookmarklets you want displayed.

title: Bookmark It!
delicious: true(default)/false
digg: true(default)/false
reddit: true(default)/false
blinklist: true(default)/false
dzone: true(default)/false
newsvine: true(default)/false
furl: true(default)/false
spurl: true(default)/false
simpy: true(default)/false
fark: true(default)/false
blogmarks: true(default)/false
myweb2: true(default)/false
wists: true(default)/false

How to use the plugin

To use, inside any view (.rhtml), use:

<%= render_BMI(title, url) %>

For example, in Typo, you would include the following in app/views/articles/read.rhtml


<%= render_BMI(@article.title, @article.location) %>

For finer control of the rendering, it also copies a css (bookmarkit.css) to public/stylesheets. To use, include in your main layout with:

<%= stylesheet_link_tag "/stylesheets/bookmarkit" %>

Adding more bookmarklets

All the bookmarklet html is in the _bookmarkit.rhtml partial.

For example, for BlinkList, I’ve used:

      <a style="font-size:22px;" title="Blink '<%= title %>'" href="http://www.blinklist.com/index.php?Action=Blink/addblink.php&Url=<%= url %>&Title=<%= title %>">
            <img style="border:0;" hspace="3" class="bookrmarkit" alt="Blink" title="Blink '<%= title %>'" src="/images/bookmarkit/blinklist.gif" />
      </a>

You should also add an entry in bookmarkit.yml

If you want more bookmakrlets included, submit your patches in the comments or send to psq_at_no_spam_nanorails_dot_no_spam_com.

Installing Rails on Mac OS X Tiger (10.4.8)

Just a few days ago, I had the opportunity to upgrade my iMac from a 20” to a 24”. Not only does it have more real estate on the screen, not only is the CPU slightly faster, but it also can take up to 3 Gb of RAM, from 2Gb, which is quite an improvement, especially if you like to run multiple VMs (using Parallel).

So it was quite a relief to us my notes to install Rails on Mac OS Tiger. Except that a lot has changed in the past 3 months since I last wrote that article, and before I update the page to make more sense to newcomers, I’ll just write down a few notes on what I had to do differently.

These reflect the installation on MacOS 10.4.8 and the latest version of MacPorts (as of 10/14/06).

First of all, Darwin Ports is no more: long live MacPorts!

Which means that here is a new location for darwin ports installer.

Then I uncovered a problem in the order of the steps:

Set the PATH before running “sudo port -d selfupdate”, not after as suggested.

One thing that I did not realize, is that you need to actually install a C compiler before you can get started! The easiest is to install XCode 2.4. There is a free registration you need to fill in.

I missed a sudo:

cp httpd.conf.sample httpd.conf => sudo cp httpd.conf.sample httpd.conf

The path to the launchctl file for apache2 has changed:

sudo launchctl load -w /Library/LaunchDaemons/org.macports.apache2.plist instead of sudo launchctl load -w /Library/LaunchDaemons/org.darwinports.apache2.plist

To initialize the database, you need to add:

add "sudo -u mysql mysql_install_db5"

The path to the launchctl file for mysql5 has changed as well:

sudo launchctl load -w /Library/LaunchDaemons/org.macports.mysql5.plist instead of sudo launchctl load -w /Library/LaunchDaemons/org.darwinports.mysql5.plist

replace rb-mysql5 with rb-mysql since rb-mysql supports both flavors

And that’s where it got a bit tricky. With that combination of MacOS 10.4.8 and the current version of MacPorts, rb-mysql fails to compile.

gcc -I. -I. -I/opt/local/lib/ruby/1.8/i686-darwin8.8.1 -I. -DHAVE_MYSQL_SSL_SET -DHAVE_MYSQL_H -I/opt/local/include/mysql5/mysql/ -I/usr/local/include -O -pipe -I/opt/local/include -fno-common -O -pipe -I/opt/local/include  -fno-common -pipe -fno-common  -c mysql.c
mysql.c: In function 'Init_mysql':
mysql.c:2015: error: 'ulong' undeclared (first use in this function)
mysql.c:2015: error: (Each undeclared identifier is reported only once
mysql.c:2015: error: for each function it appears in.)
mysql.c:2015: error: parse error before numeric constant
mysql.c:2018: error: parse error before numeric constant
{standard input}:6318:non-relocatable subtraction expression, "_cMysqlTime" minus "L00000000065$pb"
{standard input}:6318:symbol: "_cMysqlTime" can't be undefined in a subtraction expression
{standard input}:5382:non-relocatable subtraction expression, "_cMysqlRowOffset" minus "L00000000045$pb"
{standard input}:5382:symbol: "_cMysqlRowOffset" can't be undefined in a subtraction expression
{standard input}:5355:non-relocatable subtraction expression, "_cMysqlRowOffset" minus "L00000000044$pb"
{standard input}:5355:symbol: "_cMysqlRowOffset" can't be undefined in a subtraction expression
{standard input}:5333:non-relocatable subtraction expression, "_cMysqlRowOffset" minus "L00000000044$pb"
{standard input}:5333:symbol: "_cMysqlRowOffset" can't be undefined in a subtraction expression
{standard input}:4861:non-relocatable subtraction expression, "_cMysqlTime" minus "L00000000042$pb"
{standard input}:4861:symbol: "_cMysqlTime" can't be undefined in a subtraction expression
{standard input}:4359:non-relocatable subtraction expression, "_cMysqlTime" minus "L00000000041$pb"
{standard input}:4359:symbol: "_cMysqlTime" can't be undefined in a subtraction expression
{standard input}:4106:non-relocatable subtraction expression, "_eMysql" minus "L00000000041$pb"
{standard input}:4106:symbol: "_eMysql" can't be undefined in a subtraction expression
{standard input}:3957:non-relocatable subtraction expression, "_cMysqlTime" minus "L00000000040$pb"
{standard input}:3957:symbol: "_cMysqlTime" can't be undefined in a subtraction expression
{standard input}:3895:non-relocatable subtraction expression, "_eMysql" minus "L00000000040$pb"
{standard input}:3895:symbol: "_eMysql" can't be undefined in a subtraction expression
{standard input}:3824:non-relocatable subtraction expression, "_eMysql" minus "L00000000039$pb"
{standard input}:3824:symbol: "_eMysql" can't be undefined in a subtraction expression
{standard input}:3803:non-relocatable subtraction expression, "_eMysql" minus "L00000000038$pb"
{standard input}:3803:symbol: "_eMysql" can't be undefined in a subtraction expression
{standard input}:3256:non-relocatable subtraction expression, "_cMysqlRowOffset" minus "L00000000023$pb"
{standard input}:3256:symbol: "_cMysqlRowOffset" can't be undefined in a subtraction expression
{standard input}:3226:non-relocatable subtraction expression, "_cMysqlRowOffset" minus "L00000000022$pb"
{standard input}:3226:symbol: "_cMysqlRowOffset" can't be undefined in a subtraction expression
{standard input}:3200:non-relocatable subtraction expression, "_cMysqlRowOffset" minus "L00000000022$pb"
{standard input}:3200:symbol: "_cMysqlRowOffset" can't be undefined in a subtraction expression
{standard input}:2749:non-relocatable subtraction expression, "_eMysql" minus "L00000000019$pb"
{standard input}:2749:symbol: "_eMysql" can't be undefined in a subtraction expression
{standard input}:2595:non-relocatable subtraction expression, "_eMysql" minus "L00000000018$pb"
{standard input}:2595:symbol: "_eMysql" can't be undefined in a subtraction expression
{standard input}:2412:non-relocatable subtraction expression, "_cMysqlStmt" minus "L00000000016$pb"
{standard input}:2412:symbol: "_cMysqlStmt" can't be undefined in a subtraction expression
{standard input}:973:non-relocatable subtraction expression, "_eMysql" minus "L00000000008$pb"
{standard input}:973:symbol: "_eMysql" can't be undefined in a subtraction expression
{standard input}:290:non-relocatable subtraction expression, "_cMysqlField" minus "L00000000004$pb"
{standard input}:290:symbol: "_cMysqlField" can't be undefined in a subtraction expression
{standard input}:232:non-relocatable subtraction expression, "_cMysqlRes" minus "L00000000003$pb"
{standard input}:232:symbol: "_cMysqlRes" can't be undefined in a subtraction expression
{standard input}:190:non-relocatable subtraction expression, "_eMysql" minus "L00000000002$pb"
{standard input}:190:symbol: "_eMysql" can't be undefined in a subtraction expression
make: *** [mysql.o] Error 1

At this point your are either stuck waiting for an update version, or if you are not afraid to dip into the code, you can arm-twist it into working by following these steps:

rb-mysql patch

These steps should be fairly simple to follow:

cd /opt/local/var/db/dports/build/_opt_local_var_db_dports_sources_rsync.rsync.darwinports.org_dpupdate_dports_ruby_rb-mysql/work/mysql-ruby-2.7.1
sudo ruby extconf.rb --with-mysql-include=/opt/local/include/mysql5/mysql/ --with-mysql-lib=/opt/local/lib/mysql5/mysql/
add '#include "my_global.h"' to mysql.c (as the first #include)
sudo make clean
sudo make
sudo make install

And voila. Running the tests will show 3 failing tests, mostly due to imprecisions, but I think you can live with them. Further testing with rails apps did not reveal any problems with MySQL access.

Note: I’ve contacted the maintainer of that port but I have not heard back yet. I will update this post when I do.

A few bonus gems I’ve found helpful, or can’t live without

And since you are in setup mode, you might as well add them to your collection.

sudo gem install acts_as_versioned ajax_scaffold_generator BlueCloth builder fastercsv ferret gettext  htmltools mocha payment radiant  radius rcov rdig RedCloth ruby-debug rubyful_soup selenium shipping
sudo gem install map_by_method what_methods

A new Rails plugin for TextLinkAds (including support for Feedvertising)

Change of Strategy

Instead of updating my constantly breaking Typo sidebar plugin, and to implement Feedvertising from TextLinkAds, I’ve changed gears and chosen to implement just a regular plugin (very close to the new way of doing sidebar plugins in typo 4.1). This approach should work in all versions of typo, as well as any other Ruby on Rails’s application.

The support for feedvertising is slightly different than the one from the WordPress plugin that is the only option offered so far, but should be fairly close.

Installation

Get the plugin from subversion:

script/plugin install http://svn.nanorails.com/plugins/textlinkads/

or to use svn:externals and get future updates via svn update

script/plugin install -x http://svn.nanorails.com/plugins/textlinkads/

The installation will copy a file textlinkads.yml into your config directory.

The file looks like this:

key: TLA_KEY
affiliateid: 0
title: Sponsors
advertisehere: Advertise here!
testing: false
caching: true

Replace TLA_KEY with the one provided by TextLinkAds, set your affiliateid if you’d like to have a link to TextLinkAds with your affiliate id (so you can get credit if someone signs up for a TextLinkAds account). Change the title and advertisehere messages if you don’t like the defaults.

Set testing to false once you’ve verified it works (while testing=true, the plugin will use a special page provided by TextLinkAds that displays to links. However, that page does not contain any RSS links)

Finally, if the caching done by Rails is not enough, the plugin can cache the calls to retrieve the links. See the caching section for explanations on how to setup caching.

Integrating with Typo

Adding the display of regular links

To add the regular TextLinkAds links, you need to add a call to render_TLA anywhere in the rendering code. In typo, the most likely place is in your template’s default.rhtml

Here’s what my template looks like


<div id="sidebar">
  ...
  <div class="sidebar-node"><%= render_TLA %></div>
  <% response.lifetime = 6.hour %>
  <%= render_sidebars %>
</div>

to replace the original:


<div id="sidebar">
  <%= render_sidebars %>
</div>

Add the RSS links

For RSS2.0 for example, edit the file app/views/xml/_rss20_item_article.rxml to add a call to render_TLA_RSS(post_id)

here’s the original file:


  xm.item do
    xm.title post_title(item)
    if this_blog.show_extended_on_rss
      content = item.full_html
    else
      content = item.body_html
    end
    xm.description content
    xm.pubDate pub_date(item.published_at)
    ...

xm.item do
  xm.title post_title(item)
  if this_blog.show_extended_on_rss
    content = item.full_html
  else
    content = item.body_html
  end
  content += render_TLA_RSS(item.id)
  response.lifetime = 6.hour
  xm.description content
  xm.pubDate pub_date(item.published_at)

Depending of which format you need, you may need to edit a different file.

That’s it.

Integrating with other apps

For other apps, just take a similar approach and add calls to render_TLA and render_TLA_RSS where most appropriate. Both calls are accessible from any Controller or Helper class.

Caching

Set “caching: true” in textlinkads.yml. Make sure Rails is configured with some caching. Typically, you need to have


config.action_controller.perform_caching = true
config.action_controller.fragment_cache_store = :file_store, "#{RAILS_ROOT}/tmp/cache"

either set from config/environment.rb or config/environment/[CURRENT ENVIRONMENT].rb

This will cache the pages where the ads appear, and cache the feed pages. But to make sure these pages will update properly if the TextLinkAds ads inventory is updated, you must ensure the cache will expire.

The best way to do that is to use the expiring_cache_action plugin. To install:

script/plugin install http://typosphere.org/trac/browser/trunk/vendor/plugins/expiring_action_cache

Then where you added calls to render_TLA, just add:

response.lifetime = 6.hour

This way, the cached page will expire 6 hours later so that would be the lapse of time to wait to see the updated ads.

No need to expire the cache for RSS, it should get expired automatically every time an article is added.

If you use this plugin in other applications, add a comment or send me a not (psq_0×40_nanorails_0×2e_com) and I’ll add a link to your instructions or code.