Module Bankjob
In: lib/bankjob.rb


Classes and Modules

Class Bankjob::BankjobRunner
Class Bankjob::CLI
Class Bankjob::Payee
Class Bankjob::Scraper
Class Bankjob::Statement
Class Bankjob::Transaction


BANKJOB_VERSION = '0.5.2' unless defined?(BANKJOB_VERSION)

Public Class methods

Takes a string and capitalizes the first letter of every word and forces the rest of the word to be lowercase.

This is a utility method for use in scrapers to make descriptions more readable.


# File lib/bankjob/support.rb, line 62
  def self.capitalize_words(message)

Takes a date-time as a string or as a Time or DateTime object and returns it as either a Time object.

This is useful in the setter method of a date attribute allowing the date to be set as any type but stored internally as an object compatible with conversion through +strftime()+ (Bankjob::Transaction uses this internally in the setter for date for example


# File lib/bankjob/support.rb, line 16
  def self.create_date_time(date_time_raw)
    if (date_time_raw.is_a?(Time)) then
      # It's already a Time 

      return date_time_raw
    elsif (date_time_raw.to_s.strip.empty?)
      # Nil or non dates are returned as nil

      return nil
      # Assume it can be converted to a time

      return Time.parse(date_time_raw.to_s)

Takes a Time or DateTime and formats in a suitable format for comma separated values files. The format produced is suitable for loading into an Excel-like spreadsheet program being automatically treated as a date.

A string is returned with the format "YY-MM-DD HH:MM:SS". For example, the 1st of February 2009 at 2:34PM and 56 second becomes "2009-02-01 14:34:56"

Note must use a Time, or DateTime, not a String, nor a Date.


# File lib/bankjob/support.rb, line 51
  def self.date_time_to_csv(time)
    time.nil? ? "" : "#{time.strftime( '%Y-%m-%d %H:%M:%S' )}"

Takes a Time or DateTime and formats it in the correct format for OFX date elements.

The OFX format is a string of digits in the format "YYMMDDHHMMSS". For example, the 1st of February 2009 at 2:34PM and 56 second becomes "20090201143456"

Note must use a Time, or DateTime, not a String, nor a Date.


# File lib/bankjob/support.rb, line 37
  def self.date_time_to_ofx(time)
    time.nil? ? "" : "#{time.strftime( '%Y%m%d%H%M%S' )}"

converts a numeric string to a float given the specified decimal separator.


# File lib/bankjob/support.rb, line 70
  def self.string_to_float(string, decimal)
    return nil if string.nil?
    amt = string.gsub(/\s/, '')
    if (decimal == ',') # E.g.  "1.000.030,99"

      amt.gsub!(/\./, '')  # strip out . 1000s separator

      amt.gsub!(/,/, '.')  # replace decimal , with .

    elsif (decimal == '.')
      amt.gsub!(/,/, '')  # strip out comma 1000s separator

    return amt.to_f

Helps the user determine how to upload to their Wesabe account.

When used with no args, will give generic help information. When used with Wesabe account and password, will log into Wesabe and list the users accounts, and suggest command line args to upload to each account.


# File lib/bankjob/support.rb, line 170
  def self.wesabe_help(wesabe_args, logger)
    if (wesabe_args.nil? or wesabe_args.length != 2)
      puts "Wesabe ( is an online bank account management tool (like Mint)\nthat allows you to upload (in some cases automatically) your bank statements and\nautomatically convert them into a more readable format to allow you to track\nyour spending and much more. Wesabe comes with its own community attached.\n\nBankjob has no affiliation with Wesabe, but allows you to upload the statements it\ngenerates to your Wesabe account automatically.\n\nTo use Wesabe you need the Wesabe Ruby gem installed:\nSee the gem at\nInstall the gem with:\n$ sudo gem install -r --source wesabe-wesabe\n(on Windows, omit the \"sudo\")\n\nYou also need your Wesabe login name and password, and, if you have\nmore than one account on Wesabe, the id number of the account.\nThis is not a real account number - it's simply a counter that Wesabe uses.\nIf you have a single account it will be '1', if you have two accounts the\nsecond account will be '2', etc.\n\nBankjob will help you find this number by listing your Wesabe accounts for you.\nSimply use:\nbankjob -wesabe_help \"username password\"\n(The quotes are important - this is a single argument to Bankjob with two words)\n\nIf you already know the number of the account and you want to start uploading use:\n\nbankjob [other bankjob args] --wesabe \"username password id\"\n\nE.g.\nbankjob --scraper bpi_scraper.rb --wesabe \"bloggsy pw123 2\"\n\nIf you only have a single account, you don't need to specify the id number\n(but Bankjob will check and will fail with an error if you have more than one account)\n\nbankjob [other bankjob args] --wesabe \"username password\"\n\nIf in any doubt --wesabe-help \"username password\" will set you straight.\n\nTroubleshooting:\n- If you see an error like Wesabe::Request::Unauthorized, then chances\nare your username or password for Wesabe is incorrect.\n\n- If you see an error \"end of file reached\" then it may be that you are logged\ninto the Wesabe account to which you are trying to upload - perhaps in a browser.\nIn this case, log out from Wesabe in the browser, _wait a minute_, then try again.\n"
        puts "Connecting to Wesabe...\n"
        wuser, wpass = *wesabe_args
        wesabe =, wpass)
        puts "You have #{wesabe.accounts.length} Wesabe accounts:"
        wesabe.accounts.each do |account|
          puts " Account Name: #{}"
          puts "    wesabe id: #{}"
          puts "   account no: #{account.number}"
          puts "         type: #{account.type}"
          puts "      balance: #{account.balance}"
          puts "         bank: #{}"
          puts "To upload to this account use:"
          puts "  bankjob [other bankjob args] --wesabe \"#{wuser} password #{}\""
          puts ""
          if wesabe.accounts.length == 1
            puts "Since you have one account you do not need to specify the id number, use:"
            puts "  bankjob [other bankjob args] --wesabe \"#{wuser} password\""
      rescue Exception => e
        msg ="Failed to get Wesabe account information due to: \#{e.message}.\nCheck your username and password or use:\nbankjob --wesabe-help\nwith no arguments for more details.\n"
        raise msg

Uploads the given OFX document to the Wesabe account specified in the wesabe_args


# File lib/bankjob/support.rb, line 130
  def self.wesabe_upload(wesabe_args, ofx_doc, logger)
    if (wesabe_args.nil? or (wesabe_args.length < 2 and wesabe_args.length > 3))
      raise "Incorrect number of args for Wesabe (#{wesabe_args}), should be 2 or 3."
      wuser, wpass, windex = *wesabe_args
      wesabe =, wpass)
      num_accounts = wesabe.accounts.length
      if num_accounts == 0
        raise "The user \"#{wuser}\" has no Wesabe accounts. Create one at before attempting to upload a statement."
      elsif (not windex.nil? and (num_accounts < windex.to_i))
        raise "The user \"#{wuser}\" has only #{num_accounts} Wesabe accounts, but the account index #{windex} was specified."
      elsif windex.nil?
        if num_accounts > 1
          raise "The user \"#{wuser}\" has #{num_accounts} Wesabe accounts, so the account index must be specified in the WESABE_ARGS."
          # we have only one account, no need to specify the index

          windex = 1
      elsif windex.to_i == 0
        raise "The Wesabe account index must be between 1 and #{num_accounts}. #{windex} is not acceptable"
      logger.debug("Attempting to upload statement to the ##{windex} Wesabe account for user #{wuser}...")
      # Get the account at the index (which is not necessarily the index in the array 

      # so we use the account(index) method to get it

      account = wesabe.account(windex.to_i)
      uploader = account.new_upload
      uploader.statement = ofx_doc
      uploader.upload!"Uploaded statement to Wesabe account #{}, the ##{windex} account for user #{wuser}, with the result: #{uploader.status}")

Public Instance methods

Finds a selector field in a named form in the given Mechanize page, selects the suggested label


# File lib/bankjob/support.rb, line 85
  def select_and_submit(page, form_name, select_name, selection)
    option = nil
    form  = page.form(form_name)
    unless form.nil?
      selector = form.field(select_name)
      unless selector.nil?
        option = select_option(selector, selection)
    return option

Given a Mechanize::Form:SelectList selector will attempt to select the option specified by selection. This algorithm is used:

  The first option with a label equal to the +selection+ is selected.
   - if none is found then -
  The first option with a value equal to the +selection+ is selected.
   - if none is found then -
  The first option with a label or value that equal to the +selection+ is selected
  after removing non-alphanumeric characters from the label or value
   - if none is found then -
  The first option with a lable or value that _contains_ the +selection+

If matching option is found, the select is called on it. If no option is found, nil is returned - otherwise the option is returned


# File lib/bankjob/support.rb, line 114
  def select_option(selector, selection)
    options = { |o| o.text == selection }
    options = { |o| o.value == selection } if options.empty?
    options = { |o| o.text.gsub(/[^a-zA-Z0-9]/,"") == selection } if options.empty?
    options = { |o| o.value.gsub(/[^a-zA-Z0-9]/,"") == selection } if options.empty?
    options = { |o| o.text.include?(selection) } if options.empty?
    options = { |o| o.value.include?(selection) } if options.empty?

    option = options.first unless option.nil?
    return option