Playfulness at Work


Published on

This talk discusses ways to keep work playful (and as a side effect do better work), including:

* Dealing with crusty data formats and protocols in a lighthearted way

* Scripting other people’s software (whether they know it or not)

* Sharing your code with co-workers without annoying them

* Deploying your programs to honest-to-goodness paying customers

Published in: Technology, Education
1 Comment
  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Playfulness at Work

  1. 1. (Nothing says “playful” like a broken printing press from the 1840s.)
  2. 2. playfulness at work a real serious message™ with Ruby as the medium Ian Dees / @undeesHi, I’m Ian. I’m here to talk about playfulness at work, and how that relates to Ruby.
  3. 3. 1. playfulness in the workplace... 2. ...or the way playfulness works?“Playfulness at Work”—does that mean “bringing play into the workplace” or “the way playfulnessworks?”
  4. 4. yesBoth, actually. I’ll get into the strange arc of this talk in a moment.
  5. 5. first, a word of thanksBut first, I’ve got two groups of people to thank.
  6. 6. our organizers Ben Bleything Shane BeckerFirst, to our organizers Ben and Shane, thank you for putting this whole thing together.
  7. 7. our hosts Seattle.rbSecond, to our host city and its Rubyists, thank you for having us here and for making so muchgreat software.
  8. 8. Seriously—these folks have made tons of useful software: just look at it all!
  9. 9. their stuff will help your stuff flog flay heckle ... and much more!I’ll just highlight a couple of seattle.rb libraries here.... flog and flay report trouble spots in yourcode: methods that are too long or too similar; that kind of thing. heckle inserts bugs into yourcode and makes sure each bug results in a failing test case. If you’ve never run these before, catchme in the hallway later and I’ll embarrass myself by running them on my own code for you.
  10. 10. Okay, on to the topic at hand. I make oscilloscopes. There’s something gratifying about makingsomething physical that lets you see into an invisible world.... But what does that have to do withRuby?
  11. 11. why this talk?In other words, why this talk? Well, it turns out that even in a big organization like the one where Iwork, there are scattered pockets of Ruby resistance against the C++ empire.
  12. 12. why Ruby?Why is that? Aren’t we supposed to be writing embedded software here? Close. As Rich Kilmerreminded us yesterday, what we’re supposed to do is create something that solves a problem forsomeone. That might mean embedded code, a web front end for some engineering data, a one-offshell script, a document, or nothing at all.
  13. 13. (“Nothing at all... nothing at all....”)
  14. 14. Anyway.... On those days when we need to munge some data or glue a couple of programstogether, Ruby comes to the rescue.
  15. 15. why bother?There’s always a temptation is to follow the path of least acceleration and just do what theorganization thinks it wants to do. Fighting that temptation keeps our creative impulses sharp—paradoxically resulting in our doing a better job than if we’d just done what was asked or expectedof us.
  16. 16. why this talk? why bother? why Ruby? why? why? why?So, all these questions seem like they’re leading somewhere.
  17. 17. _whyDoes anyone remember why the lucky stiff’s 2004 essay, “Wearing Ruby Slippers to Work?”
  18. 18. It was about sneaking Ruby into work through the back door, using it to get little scripting tasksdone.
  19. 19. what’s changed? we can walk in through the front door now!Stealth was the only option back then. We had to prove ourselves with any unknown technology.And now... Ruby is in the top twenty languages, according to that chart we saw yesterday. We’ve gota little more leeway to storm the workplace and use Ruby when it suits the task at hand.
  20. 20. 1. dealing with crusty data formats and protocols a lighthearted wayI’d like to talk about a few of these kinds of those situations now, mainly as an excuse to bring upsome Ruby libraries you may find useful.
  21. 21. 2. scripting other peoples software ...whether they know it or not...
  22. 22. 3. sharing your code with co-workers ...without annoying them too much...
  23. 23. 4. deploying your software honest-to-goodness paying customers!...
  24. 24. *Ready?
  25. 25. 1. dealing with crusty data formats and protocols a lighthearted wayFirst, data parsing. I work at a bigco, where we’ve seen ad hoc data formats come and go. Forexample, you might hear, “That file full of test logs we just sort of started cutting and pasting into…can you figure out how many times the Foo subsystem called into the Bar module?” The goal: getthe answer quickly with your sanity intact.
  26. 26. introducing my own ad hoc format...Just to get a taste of how easy it is to wrangle arbitrary data in Ruby, I’ll introduce a toy format andthen show a few lines of code to parse it.
  27. 27. TaskParchment (with apologies to TaskPaper) - Invent my own crufty data format - Start with a good format @done - Ruin it @badidea @done - ??? - Profit!The format will be based on TaskPaper, which seems to be a reasonable format for to-do lists.Except I’m going to take everything cool out of it, so we can meaningfully talk about it in a fewslides. So, it’s like TaskPaper, but thinner: ergo, TaskParchment.
  28. 28. kschiess’s Parslet DSL’ll use the Parslet library for picking apart the data. It’s based on the idea of Parsing ExpressionGrammars—regular expressions attached to bits of code. Sounds like a hack, but there’s actually asolid theoretical foundation for it.
  29. 29. describe TaskParchment::Parser do before { @parser = } it parses a task do input = "- Task @taggedn" expected = {:desc=>"Task ", :tags=>[{:tag => "tagged"}]} @parser.task_line.parse(input).must_equal expected end # ... endThe first stage of parsing is to get the text into some kind of in-memory representation of a tree.Parslet’s representation is a conglomeration of hashes and arrays. Here’s a first taste. Notice we’reusing minitest (built into Ruby 1.9.2) to do our testing. We can invoke a single parsing rule at a time—in this case the yet-to-be-written “task_line” rule. This is a big help for testing.
  30. 30. require parslet module TaskParchment class Parser < Parslet::Parser # Example: # # - This is a task @tag # rule(:task_line) { begin_task >> >> tags.maybe >> newline } # ... end endHere’s the implementation of that rule. See how fluidly Parslet reads: a single line of a task consistsof punctuation, followed by a description, possibly some tags, and finally a newline. Each of thesecomponents is itself a rule. Let’s define those next.
  31. 31. rule(:begin_task) { str(- ) } rule(:description) { desc_char.repeat(1) } rule(:desc_char) { match([^@n]) } rule(:newline) { str("n") }These are the the basic building blocks of a task. Notice that we can match exact strings or regularexpressions.
  32. 32. rule(:tags) { tag.repeat(1).as(:tags) } rule(:tag) { begin_tag >> tag_char.repeat(1).as(:tag) >> space.maybe }Here’s how we define tags for a task. Notice the “as(:tag)” marker. This is a signal to Parslet todiscard all the other stuff (spaces and punctuation) and just keep the tag name in the final in-memory tree.
  33. 33. rule(:begin_tag) { str(@) } rule(:tag_char) { match([^@ n]) } rule(:space) { str( ) }Here are the last few pieces of punctuation we haven’t defined yet.
  34. 34. $ ruby test_task_parchment.rb Loaded suite test_task_parchment Started . Finished in 0.001903 seconds. 1 tests, 1 assertions, 0 failures, 0 errors, 0 skips Test run options: --seed 23512Now our tests pass.
  35. 35. whew!Let’s take a breath for a sec, before jumping into tasks that contain subtasks.
  36. 36. it parses a terrible plan into a tree do input = <<HERE - Invent my own crufty data format - Start with a good format @done - Ruin it @badidea @done - ??? - Profit! HERE # ... endHere’s the original TaskParchment string we wanted to parse.
  37. 37. it parses a terrible plan into a tree do # ... expected = [{:desc=>"Invent my own crufty data format", :subtasks=> [{:desc=>"Start with a good format ", :tags=>[{:tag=>"done"}], :subtasks=>[]}, {:desc=>"Ruin it ", :tags=>[{:tag=>"badidea"}, {:tag=>"done"}], :subtasks=>[]}]}, {:desc=>"???", :subtasks=>[]}, {:desc=>"Profit!", :subtasks=>[]}] # ... endHere’s the in-memory representation we want.
  38. 38. it parses a terrible plan into a tree do # ... @parser.parse(input).must_equal expected endAnd here’s the test assertion.
  39. 39. def task_line_indented_at(indent) space.repeat(indent, indent) >> task_line end def task_indented_at(indent) parser = task_line_indented_at(indent) parser >>= task_indented_at(indent + 2). repeat(0).as(:subtasks) unless indent > 20 parser end rule(:list) { task_indented_at(0).repeat(0) } root(:list)Believe it or not, we can do this with just a couple more parser rules. We’ll define these as functionsthat take an indentation parameter. We’ll cap the indentation at 20 to prevent an infinite regressionin our parser (quite easy to do in PEG parsers). That “root” bit at the bottom gives the starting rulefor parsing an entire document.
  40. 40. $ ruby test_task_parchment.rb Loaded suite test_task_parchment Started .. Finished in 0.009532 seconds. 2 tests, 2 assertions, 0 failures, 0 errors, 0 skips Test run options: --seed 63924Two passing tests.
  41. 41. yay!From here, Parslet has other tools for transforming those raw arrays and hashes into your own Rubyclasses. But let’s prepare to jump to another topic now. The lesson here is that we could have rolledour own parser and had less fun (not to mention poorer syntax error reporting). Instead, we usedRuby as an excuse for experimenting with a peculiar parsing technique.
  42. 42. 2. scripting other peoples software ...whether they know it or notNext up: scripting. One day, you may be asked to write a data converter for a nasty binary formatthat’s undocumented, unsupported, and has no developer hooks in the only software that knowshow to read it. You might try to reverse-engineer their API, or you might get nasty and just drivetheir software through the user interface.
  43. 43. something sillyRather than shame the vendor of this particular piece of software, I’d rather just go off in a differentdirection and do something silly with FFI in Ruby. We’re going to write a script that renders a black-and-white picture in the least efficient way possible: by clicking it pixel-by-pixel with the mouse.
  44. 44. chunky_png, we need to crack open the picture and find the dark pixels. To do this, we’ll use the excellentchunky_png, a pure-Ruby PNG decoder.
  45. 45. require chunky_png image = ChunkyPNG::Image.from_file filename shadows = [] (0...image.height).each do |y| (0...image.width).each do |x| gray = ChunkyPNG::Color.grayscale_teint image[x, y] shadows << [x, y] if gray < 128 end endThis code will build an array of the x/y locations we’ll need to click with the mouse.
  46. 46. FFI grab hold of the platform-specific mouse API, we’re going to use FFI, the foreign functioninterface library that started in the Rubinius project and is now available in at least three other Rubyimplementations.
  47. 47. require ffi module User32 extend FFI::Library ffi_lib user32 ffi_convention :stdcall MOUSEEVENTF_LEFTDOWN = 0x0002 MOUSEEVENTF_LEFTUP = 0x0004 attach_function :SetCursorPos, [:long, :long], :int attach_function :mouse_event, [:long] * 5, :void endTo simulate a mouse click on this particular platform, you need to import two functions: one toposition the mouse, and one to click it. Notice how the FFI functions look vaguely like Cdeclarations.
  48. 48. include User32 def mouse_click(x, y) SetCursorPos x, y mouse_event MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 mouse_event MOUSEEVENTF_LEFTUP, 0, 0, 0, 0 endHere’s how to actually simulate the click.
  49. 49. shadows.sort_by { rand }.each do |x, y| mouse_click 100 + x, 100 + y sleep 0.05 endAnd here’s how we’ll draw the picture. To save a bunch of window-management code in theseexamples, we’ll just assume the drawing app is maximized. We’ll give it a margin of 100 pixels eachway to make room for the toolbar.
  50. 50. win_gui code is even simpler if you use win_gui, a gem that provides FFI wrappers for common WindowsGUI calls.
  51. 51. require win_gui w = WinGui::Window.find :title => /- Paint$/ shadows.sort_by { rand }.each do |x, y| :point => [100 + x, 100 + y] sleep 0.05 endHere’s the entire drawing routine in win_gui. We don’t need any of the FFI setup stuff any more, orthe definition of a mouse click.
  52. 52. In the real-life story that inspired this example, we were able to convert the screen scraper to Cwith basically a few macros in Emacs. The program did its job, and we abandoned it as soon as wecould to move on to more worthy projects.
  53. 53. 3. sharing your code with co-workers ...without annoying them too muchNow, I’d like to talk about deployment. Once you’ve written that brilliant text parser, data converter,or web front end, how do you share it with your co-workers who may be on an IT-suppliedWindows XP box?
  54. 54. running from sourceThe simplest way to share code internally is to just hand your co-worker a .rb file and be done withit.
  55. 55. 2005 “just download Ruby from here”In 2005, that worked pretty well. You could just tell people, “Download the all-in-one Ruby installerfrom here, save this .rb file onto your machine, and run it.”
  56. 56. 2008 MSWin MinGW 1.8 easy scary 1.9 hard scaryThen life got complicated. Not only were there there two versions of Ruby, there were twoalternative Windows builds of Ruby. If your program depended on a gem that had C code in it, thecompiled code would only work in one particular Ruby variant.
  57. 57. 2010 MSWin MinGW 1.8 gone easy 1.9 gone easyThe story is much better now. MinGW has emerged as the winner of the C compiler battle, as far asRuby is concerned. And the Windows folks have found a way to deal with C code that works in both1.8 and 1.9.
  58. 58. RubyInstaller’s all thanks to Luis Lavena’s RubyInstaller, which installs Ruby onto your system and has anoptional dev kit that pretty much makes “gem install” work the same on Windows as it does onUNIX. You still have to worry about dependencies, but this is a huge step forward.
  59. 59. self-contained executablesAnother approach is to build binaries for people that include enough Ruby for them to run yourprogram without an extra installation step.
  60. 60. OCRA Rubyists have OCRA, the One-Click Ruby Application builder. This will bundle up yourRuby program with a copy of the interpreter and any gems you need into a single .exe file.
  61. 61. what shall we deploy?Let’s pick an app to serve as a guinea pig, so we can show a few of these techniques.
  62. 62. holman’s Boom app keeping with the Hubot chat robot from yesterday, let’s show something that appears to havemade Zach Holman the fastest draw in Campfire: a command-line clipboard manager called Boom.(Pronounce it “bewwwwwm.”)
  63. 63. ...
  64. 64. require boom Boom::Command.execute(*ARGV) C:boom> ocra boom.rb boom.exeThis app has a gem dependency (on json-pure). As long as that’s installed on our system, OCRA willfind it. We just write a trivial .rb file to launch Boom, point OCRA at it, and we get a working exe.
  65. 65. Warbler’m partial to JRuby myself, because it makes it easy to use gems that have a compiled component.JRuby binary gems (such as ActiveRecord database adapters) don’t have to be compiled for eachspecific platform. Warbler is a JRuby build tool that looks at your app’s Gemfile and creates arunnable .jar file that anyone with Java installed can run.
  66. 66. # bin/boom require boom Boom::Command.execute(*ARGV) # Gemfile source :rubygems gem boom gem httparty $ bundle $ warble jar boom.jarThe workflow with Warbler is nearly the same as with OCRA. Warbler keys off your project’s Gemfile,rather than watching your require calls.
  67. 67. 4. deploying your software honest-to-goodness paying customers!The techniques we’ve seen so far have been good enough for sharing code with co-workers, andperhaps a few customers (at work, we’ve put OCRA-based binaries on our website for end users). Atsome point, though, you begin to wonder, “Does this app need an installer?”
  68. 68. RailsInstaller you wanted to build a full installer for a Ruby app, you could start with the RubyInstaller projectand then add some installation steps that contain your own Ruby code and dependencies. That’spretty much what the RailsInstaller project from Engine Yard does. It installs Ruby, Rails, Git, andsome other dependencies onto a Windows machine.
  69. 69. BoomInstaller what would it take?As a thought experiment, what would it take to turn the RailsInstaller into a BoomInstaller?
  70. 70. # railsinstaller.yml --- :gems: :name: Gems :title: Ruby Gems :category: gems :list: - boom :boom: :name: Boom :title: Text snippets :category: gemIt’s pretty easy, as it turns out. Dr. Nic and crew have made their installation recipes configurable.You’d just need to add the Boom gem to a YAML config file.
  71. 71. # actions.rb module RailsInstaller def! components = [ BSDTar, SevenZip, DevKit, Ruby187, ] components.each do |package| section package.title download package extract package end link_devkit_with_ruby end endThen you’d need to shorten the list of installed components (unless you want to deploy Git and SQLbindings along with Boom).
  72. 72. ;railsinstaller.iss-#define InstallerName "RailsInstaller"+#define InstallerName "BoomInstaller"-DefaultDirName={sd}RailsInstaller+DefaultDirName={sd}BoomInstaller-VersionInfoDescription=Rails development environment installer...+VersionInfoDescription=Boom installer for Windows ;...The ugliest part is editing the Inno Setup script. It’s mostly just grepping through for eachoccurrence of Rails and deciding whether or not to replace it with Boom.
  73. 73. licenses and artworkYou’d also want to change licenses and artwork....
  74. 74. licenses and something for your app.
  75. 75. BoomInstaller tree/boomYou can see the changes I made in the “boom” branch of my fork of RailsInstaller.
  76. 76. That wraps up the concrete portion of the program. Let’s zoom back out to the big questions ofwhy we’re doing this.Image courtesy
  77. 77. playfulnessFor me, and I suspect for many of you, Ruby imparts a sense of playfulness. And in the next fewminutes, I’m going to prove that one can carefully cherry-pick a few items from Brain Science™ tosupport the importance of play at work.
  78. 78. Brain Science™ on play Stuart Brown Tim BrownFirst, here’s a pair of TED talks on creative play. TED is the conference speaker’s ideal substitute forresearch, by the way. Stuart Brown says, “Nothing lights up the brain like play,” and then proceedsto tell us how that works, biologically speaking. Tim Brown explains how playfulness improves theperformance of entire teams.
  79. 79. Next, there’s material I was actually exposed to as a kid... on vinyl. It’s about how to harness thehuge parallel processing unit in our heads to solve problems in ways we didn’t expect. (See alsoAndy Hunt’s Pragmatic Thinking and Learning). The gist is that play unlocks your brain’s problem-solving engine.
  80. 80. Finally, there’s I Hate People, an airplane reading book that sounds like it’s going to be Dilbertcome to life, but does a Randy-Pausch-style head fake into shaping your career. The authors talkabout the notion of being a Soloist, someone who does creative work with a supportive ensemble atwork. tl;dr: by their scrappy entrepreneurship, Soloists drag their organizations kicking andscreaming to success.
  81. 81. Which brings us here. Over the past two days, you’ve heard how crucial it is to keep yourselfengaged and challenged in your work. You’ve heard about the importance of community inpromoting that virtuous cycle that feeds success into success. My hope is that Cascadia Ruby Confdoesn’t end here. (We haven’t had lunch yet!) My hope is that we continue to connect, to supportone another, to stay playful, and to keep making great things. Long live Cascadia Ruby Conf.Cheers.
  82. 82. slides and code and code will be available at this address (eventually).