{ |one, step, back| } 108 to 117 of 191 articles Syndicate: full/short

Dependency Injection in Ruby   07 Oct 04
[ print link all ]

Introduction

At the 2004 Ruby Conference, Jamis Buck had the unenviable task to explain Dependency Injection to a bunch of Ruby developers. First of all, Dependency Injection (DI) and Inversion of Control (IoC) is hard to explain, the benefits are subtle and the dynamic nature of Ruby make those benefits even more marginal. Furthermore examples using DI/IoC are either too simple (and don’t convey the usefulness) or too complex (and difficult to explain in the space of an article or presentation).

I once attempted to explain DI/IoC to a room of Java programmers (see onestepback.org/articles/dependencyinjection/), so I can’t pass up trying to explain it to Ruby developers.

Thanks goes to Jamis Buck (the author of the Copland DI/IoC framework) who took the time to review this article and provide feedback.

What is Dependency Injection?

Consider the problem of putting together a moderately complex OO program. Typical OO programs create a bunch of objects, wire them together in interesting ways and then let the objects run. It is the first two steps, creating and wiring, that are addressed by Dependency Injection.

By the way, another term for dependency injection is Inversion of Control. Unfortunately, so many things in computer science are called inversion of control that the phrase does not evoke the right connotations with me, so I tend to avoid it. But Inversion of Control is the older term for this pattern so you will see it in many places.

A Moderately Complex Example

One of the problems with explaining Dependency Inversion is that DI only becomes really useful in larger projects. Using a simple example to explain DI leaves the listener thinking "But I can do that easily by (fill in the blank)". So my example is going to be a bit more complex, but hopefully not so large that the reader is unable to understand it.

Imagine you have a webapp that tracks the prices of stocks over time. The application is nicely partitioned into different modules that each handle a portion of the job. A StockQuotes module talks to a remote web service to pull down the current values of the stocks you are tracking. A Database module records the stock values over time. Because this data is highly competitive, you require a login to use the system and thus have an Authentication module to handle validation of user names and password. In addition to these "main" modules, there are a number of additional utility modules used by multiple modules: ErrorHandler to standardize the handling and reporting of error messages and Logger to provide a standard way of logging messsages.

A fully wired system might look something like this:

Building it Old Style!

In the bad, old days, we would just put the logic of building the web app directly into its initialize method. It might look something like this…

  class WebApp
    def initialize
      @quotes = StockQuotes.new
      @authenticator = Authenticator.new
      @database = Database.new
      @logger = Logger.new
      @error_handler = ErrorHandler.new
    end
    # ...
  end

That handles building the WebApp well enough, but what about the subordinate modules. How does the StockQuotes module find out about the logger and error handler, or how does the Authenticator find the database and logger?

We could rewrite WebApp#initialize to create everything in the right order and then pass the logger and error handler to StockQuotes. But that makes the web app rather dependent on details of the StockQuotes module. Currently the database module is created after the quote module, but suppose a change in StockQuotes causes it to need the database. That would require the WebApp to be aware of the change, rearrange the order of creation so that the database is created before the stock quotes module and finally make the database available to the quote service. Yuck!

Even worse, the WebApp knows the concrete name of every module it uses. If I wanted to create an instance of the WebApp for testing, I might want to provide a mock quote service so that I can control the quotes used in testing. Or I might want a mock database for testing. All of these choices are difficult because WebApp knows the class name of all its subordinates.

Enter the Service Locator

We would like to remove the explicit reference to class names in WebApp, but still allow it to locate the services it needs. The Service Locator pattern was designed to address this problem.

With Service Locator, we place references to services in one container and then pass that container to the modules that need to locate those services.

  def create_application
    locator = {}
    locator[:logger] = Logger.new
    locator[:error_handler] = ErrorHandler.new(locator)
    locator[:quotes] = StockQuotes.new(locator)
    locator[:database] = Database.new(locator)
    locator[:authenticator] = Authenticator.new(locator)
    locator[:webapp] = WebApp.new(locator)
  end

The initialize function for a service just uses the locator to find the services. Here is how StockQuotes might look…

  class StockQuotes
    def initialize(locator)
      @error_handler = locator[:error_handler]
      @logger = locator[:logger]
    end
    # ...
  end

Not bad. Now no service is aware of the exact class used for the other services. We can reconfigure the system easily by editted the create_application method.

We use the Service Locator pattern (and variations) at work in our Java system.

External Configuration

Although we built the service locator in Ruby code, it would not be difficult to specify the locator as a configuration file. A simple Ruby method could read the file, instantiate the objects and populate a hash table. This might allow non-programmers to tweak a configuration to their liking.

More Goodness

Another neat thing about the locator is that we can use it to configure data as well as modules. Suppose we wanted to specify the file to be used as the log file. We might modify the create_application method to include the following:

  locator[:log_file_name] = "webapp.log"
  locator[:logger] = Logger.new(locator)

And Logger would have to know that the log file was identified by :log_file_name in the locator. The Database module is another likely candidate for locator based information (e.g. DB user name and password, DB host name).

But …

As good as the Service Locator pattern is, there are still some negatives. Every class that uses the locator needs to be written expecting a locator as an argument to initialize method. This is not a natural idiom for Ruby programmer. In the absence of Service Locators, I would expect that the Logger class would be written like this …

  class Logger
    def initialize(log_filename)
      # ...
    end
    # ...
  end

which would make it unusable in a system that depended upon service locators.

Another downside is that all modules that use the locator must agree on the names of the services. For example, if MyLogger expects its file name to be under :log_filename and YourLogger expects to find its filename under :log_file then the two loggers are not plug replaceable.

Also, Suppose both StackQuotes and Datebase found their loggers using :logger, but we want to give them separate logger instances for some reason. The explicit dependence on the name of the logger service makes this a bit difficult.

And finally, the service locator did not solve the problem of creation order. The database is still created after the stock quotes module, causing problems if the stocks quotes module were modified to use the database.

None of the problems are show stoppers and there are workarounds for each, but it does make us wonder if there is a more general solution.

Finally, Dependency Injection

Dependency Injection is much like using service locators in that we identify the services by name. The big difference is that dependency injectors also take the responsibility of creating the service objects and making sure the dependent services are provided as needed.

This means that the services can be written in complete ignorance of dependency injection framework. All they need to do is make sure that they can be told about the services they need, either through parameters to a constructor, or through some kind of setter.

It also means that dependency injectors are a bit more complicated than service locators, since they also handle the creation of the services as well.

Dependency Injection in Action

How does dependency injection work? Generally, you create a DI container that is configured to know how to create the various services. Then you just ask for a service by name, and the container will create the serice (if needed) and give it to you.

For example, configuring a logger service is as easy as …

   container = DI::Container.new
   container.register(:logger) { Logger.new )

This says that the logger service is named :logger. The first time a logger service is requested, the block supplied to register will be called and a logger object will be created. Subsequent requests for a logger will return the already created logger.

To get a logger service, all you need to do is ask:

   logger = container.logger
Note:In my examples, Service Locators were hash based, so using [] to access the services seems like a natural choice. For dependency injection containers, I chose to use a message-like syntax to access services (e.g. container.logger). Either notation can be used for either service locators or dependency injection containers. In fact, the example dependency injection framework supports both selecter messages and hash-like indexing.

If a logger requires a parameter, then you can easily handle that in the registration block.

  container.register(:logger) { Logger.new("logfile.log") }

If you would rather have the logger get its log filename from the container, you can do this …

  container.register(:logger) { |c| Logger.new(c.log_filename) }

And then somewhere else you can specify the log name …

  container.register(:log_filename) { "logfile.log" }

Configuring the WebApp with Dependency Injection

Now that we’ve seen some DI in action, let’s try it on our web app …

  def create_application
    container = DI::Container.new
    container.register(:logfilename) { "logfile.log" }
    container.register(:db_user) { "jim" }
    container.register(:db_password) { "secret" }
    container.register(:dbi_string) { "DBI:Pg:example_data" }

    container.register(:app) { |c|
      app = WebApp.new(c.quotes, c.authenticator, c.database)
      app.logger = c.logger
      app.set_error_handler c.error_handler
      app
    }

    container.register(:quotes) { |c|
      StockQuotes.new(c.error_handler, c.logger)
    }

    container.register(:authenticator) { |c|
      Authenticator.new(c.database, c.logger, c.error_handler)
    }

    container.register(:database) { |c|
      DBI.connect(c.dbi_string, c.db_user, c.db_password)
    }

    container.register(:logger) { |c| Logger.new(c.logfilename) }
    container.register(:error_handler) { |c|
      errh = ErrorHandler.new
      errh.logger = c.logger
      errh
    }
  end

As you can see, it is a bit more complicated than the service locator. The main reason for the complexity is that we have moved the creation logic out of the services and into the DI container. What we have gained is the ability to inject dependencies into any object without having to make special code changes to support it.

Just a few closing notes:

  • Both constructor injection (StockQuotes) and setter injection (ErrorHandler) or a combination of both (WebApp) can be supported with this framework.
  • We can even handle cases where the creation method is not named "new" (DBI).
  • If a poorly written service didn’t provide a way to inject the services it depends upon, we could use instance_variable_set to force a dependent service into place. Obviously, this would be less than desireable.
  • The order of the registration doesn’t matter, since no service is created until everything is registered. If the StockQuotes module suddenly starts needing a database connection, no problem. We just add a reference to a database service in the creation code for StockQuotes and we are done. The DI framework worries about making sure the database is created before anything that needs it.
  • The container doesn’t have to be configured in one place. For example, we could move the first four register calls to a separate file that would allow the log file and database information to be modified independently of the rest.
  • There still needs to be agreement about service names, but now only the container knows about them. The individual services don’t care.
  • Since the DI container is responsible for all the service names and service creation, it is easy to intercept a service and wrap an AOP-like wrapper around a it.
  • Just like the service locator, a DI container could be configured through a configuration file. The configuration would be more complex (because the DI container is more complex), but still quite doable. Another idea is to use Ruby as Domain Specific Language for DI container configuration.

Summary

Both the Service Locator and Dependency Injection patterns are quite useful, but each has different tradeoffs between flexibility and complexity. Understand the differences and you will have all you need to choose the proper idiom for and give circumstance.

You can find the example framework and unit tests here:



comments

RubConf.new(2004) (Saturday)   03 Oct 04
[ print link all ]
Conference Link:www.rubycentral.org/conference/
Continuing with the semi-real time blogging of RubyConf.new(2004)…

Narf: revisiting a 2 year old (Patrick May)

Narf (Not Another Ruby Framework) is a web framework for Ruby. Patrick introduced Narf at an earlier RubyConf. The latest Narf seems to have added a number of libraries and facilities over the past few years.

One of the really interesting points in Patrick’s presentation is how he handles vandals at his wiki site. He has created a tarpit, so that known vandals are redirected to a parallel web site that gets restored every night. The vandals believe they are modifying the real site, but no one else sees the vandals changes.

Another thing to look into is the HtmlArea library for doing rich text form editing in a browser.

ruby-doc.org: Now and the Future (James Britt)

The ruby-doc site was born at the Seattle RubyConf then James Britt and Jim Freeze talked about pulling together documentation for the Ruby community. The site has grown over the years and provides some great information. The biggest downside to the site is the difficulty finding and and navigating the information. James talked about approaches to categorizing information automatically and easily.

Quote:(While James is showing how the PHP online documentation allows user written annotations): "If you are not familiar with this, and there is no reason you should be, …".

(an aside)

Bruce Williams has created a web site for pasting code. There is a section for RubyConf folk to share code. See codepaste.org.

By the way, codepaste is a Rails application, which leads us to …

Ruby on Rails … Origin, Driver, Destination (David Heinemeier Hansson)

Rails:Just enough stuff to make the creation of database backed web applications tolerable.

David shares how he discovered Ruby and had this wonderful experience with it. Rails, in part, is an attempt to bring the joy of Ruby to the masses by providing an attractive,easy to use web framework.

If you have not seen Rails, check out the ten-minute introduction flash video at media.nextangle.com/rails/rails_setup.mov.

Quotes:"Frameworks are retrospectives — They are not to be built but extracted."
Quote:"Convention over configuration — Adhere to Yesterday’s Weather, but humor newcomers with the (appearance of) choice."
Quote:"Java web frameworks make everything equally hard."

Hang on! There is a rails surprised promised after everybody gets their lunch food.

Lunch Break

(over the shoulder)

I’m sitting behind Rich Kilmer at the conference. I just saw him bring up a web page, pause with the mouse over the submit button and glance over at Chad. He then pressed submit. I think I just saw the first upload of Alph software to RubyForge. Let’s check this out (browses over to RubyForge). Yes, it’s there! Download Alph

The Many Facets of RubyGems (Jim Weirich

What can I say. I think it went well. You can see everything at onestepback.org/articles/rubygemsfacets.

YARV: Yet Another Ruby VM (SASADA Koichi)

Despite an obvious language barrier, Koichi did a great job of communicating with the conference crowd. He showed a great sense of humor. Furthermore, he brought greetings from Matz (who couldn’t make it this year due to the soon to be birth of his child).

YARV is Koichi’s attempt at a VM for Ruby code. If YARV is successful, it will become the Rite used for Ruby 2.0. This is really exciting to see progress in something that might become Rite.

Some properties of YARV include:

  • Simple stack machinge
  • Ruby C extension
  • Not bytecode, but wordcode
  • Use Ruby’s existing infrastructure (e.g. gc, parser, API).
  • YARV traverses the AST to generate code.

Currently, there are a number of basic Ruby operations that aren’t supported (method_missing comes to mind). Over the next few months Koichi will be implementing these missing feature, and also implementing the JIT and AOT (Ahead of Time) compilers for YARV. YARV will also compile direct to C code.

We wish Koichi good luck in his goals!

Quote:"Please ask Question in (1) Japanese, (2) Ruby, (3) C, or (4) Java. But I prefer not Java."
Quote:"Matz says that names are important, but the YARV name is not. For if it succeeds, it will be called RITE. And if it fails, no one will remember it."

"Test::Unit".downcase.sub(/::/,"/") (Nathaniel Talbott)

Nathaniel shared some insights into his understanding of the "Ruby Way" and how it has changed over the years. He has applied these insights toward building a more Rubyesque testing framework.

Stuff coming to test/unit:

  • Easier to write parts of the test suites (runners, assertions, etc)
  • Reloadable runners.
  • Using test metadata
  • Declarative test syntax (i.e. a Domain Specific Language for specifying tests).
      suite do
        setup { ... setup stuff ... }
        setup_once {  ... setup once ... }
        t do
          assert_this ...
          assert_that ...
        end
      end
    
  • Inline test that exist in the same file as your code.
  • The ability to put examples in your class and use them as documentation.
    • e.g. example { assert_equal 2, Adder.add(1,1) }

I’m really excited about these changes. Testing in ruby is already easy, but these changes will make it even more flexible. Cool!

Quote:"Newcomers to Ruby will bring pre (and mis-) conceptions."
Quote:"Testing is not hard. Java is hard."
Quote:"I tell them that Java is hard and their problems are not their fault … well, sometimes it is."

Surprise Presentation (Shashank Date)

Bill Venners has contacted the Ruby community about creating a web-based zine focused on Ruby. Shashank has agreed to be the editor and he has an advisory board to help him review the submitted articles. This is just a pre-announcement, there will be something more formal around the first of the year.

Quote:"We are looking for good, original articles. Of course my articles would not qualify for they would be either good or original, but not both."

(RubyConf Blackout)

Sometime during the afternoon internet access through the wireless link was lost. I can only imagine the reaction in the rest of the Ruby world as suddenly everyone at the conference went silent on the #ruby-lang IRC channel. We’re back now, but I still don’t know about the details.

Dinner … Meal supplied by Infoether

Objective-C: A Retrospective (Brad Cox)

Mr. Cox started with a review of Objective-C, some of its history and some small details of its implementation. Objective-C is used to write most of the Mac software these days.

After the Objective-C talk, Mr. Cox explored some ideas in the nature of software economics. He made the following observations.

  • Hardware is made of atoms
    • Abides by physical convservation lays
    • You can buy, sell and own it
  • Software is made of bits
    • Immune to physical conservation laws
    • You can’t buy, sell and own it.
  • Implications
    • Copyright based compensation cannot work
    • Advertising based compensation (TV model)
    • Useright base compensation never tried.

Given the above, Mr. Cox is advocating a pay-by-use/micropayments system where some kind of accounting system tracks the number of times you call a function (string compare for example) and charges your bank account for the use. Considering that a good portion of the conference attendees are open source advocates, there was a good deal of push back on these ideas. I think I see where Mr. Cox was going, he wants to create an economic system that pumps revenue into the creation of software. Unfortunately, I’m not convinced that his approach really addresses the non-tangible nature of software that he identified earlier. Nevertheless, he provided some food for thought.

You can see some of Mr. Cox’s ideas at virtualschool.edu/mybank/

Evening Activities

After the keynote talk, a good number of attendees migrated to the hotel lobby (where they have WiFi access). I didn’t see a lot of programming, but there was a lot of conversations. I started out by catching up with David Black, and then spent some time with Shashank showing him RubyGems and talking about Rake. Shashank wants to use Rake to control distributed processes, so we experimented with spawning threads from within Rake. The next step would have tasks sending commands to Rake-like Drb servers to run jobs on remote systems. It really won’t be hard to do at all.



comments

RubConf.new(2004) (Sunday)   03 Oct 04
[ print link all ]
Conference Link:www.rubycentral.org/conference/
Continuing with the semi-real time blogging of RubyConf.new(2004)…

RubyX (John Knight)

John gave a short presentation about the RubyX project, which is a linux distribution that uses Ruby in some manner. I’m a little unclear on exactly how Ruby is used in the project, but John is advocating that folks who are interested take a look at it and supported it.

Ruby on Windows (Dan Berger)

Dan has been using Ruby on windows and realized Ruby seriously lagged behind both Perl and Python in supporting the windows platform. Dan has been working on the win32util project on RubyForge to address that disparity. Daniel did include the disclaimer that he is not a Microsoft Employee, nor is he paid for this

Dan first talked about setting up services. I’m grabbing a lot of his example code because I have an immediate use for this.

To query about services …

  require "wind32/service"
  include Win32
  Service.services { |service| p service}
  Service.status("ClipSrc")
  Service.getdisplayname("ClipsSrv")

To control a service …

  Service.stop(name)
  Service.start(name)
  Service.pause(name)
  Service.resume(name)
  Service.delete(name)

To create a service …

  require 'win32/service"
  include Win32

  class MyDaemon < Daemon
    def service_main
      # Service code goes here
    rescue Exception => e
      logfile.puts "ERROR: #{e.message}"
      exit
    end
  end

  d = MyDaemon.new
  d.mainloop

To register a service

  serv = Service.new
  serv.create_service { |s|
    s.service_name = "aba"
    s.display_name = "aba"
    s.binary_path_name = "c:\\..."
  }

Daniel covered a number of other utils. In particular he identified a number of things that just work differently, but they are working on unifying the APIs between the two platforms. In particular, the RubyGems team is interested in the popen3 api so that our functional tests could be run on a Windows platform.

How Dynamic Can You Get? (Jamis Buck)

Jamis had the challenging task of convincing a bunch of dynamic language enthuisists that dependency injection can make their already flexible language even more flexible. While dependency injection in a huge advantage in a language like Java, there some questions to how much it adds to Ruby. Copland, a framework for Dependency Injection/Inversion of Control, is very patterned after a Java based framework. I think it would be interesting to Ruby-ize the framework and see what falls out.

Code generation with Ruby in a heterogenous network application (Gorden James Miller)

Gorden needed to get a largish C++ application running on a hardened computer network with a small memory footprint. There are a number of messages involved in the network solution. He used XML to describe the nature of the message and the structures sent with the message. Reading the XML with REXML, he used ERB to provide templates for C++ code generation to handle the messages. I found it fascinating to use ERB for code generation rather than web pages.

There are some more interesting details the type of network they are testing/simulating. The network is only up for brief periods of time and must survive as much as a 90% packet loss.

Lunch

Self-Organizing Afternoon Activities

After lunch, the afternoon was open for talking, programming or whatever else suits your fancy. Here’s some of the stuff I participated in.

  • Jim Freeze and I (and later Rich Kilmer) worked up a reasonable Ruby-based domain language for Jim to use to specify some work related config files. This is one of the themes I heard over and over again during the conference.
  • David Heinemeier Hansonn decided to include a "Builder style" view into Rails. If your view directory contains a ".rbuild" file, then the file will be invoked with a XML Builder object that you can use to programmatically build your html page. This is an alternative to the rhtml ERB files. Builder probably isn’t a good choice where you have a page designer involved, but it might make sense for a programmer only project, or for generating an RSS style page. David also mentioned something about doing web services with Rails. Now that would be cool.
  • Finally, I worked with Chad Fowler and Bruce Williams on the design of the new RubyGems.org website. Chad had a prototype app built, and Bruce started helping him refine it Me? I kibitz-ed and watched for the most part.

Conference Summary

What a weekend! There was an energy about this conference that I hadn’t felt before. The entire community was excited about apps like Rails and the proto-Rite VM (YARV). The ability to talk to folks actually using your software and getting feedback is quite a rush. I received all kinds of good ideas and suggestions. Unfornately, my plate is already full of things to do, so who know when I will get to all these ideas.

Wow, I can’t wait till next year!



comments

RubConf.new(2004) (Friday)   03 Oct 04
[ print link all ]
Conference Link:www.rubycentral.org/conference/
The Fourth Annual International Ruby Conference is in Virginia this year. Since we have wireless internet access in the meeting rooms, I am going to try a semi-real time blog entries. So watch this space.

Conference Introduction

David Black did his usual "Welcome to RubyConf" thing. Sounds like one or two presenters didn’t make it, so we will be doing some creative scheduling. The new PickAxe books are here, but they won’t be handed out until this afternoon (boo hiss). Oh, well.

Teaching Ruby in a Corporate Environment (Jim Freeze)

Jim is working for an EDA company. His company has established Ruby as the "knighted" language for development in this company consisting of mainly Electrical Engineers. Jim has a 3 day course on teaching Ruby to the EEs. It is oriented toward coding neophytes. I really appreciated his examples that were targetted for particular kinds of engineers.

Ruby as Maestro (Rich Kilmer)

Rich’s talk was a last minute addition to the presentation list to make up for a missing presenter. Rich’s company used Ruby to automate a component based blackboard system running on more than 300 nodes. The active agent program is a huge distributed java program, but Ruby is used to configure, build and control the system.

Rich’s project uses one of my favorite features of Ruby … the ability to create domain specific languages for specific purposes.

  wait_for "SocietyQuiesced", 2.hours do
    do_action "StopCommunications"
    do_action "StopSociety"
  end
Quote:"I loved Java at one time too. I just grew up."
Reference:cougaar.org/projects/acme

(an aside)

There seems to be a running commentary on IRC #ruby-lang if you want to listen in.

Lunch Break

Using and Extending Ruwiki (Austin Ziegler)

Ruwiki looks like a very promising wiki clone. I’ve considered using it for my comments page. Ruwiki is very extensible (tweakable markup, different markup engines, different storage backends, etc.).

(another aside)

Bill Kleb from Langley just asked for help getting Ruby to run on the IA64 architecture.

Tycho: A Proposed Ruby-based PIM (Hal Fulton)

Hal talks about his implementation of a Personal Information Manager inspired by Info Select (a.k.a. Tornado). Tycho looks like a rather interesting way to organize information. The executable node feature could do some really interesting things (I’m thinking of a contact list that could dial your phone for you … Ok, that’s lame, but you get the idea)

Quote:(speaking of other examples of mind-mapping software) "They call it Visual Mind … but they don’t provide any ScreenShots"
Quote:"People have asked for all kinds of features… everything from making it prettier to time travel."
Quote:"Hey, it’s version zero!"

Pickaxe II

Woohoo! Time to hand out the PickAxe II books.

Hacking Ruby (Paul Brannon)

Paul shares some ideas about hacking ruby code … i.e. messing around with Ruby internals, changing the meaning of built-in functions and classes, and generally having fun.

Matz_Quote:Matz: "Macros are too easy to abuse." Someone else: "But callcc is easy to abuse too." Matz: "Yes, but you have to be really smart to abuse callcc"
note:Gabriele Renzi provides a more accurate version of the quote in the Feedback section (see Feedback)

Alph (Rich Kilmer)

         In Xanadu did Kubla Khan
      A stately pleasure dome decree
    Where Alph, the sacred river, ran
    Through caverns measureless to man
           Down to a sunless sea.

Alph is a Ruby/Flash bridge allowing you to write Ruby code to control a flash application. Now that sounds simple, but there are really "interesting" hoop Rich had to jump through to get here.

Rich always thinks big. MacroMedia’s new license scheme for the layout managers makes it impossible to use layout managers with Rich’s scheme (without paying a lot of money). So, Rich is thinking about implementing an open source component library to run on the Flash VM. (note to self: Never piss off Rich)

Quote:Question (refering to MacroMedia): "Are they really that stupid?" Rich: "Yes."

After Conference Activities …

The formal part of the conference was over around 9:30 and we had to vacate the meeting room so that the hotel could lock it up. A large fraction of the conference attendees drifted into the hotel lobby and claimed any spot that was near a power out to continue talking and hacking. Here’s a quick rundown on some of the mini-gatherings:

  • Charlie Mills was helping Bill Kleb get Ruby compiled for the IA64 archtecture. Charlie is the fellow who helped Rich Kilmer and Chad Fowler with their DNSSD service wrapper at OSCON this year. It looked like Bill and Charlie had some success by disabling optimization on the C compiler.
  • At least on person was working on their presentation for the next day.
  • There was a fairly large group talking to Charles L. Perkins regarding the history of Smalltalk. Charles was involved with the early Xerox Parc Place developers and had some good inside stories about the early days of Smalltalk. Later when I dropped by it sounded as if they had moved past Smalltalk history and were discussing some of the capabilities of Prolog.
  • Right next to the Smalltalk history group was another cluster of folks watching as David Heinemeier Hansson helped Jim Freeze through a tutorial on Rails. I’ve used Rails a little bit and am very impressed with the framework. When I dropped by, Jim had just got a login screen working for the demo weblog.
  • Chad Fowler was helping Shashank Date work on Gemifying some of the windows tools that Shashank is planning on releasing. This is where I landed for a while. By the end of the evening, we had Shashank’s WxRuby application running as a gem.

Oh, and by the way, it looks like Genx4R is the 100th ruby app/library to be packaged as a Gem.

(More Ruby Fun tomorrow)



comments

99 Gems on the Wall   01 Oct 04
[ print link all ]
GemWatch is reporting that 99 unique Gems have been released. We were at 90 at the beginning of the week and I was hoping that we could hit 100 by RubyConf. Looks like we might reach that goal during the conference!

If we hit 100 before my talk tomorrow, I will add a mention of that Gem to the presentation (how about that for motivation).



comments

Cautiously Optimistic   23 Sep 04
[ print link all ]
I am cautiously optimistic that a minor change in UseMod wiki code that I am using for feedback may have slowed down the wiki spam bots a bit. The fix is very simple. I just require that any links on the wiki page spell the protocol portion of their link URL in upper case.

For example, if I wish to link to http://onestepback.org on a wiki page, then I must spell the link as HTTP://onestepback.org. Otherwise the updated page will be rejected.

Since the wiki spammers currently use lower case for "http", their page updates are rejected. Now this will only work until the spammer catch on and start uppercasing the HTTP. As long as the number of sites that enforce this rule is small, it should be a while before they catch on (one hopes).

Since I have implemented this patch a week ago, I have had only one instance of spamming. Yes, the spammer used an upper case HTTP. I don’t know if that particular spammer always uses upper case, or just responded intelligently to my error message. But that hasn’t been repeated. Like I say, I’m cautiosly optimistic.

You can find the patch here:

The patch was created against version 1.0 of UseMod wiki.

By posting this, I’ve (probably) increased the number of sites using this anti-spam technique and have brought the day closer to when the spammers figure it out. Oh, well. We will worry about that when it happens.



comments

Staying Simple   13 Sep 04
[ print link all ]
One of the most important lessons I have ever learned as a software developer is to keep things simple. It’s also one of the hardest lessons to remember, because as programmers, we revel in the complex. Sometimes I am forced to reconsider designs where I let my love of complexity override my good sense for simplicity.

Revisiting Builder

Here’s one small example where complexity got away from me. My last blog entry mentioned BuilderObjects used to build XML markup. It turns out that these builders are really useful and I’ve been making good use of them in my latest web project.

However, as I was using, I discovered a rather annoying feature. If you recall, builders depended on the method_missing feature of Ruby to implement arbitrary XML tags on the fly. But, in order for code like this …

   builder.p { em("Hello World") }

to work, the em message must be sent to the builder object. Normally unadorned messages are sent to self. However, the builder object evaluated its block in a special way to hijack the value of self to point to itself.

It is really cool that we just need to mention the tag name and it automatically gets generated. But there is a downside. Suppose we replace the literal "Hello World" with a method called greet that dynamically determines the proper form of greeting. Now the code looks like this…

  builder.p { em(greet) }   # greet is a method

Now, not only em, but also greet gets sent to the builder. This is wrong, greet is a method defined on the current object, not the builder. To get around this, we must save the value of self outside the block and make it available under a different name. Perhaps like this…

  s = self
  builder.p { em(s.greet) }

As I was using builder I notice I was doing the s = self step on almost everytime I used a builder. And when I forget to use the s, I would get weird bugs in my HTML output.

Clearly I was being too clever (in my defense, I just copied the Groovy design for their builders). So I fixed the builder to use normal blocks with normal evaluation and the number of accidental bugs went way down. The greet example now reads like this under the new version of builder:

  builder.p { builder.em(greet) }

Now, since is it awkward to keep mentioning the builder name over and over, it would be nice to have a shortcut. Hence builders pass themselves as a parameter to the blocks. This gives us the opportunity to use a shorter name, but only with in the block. Again, the greet example is

  builder.p { |xml|  xml.em(greet) }

(The shorter name isn’t a big advantage here, but is a big convenience when many tags are created.)

So, it gets back to that old adage: KISS — Keep It Simple Stupid!

Postscript …

Builder is availble as a gem. Version 1.0.0 has the new, simpler interface. Because Builder is a gem, you can install both versions and use either, as long as you don’t mix their usage in a single application. To use the new version (after installing), just say

  require_gem 'builder', '~> 1.0'

This allows you to use any version greater than 1.0 but less that 2.0. To use the old interface, say …

  require_gem 'builder', '~> 0.1'

This allows any version 0.1 or later, but rejects version 1.0 and up. The ~> operator is called the Pessimistic version constraint is is helpful when versions of software have incompatible interfaces. The RubyGems wiki has more information about pessimistic version constraint and about versioning policies.



comments

Builder Objects   24 Aug 04
[ print link all ]

Builder Objects

Sometimes you get good ideas from other languages.
I’ve had fun with this little package over the past few days. The Groovy language was the big buzz at the No Fluff, Just Stuff conference (see NoFluff2004) a few weeks ago. So I’ve been looking at some groovy stuff recently (and I did a Groovy presentation at our local Java group), and I stumbled across the builder library that comes with Groovy. "What a cool idea" I thought and saw that it would be easy to implement in Ruby. So here is the result.

The basic idea is that you create a builder object and then send it messages. It responds to the messages by building up a data structure based on those messages.

Ack, it’s easy to see it in action than to describe it. For example, the following code …

  builder = Builder::XmlMarkup.new("", 2)
  puts builder.person {
    name("jim")
    phone("555-1234", "local"=>"yes")
    address("Cincinnati")
  }

will print …

  <person>
    <name>jim</name>
    <phone local="yes">555-1234</phone>
    <address>Cincinnati</address>
  </person>

So, an XML Markup object produces XML markup. Not very surprising until you think about the methods send to the builder. Where is the person method defined? Or the name method?

Actually, they are not defined anywhere. Instead we have defined a generic method_missing method and handles any undefined method and adds the name of the method to the XML markup we are building. In addition we use code blocks to capture the nested nature of the XML. The result is a very natural way to programmatically generate XML markup.

And it goes beyond just markup. If you use Bulder::XmlEvents instead of Builder::XmlMarkup, the same syntax will generate SAX-like events instead of markup. Or you can use a DOM builder to generate a DOM object tree (I haven’t implemented the DOM version, but it should be straightforward).

There are a couple of "gotchas" with this approach that you should be aware of …

  • Within the builder code blocks, any method call with an implicit object target needs to be sent to our builder. To achieve this, the code blocks are evaluated with instance_eval which changes the value of self to be the builder. This is OK until you want to call a method in the calling object. Since self is no longer the calling object, you have to explicitly provide the caller. Builders make this easy by setting the +@self+ instance variable to the caller.
  • What happens when you need an XML tag that has the same name as an existing method name (e.g. id )? The builder objects handle this by inheriting from BlankSlate. In addition, all the implementation methods in a builder begin with an underscore, which makes them unlikely candidates for XML tags.

Because Builder objects lack the normal methods that most objects have makes working with them rather interesting. Consider the following irb session where we create a builder object and let irb try to display it (by sending it an inspect message):

    >> x = Builder::XmlMarkup.new
    => <inspect/>
    >> x
    => <inspect/><inspect/>
    >> x.to_s
    => "<inspect/><inspect/><to_s/>"
    >> x.methods
    => "<inspect/><inspect/><to_s/><methods/>"
    >> x + 5
    => "<inspect/><inspect/><to_s/><methods/><+>5</+>"
    >> x
    => <inspect/><inspect/><to_s/><methods/><+>5</+><inspect/>

Every time we send a message to the builder it records the name of the message. In a way, the builder has become a trace object, recording all the messages send to it. Weird.

Finally, consider this last example. Suppose we have a method that uses a builder to generate an RSS feed. Here is the method in part ..

  def generate_rss(builder, a_title, a_link, items)
    builder.rss("version" => "0.91") {
      channel {
        title a_title
        link  a_link
        # ... code missing ...
        items.each do |it|
          item {
           title it.title
           link  it.link
           description it.description
          }
        end
      }
    }
  end

Now a single method (generate_rss) can generate XML markup text, a DOM tree or a series of SAX events, all depending on what kind of builder object you pass to the function. That’s slightly cool.

As I mentioned, I got this idea from the Groovy language. Groovy has some other builders as well. For example there is an Ant task builder that integrates with ant. It is a very flexible technique that really shows off the power of a dynamic language.

Note: You can find the Builder object code in the RubyGem builder-0.1.1.gem available on the RubyGem server on RubyForge.



comments

RSS Feed for GemWatch Available   21 Aug 04
[ print link all ]
I’ve setup a simple RSS feed for newly released gems. The same file that builds the GemWatch HTML file now also builds an RSS 2.0 file. You can find the feed at http://onestepback.org/gemwatch.rss.

This is the first hand-built RSS feed I’ve put together, so let me know if I’ve screwed something up.

By the way, the idea from the feed came from the Dave's Article at the Pragmatic Automation web site



comments

No Fluff, Just Stuff Conference 2004   09 Aug 04
[ print link all ]
I just got back from the Southern Ohio Java Symposium, a.k.a. the No Fluff, Just Stuff conference in Cincinnati. Wow, what a great weekend. I blogged in detail about the conference last year (NoFluffJustStuffFriday), so I’m not going to go into detail about each session. Jay Zimmerman takes this conference all over the states and targets a lot of cities that might not otherwise have a lot of opportunities for technical conferences. If No Fluff, Just Stuff comes around to your city, grab your team and attend. It will be worth your time.

A couple of themes emerged from the conference. Aspect Oriented Programming may finally be coming of age. I first heard of AOP about 4 years ago. This year, there seemed to be more substance behind the buzz. We shall see.

Another theme was the the strong belief among the speakers that although Groovy is still very much in its infancy, it has a strong possibility become very significant in the next few years. If you haven’t heard of Groovy, it is a dynamic scripting language built on top of the JVM. Unlike other JVM languages such as Jython and JRuby, Groovy is designed to work closely with Java and especially the Java libraries. A brief look at Groovy reveals a lot of similarities with Ruby. I’ll be looking at Groovy in more detail.

I’ve got a couple ideas for blog entries inspired by the conference and I’ll post them here in time.


comments

 

Formatted: 15-Mar-10 20:21
Feedback: jim@weirichhouse.org