Mr eel

Blogmate: Blogging from the Code Editor

Just testing out Blogmate. It’s a Textmate plugin. It seems bizarre to post to a weblog using a code editor, but I like the idea. For one thing having Textmate’s shortcuts on hand is really sweet. Same with all the other bundles like Math.

Now, did it all work?

Posted on June 28th, 2007 | There are 0 comments

Camping: Adding Some Railsisms

I really love Camping. It’s packed full of magic and is just perfect for tiny projects. That said it can be a little obtuse at times and the documentation covers the API, but is a bit light-on when covering actual usage.

So, as a result I’ve spent a lot of time hacking about the code trying to figure things out. The upshot of this is a fair grasp of how the framework works. That means getting to extend it.

Camping.send(:include, SuperFunTime) => weeeeeee!

Now, Camping isn’t Rails. It doesn’t have the same philosophy, so I think it would be a mistake to try and make it into Rails. That said, there are some nice ideas that we can nick and use in our Camping apps.

I want a params hash. Camping has input, which is just a hash of parameters passed in via the query-string or a POST. Rails params however is a nested hash. Using the [] notation in your input names, you can have the parameters automatically nested for you:

patch[version], patch[name] => {'patch' => {'version' => value, 'name' => value}}

So, lets make our own version for our controllers!

module Railsisms
  def params
    return @params if @params
    @params = @input.inject(Hash.new {|h, k| h[k] = {}}) do |hash, key_value|
      if key_value[0].include?('[')
        name = key_value[0].match(/(w+)[(w+)]/)
        hash[name[1]][name[2]] = key_value[1]
      else
        hash[key_value[0]] = key_value[1]
      end
      hash
    end
  end
end

To use this you include it in your controllers. You can then access it by calling #params. The first time it’s hit parses the content of the @input attribute. Subsequent calls just return the hash.

Posted on June 28th, 2007 | There are 0 comments

Injecting Modules into Classes

Inheritance is not always the answer. More often than not, code that needs to be shared between classes can be put into a module. Then you just include it inside you class. Easy as pie. Still in the case where there are multiple classes that you want to mix a module into it can be a bit annoying to have to call include inside each class. So a better way to do this is to inject the module into the class. I have no idea if this classifies as Dependency Injection, but well… that’s what what this example does.

Module.class_eval do
  def inject(method, *klasses)
    klasses.each {|klass| klass.send(method, self)}
  end

  def inject_all(method, options)
    if options[:modules].is_a? Array
      options[:modules].each {|mod| mod.inject(method, *options[:classes])}
    else
      options[:modules].inject(method, options[:modules])
    end
  end
end

We extend the module class using the class_eval method — meta-programming FTW. The first method #inject lets us specify either #include or #extend as the means of mixing the module and a list of classes you want to operate on. So lets imagine I have a module called SuperFunTime.

SuperFunTime.inject(:include, ClassOne, ClassTwo)

SuperFunTime injects itself into the classes using #include. Just a quick note about #include and #extend. These are both private methods for a class, so you can’t call then externally. To get around this we use #send, which lets us call private methods. Naughty! But Nice. The #include method is for mixing in instance methods and #extend is for class methods. There are ways to add both using just #include, but that’s for another time.

Now our second method #inject_all is a little more tricky. It lets us specify a list of modules and a list of classes. So if you have a heap of classes you want to extend with multiple modules, this is a nice shortcut. It’s indented that you call this on the Module class not a module you have specified.

Module.inject_all(:include, :modules => [SuperFunTime, GetBently], :classes => [ClassOne, ClassTwo])

This bit of code will then mix SuperFunTIme and GetBently into the list of classes we pass in. For simple cases, this is overkill, but I’ve definitely found it useful, so please consider my humble bit of code.

Posted on June 28th, 2007 | There are 0 comments

Prototype DOM Builder Gets Nice!

Prototype 1.5 now includes a DOM builder, which is awesome because I can now ditch my own little extensions to the library. The reason I’m happy about this is that I can make my own libraries which depend on Prototype, without having to also include my own DOM Builder lib. I can just depend on Prototype’s. Nice!

As nice as the builder is, I’m not too keen on the syntax. It makes sense, but it’s not all that nice. Too verbose for my tastes. Here’s an example:

new Element('a', {href:'/posts/1'})

Now that’s pretty nice, but if I’m coding some nice, degradable widgets, I might be creating a whole bunch of nodes. Soon enough that’d get annoying. But for me the real downside to this syntax is there is no way to nest nodes as you create them. Instead you have to do something like:

var anchor = new Element('a')
anchor.appendChild(document.createTextNode('weeeeeeee')

All that just to create an anchor with text? It’s nicer than the default DOM methods, but not as nice as it could be. So I’ve created my own extension to the Builder. Firstly it gives us a shortcut; $E(). Secondly it lets us nest elements as we create them and it automatically handles creation of text nodes. Here it is in it’s entirety

var $E = function(tag, content, options) {
  if (tag == 'text') return document.createTextNode(content);
  if (!options) options = {};
  var element = new Element(tag, options);
  if (content) {
    if (content.constructor == Array) {
      for (var i=0; i < content.length; i++) {
        if (typeof content[i] == 'string') content[i] = $E('text', content[i]);
        element.appendChild(content[i]);
      };
    }
    else {
      if (typeof content == 'string') content = $E('text', content);
      element.appendChild(content);
    }
  }
  return element;
};

Some example usage:

$E('a', 'weeeeeee', {className:'hotHotLink'})
$E('ul', $E('li', 'this is the li content'))

Posted on June 27th, 2007 | There are 0 comments

All contents © 2005—2007 Luke Matthew Sutton