Playfulness at Work

82
(Nothing says “playful” like a broken printing press from the 1840s.)

description

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

Transcript of Playfulness at Work

Page 1: Playfulness at Work

(Nothing says “playful” like a broken printing press from the 1840s.)

Page 2: Playfulness at Work

playfulness at worka real serious message™ with Ruby as the medium

Ian Dees / @undees

Hi, I’m Ian. I’m here to talk about playfulness at work, and how that relates to Ruby.

Page 3: Playfulness at Work

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 playfulness works?”

Page 4: Playfulness at Work

yes

Both, actually. I’ll get into the strange arc of this talk in a moment.

Page 5: Playfulness at Work

first, a word of thanks

But first, I’ve got two groups of people to thank.

Page 6: Playfulness at Work

our organizers

Ben Bleything Shane Becker

First, to our organizers Ben and Shane, thank you for putting this whole thing together.

Page 7: Playfulness at Work

our hosts

Seattle.rb

Second, to our host city and its Rubyists, thank you for having us here and for making so much great software.

Page 8: Playfulness at Work

Seriously—these folks have made tons of useful software: just look at it all!

Page 9: Playfulness at Work

flog

flay

heckle

... and much more!

their stuff will help your stuff

I’ll just highlight a couple of seattle.rb libraries here.... flog and flay report trouble spots in your code: methods that are too long or too similar; that kind of thing. heckle inserts bugs into your code and makes sure each bug results in a failing test case. If you’ve never run these before, catch me in the hallway later and I’ll embarrass myself by running them on my own code for you.

Page 10: Playfulness at Work

Okay, on to the topic at hand. I make oscilloscopes. There’s something gratifying about making something physical that lets you see into an invisible world.... But what does that have to do with Ruby?

Page 11: Playfulness at Work

why this talk?

In other words, why this talk? Well, it turns out that even in a big organization like the one where I work, there are scattered pockets of Ruby resistance against the C++ empire.

Page 12: Playfulness at Work

why Ruby?

Why is that? Aren’t we supposed to be writing embedded software here? Close. As Rich Kilmer reminded us yesterday, what we’re supposed to do is create something that solves a problem for someone. That might mean embedded code, a web front end for some engineering data, a one-off shell script, a document, or nothing at all.

Page 13: Playfulness at Work

(“Nothing at all... nothing at all....”)

Page 14: Playfulness at Work

Anyway.... On those days when we need to munge some data or glue a couple of programs together, Ruby comes to the rescue.

Page 15: Playfulness at Work

why bother?

There’s always a temptation is to follow the path of least acceleration and just do what the organization 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 expected of us.

Page 16: Playfulness at Work

why this talk?

why bother?

why Ruby?

why?

why?

why?

So, all these questions seem like they’re leading somewhere.

Page 17: Playfulness at Work

_why

Does anyone remember why the lucky stiff’s 2004 essay, “Wearing Ruby Slippers to Work?”

Page 18: Playfulness at Work

It was about sneaking Ruby into work through the back door, using it to get little scripting tasks done.

Page 19: Playfulness at Work

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 got a little more leeway to storm the workplace and use Ruby when it suits the task at hand.

Page 20: Playfulness at Work

1. dealing with crusty data formats and protocols

...in a lighthearted way

I’d like to talk about a few of these kinds of those situations now, mainly as an excuse to bring up some Ruby libraries you may find useful.

Page 21: Playfulness at Work

2. scripting otherpeople's software

...whether they know it or not

...

Page 22: Playfulness at Work

3. sharing your code with co-workers

...without annoying them too much

...

Page 23: Playfulness at Work

4. deploying your software

...to honest-to-goodness paying customers!

...

Page 24: Playfulness at Work

*

Ready?

Page 25: Playfulness at Work

1. dealing with crusty data formats and protocols

...in a lighthearted way

First, data parsing. I work at a bigco, where we’ve seen ad hoc data formats come and go. For example, 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: get the answer quickly with your sanity intact.

Page 26: Playfulness at Work

introducing my ownad hoc format...

Just to get a taste of how easy it is to wrangle arbitrary data in Ruby, I’ll introduce a toy format and then show a few lines of code to parse it.

Page 27: Playfulness at Work

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 few slides. So, it’s like TaskPaper, but thinner: ergo, TaskParchment.

Page 28: Playfulness at Work

kschiess’s Parslet DSLhttps://github.com/kschiess/parslet

We’ll use the Parslet library for picking apart the data. It’s based on the idea of Parsing Expression Grammars—regular expressions attached to bits of code. Sounds like a hack, but there’s actually a solid theoretical foundation for it.

Page 29: Playfulness at Work

describe TaskParchment::Parser do before { @parser = TaskParchment::Parser.new }

it 'parses a task' do input = "- Task @tagged\n" expected = {:desc=>"Task ", :tags=>[{:tag => "tagged"}]}

@parser.task_line.parse(input).must_equal expected end

# ...end

The 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’re using 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.

Page 30: Playfulness at Work

require 'parslet'

module TaskParchment class Parser < Parslet::Parser # Example: # # - This is a task @tag # rule(:task_line) { begin_task >> description.as(:desc) >> tags.maybe >> newline }

# ... endend

Here’s the implementation of that rule. See how fluidly Parslet reads: a single line of a task consists of punctuation, followed by a description, possibly some tags, and finally a newline. Each of these components is itself a rule. Let’s define those next.

Page 31: Playfulness at Work

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 regular expressions.

Page 32: Playfulness at Work

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 to discard all the other stuff (spaces and punctuation) and just keep the tag name in the final in-memory tree.

Page 33: Playfulness at Work

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.

Page 34: Playfulness at Work

$ ruby test_task_parchment.rb Loaded suite test_task_parchmentStarted.Finished in 0.001903 seconds.

1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

Test run options: --seed 23512

Now our tests pass.

Page 35: Playfulness at Work

whew!

Let’s take a breath for a sec, before jumping into tasks that contain subtasks.

Page 36: Playfulness at Work

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

# ...

end

Here’s the original TaskParchment string we wanted to parse.

Page 37: Playfulness at Work

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=>[]}]

# ... end

Here’s the in-memory representation we want.

Page 38: Playfulness at Work

it 'parses a terrible plan into a tree' do

# ...

@parser.parse(input).must_equal expected end

And here’s the test assertion.

Page 39: Playfulness at Work

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 functions that take an indentation parameter. We’ll cap the indentation at 20 to prevent an infinite regression in our parser (quite easy to do in PEG parsers). That “root” bit at the bottom gives the starting rule for parsing an entire document.

Page 40: Playfulness at Work

$ ruby test_task_parchment.rb Loaded suite test_task_parchmentStarted..Finished in 0.009532 seconds.

2 tests, 2 assertions, 0 failures, 0 errors, 0 skips

Test run options: --seed 63924

Two passing tests.

Page 41: Playfulness at Work

yay!

From here, Parslet has other tools for transforming those raw arrays and hashes into your own Ruby classes. But let’s prepare to jump to another topic now. The lesson here is that we could have rolled our own parser and had less fun (not to mention poorer syntax error reporting). Instead, we used Ruby as an excuse for experimenting with a peculiar parsing technique.

Page 42: Playfulness at Work

2. scripting otherpeople's software

...whether they know it or not

Next up: scripting. One day, you may be asked to write a data converter for a nasty binary format that’s undocumented, unsupported, and has no developer hooks in the only software that knows how to read it. You might try to reverse-engineer their API, or you might get nasty and just drive their software through the user interface.

Page 43: Playfulness at Work

something silly

Rather than shame the vendor of this particular piece of software, I’d rather just go off in a different direction 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.

Page 44: Playfulness at Work

chunky_pnghttps://github.com/wvanbergen/chunky_png

First, we need to crack open the picture and find the dark pixels. To do this, we’ll use the excellent chunky_png, a pure-Ruby PNG decoder.

Page 45: Playfulness at Work

require 'chunky_png'

image = ChunkyPNG::Image.from_file filenameshadows = []

(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 endend

This code will build an array of the x/y locations we’ll need to click with the mouse.

Page 46: Playfulness at Work

FFIhttps://github.com/ffi/ffi

To grab hold of the platform-specific mouse API, we’re going to use FFI, the foreign function interface library that started in the Rubinius project and is now available in at least three other Ruby implementations.

Page 47: Playfulness at Work

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, :voidend

To simulate a mouse click on this particular platform, you need to import two functions: one to position the mouse, and one to click it. Notice how the FFI functions look vaguely like C declarations.

Page 48: Playfulness at Work

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, 0end

Here’s how to actually simulate the click.

Page 49: Playfulness at Work

shadows.sort_by { rand }.each do |x, y| mouse_click 100 + x, 100 + y sleep 0.05end

And here’s how we’ll draw the picture. To save a bunch of window-management code in these examples, we’ll just assume the drawing app is maximized. We’ll give it a margin of 100 pixels each way to make room for the toolbar.

Page 50: Playfulness at Work

win_guihttps://github.com/arvicco/win_gui

The code is even simpler if you use win_gui, a gem that provides FFI wrappers for common Windows GUI calls.

Page 51: Playfulness at Work

require 'win_gui'

w = WinGui::Window.find :title => /- Paint$/

shadows.sort_by { rand }.each do |x, y| w.click :point => [100 + x, 100 + y] sleep 0.05end

Here’s the entire drawing routine in win_gui. We don’t need any of the FFI setup stuff any more, or the definition of a mouse click.

Page 52: Playfulness at Work

In the real-life story that inspired this example, we were able to convert the screen scraper to C with basically a few macros in Emacs. The program did its job, and we abandoned it as soon as we could to move on to more worthy projects.

Page 53: Playfulness at Work

3. sharing your code with co-workers

...without annoying them too much

Now, 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-supplied Windows XP box?

Page 54: Playfulness at Work

running from source

The simplest way to share code internally is to just hand your co-worker a .rb file and be done with it.

Page 55: Playfulness at Work

2005

“just download Ruby from here”

In 2005, that worked pretty well. You could just tell people, “Download the all-in-one Ruby installer from here, save this .rb file onto your machine, and run it.”

Page 56: Playfulness at Work

2008

MSWin MinGW

1.8 easy scary

1.9 hard scary

Then life got complicated. Not only were there there two versions of Ruby, there were two alternative Windows builds of Ruby. If your program depended on a gem that had C code in it, the compiled code would only work in one particular Ruby variant.

Page 57: Playfulness at Work

2010

MSWin MinGW

1.8 gone easy

1.9 gone easy

The story is much better now. MinGW has emerged as the winner of the C compiler battle, as far as Ruby is concerned. And the Windows folks have found a way to deal with C code that works in both 1.8 and 1.9.

Page 58: Playfulness at Work

RubyInstallerhttps://github.com/oneclick/rubyinstaller

It’s all thanks to Luis Lavena’s RubyInstaller, which installs Ruby onto your system and has an optional dev kit that pretty much makes “gem install” work the same on Windows as it does on UNIX. You still have to worry about dependencies, but this is a huge step forward.

Page 59: Playfulness at Work

self-contained executables

Another approach is to build binaries for people that include enough Ruby for them to run your program without an extra installation step.

Page 60: Playfulness at Work

OCRAhttps://github.com/larsch/ocra

Windows Rubyists have OCRA, the One-Click Ruby Application builder. This will bundle up your Ruby program with a copy of the interpreter and any gems you need into a single .exe file.

Page 61: Playfulness at Work

what shall we deploy?

Let’s pick an app to serve as a guinea pig, so we can show a few of these techniques.

Page 62: Playfulness at Work

holman’s Boom apphttp://github.com/holman/boom

In keeping with the Hubot chat robot from yesterday, let’s show something that appears to have made Zach Holman the fastest draw in Campfire: a command-line clipboard manager called Boom. (Pronounce it “bewwwwwm.”)

Page 63: Playfulness at Work

...

Page 64: Playfulness at Work

require 'boom'Boom::Command.execute(*ARGV)

C:\boom> ocra boom.rb

boom.exe

This app has a gem dependency (on json-pure). As long as that’s installed on our system, OCRA will find it. We just write a trivial .rb file to launch Boom, point OCRA at it, and we get a working exe.

Page 65: Playfulness at Work

Warblerhttps://github.com/nicksieger/warbler

I’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 each specific platform. Warbler is a JRuby build tool that looks at your app’s Gemfile and creates a runnable .jar file that anyone with Java installed can run.

Page 66: Playfulness at Work

# Gemfilesource :rubygemsgem 'boom'gem 'httparty'

$ bundle$ warble jar

boom.jar

# bin/boomrequire 'boom'Boom::Command.execute(*ARGV)

The workflow with Warbler is nearly the same as with OCRA. Warbler keys off your project’s Gemfile, rather than watching your require calls.

Page 67: Playfulness at Work

4. deploying your software

...to honest-to-goodness paying customers!

The techniques we’ve seen so far have been good enough for sharing code with co-workers, and perhaps a few customers (at work, we’ve put OCRA-based binaries on our website for end users). At some point, though, you begin to wonder, “Does this app need an installer?”

Page 68: Playfulness at Work

RailsInstallerhttps://github.com/railsinstaller

If you wanted to build a full installer for a Ruby app, you could start with the RubyInstaller project and then add some installation steps that contain your own Ruby code and dependencies. That’s pretty much what the RailsInstaller project from Engine Yard does. It installs Ruby, Rails, Git, and some other dependencies onto a Windows machine.

Page 69: Playfulness at Work

BoomInstallerwhat would it take?

As a thought experiment, what would it take to turn the RailsInstaller into a BoomInstaller?

Page 70: Playfulness at Work

# railsinstaller.yml---:gems: :name: Gems :title: Ruby Gems :category: gems :list: - boom

:boom: :name: Boom :title: Text snippets :category: gem

It’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.

Page 71: Playfulness at Work

# actions.rbmodule RailsInstaller

def self.build!

components = [ BSDTar, SevenZip, DevKit, Ruby187, ]

components.each do |package| section package.title download package extract package end

link_devkit_with_ruby endend

Then you’d need to shorten the list of installed components (unless you want to deploy Git and SQL bindings along with Boom).

Page 72: Playfulness at Work

;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 each occurrence of Rails and deciding whether or not to replace it with Boom.

Page 73: Playfulness at Work

licenses and artwork

You’d also want to change licenses and artwork....

Page 74: Playfulness at Work

licenses and artwork

...to something for your app.

Page 75: Playfulness at Work

BoomInstallerhttps://github.com/undees/railsinstaller-windows/

tree/boom

You can see the changes I made in the “boom” branch of my fork of RailsInstaller.

Page 76: Playfulness at Work

That wraps up the concrete portion of the program. Let’s zoom back out to the big questions of why we’re doing this.

Image courtesy http://www.flickr.com/photos/chrisbevan/2476405

Page 77: Playfulness at Work

playfulness

For me, and I suspect for many of you, Ruby imparts a sense of playfulness. And in the next few minutes, I’m going to prove that one can carefully cherry-pick a few items from Brain Science™ to support the importance of play at work.

Page 78: Playfulness at Work

Brain Science™ on play

Stuart Brown Tim Brown

First, here’s a pair of TED talks on creative play. TED is the conference speaker’s ideal substitute for research, by the way. Stuart Brown says, “Nothing lights up the brain like play,” and then proceeds to tell us how that works, biologically speaking. Tim Brown explains how playfulness improves the performance of entire teams.

Page 79: Playfulness at Work

Next, there’s material I was actually exposed to as a kid... on vinyl. It’s about how to harness the huge parallel processing unit in our heads to solve problems in ways we didn’t expect. (See also Andy Hunt’s Pragmatic Thinking and Learning). The gist is that play unlocks your brain’s problem-solving engine.

Page 80: Playfulness at Work

Finally, there’s I Hate People, an airplane reading book that sounds like it’s going to be Dilbert come to life, but does a Randy-Pausch-style head fake into shaping your career. The authors talk about the notion of being a Soloist, someone who does creative work with a supportive ensemble at work. tl;dr: by their scrappy entrepreneurship, Soloists drag their organizations kicking and screaming to success.

Page 81: Playfulness at Work

Which brings us here. Over the past two days, you’ve heard how crucial it is to keep yourself engaged and challenged in your work. You’ve heard about the importance of community in promoting that virtuous cycle that feeds success into success. My hope is that Cascadia Ruby Conf doesn’t end here. (We haven’t had lunch yet!) My hope is that we continue to connect, to support one another, to stay playful, and to keep making great things. Long live Cascadia Ruby Conf. Cheers.

Page 82: Playfulness at Work

slides and codehttps://github.com/undees/cascadiaruby

Slides and code will be available at this address (eventually).