Metaprogramming.apply(Aspects)

Posted by Mathew Abonyi Wed, 10 Oct 2007 01:51:00 GMT

I’m just briefly breaking my silence (which must continue a bit longer) to raise awareness about an impressive and very useful gem which has just come onto the scene: Aquarium.

Although I don’t follow Aspect Oriented Programming very closely, I know about its benefits and use it when necessary. All Rails developers, for example, employ its principles without even knowing it: before, after and around filters, proxies to alter execution, callbacks and the like. If you don’t know about AOP yet, basically it’s the practice of separating components which appear across the various classes of an application to provide system or class-agnostic functionality—for example, logging, authentication or object persistence.

Having these components intrude on, for example, calculating a bank balance really gets annoying after a while. Imagine every moderately sized script you write needing lots of diagnostic messages, custom event handlers, and the like. It gets very tedious because you know there’s the ideal of reuse just around the corner.

Clean, Ruby-esque AOP… a Pipe Dream?

Imagine methods without calls to a logger, without a litter of ‘if/then/else’ for authentication or authorisation, without calls to save an object and checking if it returns true or false… What you’ll get is the raw functionality of your class or module, which… umm, is as it should be. Wrapping a method (using cuts, wrapping or aspects from AOP) lets you extract common functionality which cuts across objects.

Until now, there’s only been the Facets libraries for AOP (‘advice’, ‘aop’ and ‘cut’ in Facets 2.0.0) and AspectR. The former is buggy at best and the latter is like returning to Java. So I wasn’t very keen on AOP in Ruby at all. Metaprogramming seemed easy enough and didn’t need the structure of AOP to follow.

Enter Aquarium. It’s a glass of clean water in a desert. Ruby-style. The most important thing it offers is a ‘Join Point Model’ which will make you wet. In other words, it is very easy to intercept method calls to wrap them and introduce functionality before or after the original definition.

Sadly, I don’t have the time for an extensive example from my own coding, so I’ll have to rip off the example from Aquarium’s own README which I think illustrates its power best (with a few modifications to show how I use it at the moment): method tracing.

Example: Method Tracing


require 'rubygems'
require 'aquarium'
require 'aquarium/aspects/dsl/object_dsl' # makes the Aspects DSL (before/after/around) system-wide

module Example
  class Foo
    def initialize *args
      p "Inside:   Foo#initialize: args = #{args.inspect}"
    end
    def do_it *args
      p "Inside:   Foo#do_it: args = #{args.inspect}"
    end
  end

  module BarModule
    def initialize *args
      p "Inside:   BarModule#initialize: args = #{args.inspect}"
    end
    def do_something_else *args
      p "Inside:   BarModule#do_something_else: args = #{args.inspect}"
    end
  end

  class Bar
    include BarModule
  end
end

p "Before advising the methods:"
foo1 = Example::Foo.new :a1, :a2
foo1.do_it :b1, :b2

bar1 = Example::Bar.new :a3, :a4
bar1.do_something_else :b3, :b4

# EXAMPLE 1: Wrapping methods on multiple classes using the powerful 'types' matcher.
# It can take constants or regular expressions. Also demonstrates method_options, which
# can specify public, protected, private, inherited or local methods.

module Example
  include Aquarium::Aspects::DSL::AspectDSL
  around :types => [Foo, Bar], :methods => :all, 
  :method_options => :suppress_ancestor_methods do |jp, *args|
    begin
      p "Ex. 1: Entering: #{jp.target_type.name}##{jp.method_name}: args = #{args.inspect}"
      jp.proceed
    ensure
      p "Ex. 1: Leaving:  #{jp.target_type.name}##{jp.method_name}: args = #{args.inspect}"
    end
  end
end

# EXAMPLE 2: Wrapping one or more methods very simply.
module Example
  class Bar
    after :do_something_else do |jp, *args|
      p "Ex. 2: Simply after: #{jp.target_type.name}##{jp.method_name}: args = #{args.inspect}"
    end
  end
end

# EXAMPLE 3: Using the Aspect class without the DSL

include Aquarium::Aspects
Aspect.new :before, :method => :do_it, :type => Example::Foo do |jp, *args|
  p "Ex. 3: Simply before (using Aspect.new): #{jp.target_type.name}##{jp.method_name}: args = #{args.inspect}"
end

# EXAMPLE 4: Contain aspects within a namespace for clarity. These are executed
# upon loading the source.

module Example
  module AllAspects
    include Aquarium::Aspects::DSL::AspectDSL
    before :do_it, :type => Example::Foo do |jp, *args|
      p "Ex. 4: Simply before (using namespaces): #{jp.target_type.name}##{jp.method_name}: args = #{args.inspect}"
    end
  end
end

# And now see the result...
puts
p "After advising the methods. Notice that #intialize isn't advised:"
foo2 = Example::Foo.new :a5, :a6
foo2.do_it :b5, :b6

bar1 = Example::Bar.new :a7, :a8
bar1.do_something_else :b7, :b8

You could argue that the namespace for Aquarium needs a little work and the availability of Aspect.new system wide should be a given, but adding that oneself until Aquarium has it is no big deal. I also didn’t demonstrate pointcuts, another option to around, before and after. With a pointcut, you can encapsulate the type, scope and method names—perhaps as a constant, class variable or class method to reduce dependencies.

Overview

All in all, the DSL is already miles better than anything else out there and Aquarium was only released to the public months ago. To summarise, I think this DSL is killer because it is…

  • clear about what is affected
  • easy to separate from what is affected
  • rich with selectors (type, scope, name, pointcut)
  • able to choose whether to alter method execution

So, I would definitely suggest giving it a go and seeing how it can modularise some of your ‘cross cutting concerns’. I also suggest the Aquarium README and the reading recommended on its site, http://aquarium.rubyforge.org/.

Posted in ,  | Tags , , , , ,  | 2 comments | 1 trackback

Distributed SCMs: Be Smart, Use the Bleeding Edge

Posted by Mathew Abonyi Sun, 05 Aug 2007 04:38:00 GMT

I’ll preface this title by admitting I’ve not paid much attention to my version control software. When you’re smart (cue 16-ton weight), you try to think ahead and choose software or hardware which will vastly reduce the effort of programming. While staying on the edge with most things, for version control I neglected to really think about it. I had a very brief and painful time with CVS and promptly converted to Subversion because it was already installed, everyone used it, it was much easier, it solved the immediate problems with CVS I had, and it did what I needed to do at the time.

Of course, in programming, we all know that “what I needed to do” is never the same as “what I need to do now”. I have a lot more code to manage, I have a lot of projects which I am developing on my own or manage or take a serious part in, and I have more projects which I want to dip my feet into. The number of snippets, little things here and there, and feedback or changes from other people has meant a lot of branches, tags, svn cp and other management issues.

Now, this might sound like a “shiny thing” moment, but it is completely different from shiny thing syndrome. My advice to practically everyone reading this is to keep moving at the cutting edge of version control… and that doesn’t mean upgrading to Subversion 1.4.4. It means that there are genuine advances in version control which go well beyond Subversion and well beyond what it will ever be. I’ve known about distributed SCMs for a while, but haven’t really bothered looking into it. I’m writing this post because I think a lot of people are in the same boat. Subversion is supported, if not as the default SCM, practically everywhere: Sourceforge, Rubyforge, Google Code, Unfuddle, Lighthouse, etc. Every respectable project hosting service uses Subversion and almost every plugin, gem or developer out there at the moment references to Subversion repositories. Well, I’m going to make a serious splash in this department and say: it’s time to move on from Subversion. I have completely grown out of it, and I’m not even managing a codebase as large as Rails (or even ActiveRecord).

The single biggest development in version control over the last umpteen years, but specifically the last 2, is distributed version control. Basically, that means everyone’s working copy is a repository, every commit is local and every ‘commit’ to another repository is a ‘push’ (every ‘update’ from another repository is a ‘pull’)—that means merging is the default behaviour when you communicate with another repository. In other words, every repository is a branch. This way, distributed version control doesn’t force a single source code management architecture. These systems are completely open and the advantages to this model-less model are (obviously) endless:

  • most importantly, distribution is all about caring more about the coder-to-code relationship than the coder-to-project relationship
  • Distributed SCMs are orders of magnitude faster in almost every department
  • they usually use a bit more space
  • actual commits are almost instant, since they are local
  • merging is simplified enormously—it’s the daily business of a DSCM
  • no need for an update—you merge when ready
  • development can happen anywhere—no connection required
  • no default model of management or hierarchy
  • much more intuitive code structure—do what you like
  • no need for access control, but it’s there if you need it
  • etc.

You’ll see the benefits just in the development of your own code, without merging with anyone else’s repositories but your own, that distribution of management doesn’t require distribution of code. You can use any methodology, any hierarchy, any paradigm of managing your code that you wish.

Returning to my original point, staying at the bleeding edge doesn’t mean upgrade to the next minor or major version to get the latest feature. It means upgrade, or change software if necessary, to use the latest breakthroughs which will cut costs and save time for your own projects or your company’s projects.

Now, you’re probably asking which distributed SCM to use. I personally go for Mercurial for the following reasons:

  • SVK is a lie—it is not distributed; it just distributes SVN, which is like a multiplying a herd of turtles
  • much faster than Arch, Darcs and Bazaar
  • only Git is faster—but that was developed by Linus Torvalds
  • Mercurial and Git are relatively younger, meaning they’ve learnt from the mistakes of other DSCMs
  • Mercurial and Git come from the same background (the BitKeeper drama) and BitKeeper, according to Torvalds, was the only SCM worth using
  • Mercurial compares feature-for-feature with other DSCMs, and then some
  • Mercurial uses less space than others
  • very user friendly CLI—by far the easiest to learn of the DSCMs
  • decent documentation
  • a few project hosting sites already (like ShareSource)
  • ability to have Mercurial support using the HTTP front-end, even where you normally wouldn’t have it (i.e. Sourceforge and Rubyforge)
  • If you don’t have a feature in Mercurial, find or code a Mercurial extension—it is fully customisable

My Forceful Conclusion

Like the language you are using (Ruby 1.8), the framework (Rails 1.2), the testing suite (RSpec 1.0 and Mocha), the operating system (Mac OS X 10.4 and Linux 2.6), the web server (nginx, LiteSpeed, or lighttpd), the application server (LiteSpeed or Mongrel), the blogging software (Typo or Mephisto), even the hardware (Amazon S3/EC2 or many dual core 2+ GHz servers with many GBs of RAM), you need to use the latest version control software (Mercurial 0.9.4 or Git 1.5). If you don’t, it’s like using an old server with 333MHz & 256MB RAM, or Apache 1.3 & mod_ruby. But unlike an out-moded application server, SVN will make you a slower developer than the one sitting next to you using Mercurial, Git, Darcs or Bazaar. Oh, and I needn’t mention that he’ll get all the babes too.

Posted in , ,  | Tags , , , , , , , , ,  | 10 comments | no trackbacks

Older posts: 1 2 3 ... 27