2008 Predictions

Written on 2:59:00 PM by S. Potter

Since the end of the year is almost upon us close associates of mine have been asking my predictions for the new year. While obliging them here I suspect in 12 months time I will review this blog posting in (most likely) absolute humiliation and horror as most that make predictions do.

  • Web 2.0 will die in 2008 and Web 3.0 will reach adolescence in comical fashion, but will not receive the same private equity interest of its predecessor.
  • Carbonless Datacenter will be the IT buzz phrase for a good part of 2008.
  • SaaS will morph into yet another acronym (hopefully one that makes more sense in terms of capitalization) that will take into account yet another business model twist.
  • DHH will continue to be perceived by many inside and outside the Ruby/Rails world as an arrogant a$$.
Lastly, I predict that Tony Blair will concede and sincerely apologize for his disastrous 2002 prediction that Weapons of Mass Destruction (WMD) exist in Iraq that has already caused a much higher death toll (that continues to mount) orders of magnitude higher than the unmistakable military fiasco of the Charge of the Light Brigade. Sorry I forgot Mr Blair doesn't have a sincere atom in his body and this is only a tech prediction list.

Don't outsource your thinking process. Think for yourself!

Written on 10:54:00 PM by S. Potter

In the wirlwind of political turmoil and occasional nuance of late with the upcoming primary elections almost upon us, I realized just how offensive the two-party primary elections ought to be to a "free" country. Why isn't everyone else as outraged as I am? Traversing various circles I see Democrats hashing out their own political differences on foreign policy, social security and the overloaded and antiquated driver's license system to create a unified platform for the general election. As if *ALL* Democrats think the exact same way. Republicans on the other hand are dumb-founded and depressed by the lack of quality mainstream and viable candidate(s), not to mention trying to figure out what on earth their united platform is after a president that has probably done more to harm their party than any other individual in the last 25 years (hey, don't look at me or the Democrats, neither of us voted for him in the first place!:)). Oh, I do enjoy being smug! Being a foreigner I always found it odd that Americans (generally) seem more willing to outsource their political reasoning to one of two political parties (at least in contrast with my European experiences). I have witnessed the deferral of political reasoning to their parties without thinking on so many occasions I have lost count. If their *party* is anti-war, then they need to be too, no questions asked. Alternatively if they are with the *other party* they need to buy into the whole Cheney-masterminded war on terror doctrine, where we must "take the war to the enemy", whatever that means. Now back to the technical world (and this transcends borders). I see the same type of decision/reasoning outsourcing. In 1997, Apple coined the slogan "Think Different". Fast forward 10 years and you have an ever growing Apple fan base that doesn't question (at least not Apple or Jobs), it just doesn't "think". I am not saying Apple hasn't done anything good, but all you Apple fan boys (and girls) just think for yourselves once in a while, puh-lease! To be fair and candid, it is not limited to the Apple fan base. The Linux community (of which I am party) has been a huge propaganda machine for just as long. A friend of mine who is primarily a Windoze user (sorry, I just couldn't resist) suggests that the Linux community has been pushing Linux (or at least certain distributions of it) as "just as usable out of the box as Windows", which he thinks is a sad and pathetic misrepresentation. To be honest, I agree with him. I don't think any Linux distribution is quite as end-user friendly as Windows out of the box and to me Linux is definitely NOT about that. The Ubuntu weekender geeks that desperately try to be the geekiest person in the room should take note (among others). Forget pushing Linux propaganda on others (remember we don't like Jobs-indoctrinated fan boys/girls selling us the Apple way either). Accept Linux for what it is: a more exclusive club that sets the bar higher than the others! Why should we be trying to lower the bar or grade? Let the newbies reach for the higher bar to get started, so we can attract a higher quality user base than the other two options. The BSD community is a great example! I also feel this way on the Ruby front. I do wish all those nuts-oh "Rails rulz" propagandists would steer clear of all the PHP and Java bashing and Rails hyping. Do you really want all the subpar PHP and Java people (the ones that need hype so they can outsource their decision making to move over) in our world? I personally only want to cherry pick the Java, PHP, Python, etc. developers that actually think for themselves and willingly and enthusiastically step over to the Ruby side. In summary, if you want someone else to think on your behalf go ahead it is a "free" country in that respect (if you aren't in a free country remember nobody owns your internal thoughts so you may as well abuse that freedom as much as you can:)!). However, you should also realize that it is only the free-thinkers that don't conform to mass expectations and opinions that are the ones changing things (mostly for the better, but feel free to disagree with me and form your own opinion:)).

My .gitconfig

Written on 7:29:00 AM by S. Potter


[user]
  name = Susan Potter # make sure you change this
  email = me@susanpotter.net # make sure you change this
[color]
  diff = auto
  status = auto
  branch = auto
[diff]
  rename = copy
  color = auto
[apply]
  whitespace = strip
[pager]
  color = true
[status]
  color = auto
[alias]
  co = "checkout"
  ci = "commit"
  ciall = "commit -a -v"
  unmerge = "reset --hard ORIG_HEAD"
  lsbr = "branch -a" # list all branches, even remote ones
  mkbr = "branch" # create branch if you specify a branch name after it, e.g. git mkbr upgrading_rails
  # remove branch named after it, e.g. git rmbr upgrading_rails
  rmbr = "branch -d"
  # rename branch from one name to another
  mvbr = "branch -m"
  # 
  track = "branch --track"
  # list all tags, to keep commands consistent, e.g. git lstag
  lstag = "tag -l"
  # create a new tag based on specified commit
  mktag = "tag -a"
  # remove existing tag by name
  rmtag = "tag -d"
  # rename tag from one name to another
  mvtag = "tag -m"
  # create new remote repository for project
  mkrem = "remote add"
  # list remote repositories
  lsrem = "remote"
  # show status, keep same as svn command I used most frequently
  st = "status"
  # another alias for status that some scripts might use
  stat = "status"
  # fetch and rebase from svn repository
  spull = !git svn fetch && git svn rebase
  # push keeping each local commit as atomic.
  spush = !git svn dcommit
  # initialize all submodules
  modinit = "submodule init"
  # update all submodules
  modup = "submodule update"
  # show status of all submodules
  modst = "submodule status"
  # add new submodule, i.e. git modadd module-name url
  modadd = "submodule add"
  # show last 15 log entries
  recentlog = "log -n 15"
  # push local committed changes to rubyforge and origin (usually GitHub)
  osspush = !git push rubyforge master && git push origin master
  # pull changes from rubyforge and origin (usually GitHub)
  osspull = !git pull rubyforge master && git pull origin master
  # sync (pull then push) from rubyforge and origin (usually GitHub)
  osssync = !git osspull && git osspush
Place your .gitconfig file in your home directory, e.g. /home/username Last updated: 2008-06-28

Twitter4R v0.3.0

Written on 2:18:00 AM by S. Potter

After Twitter.com added a few extra APIs for favoriting statuses in October, I finally got around to adding them to Twitter4R tonight. Since there is new functionality I have released it as version 0.3.0. To install all you need to do is:

$ sudo gem install twitter4r
The Rubyforge mirrors might still be syncing if you are doing this soon after I post this message. The changes to the library include:
  • Added Twitter::Client#authenticate? method that is a lightweight way to verify a Twitter user's credentials
  • Favorites: allows Twitterers to add statuses to their favorites, list all their current favorites and remove any statuses from the list.
Example code for verifying a user would look like:
gem('twitter4r', '>=0.3.0')
require('twitter')

client = Twitter::Client.new
unless client.authenticate?("macdeveloperthatlovesleopard", "iamasciolists")
  puts "Password does not describe user well!"
end

if client.authenticate?("developerthatdoesnotpretendtobegraphicdesigner", "seriousdevelopernotfakestevejobswannabe")
  puts "Password does describe user well!"
end
Example code for using the favorites API:
gem('twitter4r', '>=0.3.0')
require('twitter')
require('twitter/console')

client = Twitter::Client.from_config('some/path.yml')
statuses = client.favorites
ids = statuses.collect {|s| s.id }
# clean favorites list by deleting
statuses.each {|s| client.favorite(:remove, s) }

# now add back first status id from original favorites list
client.favorite(:add, ids[0])
Enjoy!

The Democratic debate, Ann Coulter and cryptographic utilities for Ruby application developers

Written on 11:11:00 PM by S. Potter

An unlikely trio, but... After watching the flip flop of Clinton in the Democratic debate, the subsequent carcass ravaging by her competition, UFO spotting by spock (aka Kucinich) and two great stinging one-liners from Joe Biden about the presidential [dis]qualification of candidate 911, I stumbled across the infinitely dim and unimpressive Ann Coutler. She was attempting to justify yet another deliberately outlandish statement she made last week, a desperate bid to remain in the limelight to which she is so addicted. At this point, I remembered I wrote a RubyGem a few weekends ago that might be able to decrypt these events and make sense of it all (well maybe!?). A few weekends ago I released metafusion-crypto as a Ruby Gem. I hadn't announced it yet as it got lost in the shuffle of some personal matters that forced me to take five days off work unexpectedly. So here is the belated introduction to that small, humble Gem. This metafusion sub-project is basically just two utility classes: Metafusion::Crypo::PrivateKey and Metafusion::Crypo::DigitalSignature for application developers that don't want to worry about twiddling bits. To install you only need to do the following:

$ sudo gem install metafusion-crypto
An example of DigitalSignature usage might be:

require 'metafusion/crypto'

priv_key = Metafusion::Crypto.generate_key_pair
# this returns OpenSSL::PKey::RSA object into priv_key

include Metafusion::Crypto
sig = DigitalSignature.from_keys('rsa_key.pub', 'rsa_key')
original_text = "my clear text message"
crypted_text = sig.encrypt(original_text)
plain_text = sig.decrypt(crypted_text)
puts "It worked - let's celebrate!" if original_text == plain_text
An example of PrivateKey usage might be:

include Metafusion::Crypto
pkey = PrivateKey.new('mypassphrase')
original_text = 'Yo yo yo.  What up dog?'
crypted_text = pkey.encrypt(original_text)
plain_text = pkey.decrypt(crypted_text)
puts "Let's roll and celebrate - it worked" if original_text == plain_text
I look forward to application developer's feedback. I promise to return to the agile anti-pattern "series" soon. Update: Since two people asked, I have stuck to my tradition of releasing with only 100% C0 code coverage of the project's RSpec examples.

Agile Anti-Patterns, Part 1

Written on 9:16:00 PM by S. Potter

In the last 12 months I had the fortune to work at a client that calls themselves "agile". In fact they use marketing blurbs similar to "on the cutting edge of agile Ruby on Rails development" (I said *similar* because I didn't want to identify them uniquely). The problem is when I briefly worked on a project for them, the truth was very far from their marketing spiel. At a minimum I can say that this was the case on the particular project I briefly worked on, but more likely to have been much more wide spread in the firm since the consulting manager managed all the client projects from what I could tell. Why was I fortunate? Well first of all, it was a very brief encounter. I just couldn't bring myself to work in that environment for long (and thus you will not find it on my resume at all - nice try). Perhaps that says something about me too (I know it does - it is a liability, I realize this and plan to work on it, but on the other hand I feel as if I shouldn't have to compromise my professional values to the level of this case). Secondly, like I imagine is the case with most people, I learn more from bad experiences than good experiences. I would also like to say this is by no means the only company that likely falls into this "let's talk about agile so much we pretend that in itself is being agile" trap. I see 1,000s of job ads or consulting firm spiels saying agile-this, agile-that. Probably only 10-20% of the spiels are decently aligned with reality (of course, I have no way of knowing or measuring this, but it seems like a sensible guess to me;)). Now, is any agile team perfect? No. At least not in my experience. Maybe there are perfect teams and I am just a loser that has never experienced one, but since I have never met a perfect person ever, I imagine this to be improbable. The reason for my deduction is that since software agility is based on people and relationships NOT just tools, then only a team with perfect members could even dream of producing the perfect dynamic. The thing is, you don't need an agile team to be perfect. That is the premise of each agile practice I follow personally. If you think about it, agile practices assume people at their core are faulty some of the time in some way, because we are mere mortals, not software gods (though at times developers do get a God-complex, which is one of our many faults - I am guilty here too)! Ooops, tangent, but is serves a point here too. I do not ever wish to imply that following the letter of all the agile practice "rules" will make you truly and *perfectly* agile. I do not even think that is even the point. For me it comes down to Kaizen, which in short means, to iteratively eliminate waste from a process each time you repeat and to break those iterations down into bite size pieces so you can track and measure progress (actually that is a very coarse way to describe Kaizen, which is a word I love so much I tattooed it on my body somewhere - just kidding.....or am I?). Feel free to search the web for a better description (there are a few), but it is a word the more you read about (at least for me) the more you appreciate. Perhaps we will never be perfect agile developers. Perhaps we will never be perfect business analysts, coders, managers, deployers, system administrators, database administrators, etc. That to me is almost irrelevant. The idea is we just need to strive to simply "do better" each time and have the right attitude. This was really just the kick off piece (aka rant) for this "series" on anti-patterns relating to agile practices. I'll have concrete examples coming soon in part 2, 3, etc. Stay tuned. PS If you are searching for an "agile" development post, don't despair, there are some decently agile places out there. They may not be perfect, but hopefully better than the client project I will discuss.

Coming out....in support of DHH [to some extent]

Written on 9:16:00 PM by S. Potter

Since today is National Coming Out Day in the US I thought I would use it somehow in my blog entry. Yes folks I am "coming out" in support of [some of] DHH's decisions. Earlier today I noticed a lot of blog chatter regarding Rails and what various people think it *should* or should not be. While I agree with some of these people's sentiments that DHH is an arrogant ass, I also think he has made *some* smart decisions (though on a number of mid/lower-level issues I think he has it totally wrong). Now reread this paragraph until you get the point that I still think he is an arrogant ass with a God complex. Firstly, let's discuss the "Twitter" issue. Yes, I use Twitter (occasionally), though I see it's biggest impact (for my personal interests) in marketing rather than tweeting every time I use the bathroom (note: I am not part of the look-at-me-NOW-Gawd-damn-it generation - I was a humble 70s baby). In fact, the marketing potential is what drove me to develop Twitter4R in the first place. What I don't like about Twitter is due to their own lack of foresight they desperately tried to blame a framework! An application framework because they couldn't get their act together to do the necessary scalability testing of their whole deployment environment (which includes many things, only one of them is the application framework) before hand. This is the same application framework that got them rolled out into production earlier than they would have with other frameworks. The same framework that allows them NOW to scale to crazy amounts of hits per second! I attempted to follow the dialog between the Twitter developers and Rails Core team, though not too successfully because it occurred through non-interlinked blog posts and comments. However, from what I could gather the Twitter folk seemed to think it should be up to the application framework to create an already baked in way for them to setup replication models. If they were using a PHP framework, they wouldn't have bothered asking since all the DB code is in the view anyway! Why is that the responsibility of the application framework? Now if Rails has somehow prevented them from being able to create ActiveRecord extensions to do this, I might understand, but as far as I can see....this just isn't so. Secondly, the CDBaby rant. If CDBaby took 2 years to release using Ruby/Rails, then they must have had much bigger problems than Rails itself! Sorry to be blunt, but who takes that long anymore, even in a Java stack? Ok, now for some leveling. I still think DHH is an ass and an extremely arrogant ass at that. I still think he has ridiculous ideas about trademarking logos (that he didn't even pay for), definitions of what components are, how vendor/* is better than utilizing Ruby Gems in virtualized or dedicated environments (WTF?) and general dependency loading in Rails. Plus, did I mention I think he is an arrogant ass? Not to mention a former PHP programmer...enough said! Good night.

Twitter4R Announcements

Written on 8:46:00 PM by S. Potter

First off I have been meaning to post an entry letting people know of a recently released Rails application written by Sergio Santos called TwitterNotes that is using the Twitter4R gem. Thanks Sergio (and anyone else that worked on the project - sorry if I left you off). Also Sergio emailed yesterday requesting message paging support with a suggested patch (thanks for contributing back - muchas gracias). This evening I released Twitter4R version 0.2.5 that incorporates the necessary code changes for this support. What does this mean? Previously you would have only been able to get the last 20 sent/received direct messages from the Twitter4R bindings doing something like the following:


messages = client.messages(:sent)
Now you can request specific pages like so:

messages = client.messages(:sent, :page => 2)
The same is true for the :received case. To install the Twitter4R RubyGem simply run the following in your terminal/console:
$ sudo gem install twitter4r
Give the Rubyforge mirrors a few hours to sync if you are only getting Twitter4R v0.2.4. Thanks and enjoy!

Custom Processors for ActiveWarehouse ETL

Written on 12:45:00 AM by S. Potter

For anyone interested in extending ActiveWarehouse ETL's features with custom pre/post processors, I thought I would share this piece of code that I wrote in August for a personal project I am working on. The example should provide you with enough details for you to create your own custom processors.


# Written by Susan Potter  under open source MIT license.
# August 12, 2007.

require 'net/ftp'

module ETL
  module Processor
    # Custom pre-processor to download files via FTP before beginning control process.
    class FtpDownloaderProcessor < ETL::Processor::Processor
      attr_reader :host
      attr_reader :port
      attr_reader :remote_dir
      attr_reader :files
      attr_reader :username
      attr_reader :local_dir
      
      # configuration options include:
      # * host - hostname or IP address of FTP server (required)
      # * port - port number for FTP server (default: 21)
      # * remote_dir - remote path on FTP server (default: /)
      # * files - list of files to download from FTP server (default: [])
      # * username - username for FTP server authentication (default: anonymous)
      # * password - password for FTP server authentication (default: nil)
      # * local_dir - local output directory to save downloaded files (default: '')
      # 
      # As an example you might write something like the following in your control process file:
      #  pre_process :ftp_downloader, {
      #    :host => 'ftp.sec.gov',
      #    :path => 'edgar/Feed/2007/QTR2',
      #    :files => ['20070402.nc.tar.gz', '20070403.nc.tar.gz', '20070404.nc.tar.gz', 
      #               '20070405.nc.tar.gz', '20070406.nc.tar.gz'],
      #    :local_dir => '/data/sec/2007/04',
      #  }
      # The above example will anonymously download via FTP the first week's worth of SEC filing feed data
      # from the second quarter of 2007 and download the files to the local directory +/data/sec/2007/04+.
      def initialize(control, configuration)
        @host = configuration[:host]
        @port = configuration[:port] || 21
        @remote_dir = configuration[:remote_dir] || '/'
        @files = configuration[:files] || []
        @username = configuration[:username] || 'anonymous'
        @password = configuration[:password]
        @local_dir = configuration[:local_dir] || ''
      end
      
      def process
        Net::FTP.open(@host) do |conn|
          conn.connect(@host, @port)
          conn.login(@username, @password)
          remote_files = conn.chdir(@remote_dir)
          @files.each do |f|
            conn.gettextfile(remote_file(f), local_file(f))
          end
        end
      end
      
      private
      attr_accessor :password
      
      def local_file(name)
        File.join(@local_dir, name)
      end
      
      def remote_file(name)
        File.join(@remote_dir, name)
      end
    end
  end
end
The key things to note from this is that you are at present required to:
  • define all your custom processors with in the ETL::Processor module
  • name your custom processor class in the form XXXXProcessor
  • need to extend (or really just adhere to the message interface of) ETL::Processor::Processor class defined in ActiveWarehouse ETL
  • define initialize taking two arguments (look above for guidance)
  • define a process method to do what you need do before or after the control process runs (for pre and post processors respectively)
Hope this helps someone customize ActiveWarehouse more easily, since the only bad thing I have found with ActiveWarehouse is lack of documentation.

Separating Rails Layout Associations

Written on 9:16:00 PM by S. Potter

I just realized I hadn't shared this code yet on this blog. I will also be including it in one of the metafusion subprojects (coming soon). I've been using it when needed in my Rails projects on and off for the last several months.

The Problem

You have some controllers you include into your application from plugins or engines and you want to associate a particular layout to the plugin/engine controller from within your application (without changing anything in the plugin or engine - of course!). There is not good way of doing this nicely in Rails presently as each controller usually defines controller-wide layouts within its definition.

The Solution

[finsignia/paths.rb]

# See:
# * Finsignia::Paths

# Contains helper methods related to paths and resolving modules and 
# classes from paths.  Provides for helpful mixin for various applications.
module Finsignia::Paths

  def self.included(base)
    base.extend ClassMethods
  end
  
  # Contains class methods for Finsignia::Paths mixin
  module ClassMethods
    @@element_postfixes = {:model => '', :controller => 'Controller'}

    def resolve_model(path)
      resolve_element :model, path
    end
  
    def resolve_controller(path)
      resolve_element :controller, path
    end
    
    def normalize_module_name(path)
      list = path.split('_')
      list.collect do |item|
        item.capitalize
      end.join
    end
    
    # Resolves path to a type of Rails element 
    # (e.g. model, controller, etc.).
    # 
    # Path refers to 'internal' path, NOT require path:
    #  'users/users' #=> internal path
    #  'users/users_controller #=> require path
    def resolve_element(type, path)
      first, rest = path.split('/')
      mod = ObjectSpace.const_get(normalize_module_name(first))
      while rest
        first, rest = rest.split('/')

        unless rest
          mod = mod.const_get(normalize_module_name("#{first}_#{@@element_postfixes[type].downcase}"))
        else
          mod = mod.const_get(normalize_module_name(first))
        end
        # I doubt this will ever get this far as per expeceted behavior of ObjectSpace and Module...so commented it out following unreachable line.
        #raise NameError.new("#{type} constant not found for internal path #{path}") unless mod
        return mod unless rest
      end      
    end
    
    # Separating this so we can stub this method out in specifications.
    def require_controller(path)
      require("#{path}_controller") unless "test" == ENV["RAILS_ENV"]
    end
    
    # Separating this so we can stub this method out in specifications.
    def require_model(path)
      require(path) unless "test" == ENV["RAILS_ENV"]
    end
  end
end

[finsignia/layouts.rb]

# See:
# * Finsignia::Layouts
# * Finsignia::LayoutsError

# Raised when an exceptional condition arises in the Layouts 
# mapping process.
class Finsignia::LayoutsError < Exception; end

# Provides a closure and declarative way to define layout
# mappings for an application that utilize controllers, views, 
# etc. from plugins.
#
# The closure approach simulates the Rails Routing approach 
# closely, like:
#  require 'application'
#  
#  Finsignia::Layouts.map do |map|
#    map.connect 'users/sessions', 'layout_name'
#  end
#
# The declarative approach looks like the following:
#  require 'application'
#  include Finsignia
#  Layouts.map 'users/sessions', 'layout_name'
# 
module Finsignia::Layouts
  class << self
    def connect(controller_path, layout)
      require_controller(controller_path)
      # resolve controller class and call .layout(layout) on it.
      controller = resolve_controller(controller_path)
      controller.layout(layout)
    end
    
    def map(&block)
      yield self if block_given?
    end
  end

#  private
    include Finsignia::Paths    
end

So basically in a file like config/layouts.rb of our application we have something like:

Finsignia::Layouts.map do |map|
  map.connect 'users/sessions', 'session'
  map.connect 'users/users', 'users'
  map.connect 'accounts/transaction', 'account'
end

Then include the config/layouts.rb file in config/environment.rb and you have separated your concerns relatively nicely and easily. When I release metafusion-rails my plan is that instead of installing the finsignia/paths.rb and finsignia/layouts.rb in the lib directory you would be able to do something like the following at the end of your config/environment.rb file:

gem('metafusion-rails', '=MFR_VERSION')
require 'metafusion/rails'

Finsignia::Layouts.map do |map|
  map.connect 'namespace/resource', 'layout'
  # etc....
end
Alternatively you could still keep the layouts.rb file if your environment.rb is getting large and keep the layouts.rb include in your environment.rb.

Model-Conductor-Controller fix

Written on 11:42:00 AM by S. Potter

The New Bamboo blog talked about Presenters & Conductors last month. The demonstration Rails application was broken, so the enclosed link to a zip file contains the fixes I made to get the application running as expected. The offending code was in lib/action_conductor/lib/action_conductor/errors.rb:


module ActiveRecord
  class Errors
   def add_conductor_errors_for(record, mapping)
    return if record.valid?
    record.errors.each do |attribute, message|
      self.add(mapping[attribute.to_sym], message)
    end
   end
  end
end

The fixed code changed one line as so:

module ActiveRecord
  class Errors
   def add_conductor_errors_for(record, mapping)
    return if record.valid? or record.new_record?
    record.errors.each do |attribute, message|
      self.add(mapping[attribute.to_sym], message)
    end
   end
  end
end

The fixed zip file can be found at my 4shared account and also fixes the minor view errors.

Anonymous Classes: Java's Synthetic Closure, Part 2

Written on 10:16:00 PM by S. Potter

So in the last installment we saw something like the following (paraphrased) in Java:


    executeTransaction(session, new PersistenceExecutor() {
      public PersistenceExecutionResult execute() {
        return new PersistenceExecutionResult(session.save(persistableObject);
      }
    });

To accomplish the same thing, though in a more error prone manner, we might just do the following:
  session.open();
  Object obj = session.save(persistableObject);
  session.close();
Now you say, well that looks better. Well yes and no. The problem with this approach arises when the logic in between session.open() and session.close() is more involved. If we had 10 lines of code between the open/close markers and we came to refactor the logic, but omitted the call to close, our tests (if they are purely state-based tests from the TDD world) would probably not catch this omission at the level we would want to catch it. Remember the more levels you need to debug to find the issue the more time you have wasted not doing useful work on new stories or fixing defects, etc. Of course, in the Ruby world where closures are just there for the taking, we don't have to justify ugly looking code to help package reusable "wrapper" logic like above. In a Ruby project that utilizes ActiveRecord we can simply write:

# When a corporate action like merger, spinoff, etc. occurs for an issuer, usually 
# a new option has a basket underlying instead of just a set of equities, so code might 
# look like the following.
Option.transaction(equityOption, basketOption) do
  equityOption.substitute_with(basketOption)
  equityOption.expire
  basketOption.activate
end

So that I don't sound completely smug to my Java friends, I will let you decide which code snippet looks more beautiful!:) Update: One of my Java friends says there are no closures yet in Java 6, but there are talks about closures in 7 (or will it be version 96 next?), but it is all up in the air.

Anonymous Classes: Java's Synthetic Closure

Written on 10:50:00 PM by S. Potter

I've been meaning to write this blog post since late last year when I revisited the Java world after a hiatus of adventures in the Ruby and Python worlds. When I was a Java advocate (many moons ago it seems now), I really didn't appreciate the true beauty that is anonymous classes. I saw them as ugly and reducing code readability. Syntactically I still agree with that assessment, but conceptually I couldn't disagree more now. When revisiting Java last year for a client, I found myself really missing blocks from the Ruby world. The following common snippet of code in Ruby passes a block to a method:


  connection = Net::HTTP.new
  connection.use_ssl = true if ssl?
  connection.start do |connection|
    request = yield connection if block_given?
    request.basic_auth(@login, @password) if require_auth
    response = connection.request(request, body)
    handle_rest_response(response)
    response
  end

[This code is taken from the Twitter4R project and is slightly paraphrased.] What we are doing here is wrapping a block within a begin/end API construct. So the previous snippet of code is effectively the same as (and in fact this is the way the above gets executed):

  connection = Net::HTTP.new
  connection.use_ssl = true if ssl?
  connection.open
  request = yield connection if block_given?
  request.basic_auth(@login, @password) if require_auth
  response = connection.request(request, body)
  handle_rest_response(response)
  connection.close
  response

Why is the first so much better from a code maintainability and readability perspective? In my view the main benefits include (but are not limited to):
  • Passing in a block ensures we close the connection (as in the above code example). If we forgot to "end" the block in the first snippet we would get a SyntaxError in Ruby which would remind us to close the block (and thus connection implicitly). Which means less likely to introduce defects related to not closing connections unlike second code example.
  • Putting the code to be executed passed in with a block improves code readability and in my view that is always a good thing. It is more obvious to fellow developers what is going on.
Now in Java we can't simulate the code above in Java syntax...or can we? I suggest with a few more {} and () and ; we can accomplish the same as above. And if we cover our eyes with our hands and squint through the gaps in our fingers, we might be happy with the way it looks (OK, it is ugly, but this is the world of Java people!). Below is an example of a similar technique as the Ruby example above, but this time in Java and related to database transaction (specifically in Hibernate3 below):

// In PersistenceExecutionResult.java we define the following....
public class PersistenceExecutionResult {
  private Object result;
  public PersistenceExecutionResult(Object theResult) {
    result = theResult;
  }

  public Object getResult() { return result; }
  public void setResult(Object theResult) { result = theResult; }
}

// Now in PersistenceExecutor.java we define....
public interface PersistenceExecutor {
  public abstract PersistenceExecutionResult execute() throws Exception;
}

// Now in BaseDAO.java we have the following....
public class BaseDAO {
  public Long create(BaseObject persistableObject) throws Exception {
    final Session session = ....; // get session somehow - irrelevant to demonstration of synthetic closures in Java
    return (Long)executeTransaction(session, new PersistenceExecutor() {
      public PersistenceExecutionResult execute() {
        return new PersistenceExecutionResult(session.save(persistableObject);
      }
    });
  }

  // other stuff, but just adds noise to example.

  protected ExecutionResult executeTransaction(Session session, PersistenceExecutor executor)
    throws Exception {
    Transaction tx = null;
    PersistenceExecutionResult result = null;
    try  {
      tx = session.beginTransaction();
      result = executor.execute();
      tx.commit();
    } catch (Exception e) {
      if (null != tx) tx.rollback();
    } finally {
      session.close();
    }
    return result;
  }
}

Now what on earth is going on? Anyone? Well I will explain in a subsequent posting and compare it to the equivalent code without using anonymous classes to wrap begin/end API constructs. Right now I am too tired and lazy, but I will explain soon, fear not. Notes: the above works with JDK 1.4 because when I wrote this it had to be 1.4 compliant (therefore fancy generic interface definition to DAO was not possible). I have only used JDK 1.5 (aka 5) and not 6. Is there a nicer way of doing this in 6?

RESTful API & Web Service Statuses

Written on 10:41:00 PM by S. Potter

As I have browsed and read newsgroups, mailing lists, articles and even books related to RESTful APIs and Web Services very few tackle the issue of which HTTP status messages are most relevant for the communication at hand. Below is a list of the few HTTP status codes that I most commonly use in the development of my own APIs and Web Services for my clients:

  • 404 “Resource Not Found”: Yes, you read that right. When a resource of a specific given ID does not exist in our datastore (whether due to it being an invalid ID or due to the resource having been deleted from our datastore). For example, “GET /people/2349594934896107” may not exist in our database, so what do we display? Just the bare “people/show” view with a flash message saying no person with that ID exists? I should say not – at least not in our RESTful world. However, if we are using something like acts_as_paranoid or home-grown paranoid delete mechanism and we know the resource used to exist in our datastore, we could respond with 410 “Gone”. The benefit of this is that we are communicating something useful back to the RESTful consumer, whether that is an end-user or another application or web service.
  • 401 “Unauthorized”: Returned when consumer either did not provide credentials to view restricted resource or the authentication and/or authorization failed. This assumes your application uses the Basic or Digest HTTP Authentication scheme as according to the HTTP protocol the server needs to respond with specific HTTP headers in various circumstances of returning this status code.
  • 503 “Service Unavailable”: Used when taking the site down for maintenance. I find this very handy indeed when upgrading the RESTful web services I deploy at work. For my projects I create a stub Rails application that responds with a 503 for each valid type of request that comes in. Clients of my services are usually services themselves or other applications, so this helps client developers that consume my web services know that this is a temporary blip and should be due to schedule maintenance (and a good reminder for them to check the emails I sent them over the weekend instead of ignoring them). The best and easiest way to do this is to create a new routes.rb file that contains all valid requests and points it to a controller/action that responds with a 503.
  • 201 “Created”: Very handy, especially in the context of designing RESTful web services. Status should be sent when replying to a POST that created a new resource on the server.
  • 202 “Accepted”: Yes, slightly obscure, but very handy. Signals to consumer of application (whether end-user human or REST client application) that a long running/executing job has been submitted for processing, but we do not yet know the outcome of the “job” or “process”. Of course, as with all of the above, to an end-user we would render a nicely designed web page telling them this, but if for some reason a bot (authorized or not) or web services client accessed our application, they would be able to determine some useful information from the status.
Please let me know any other HTTP status codes you find most useful when designing RESTful APIs. Update [2007-08-20]: Below are another couple of HTTP status codes, explanations and examples to aid in the more consistent development of true RESTful APIs:
  • 301 "Moved Permanently": This is a good one when deprecating RESTful APIs. For example, say you are no longer going to support the resource (and all related individual CRUD and custom methods) /corporate_bonds and instead provide the following resource "/bonds/:type" where :type might include corporate, municipal and government. Whenever a valid request is made to the "/corporate_bond" resource I will return a 301 HTTP status to the consumer to inform them that they need to use the new API.
  • 307 "Temporary Redirect": This is a nifty little status when you have temporarily suspended a resource, for example a "/users" resource. If a user is delinquent on their latest monthly installment, you want to start redirecting all calls to their profile, etc. to a temporary page that informs consumers of the RESTful site that this account is not current. There are probably many other good uses of this too. If you know of one, please leave a comment and educate me further.

Story-level BDD specing using rBehave

Written on 12:51:00 PM by S. Potter

For those in the BDD world that have been asleep at the wheel over the last month or two, Dan North has released rBehave which wraps around RSpec and allows developers to spec their code at the story level, which has been missed in RSpec (though I don't know what I would do without RSpec). Dan mentions that he is discussing integrating his work into RSpec at some point in the future, but in the meantime Dan's rBehave framework is currently at version 0.3.0 and is available for install as a Ruby Gem as usual: sudo gem install rbehave Dan gives an example of rBehave using a bank transfer story on his blog. Below I give another example of authenticating a user:

require ‘rubygems’
require ‘rbehave’
require ’spec’ # for "should" method

require ‘user’ # the actual application code

Story "authenticate user",
%(As a user of the system I wish to authenticate and access my account information.) do

  Scenario "correct password is supplied but user has been suspended" do
    Given "my user account is created", User.new do |user|
      @user = User.new
    end
    Given "my username is", "myusername" do |username|
      @user.username = username
    end
    Given "my password is", "mypassword" do |password|
      @user.password = password
    end
    Given "my user account has been suspended", true do |suspended|
      @user.suspended = suspended
    end
    When "I attempt to authenticate with", "mypassword" do |password|
      @authenticated = @user.authentiate?(password)
    end
    Then "my user account should respond with authentication status of", false do |status|
      @authenticated.should be(status)
    end
  end

  Scenario "correct password is supplied and user is active and verified" do
    Given "my user account is created", User.new
    Given "my username is", "myusername"
    Given "my password is", "mypassword"
    Given "my user account has been suspended", false
    Given "my user account has been activated", true do |activated|
      @user.activated = activated
    end
    When "I attempt to authenticate with", "mypassword"
    Then "my user account should respond with authentication status of", true
  end

  # A few other scenarios go here, like non-activated user (but not suspended), etc.
  # Left as exercise to the reader.
end
Please note, i do not need to restate the Given, When or Then blocks passed in after they are defined once!

Twitter4R v0.2.4 Released

Written on 3:24:00 PM by S. Potter

Version 0.2.4 of Twitter4R was released last night with the main change that makes the library much easier to use in Ruby on Rails applications. To install/update Twitter4R v0.2.4 just use the following command: sudo gem install twitter4r For more information on how to integrate Twitter4R with Rails see the following links:

My Blackle Energy Consumption Experiment

Written on 11:14:00 AM by S. Potter

I received a forward from a contact earlier today touting the energy savings of using Blackle instead of Google to search. My immediate thought was surely there would be no difference for an LCD monitor, or would there? I can, of course, see for CRT monitors energy savings are highly likely. The following is the energy saving content from the email forward I received:

We probably all use google several times a day - here's something to consider: When your screen is white, be it an empty word page, or the Google page, your computer consumes 74 watts, and when its black it consumes only 59 watts. An article about the energy saving that would be achieved if Google had a black screen, taking into account the huge number of page views, worked out at a saving of 750 mega watts/hour per year. In a response to this article Google created a black version of its search engine, called Blackle, with the exact same functions as the white version, but with a lower energy consumption, check it out: http://www.blackle.com
Since I work for a very energy conscious firm and I have been cycling or walking EVERYWHERE around town this summer (except I allow myself use of my car one afternoon a week maximum), I was curious and had to get to the bottom of it.

Hypothesis

There will not be any significant change in power usage (aka wattage) when viewing Google.com in my web browser versus viewing Blackle.com in the same web browser window. (I must confess I already knew how LCD monitors worked before conducting this experiment, but there are a surprising number of people who do not believe the backlight of an LCD monitor is on for the entire LCD regardless of what is being shown - ignorance is bliss for some).

Conditions

  • Dimensions of browser on screen must remain constant for both page views
  • Power reader is monitored for 30 seconds while each page is being displayed and all reading changes will be noted

Equipment

  • P3 Kill-A-Watt reader (x1)
  • 17" Dell LCD monitor (x1)
  • [Indirectly used] Dell workstation (x1)
  • Cell phone (to time page view durations)

Preparation

  1. Power off LCD monitor and unplug from power supply
  2. Plug in power reader into power supply and plug LCD monitor plug into power reader socket
  3. Select power reading (can choose from voltage, power and current "real-time" readings)
  4. Record initial "power off" reading
  5. Power on LCD monitor
  6. Record initial "power on" reading

Method

  1. Open web browser and maximize window
  2. Surf to http://google.com
  3. Start cell phone timer as soon as page is loaded
  4. Monitor power reading for 30 seconds and note any changes to reading
  5. In same web browser window surf to http://blackle.com
  6. Start cell phone timers as soon as page is loaded
  7. Monitor power reading for 30 seconds and note any changes to reading

Results

Initial "power off" reading: 0W (zero Watts) Initial "power on" reading: 20W (twenty Watts) after 3-4 seconds Google reading: started at 20W, no change for 30 seconds, ended at 20W. Blackle reading: started at 20W, no change for 30 seconds, ended at 20W.

Analysis

As we can see there was no significant change in the reading (to stated equipment error of plus or minus 0.2%). Therefore our hypothesis is shown to hold in this experiment using the equipment identified above and I conclude that Blackle has next to zero impact on energy consumption of LCD monitors. The good news is that my LCD monitor consumes much less than Blackle's claim of 50-75 Watts regardless of the search engine presentation I use!:)

Twitter4R v0.2.3 Released

Written on 12:50:00 PM by S. Potter

As promised earlier this week Twitter4R v0.2.3 made it out this weekend. Twitter4R v0.2.3 has just been uploaded to Rubyforge in Ruby Gem, tgz and zip formats. It may take up to several hours for the Rubyforge download mirrors to sync, so you may not be able to download it for a while. To upgrade/install using Ruby Gems: $ sudo gem install twitter4r I am in the process of updating the project website. So bear with me while I take care of this. Release Notes:

  • Fixed defect #31 such that passing string screen name as for user argument is handled correctly.
  • Fixed #30 typo: respond_to -> respond_to?
  • Added relevant exception handling for #message(:post, ...) case #32
  • Add ability to pass in Twitter::User object to Twitter::Client#user(...) #33
  • Added stats Rake task
  • Updated RDoc for Twitter::Client#user to warn against using it to get followers of authenticated user and updated ArgumentError raising logic as per #29.
Enjoy!

Twitter4R v0.2.2 Released

Written on 5:21:00 PM by S. Potter

As solo already noted in the comments for the Twitter4R v0.2.1 Released blog entry from yesterday, version 0.2.2 is already here:) I usually wait 4 hours before I blog about it to let the Rubyforge gem servers sync. Changes in the version 0.2.2 release include:

  • Fixed URI paths for user, messaging and friendship APIs (#25)
  • Added action checks for Twitter::Client methods: #user, #my, #message, #messages, #status, #timeline, #friend (#26)
  • Added 'source' configuration documentation.
  • Added missing attributes for Twitter::User (#28)
The most notable thing is that I add the protected and profile_image_url attributes to the Twitter::User model class, which were missing previously.

Twitter4R v0.2.1 Released

Written on 10:30:00 AM by S. Potter

To install/update Ruby gem use: sudo gem install twitter4r OR sudo gem update twitter4r Note: It may take some hours before the Rubyforge mirrors are in sync. The ‘source’ feature was added to Twitter v0.2.1 such that in the web interface Twitter users can see which application the messages were sent from. The default is ‘twitter4r’ with link ‘http://twitter4r.rubyforge.org’. You can change this, but you will also need to contact Twitter developers (not Twitter4R) to set it up for you on the server side. To set relevant client configuration in Twitter4R you can do the following:


require('rubygems')
gem('twitter4r', '>=0.2.1')
require('twitter')

Twitter::Client.configure do |conf|
  conf.source = 'mysourceidfromtwitter'
end
Remember you will need to contact the Twitter developers to setup a source id and url with them before the above code snippet will start working. Enjoy!

Twitter4R v0.2.0: Friendship API

Written on 8:15:00 AM by S. Potter

After a week I realized I had not yet written about the Friendship API in Twitter4R and it is one of the simpler the parts of the core library. Below shows some code to add and remove a friend:


require('rubygems')
gem('twitter4r', '>=0.2.0')
require('twitter')

client = Twitter::Client.new(:login => 'mylogin', :password => 'mypasswd')
# we can add a friend by using their unique integer id or user object.
screen_name = 'myfriend'
user = Twitter::User.find(screen_name)
id = user.id

# Like any of the following....
client.friend(:add, user)
client.friend(:add, id)

# We can also use any of the following APIs to do exactly the same thing...
client.friend(:remove, user)
client.friend(:remove, id)
See related Twitter4R RDoc.

Twitter4R v0.2.0: Extras API

Written on 6:44:00 PM by S. Potter

There are a couple of miscellaneous features in Twitter4R that I wasn't comfortable officially supporting like the featured users method in the Twitter REST API that is documented to do one thing and implemented to do another by the actual Twitter developers. Obviously I don't control what the Twitter developers do, so while I wanted to support this REST API in the library to achieve 100% Twitter REST API coverage I also wanted to emphasize that if the Twitter REST API implementation changes to work as documented then Twitter4R may not work well or at all for this method. To get an Array of Twitter::User objects that represent Twitter's current featured users we would write code like:


require('rubygems')
gem('twitter4r', '0.2.0')
require('twitter')

# Now we must include this extra file import or we will get a NoMethodError
require('twitter/extras')

client = Twitter::Client.new
users = client.featured(:users)
Also in the Twitter4R Extras API is a class helper method added to Twitter::Client called from_config which takes up to two arguments. Here is an example:

require('rubygems')
gem('twitter4r', '0.2.0')
require('twitter')

# I have a 'twitter.yml' file in subdir 'config' that I will load credential from.
# This feature of Twitter::Client is in the Extras API and you need to require
# twitter/console to access it.
require 'twitter/console'
config_file = File.join(File.dirname(__FILE__), 'config', 'twitter.yml')
twitter = Twitter::Client.from_config(config_file)

# By default the from_config method selects the credentials defined
# for the 'test' environment.  The twitter.yml file will look something like this:
#  test:
#    login: mylogin
#    password: mypasswd
# If, however, we wanted another environment's credentials to be used we pass that 
# in as the second argument to from_config like so...
twitter = Twitter::Client.from_config(config_file, 'dev')
Again here is a link to the Twitter4R v0.2.0 RDoc.

Twitter4R v0.2.0: User API

Written on 6:33:00 PM by S. Potter

The User API provides an easy Ruby API that maps onto a less consistent Twitter REST API, which is good news for Twitter4R users:) The following example code snippet shows how you would use the Twitter4R User API:


require('rubygems')
gem('twitter4r', '0.2.0')
require('twitter')

# I have a 'twitter.yml' file in subdir 'config' that I will load credential from.
# This feature of Twitter::Client is in the Extras API and you need to require
# twitter/console to access it.
require 'twitter/console'
config_file = File.join('config', 'twitter.yml')
twitter = Twitter::Client.from_config(config_file)

# Gets the Twitter::User object for user with screen name otherlogin.
user = twitter.user('otherlogin')
# Gets an Array of Twitter::User objects that represent the friends of user 
# with screen name otherlogin.
friends = twitter.user('otherlogin', :friends)

# Gets the Twitter::User object for the user we used to authenticate above
# using the Twitter::Client.from_config method in the Extras API above.
me = twitter.my(:info)

# Gets the Array of Twitter::User objects that represent the followers of 
# the user whose credentials were used to authenticate with the Twitter service.
myfollowers = twitter.my(:followers)
# The same can be done for friends, i.e.
myfriends = twitter.my(:friends)

# Alternative we can use the equivalent Model APIs like so....
myfollowers = me.followers
myfriends = me.friends
Hope you find this helpful. If you want to learn more about the User API you should consult the Twitter4R v0.2.0 RDoc.

Twitter4R v0.2.0: Messaging API

Written on 9:18:00 AM by S. Potter

Probably one of the more recent additions to the Twitter REST API has been that of direct messaging. In the Twitter web interface we were introduced to the supported construct of direct messaging only a couple of months ago. The Messaging API in Twitter4R provides access to these new supported features via two instance methods on the Twitter::Client class: #message and #messages. Below we can see an example of a common non-trivial usage of this API:


gem('twitter4r', '>=0.2.0')
require('twitter')

client = Twitter::Client.new(:login => 'mylogin', :password => 'mypassword')
received_messages = client.messages(:received)
# Now do whatever it is you wish to do with the Array returned and assigned to received_messages.
# It is an Array of Twitter::Message.  Twitter::Message has the following attributes:
# * sender (Twitter::User)
# * recipient (Twitter::User)
# * text (String)
# * created_at (Time)

new_message = client.message(:post, 'I am addicted to Twitter', 'myfriendslogin')
# Note: if we had a handle to our friend's Twitter::User object we could substitute that object
# for the screen name screen given (i.e. 'myfriendslogin' in the above example).  This would 
# look like:
#  new_message = client.message(:post, 'I am addicted to Twitter', user)

# Now we realized sending that message was a big mistake, so we try to delete is quickly:
deleted_message = client.message(:delete, new_message)
To find out more about the Messaging API of Twitter4R visit the v0.2.0 RDoc and select the examples/messaging.rb link from the top left frame. Also note that you can create new direct messages via the Model API of Twitter4R as well. Happy direct messaging on Twitter using Twitter4R.

Twitter4R v0.2.0: Model API

Written on 8:53:00 AM by S. Potter

For those that are familiar with the ActiveRecord API, the Model API of Twitter4R should feel fairly natural for you. Currently the raw Twitter REST API inherently exposes three types of models:

  • Status
  • User
  • Message
These model classes are available via the Twitter4R classes Twitter::Status, Twitter::User and Twitter::Message respectively. What you will notice on these classes is that they have the familiar find and create class methods that behave almost identically to ActiveRecord::Base subclasses (or models as we call them in Rails applications). There is one difference, due in part to the slightly different way "connections" to the Twitter4R "datasource" is handled. So below is a non-trivial example of using these ActiveRecord style methods:

gem('twitter4r', '>=0.2.0')
require('twitter')

client = Twitter::Client.new(:login => 'mylogin', :password => 'mypassword')
# I want to post a new status to my timeline, which can also be do by:
#  client.status(:post, 'My new status message')
status = Twitter::Status.create(:client => client, :text => 'My new status message')

user = Twitter::User.find('dictionary', client)
message = Twitter::Message.create(:client => client, :recipient => user, :text => 'canadaphile')
What we see here is very similar to ActiveRecord style models, but not quite. In the find method invoked on Twitter::User above we supplied a second argument, which is the client (the client context or "connection") object. We also make sure to pass in a :client key-value pair in each create method called above. Without it an ArgumentError would be raised by each create call. A few notes:
  • Twitter::User does not define a meaningful create method since Twitter doesn't allow the creation of new user account via their REST API
  • Twitter::User model has some class and instance helper methods, which are described more in the RDoc (see link below)
See the v0.2.0 RDoc for Twitter4R and select the examples/model.rb link from the top left frame.

Twitter4R v0.2.0: Status API

Written on 9:35:00 PM by S. Potter

The Status API deals with access to single status objects on the Twitter server. Since "updating" (in the traditional sense) a status in Twitter is non-existent (you update your timeline with a brand new status, but you cannot edit individual status text after created), Twitter4R provide a CRD-style interface (rather than true CRUD-style). For example, to post a new status to your timeline you might write code like the following:


gem('twitter4r', '>=0.2.0')
require('twitter')
client = Twitter::Client.new(:login => 'mylogin', :password => 'mypassword')
status = client.status(:post, 'Learning the Twitter4R Status API in 60 seconds.')
To retrieve the full status object (Twitter::Status instance - see Twitter4R Model API documentation) given a unique status ID you can code:

# Assume code in previous code snippet
status = client.status(:get, 140684282) # => should be announcement status of Twitter4R v0.2.0 release
# Now you can query the attributes of the status like...
puts "#{status.user.screen_name}: #{status.text} @ #{status.created_at}"
To delete the status we initially posted above (or any other status of yours that you have a handle to (a handle can be just the unique integer status ID or the status object itself) we can code:

# Again assume code from first code snippet above...
client.status(:delete, status) # here we can pass in the full object or just the id, it doesn't matter.  Same as in #status(:get, ...) case.

Upgrading from 0.1.x

To upgrade from Twitter4R v0.1.x you probably need to replace code like:

client.update('My status message text')
With code like the following:

client.status(:post, 'My status message text')
There were no APIs in Twitter4R v0.1.x for the single status :get and :delete use cases.

Twitter4R v0.2.0: Timeline API

Written on 9:06:00 PM by S. Potter

The Timeline API segment of Twitter4R provides access to the REST API that deals with public, friend and [public] user status timelines. For example, to get an Array of statuses representing your own timeline on Twitter all you would need to type was:


gem('twitter', '>=0.2.0')
require('twitter')
client = Twitter::Client.new(:login => 'mylogin', :password => 'mypassword')
timeline = client.timeline_for(:me)
If your timeline is public and you do not need to invoke any authenticated methods, you do not need to pass in login/password credentials to the Twitter::Client constructor, just Twitter::Client.new is all that is needed. Other than the :me timeline, the following are available:
  • :public - returns the public timeline on Twitter (last 20 statuses)
  • :friends - returns a timeline of all your friends' statuses
  • :friend - returns a timeline of one particular friend
  • :user - returns timeline of one particular user
Different options are available with each of the timelines described above. To read more consult the Twitter v0.2.0 official RDoc documentation.

Upgrading from 0.1.x

If you are upgrading from Twitter4R v0.1.x you will probably have code that looks similar to the following:

client = Twitter::Client.new(:login => 'mylogin', :password => 'mypassword')
timeline = client.public_timeline
All you will need to do is change the last (second) line above to:

timeline = client.timeline_for(:public)

Twitter4R v0.2.0: Configuration API

Written on 2:48:00 PM by S. Potter

The Configuration API allows applications using Twitter4R configure static connection options such as:

  • connection protocol (e.g. HTTP and SSL)
  • host name and port (good for testing or if the Twitter REST API moved to a different subdomain)
  • proxy settings (i.e. proxy host, port, username and password)
  • user agent identifier
  • X-Twitter-Client* HTTP headers for Twitter's internal usage analysis
This API intentionally tries to look like the Rails initializer/configuration API:

require('rubygems')
gem('twitter4r', '0.2.0')
require('twitter')

Twitter::Client.configure do |conf|
  # We can set Twitter4R to use :ssl or :http to connect to the Twitter API.
  # Defaults to :ssl
  conf.protocol = :ssl

  # We can set Twitter4R to use another host name (perhaps for internal
  # testing purposes).
  # Defaults to 'twitter.com'
  conf.host = 'twitter.com'

  # We can set Twitter4R to use another port (also for internal
  # testing purposes).
  # Defaults to 443
  conf.port = 443

  # We can set proxy information for Twitter4R
  # By default all following values are set to nil.
  conf.proxy_host = 'myproxy.host'
  conf.proxy_port = 8080
  conf.proxy_user = 'myuser'
  conf.proxy_pass = 'mypass'

  # We can also change the User-Agent and X-Twitter-Client* HTTP headers
  conf.user_agent = 'MyAppAgentName'
  conf.application_name = 'MyAppName'
  conf.application_version = 'v1.5.6'
  conf.application_url = 'http://myapp.url'
end
If using Twitter4R (open source Ruby library for Twitter REST API) in a Rails application I would recommend adding the necessary Twitter4R configuration code in config/twitter.rb then including it in the config/environment.rb file to keep configurations separate.

Upgrading from 0.1.1

In Twitter4R v0.1.1 you could configure protocol, server host, server port and proxy settings using the following code:
Twitter::Client.conf(
  :ssl => true, 
  :port => 443, 
  :proxy_host => 'myproxy.host', 
  :proxy_port => 8080)
This API no longer exists, so if upgrading to Twitter4R 0.1.1 you will need to change the above to something like the following:
Twitter::Client.configure do |conf|
  conf.protocol = :ssl
  conf.port = 443
  conf.proxy_host = 'myproxy.host'
  conf.proxy_port = 8080
end
This change shouldn't be too painful for people I hope. Please refer to the Twitter4R v0.2.0 RDoc for more information.

Twitter4R 0.2.0 Release

Written on 12:01:00 PM by S. Potter

It's been almost two weeks, but Twitter4R had some major refactoring and updating between 0.1.1 and 0.2.0. So I hope you will think the wait was worth it. As usual you can grab the latest Ruby Gem (once all Rubyforge mirrors have synced) with: sudo gem install twitter4r. I am still in the process of pushing out the new Ruby Gem to Rubyforge and updating the website for the project, but the release is code complete. Some changes are quite significant in some areas. The basic idea of this release (0.2.0) was to cover the official published Twitter REST API 100% and remove some of the clutter the first two releases of Twitter4R introduced partly due to mirroring Twitter's inconsistent API. In the same fashion as both previous releases of Twitter4R, version 0.2.0 has 100% C0 code coverage via RSpec specifications. I separated out Twitter4R library into various API segments:

I will write a blog entry as an introduction to each segment over the next couple of days. In the meantime I will finish publishing the new website documentation and leave you with the RDoc for version 0.2.0. Updates:

Twitter API's unRESTfulness causing trouble

Written on 11:07:00 AM by S. Potter

I am now designing my sixth (6th) set of web services on my new project. The previous five (5) and this current venture were/are for the purpose of allowing partners and internal developers to integrate with a "platform" of some kind. These clients varied from Fortune 500 companies, prestigious privately held firms and most recently a VC-funded startup. The previous two web service APIs were based on the REST "architecture" (yes, I know that word gets used too much, but I use it now for lack of a better word at this moment). The previous REST API was in fact fully RESTful and my new software product design also has the aim of RESTfulness at all points. During my travels meandering through the windy trails of designing REST APIs, I have realized just how important being truly RESTful is to REST APIs. Yes, reread that sentence again. if it doesn't make sense I recommend reading RESTful Web Services, which should clarify this distinction for you. For me, the biggest plus of RESTful web services is that it promotes consistency at all levels. Moving on...where was I going? Ah, yes, Twitter... The routes.rb for twitter.com seem to be a little misleading. It seems a mix and match approach was taken to developing a "RESTful" API for twitter.com using Rails. The current RESTful Rails convention is the following (in a nutshell):

  • GET /resources/:id.:format => get information about resource with specified :id
  • POST /resources => create new resource using data given in request body
  • PUT /resources/:id.:format => update resource with specified :id using data given in request body
  • DELETE /resources/:id.:format => delete resource with specified :id and return resource in :format given
This is all great, however, in twitter.com it seems there was a previous ugly step-child of a "RESTful" design implemented before Rails 1.2 standardized on the above approach (just speculating here). The evidence of this exists in multiple places in the Twitter API's inconsistency, however, one case in particular creates a defect in my view (and I am being kind by not considering all RESTful violations as defects, since the Twitter developers need to cater to the weekend PHP and VB.NET hackers that might not care about the beauty of REST's simple yet consistent design and purpose - I will give the Twitter developers the benefit of the doubt for this article): In the users#show API there seems to be a conflict. For example, GET /users/show.json?id=mylogin will, in fact, return the user information for user with screen_name ‘show’ instead of ‘mylogin’, which is obviously not the intention of the above HTTP call, especially when /users/show is an advertised way to access user information in the official Twitter API docs. Yes, I know I can call GET /users/mylogin.json and be done with it, but the misleading defect still remains. Interestingly, GET /users/show?id=mylogin does yield the expected result assuming you have the Accept HTTP header set to the appropriate mime-type (e.g. 'text/x-json'). Screen names that cause potential conflicts (if using Rails defaults) are: create, destroy, update, new, show. All of which exist at twitter.com. My point here isn't to blast Twitter developers necessarily, simply to ask all developers (or if you must title yourself this way "architects") to consider the importance of creating consistent APIs, preferably based on open standards/recommendations like REST. And most importantly, if a REST API isn't truly "RESTful", don't say it is!:) You will get more respect for being honest from people that know what it really means. Related post:
  • Twitter4R v0.2.0 Released - the ultimate open source library that provides Ruby bindings for 100% of the officially documented Twitter REST API.

Marrying Autotest with RSpec on Gnome

Written on 5:54:00 PM by S. Potter

For those that don't know marrying Autotest with RSpec is a God send and I highly recommend it for pure Ruby as well as Rails application development. On the Mac OS X and KDE desktop environments there are builtin Autotest extensions that visually flag erroneous or successful RSpec runs with a standard desktop notification message. For Gnome users on Linux running Autotest with RSpec you may have seen a couple of ${HOME}/.autotest customizations out there that don't work with RSpec v1.x+. Below is my ${HOME}/.autotest that works with RSpec 1.0.5:


require('autotest/redgreen')
require('autotest/timestamp')

module Autotest::GnomeNotify

  # Time notification will be displayed before disappearing automatically
  EXPIRATION_IN_SECONDS = 3
  ERROR_STOCK_ICON = "gtk-dialog-error"
  SUCCESS_STOCK_ICON = "gtk-dialog-info"

  RE_RSPEC_SUMMARY = Regexp.new(/(\d+) examples, (\d+) failure/)

  class << self
    # Convenience method to send an error notification message
    #
    # [stock_icon]   Stock icon name of icon to display
    # [title]        Notification message title
    # [message]      Core message for the notification
    def notify(stock_icon, title, message)
      options = "-t #{EXPIRATION_IN_SECONDS * 2500} -i #{stock_icon}"
      system "notify-send #{options} '#{title}' '#{message}'"
    end

    def compose_message(at)
      specs, failures = 0, 0
      at.results.scan(RE_RSPEC_SUMMARY) do |s, f|
        specs = s.to_i
        failures = f.to_i
      end
      "#{specs} specs, #{failures} failures"
    end
  end

  Autotest.add_hook :red do |at|
    notify ERROR_STOCK_ICON, "Some specs failed.", compose_message(at)
  end

  Autotest.add_hook :green do |at|
    notify SUCCESS_STOCK_ICON, "All specs passed.  Have a beer!", compose_message(at)
  end
end
I assume there this will work with RSpec 1.x, but I have only tested with RSpec 1.0.5. Enjoy!

RSpec "should_yield"?

Written on 4:51:00 PM by S. Potter

While writing some specifications for the 0.2.0 version of Twitter4R (soon to be released - honestly), I realized there was no clean way to do #should_yield, so I thought I would offer my suggestion for the developers of RSpec to critique as appropriate. Assume we have a method signature that takes a block and should yield an object to the block given (if one is given). Something like the following:

class Client
  class << self
    def configure(&block)
      # implementation of Client.configure
    end
    # rest of class methods for Client class go here...
  end
  # rest of instance methods for Client class go here....
end
Where usage for the Client.configure method should look something like the following:
Client.configure do |conf|
  conf.proxy_host = 'myproxyhost'
  conf.proxy_port = 8080
  conf.proxy_user = 'me'
  conf.proxy_pass = 'mypass'
end
The object yielded to the block given is an instance of Config which has settable attributes: proxy_host, proxy_port, proxy_user, proxy_pass. The question is how do we write RSpec specifications for this? First we need to define what we want to specify for the expected behavior. My first attempt is to describe (in English) that:
  • Client.configure should
    • accept a block as argument
    • yield a Config instance to the given block
The way I did this in the Twitter4R (open source Ruby library for the Twitter REST API) project specifications was something like the following:
describe Client, ".configure" do
  before(:each) do
    @has_yielded = false
    @block = Proc.new do |conf|
      conf.is_a?(Config).should be_true
      @has_yielded = true
    end
  end

  it "should accept a block as argument" do
    lambda {
      Client.configure {}
    }.should_not raise_error
  end

  it "should yield a Config object to given block" do
    Client.configure(&block)
    @has_yielded.should be_true
  end

  after(:each) do
    @has_yielded, @block = nil
  end
end
What I would really like to do something like the following instead:
describe Client, ".configure" do
  it "should accept a block as argument" do
    lambda {
      Client.configure {}
    }.should_not raise_error
  end

  it "should yield a Config object to given block" do
    lambda {
      Client.configure(&spec_block(1))
    }.should yield_with(Config)
  end
end
I haven't extended the Proc class to this point, but if/when I do I will post it to this blog and perhaps also to the RSpec mailing list. If you have suggestions on how to improve the more manual way (the first RSpec code listing above), please do let me know. I have not yet played around with inheritable describe blocks yet, but I have hunch that would DRY up some of the code for if needed for many contexts. Alternatively if you have suggestions on how to improve the second RSpec code listing (directly above) to be more clear, please do let me know that too. I realize the developers of RSpec don't want to have too many #should_XXX methods, so I am open to suggestions (since I also agree with that notion). UPDATE: I created a new feature request and dialog with RSpec developers at: [#11949]

Good Riddance to Bad Rubbish

Written on 7:30:00 AM by S. Potter

While I usually do not express views about current affairs and political figures on this blog (except to highlight a point as applied to software), I couldn't let this beautiful day go by without a mention of the sheer delight I am experiencing being freed from the shackles and chains that was Tony Blair's premiership. My only regret is this has come ten years too late. Tony Blair bitten by British bulldog
For those that do not know what our "fabulous" Tony has done domestically in UK, you should watch the British documentary Taking Liberties released earlier this summer. You can view the trailer below: If you still want to know more about the police state in UK created by Blair (all in the convenient name of "terrorism"), just ask Jean Charles de Menezes...oh wait, you can't. This Brazilian man was shot dead by Blair's police state at the Stockwell Tube station on the London underground. He was NOT affiliated to the London bombings in 2005 or any other terrorist plot or organization.

Twitter4R 0.1.1 Released

Written on 12:36:00 PM by S. Potter

Twitter4R, the open source Ruby project for the Twitter REST API, version 0.1.1 has been released this afternoon as a Ruby Gem. It may take a little time for the new Gem to appear on the Rubyforge gem servers, but once replicated to all mirrors you should be able to do the following: require 'rubygems' gem 'twitter4r', '0.1.1' require 'twitter' client = Twitter::Client.new(:login => 'mylogin', :password => 'mypassword') timeline = client.public_timeline timeline.each do |status| puts "#{status.user.screen_name}: #{status.message}" end This will get you started. Changes from 0.1.0 to 0.1.1 include:

  1. Addition of proxy support
  2. Addition of SSL support
  3. Migration from RSpec 0.8.2 to 1.0.x specifications
For proxy support you can do the following: require 'rubygems' gem 'twitter4r', '0.1.1' require 'twitter' Twitter::Client.config(:proxy_host => 'myproxy.host', :proxy_port => 8080) # continue using Twitter4R API as above. Or if you have a proxy server that requires authentication, you can do the following: Twitter::Client.config(:proxy_host => 'myproxy.host', :proxy_port => 8080, :proxy_user => 'myproxyuser', :proxy_pass => 'myproxypass') # continue using Twitter4R API as above. To force Twitter4R to use SSL, we would write the following configuration code before using the client API: Twitter::Client.config(:ssl => true, :port => 443) Enjoy!

Twitter4R Revamp

Written on 9:14:00 AM by S. Potter

While nothing has yet to be release for Twitter4R since version 0.1.0, it has not be standing still. A number of changes will be released within the next few business days. Firstly, I will be releasing Twitter4R 0.1.1 with just two public API changes (and one internal change):

  • [PUBLIC] SSL support
  • [PUBLIC] Proxy support
  • [INTERNAL] Migration from RSpec 0.8.2 to 1.0.x
Secondly, I will also be releasing Twitter4R 0.2.0 very soon after 0.1.1, which will contain a number of API changes. These changes are mostly documented in the comments of ticket #6 in Twitter4R's Retrospectiva project. I plan writing much more RDoc documentation for 0.2.0 and subsequent 0.2.x releases. Lack of documentation was a problem with the 0.1.0 release. The reasoning for the quick succession of releases of Twitter4R is so that 0.1.x versions can have the same public API, whereas 0.2.x signifies a change in API to allow us to cover more of the Twitter REST API without cluttering the Ruby API too much. A major reason for the API revamp was to hide some of the very unRESTful and asymmetric things the Twitter team have exposed in their public API. If any of the Twitter team are reading this, I recommend RESTful Web Services. I admit to dropping the ball on the next release of Twitter4R gladly, because it is true. I violated the first rule of agile development: Do not bite off more than you can chew! I apologize and will learn from it. Thanks for your patience.

Twitter4R API Changes

Written on 7:15:00 PM by S. Potter

Twitter4R 0.1.1, will have three new features:

  • Proxy support. Thanks to Kaiichi Matsunaga for donating the code.
  • SSL support. It will now be the default, but Twitter4R client can easily be configured to use HTTP instead
  • Major API refactoring such that Twitter4R feels more like Ruby and to obtain 100% programmable client coverage of the Twitter REST API. Refer to ticket #6 for more information on this
I'll be posting more on the changes and showing examples in later blog entries shortly after the release. Update: Refer to the following blog entries for updates to this:

The Java Dogma and Downfall

Written on 9:40:00 AM by S. Potter

Despite applying his talents towards a despicable cause, Joseph Goebbels (Hitler's propaganda minister from 1933) had amazing insights into public behavior and described his most famous observations:

If you tell a lie big enough and keep repeating it, people will eventually come to believe it. The lie can be maintained only for such time as the State can shield the people from the political, economic and/or military consequences of the lie. It thus becomes vitally important for the State to use all of its powers to repress dissent, for the truth is the mortal enemy of the lie, and thus by extension, the truth is the greatest enemy of the State.
Using Goebbels' wisdom Java™ successfully launched their propaganda campaign initially against C/C++ with the rhetoric that Java guards against programming errors unlike its predecessor. Yes, Java does lean very heavily toward a dictatorship style of governing. This is without doubt. But does this translate to guarding against developers' own stupidity? When moving from C++ and Perl to Java back in 1997 I appreciated this dictatorship-style of governing very much. It removed *many* cycles of thought from development and I became lazy. Earlier in this new millennium I moved to a more democratic language called Python, where I enjoyed this new found freedom. Soon after the Python doctrine embedded deep-rooted values in me, I discovered the [almost] Libertarian utopia that is Ruby (though not perfect, thus the [almost]). Where am I going with this? Excellent question. Where was I? Ah yes, Java™, propaganda, Goebbels, etc.... Now that Java™ has new mortal enemies - Ruby, Python and other non-static languages that provide significant productivity gains as compared with large bloated development in Java - the Java™ machine is using the same rhetoric. Is it working? In short, for the software Lemmings that don't think, it is working well. Due in large part to the Java Dogma. In truth I find the liberty offered by these non-static languages (yes, Python that still includes you, weird *strongly*-typed cousin) provide a much greater guard against programmer stupidity than the forced dictatorship style of Java™. This is because the developer and development team must take full ownership of the responsibility for their work and thus disciplined, smart and agile developers tend to flock to these languages, leaving the lazy developers only looking for a nice pay check for using the right buzzwords at the right time in the Java world. Java now faces a big problem: how to not let this lazy and uninspiring critical mass not weigh it's reputation down too much? This is an excellent question and one I fear Ruby will need to find an answer to soon, if PHP hackers with no real software development understanding keep flocking to Ruby through Ruby on Rails. The growing popularity of Ruby due to Rails will bring the current community (you and I) problems that we will need to start thinking about now if we are to survive the consequences. Or we will suffer a very similar fate to Java, which would be very sad indeed.

Are you a Cowboy Monkey?

Written on 8:08:00 AM by S. Potter

Everyone of us has probably met one. You may have worked for one as a subordinate. You may even be one. Though many people do not know what a Cowboy Monkey is. In a nutshell a Cowboy Monkey is that person on your team that jumps in to save the day with a hack. They are the ones that repeatedly put out the same fires instead of making sure they don't start them again. A Cowboy Monkey can't be bothered to take 5 minutes a day to commit code changes into a SCM and resync their working copy, but spends half the day in meetings not achieving anything. They are generally horrible dates because they spend all dinner talking about "quick and dirty" solutions. Generally a Cowboy Monkey:

  • does not believe in any type of SCM system.
  • does not think about the longer-term consequences of their actions only the immediate effects.
  • does not believe in unit, functional, integration, performance or system testing or specifications of any kind.
  • prefers to test in production rather than create a whole new staging environment.
  • has not heard of best practices.
  • has a tendency to build silo systems.
  • has a total disregard for the software architecture of a project.
  • usually has no real understanding of design patterns of any kind.
  • thinks PHP is so good that it can solve the war in Iraq.
  • gives smiles and nods but delivers very few of the promises they make and almost nothing on time.
If you nodded at least 5 times in agreement with any of the statements above, you might be a full-fledged Cowboy Monkey. Do not fear, you are amongst friends and we will help you overcome this disorder. If you are not one, how many Cowboy Monkey's have you worked with thus far? Please feel free to share your (anonymous please) stories of the Cowboy Monkey's you've met on the job in comments.

Higher Order Messaging

Written on 11:37:00 AM by S. Potter

In response to a devChix blog entry called RUBY: DRY up your Enumerations, I thought I would offer my alternative to the suggested notion of higher order messaging below:

# In reference to http://www.devchix.com/2007/05/25/ruby-dry-up-your-enumerations/
# Examples by Susan Potter [http://susanpotter.net]

# Framework code...

class ProxyBase
  attr_accessor :collection
  
  def initialize(collection)
    @collection = collection
  end
end

class AnyProxy < ProxyBase
  def method_missing(sym, *args)
    @collection.each do |item|
      return true if item.send(sym, *args)
    end
  end
end

class AllProxy < ProxyBase
  def method_missing(sym, *args)
    @collection.each do |item|
      return false unless item.send(sym, *args)
    end
  end
end

module LinkingVerbMixin
  def self.included(base)
    base.extend(ClassMethods)
  end
  
  module ClassMethods
    def any(collection)
      AnyProxy.new(collection)
    end
    
    def all(collection)
      AllProxy.new(collection)
    end
  end
end

class Are
  include LinkingVerbMixin
end

class Do
  include LinkingVerbMixin
end

# Application code...

class Stooge
  attr_accessor :name, :bald
  
  def initialize(name, bald)
    @name = name
    @bald = bald
  end
  
  def bald?
    @bald
  end
end

mo = Stooge.new("Mo", true)
larry = Stooge.new("Larry", false)
curly = Stooge.new("Curly", false)
stooges = [mo, larry, curly]

Are.all(stooges).bald?
Are.any(stooges).bald?

# This can be extended further to do stuff like Do.all(stooges).have(:name, :eql?, "Mo"), etc.
Or see the pastie.

RSpec 1.0.0 Finally Here!

Written on 3:32:00 PM by S. Potter

Thanks to the RSpec team for finally delivering RSpec, which I have been excited about for quite some time. Having about 5 projects (2 pure Ruby, 3 Rails) using RSpec 0.8.2 and 2 other projects still on (0.7.5) I was a little worried about migrating to the new RSpec 1.0.0 compliant API, which is a fair bit different than some of what was allowed in 0.8.2 and prior. However, the RSpec developers in all their wisdom foresaw this problem and wrote spec_translate where you simply give it the from directory and to directory in that order on the command line. Now it doesn't do everything, so don't expect too much, but it will convert all the context, setup, specify and teardown blocks to describe, before, it and after blocks respectively with very little fanfare or effort required from us. The gotchas for those of us that ignored the Test::Unit Cheat Sheet on the website for recommended RSpec usage (I am going to own up to it, yes):

  1. .should.eql expected_value becomes .should eql(expected_value)
  2. .should.not... becomes .should not...
  3. .should.be expected_result becomes .should be(expected_result)
Let me know if you find different varieties of the above API differences.

Are you a Pig or a Chicken?

Written on 8:06:00 PM by S. Potter

Grey's Anatomy fans amongst us may remember an episode last season (season two) where a patient's boyfriend introduced us to the idea of whether you are a Pig or a Chicken in a relationship. The idea is that when you have a nice greasy breakfast of bacon and eggs, you have the Pig who is completely committed to delivering the breakfast and the Chicken who is only involved in contributing to the breakfast. So in the Grey's Anatomy episode the boyfriend was trying to be a Pig in his relationship and be fully committed to his heavily pregnant girlfriend. For me this wasn't a new concept, having read about Scrum as a software project management practice or adaptive process (if we are really forced to use the word "process") over the last 3 years. Now in Scrum the idea is the same, except applied to the software project not a relationship between two people. The Pigs are generally the people who are 100% committed to the project/product (e.g. Developers, DBAs, BAs, QAs, etc.) and the Chickens are the people that have a stake in the project but only contribute to the project. They are not 100% committed (e.g. business stakeholders, management, etc.) just involved in the project. Are you a Pig or a Chicken? Pig and Chicken cartoon

Ruby Equality

Written on 12:48:00 PM by S. Potter

Since two people asked me in the space of 5 days about this, I thought I'd post a quick demystification of Ruby's "equality" facilities. Namely equal?, ==, eql? and ===.

  1. equal? - always (or should be) purely identity equality, and *should never* be overridden by subclasses
  2. == - by default is object identity equality, but *can* be overridden by subclasses
  3. eql? - is object value equality and *should* be overridden by subclasses
  4. === - by default same as #==. It is overridden by subclasses that need a "case" equality defined to make sense when used in the case statements in a when construct.

ActiveRecord::Base#to_xml woes

Written on 11:10:00 PM by S. Potter

For us Railies that use namespaced models inside Ruby modules and are relying on ActiveRecord::Base#to_xml, do not fret any longer. To reproduce the problem you just need to do something like: users = Legacy::User.find(:all) #=> [] users.to_xml #=> <?xml version=\"1.0\" encoding=\"UTF-8\"?><legacy/users></legacy/users> The XML produced by #to_xml is NOT valid XML for this case. It's been a small thorn in my side for a week or so now and I've been living with it by adding the following to a file that is eventually/implicitly required by environment.rb:

# Patch for Rails #8305 that I submitted on 2007-05-09
# We'll need to keep an eye out upon next Rails Gem upgrade whether or not this 
# fix has been applied to remove this or not.
require 'active_record'
require 'active_support'

class ActiveRecord::Base
  alias :old_to_xml :to_xml
  def to_xml(options = {}, &block)
    options[:root] ||= self.class.name.tableize.singularize.gsub(/\//, ':') # for models that are namespaced in Ruby module
    old_to_xml(options, &block)
  end
end

module ContainerPatchMixin
  def self.included(base)
    base.class_eval %{
      alias :old_to_xml :to_xml
    }
    base.send(:include, InstanceMethods)
  end
  
  module InstanceMethods
    def to_xml(options = {})
      contained_class = self.first.class.name
      options[:root] ||= contained_class.tableize.gsub(/\//, ':') # for models that are namespaced in Ruby module
      options[:children] ||= contained_class.tableize.singularize.gsub(/\//, ':') # for models that are namespaced in Ruby module
      old_to_xml(options)
    end
  end
end

class Array
  include ContainerPatchMixin
end
Now what is produced is something more along the lines of: <?xml version=\"1.0\" encoding=\"UTF-8\"?><legacy:users></legacy:users> Which is valid XML. Today I finally reported this edge case defect on dev.rubyonrails.org (#8305) as I hadn't seen anyone else report it yet. Thankfully this is Ruby and this is all possible while the Rails Core team determine if my not so elegant fix is worthy to be in the next Rails version or not.

Twitter4R 0.1.0 Released

Written on 11:55:00 PM by S. Potter

This weekend I had a couple of hours to work on Twitter4R (finally). Today (Sunday) saw the release of Twitter4R 0.1.0 on Rubyforge as a Gem. It may take the Rubyforge gem mirrors a few hours to copy it over, but by Monday morning (US time) you should be able to do the following: $ sudo gem install twitter4r $ irb irb(main):001:0> require 'twitter' => true irb(main):002:0> client = Twitter::Client.new :login => 'screen_name', :password => 'password' => # irb(main):003:0> statuses = client.public_timeline => [#, ...] irb(main):004:0> statuses.each do |status| irb(main):005:1* puts status.text, status.user.screen_name irb(main):006:1> end the puts output would go here.... irb(main):007:0> client.update 'Pure Ruby API for Twitter: http://twitter4r.rubyforge.org' => nil It supports utf8 strings so this should not be a worry when querying the public timeline! See Twitter4R Rubyforge Website for more details or the Twitter4R Project Blog. Enjoy!