Wednesday, October 5, 2011

For Hire!

   Well that didn't quite go as planned!  :-(

   Long story short, I've been "let go".  Turns out Rosetta Stone needed more of an ActionScript guru than I was able to turn into over the course of about ten months, starting from scratch.  Of course, it didn't much help that they refused to send me to training on it.  Reading AS books and following AS blogs in my spare time, listening to AS podcasts during my commute, and asking my colleagues about their approaches when writing or analyzing AS, was not enough to make it "click" for me.  Oh well.

   Now I'm trying to turn that into a BID -- Blessing In Disguise -- by getting back to trying to land work in modern languages with a bright future (which I don't believe ActionScript has).  You can help by checking out the Availability page on my main web site, and telling me about suitable opportunities.

   A bit of background:

   I learned Java on my own time, expense, and initiative (the same way I learned many other things), back around 1997 or so.  But, I didn't push hard enough to land serious work in it.  Sure, I used it on my own, and got little snippets of work here and there, like a target for the static analyzer at Secure Software, and modifying a JSP page at BAE... but nothing "real" that would get me "street cred" in it.

   Now look at the job market.  Java seems to be about 3/4 of the job ads around here... and even the simplest of them, other than for freshouts, demand five solid years of recent hands-on enterprise-level (read: paid) experience in J2EE, with all the "trimmings" like Hibernate, Struts, Spring, and so on.  Those of us who aren't fresh out of school, and haven't had significant Java experience yet, are effectively locked out of ever getting it.  (Unless of course we luck into a very open-minded employer, but those are a rarity -- especially with the now-perennial surplus of Java coders.)

   So much for Java.  Now let's look at Ruby.

   Ruby is new enough that companies are having a hard time finding people.  (Of course, that doesn't stop HR from insisting on years of experience, but as always, the best way is to bypass the drones with their checklists of words they don't really grok.)  It's highly productive, with much less boilerplate than ActionScript, never mind Java.  Even aside from Rails, it's taking off like crazy.  And... it's very easy to learn.  I learned enough in one hour to do the "homework" Comcast assigned after the interview.  This means we may soon face a perennial surplus of Ruby coders as well.

   I missed the Java boat, and don't want to miss, if you'll pardon the expression, the Ruby yacht.

   (No, I didn't make that all up just to use the pun.  But it was there, and those of you who know me, know that that means I just had to use it.  If you don't get it, click here.)

    To reiterate the main point, you can help by checking out my consulting web site, and telling me about suitable opportunities.

Monday, June 6, 2011

Not the FTP Client

   Just a disclaimer: this site is not connected with the Codosaurus FTP client, found at http://grilledbacon.com/.  I only discovered that yesterday, and by an amazing coincidence my first iPad will supposedly be arriving soon (anniversary present), so I haven't even tried it.

Sunday, April 24, 2011

Password Security

   Some of you might know that this blog is not my only means of communicating technical information.  I have also led classes, mentored colleagues, given briefings, and so on, at work.  What some of you might not know is that I also give speeches at my Toastmasters club.  Occasionally the topics are technical, such as this one I gave last Thursday.  (Okay, it's nothing new for me, but it may be for you....)

===8<---cut here---

What's the Good Word?

   Raise your hand if you've got anything important protected by a password... and keep it up if you know how to pick a good password.

   Mr. Toastmaster, fellow Toastmasters and honored guests, if you put your hand up, but didn't keep it up, you need to listen up.  First we'll look at what makes a *bad* password, so you can avoid them, and then, some ideas on how to make a good one.

   A bad password is one that is easily guessed.  In the movies, the clever hero racks his brain for the one word that the villain uses to protect his evil schemes.  Reality, though, isn't usually like that.  Most attackers aren't after *your* account, but *any* they can crack.  They won't research you, let alone type guesses one by one!  Pros use tools that try hundreds of guesses per second, from lists.  So it boils down to what's on the lists.

   There are generally three levels of lists.

   First are common passwords.  Some might be otherwise decent passwords, but so many people use them, that now they're worthless.  Password.  Short phrases like LetMeIn.  ILoveYou.  Finger-drumming patterns like 123456, Qwerty, and Asdfgh.  And the name of the site.  If a lot of other people are probably using your password... change it as soon as you get home!

   Next come words of types commonly used for passwords.  Names for people (first, last, or nick names), pets, places, schools, sports and their teams, from all over the world.  So if your password is the name of your significant other... or your chihuahua (like Paris Hilton did)... or any of these others... change it as soon as you get home!

   Last comes the entire dictionary, or rather all the dictionaries they can find, including jargon and foreign languages.  So if your password is in any dictionary, what should you do?  Yes, change it as soon as you get home!

   It's not enough to spell a word *backwards*, or substitute digits for letters they resemble, like zero for o and one for i.  These tricks are so well known that attackers also use lists like that.

   I've scared you enough for one night, so let's look on the bright side: what's left for good passwords?

   The best passwords are long and random, with upper and lowercase letters, digits, and punctuation.  The bad news is, that makes them hard to remember!  The good news is, you don't have to!

   First, you can use multiple words.  For an account at SunTrust Bank, the name might remind you of sunburn, and the "trust fall" team-building exercises.  Put them together, in either order, with some numbers or punctuation in the middle or on the ends.  Maybe you got sunburn on a team-building retreat in 1987, so use "1987FallBurn".

   Next, try a virtual password, based on a longer set of words.  Pick a line from a story, a song, or a poem.  Take the first letter from each word, or the second or last or whatever.  Using the first letters of the first line of "The Star Spangled Banner" yields "Oscys,btdel".

   Lastly, use multiple ways of altering words.  Digitize every *other* eligible letter.  Add something to each digit.  Press Shift on every other character.  Doing all that to the worst password of all, "password", gives "p&s*wOrD".  Much better!  Doing it to the virtual password we made earlier, gives "O8CyS,0tD6L".  I'm sure that's not in any dictionary!

   Just pick a memorable starting word, or set of words, and a few ways to combine and change them, and remember those.  Then you can *recreate* your password any time you want!

   To recap:

   DON'T use common passwords, or common words, including names, places, sports, and teams, even with digits put in for letters, and backwards.

   DO use multiple words, virtual passwords derived from several words, or at least long words, and alter them, to get a mix of upper and lowercase letters, digits, and punctuation.

   With these few simple secrets, you can keep your secrets secret.

===8<---cut here---

   Of course, there's a lot more I could have said, like not just using letters because of brute-force attacks, quote some gurus, how to keep them safe, and mention specific password leaks like the big ones from Hotmail and Gawker... but this was for Project #5 out of the "Speaking to Inform" advanced manual (now finished, yay!), so I had a dictated timeframe of six to eight minutes. The above just barely fit within the grace period, coming in at about 8:20.

Saturday, November 20, 2010

Code Kata Nine: Supermarket Checkout, Part B (Decouple)

   As you may recall, before life (real life, not Conway's) got rather busy, I left you hanging off the cliff of Dave Thomas's Code Kata #9.  I had finished the "just make it work" part.  Now for the "decouple the price rule format" part.

   As Dave (the other Dave) says, there are many ways to do it.  I chose to use the Strategy Pattern.  (If you're not familiar with design patterns, you really need to go read up on them.)

   As you can see in the code below, the piece that gets swapped out is actually very small.  It does nothing but parse the pricing rules.  How they get applied remains the same.

#! /usr/bin/ruby

# Dave Thomas Code Kata #9: Supermarket Checkout
# See http://codekata.pragprog.com/2007/01/kata_nine_back_.html
# Solution by Dave Aronson
# VERSION B: decouple the pricing format.


class CheckOut

  def initialize rules, parser
    @items = {}
    # only really need this in total, but IRL a customer
    # might just want a price-check on something....
    @pricer = Pricer.new rules, parser
  end

  def scan sku
    @items[sku] = 0 if @items[sku].nil?
    @items[sku] += 1
  end

  def total
    tot = 0
    @items.each_pair { |sku, qty| tot += @pricer.price sku, qty }
    tot
  end

end


# separate from CheckOut 'cuz IRL there may be price-check stations
class Pricer

  def initialize rules, parser
    @prices = parser.parse rules
  end

  def price sku, quantity
    qty = quantity
    tot = 0
    while qty > 0 do
      deal_qty, deal_price = find_best_deal sku, qty
      num_deals = qty / deal_qty
      tot += num_deals * deal_price
      qty -= num_deals * deal_qty
    end
    tot
  end

  private

  def find_best_deal sku, qty
    prices = @prices[sku]
    if prices.nil?
      puts "ERROR: NO PRICE FOUND for #{sku}!"
      return nil
    end
    deal = nil
    prices.each_pair do |rule_qty, rule_tot|
      if rule_qty > qty then
        break
      else
        # Inefficient to keep overwriting this,
        # but shouldn't happen too much....
        deal = [rule_qty, rule_tot]
      end
    end
    puts "ERROR: NO PRICE FOUND for #{qty} of #{sku}!" if deal.nil?
    deal
  end

end


# now we get to the heart of the matter:
# different ways to parse prices.
# essentially the strategy pattern.


class CSVPriceParser
  def parse rules
    prices = {}
    rules.each do |line|
      line.chomp!
      cur_sku, qty, tot = line.split ","
      prices[cur_sku] = {} if prices[cur_sku].nil?
      prices[cur_sku][qty.to_i] = tot.to_i
    end
    prices
  end
end


CSV_RULES = [
  "A,1,50",
  "A,3,130",
  "B,1,30",
  "B,2,45",
  "C,1,20",
  "D,1,15",
]


class YAMLPriceParser
  def parse rules
    prices = {}
    cur_sku = ''
    rules.each do |line|
      line.chomp!
      if line[0..0] == "\t"
        qty, tot = line[1..-1].split "\t"  # 1 to ignore tab
        # really should check for cur_sku not being set yet,
        # though that would mean it's an invalid file....
        prices[cur_sku] = {} if prices[cur_sku].nil?
        prices[cur_sku][qty.to_i] = tot.to_i
      else cur_sku = line
      end
    end
    prices
  end
end


YAML_RULES = [
  "A",
  "\t1\t50",
  "\t3\t130",
  "B",
  "\t1\t30",
  "\t2\t45",
  "C",
  "\t1\t20",
  "D",
  "\t1\t15",
]


# TESTS, initially supplied by Dave Thomas,
# but refactored by Dave Aronson
# to support swapping out strategies

require 'test/unit'

class TestPrice < Test::Unit::TestCase

  def helper_test_incremental rules, parser
    co = CheckOut.new rules, parser
    assert_equal 0, co.total
    co.scan "A";  assert_equal( 50, co.total)
    co.scan "B";  assert_equal( 80, co.total)
    co.scan "A";  assert_equal(130, co.total)
    co.scan "A";  assert_equal(160, co.total)
    co.scan "B";  assert_equal(175, co.total)
  end

  def helper_test_totals rules, parser
    assert_equal(  0, price("", rules, parser))
    assert_equal( 50, price("A", rules, parser))
    assert_equal( 80, price("AB", rules, parser))
    assert_equal(115, price("CDBA", rules, parser))

    assert_equal(100, price("AA", rules, parser))
    assert_equal(130, price("AAA", rules, parser))
    assert_equal(180, price("AAAA", rules, parser))
    assert_equal(230, price("AAAAA", rules, parser))
    assert_equal(260, price("AAAAAA", rules, parser))

    assert_equal(160, price("AAAB", rules, parser))
    assert_equal(175, price("AAABB", rules, parser))
    assert_equal(190, price("AAABBD", rules, parser))
    assert_equal(190, price("DABABA", rules, parser))
  end

  def price goods, rules, parser
    co = CheckOut.new rules, parser
    goods.split(//).each { |item| co.scan(item) }
    co.total
  end

  def helper_test rules, parser
    helper_test_incremental rules, parser
    helper_test_totals rules, parser
  end

  def test_rulesets
    helper_test CSV_RULES, CSVPriceParser.new
    helper_test YAML_RULES, YAMLPriceParser.new
  end


end

   As always (just had to continue starting each paragraph with "As"!), it's now your turn.  How would you have designed and/or implemented this differently?  Leave a comment.

Sunday, November 14, 2010

And now for something completely different...

   Okay, maybe not completely different, as it will still be code-related.  But, coding time is at quite a premium right now.  So, until I manage to get some time to sit down and code for fun, I'm just going to post little snippets of advice. 

   I'm working on a team with some newer developers -- just a few years under each belt, versus my decades.  One advantage, as far as this blog goes, is that it lets me spot times when they make newbie-ish mistakes that I've since learned solutions to.  Then I can share those solutions with you!  (And of course my colleagues.)

   The first one is to keep track of work not-yet-done, or klugily done in a way that should be fixed ASAP. 

   On one of the programs we're working on now, there's a part that counts down "3, 2, 1" in some particular (human) language, depending on the "content set" it's using.  (As you may recall, I'm now working for Rosetta Stone.)  We got a bug report that this was always in Spanish, even if the content set it was dealing with was in French, English, etc.  Turns out this wasn't so much a bug per se, as an unimplemented feature -- or rather a chain of unimplemented features. 

   It turns out that the guy who originally wrote the program did not know how to make Part A (which knows all about the content set) tell Part B anything.  So, he hardcoded a bunch of things, such as that Part B should launch Part C (which does the countdown) in Spanish.

   Since he's fairly new, I can't fault him too much for not knowing how to do something.  I had to Google it myself.  I can fault him for not trying to find out, though!  Over on my other blog (personal excellence blog Dare2XL), I have an old post about asking for help.  But that's only part of the story -- the non-coding part, and this is my coding blog.  B-)

   The other part is how to keep track of such unimplemented features, temporary kluges, etc.  How do you avoid releasing something as "done", only to get bug reports due to such things?

   What I've usually done is to mark them with "TODO" or "FIXME" comments.  When I think I've finished, I grep the code, configs, etc. for such tags.  (Use grep, not your eyeballs, even if you're using an editor that syntax-highlights those words.  Grep is much more reliable -- and when combinded with find or ls -R, it's much more certain to find all the relevant files.)

   Some of them are about ideas for future features, so I use a colon, and those I mark as "TODO MAYBE:" or "TODO LATER:", rather than simple "TODO:".  Note how the colon is now not next to TODO, so I can grep for "TODO:", not just "TODO".

   Another way is to use your issue-tracker (such as Bugzilla, Jira, Trac, etc.) to make small tickets for yourself for each such feature.  The effectiveness of this approach depends on many factors, such as your team's policy on making tickets, your tool's complexity, and its filtering ability.  If you can easily make tickets that come quickly to your attention without getting in other people's way, great.  If not, you can still use a private task list, be it in some purpose-built tool, a spreadsheet, a simple text file, or whatever -- just so long as you refer to it often.  (I won't go into tons of advice on how to use a task list; surely there are gazillions of web pages on that.  That said, though, Google "Getting Things Done" for one very good approach.)

   Yet another way is to make sure that the requirement is covered by a test.  (Our group is not doing much developer-level testing right now; I intend to fix that.)  In this case, there could have been a test like "Make sure that a French content set makes the countdown launch in French. Repeat for Spanish."  (Or maybe, to get broader coverage, pick two random languages each time.)

   So now, as always, dear reader, it's your turn!  How do you keep track of unfinished, or klugily finished, work, to avoid releasing it into "the wild"?  Leave a comment.

Tuesday, October 5, 2010

Ads?  What ads?

   Oops!  Sorry for the large number of ads on here.  Long story short I goofed the setup.  I knew they wouldn't show up for me, due to Adblock Plus.  (One of my most favoritest Firefox plugins EVAR!)  What I didn't realize is that it would also block their representation on the Blogger layout design screen!  So I had a bunch, at the top, when I thought I had none, and intended to just put one at the bottom.  Mega chalupa!

Sunday, October 3, 2010

Rosetta Stone: the company, and my own

   In other news, I have accepted a post with Rosetta Stone, in Rosslyn VA, and will be starting on October 18.  Oddly enough, I'll be working mainly in ActionScript and Flex... neither of which I know yet!  I'm told that ActionScript is generally similar to JavaScript, which I do know, so at least that's a head-start. 

   What does this mean for you, my loyal readers?  I may soon start doing some of these katas in those languages.  Are you disappointed that I'll be discontinuing Ruby?  You were bound to be disappointed that way eventually anyway.  My plan has long been to build a matrix of problems (such as Dave Thomas katas, other katas, normal life, N-dimensional life, multi-lifeform life, etc.), versus languages, with links to blog posts showing solutions for each problem in each language.  Sort of... my own little electronic Rosetta Stone.