Mike Dalessio's blog
Pivotal NYC was lucky enough to have Ben Stein in the office to give a beta presentation entitled "Beyond the Hype: What it Really Takes to Build a Technology Business on the Cloud". Ben has built his own successful startup, Mobile Commons, using some of the principles he discussed.
Boiled down to its essence, Ben's theme was (with apologies to Chris Carter): Trust No One.
More specifically, Ben talked about ways to build your business and technology to better handle the inevitable failures of your vendors, partners and infrastructure providers.
Technical highlights from Ben's talk included a recipe for constructing a vendor-agnostic library (heretofore known as the "Stein Stack"), and actionable rules-of-thumb to make API consumers and producers more robust and debuggable.
If you're interested in attending a future Pivotal NYC Tech Talk, please subscribe to the mailing list.
Points of Interest
Following a recipe from Dan Chak's Enterprise Rails, we came across code following this pattern:
class Thing def foo def bar(args) # some code end # some code that calls bar() end endThe structure of this code suggests that
baris scoped only within the context offoo. But alas, that is not the case. Ruby simply definesThing#barthe first timefoois called, analogous todefine_method :bar. Misleading syntax, for sure.Ryan Davis's Flay is awesome. If you're not familiar with it, Flay parses your Ruby and compares subtrees with each other to find where code has been duplicated (or nearly so). Run on a codebase of over 20,000 lines of Ruby, Flay was able to quickly indicate places where we had duplicate code lying around, as well as many likely targets for refactoring work. We've found it to be helpful in keeping code DRY.
This looks interesting: MagicPrefs gives you gestures for your Magic Mouse (Yes, Pivotal NYC has Magic Mice, as well as 27" iMacs. Apply today!).
In Snow Leopard, mapping CapsLock => Ctrl when two keyboards are plugged in is problematic. You can do it by plugging in one keyboard at a time and mapping each one individually. If anyone knows the story behind why this is, or how to deal more easily, please comment!
Another interesting link: Open Frameworks is a "creative coding" toolkit (like Processing) that's implemented in C++. Why ask why?
Another lesson learned the hard way: starting up a bunch of leopard machines at the same time wreaks havoc on the network for a few minutes. Packet storm, dropped packets, etc. Is Bonjour to blame? Inquiring minds want to know.
If you're trying to stub a subclass of
ActionMailer::Basewith Double Ruby (also known as RR), and you're having issues, try stubbing the method onActionMailer::Basedirectly. There's some weirdness there withmethod_missingin Rails 2.3.
Help Wanted
Does anyone know how to make
command-1throughcommand-9switch tabs inside ofTerm.app? By default, these keys are bound to switching windows, and we'd love to be able to do this on tabs instead.Rails's
select_tag(..., :multiple => true)option doesn't properly setselectedon the generated options. This appears to be a boog, and anyone who's interested in helping a Pivot write a patch, please comment below!
Happy Holidays from NYC!
Interesting
- Someone noticed that rspec's
should_not(rdocs here) returns false when the spec passes, whereasshouldreturns true when it passes. This has unexpected results when ashould_notis used within a Webratwait_forloop (code here) -- wait_for loops until its body returns true. Fail! - John Resig has implemented a
jQuery.requiremethod that should be in the next release. Check out the commit and the lengthy discussion here. Everyone's a critic. - One Pivotal project that recently switched from MySQL to Postgres noticed that PG sorts NULL values differently than MySQL. The default in PG is NULLS FIRST when ordering DESC, and NULLS LAST otherwise. You can override this behavior by using a NULLS FIRST or NULLS LAST clause in your ORDER BY.
- Someone was reminded the hard way that Ruby's
rescue, by default, only catches exceptions inherited from StandardError.
Help
- Does anyone know of a service or library that will convert an email into a tracker story? The use case is stake holders who send UI/UX requirements within emails with attachments, etc.
Sandi Metz, a Dukie visiting from NC, will be talking about SOLID principles of software development:
- Single Responsibility
- Open Closed
- Liskov Substitution
- Interface Segregation
- Dependency Inversion
All of Sandi's code is available here.
Change
Fact: your application is going to change. How will your application handle that change?
Robert Martin says your app can behave a couple of different ways:
- Rigid: Making a change somewhere will break something somewhere else.
- Fragile: You can't predict where that break will be.
- Immobile: It's hard to change your code.
- Viscous: It's easier to do the wrong thing than to fix things.
In the beginning, though, your app was perfect. "Dependencies are killing you!"
Design might save you.
The Design Stamina Hypothesis says that, after a certain point, you'll have done better if you had designed first.
"Skip design, if you want your app to fail."
To avoid dependencies, your design should be:
- Loosely coupled
- Highly cohesive
- Easily composable
- Context independent
Ignorable Rules
SOLID principles we can ignore in ruby:
Interface Segregation
Really only a problem for statically-typed, compiled languages. Because we're in Ruby, we don't have this problem! Win!
"Dynamic languages obey this rule in the most extreme way possible: duck typing."
Liskov Substitution
When you design, don't break the contract of the superclass in the subclass.
Testing Interlude
Sandi draws her examples of applicatoin change from the source code at: http://skmetz.home.mindspring.com/img28.html.
Lesson #1: Resistance is a Resource.
- Don't be attached to your first idea
- Embrace the friction
- Fix the problem
If testing seems hard, examine your design. Tests depend upon the design of the code. "TDD will punish you if you don't understand design."
During refactoring, ask yourself:
- Is it DRY?
- Does it have one responsibility?
- Does everything in it change at the same time?
- Does it depend on things that change less often than it does?
The answers should all be 'yes'.
Important Rules
Sandi references her code to demonstrate when and how to mock and use dependency injection to achieve Single Responsibility, in which a class both downloads and acts upon the downloaded data.
She urges developers to do the simplest possible refactoring when extracting responsibilities from a class.
"Refactor, not because you know the abstraction, but because you want to find it."
Sandi uses a very interesting example of building a Config class which behaves differently in different Rails environments. The first version had a lot of smell, and with a combination of hash parameters, YAML file, and metaprogamming, she demonstrates how to be open for extension, but closed for modification.
Sandi explains that paying attention to your classes' dependencies is important. If a relatively static class is dependent on a class that changes more often, that's a smell! Use dependency injection to avoid Dependency Inversion.
Summary
"TDD, BDD and DRY are all good, but they are not enough."
"Design because you expect your app to succeed, and the future to come."
Sandi recommends reading:
Dan Yoder is the Director of Development at ATTi R&D, and will be talking about Waves, a Ruby architectural framework for developing RESTful apps.
A Brief History
Ruby web development came of age with MVC and Rails. Later, people who didn't need a full MVC invented Sinatra and other frameworkes. Which brings us to today, and ...
Waves Introduction
Waves can do simple apps in just a few lines of code. And by using "foundations", developers can build more advanced apps with MVC-like functionality. You can build your own foundation for whatever web framework you envision (there are several for MVC and REST).
Waves supports rack::cache and JRuby. It's Actually In Production(tm)!
Web as Services
As more rich browser apps use AJAX and COMET, server-side APIs are becoming more important. This is where REST shines.
"HTTP isn't MVC, but our frameworks think in MVC."
REST and ROA
"REST" shouldn't be applied to things that are "REST-influenced" (just ask Roy). Dan likes to use "Resource-Oriented" for these situations.
HTTP-based ROA uses the existing infrastructure, and has proven scalability. HTTP defines a protocol for a distributed hash table:
- put(key, value)
- get(key)
- delete(key)
Q: "What about post?" A: "Post is for 'everything else'." Some things aren't clearly RESTful, and post is the catch-all for other operations.
What's in the hash? Resources, and keys are the URIs.
What's the point? Platform-neutral distributed objects! RDF can be used to describe discoverable resources.
ROA in action: rss/atom. It's one link to a resource describing your blog. "Boom! Podcasts for free." Dan describes this as the law of "unintended consequences," in a good way.
Edge caching is another big win for HTTP-based ROA.
How Does Waves Help?
Waves makes it easier to write resourceful applications like this today. New foundations will make it even easier going forward.
You can check out Waves at http://rubywaves.com, and on their Google Group.
Eleanor McHugh, a physicist by training, will be talking about how to make *nix systems work naturally within the Ruby environment.
The Unix Way
Eleanor actually hates Unix, but recognizes that it's a very effective operating system for getting things done. It's DRY: build little things, build them well, don't build them twice. There's a natural marriage between agile Ruby and the Unix philosophy.
Unix provides basic services which make it a very useful OS to "muck about with":
- virtual memory
- process management
- hierarchical file system
- user permissions
- interprocess communication
Ruby provides some "really lovely" utilities:
- Kernel.system
- Kernel.spawn
- IO.popen
- Open3.popen3
However, if you're doing a lot of IO, you end up doing a lot of select()s and keeping a lot of file descriptors open.
System Calls with Ruby DL
DL, which Ruby delivers out of the box, is a way to wrap a C library with a Ruby call. This is a nice way to access the underlying kernel system calls without relying on the Ruby IO implementations.
This is superior to Ruby's syscall, in that you can actually get results back from the function call.
Using mmap allows you to do much faster memory reads, rather than do slower file reads.
Using kqueue/epoll/inotify allows you to build evented ruby (like EventMachine, but without EventMachine).
Using pipes allows you to build efficient IPC.
The drawback is that using DL means more verbose code, and more error prone code. (Pointer math FTL!) So, for things like sockets, use the Ruby API unless you specifically need kernel-level eventing.
Multicore
The lack of real thread support in Ruby can be addressed by using multiple processes, held together with IPC (sockets, pipes, memory mapped files). This is the traditional "Unix way" for handling multiple processes.
Gregory Brown is the creator of Ruport and Prawn, and the author of the upcoming Ruby Best Practices. He'll be talking about the various-and-sundry Ruby implementations.
Moving to 1.9
On Ruby 1.8, strings are sequences of bytes. On Ruby 1.9, strings are proper characters (not bytes!). Even if your app only speaks "American", you still need to be aware of this to handle data properly. Plus, some of the new syntax in 1.9 is not backwards compatible with 1.8.
Recommended steps for upgrading from 1.8 to 1.9:
- make sure you have good test coverage!
- make sure your test are checking the output (some end-result validation)
- run on 1.9
- hammer on your code until the tests pass
- decide whether to continue to support 1.8
Prawn only officially supports 1.8.6 and 1.9.1 to make life easier, but if support more versions is necessary for your project, check out ZenTest's multiruby features.
Greg recommends using conditional-execution blocks to make version-dependent code look nicer:
if RUBY_VERSION < "1.9"
def ruby18
yield
end
else
def ruby18
end
end
Greg opines that moving to Ruby 1.9 is not a magic bullet, but has lots of advantages, so try it out!
Ruby 1.8.7
Ruby 1.8.6 is a workhorse (insert image of beat-up pickup truck). Ruby 1.9 is a Lamborghini (we think). "What the hell is 1.8.7?"
Answer: 1.8.7's patch set is largely 1.9 backports. It's a platypus!
However, this doesn't mean that code written for 1.9 will magically work on 1.8.7. Or that code written for 1.8.7 will work on 1.8.6.
What should authors be doing? Should we release for 1.8.6 or 1.8.7? Greg recommends releasing for 1.9, especially if you're writing a Ruby book (wink wink).
Peanut Gallery
Eric Hodel (maintainer of Rubygems), is planning on dropping 1.8.6 support within the next year, but continuing support for 1.8.7 and 1.9.
Writing Extensions
FFI (Foreign Function Interface) is supported "all over the place", and is an alternative to writing a C extension. FFI works across implementations (JRuby, Rubinius, and MRI).
On Windows, Greg proclaims that JRuby is the easiest way to wrap a C library. "WTF?"
Oversimplified Explanations of Ruby Variations
According to Greg. (Not all of the nuance may be captured here, since Greg was moving pretty quickly. Blame me, not him.)
- 1.8.6 is ubiquitous, and may be slowing adoption of other, better interpreters.
- YARV (1.9) is faster than Matz's implementation and is the only complete m17n implementation of Ruby.
- Ruby Enterprise has a great installer!
- JRuby is great and new, but requires C extensions to be rewritten
- Rubinius is what created the RubySpec project and FFI, and is very innovative.
- MacRuby is, um, Ruby for Macs.
Good morning! Today Ben Woosley and I will be live-blogging what's going on during Goruco at Pace University.
You can check out some photos of the conference.
We all have multi-core machine these days, but most rspec suites still run in one sequential stream. Let's parallelize it!
The big hurdle here is managing multiple test databases. When multiple specs are running simultaneously, they each need to have exclusive access to the database, so that one spec's setup doesn't clobber the records of another spec's setup. We could create and manage multiple test database within our RDBMS. But I'd prefer something a little more ... ephemeral, that won't hang around after we're done, or require any manual management.
Enter SQLite's in-memory database, which is a full SQLite instance, created entirely within the invoking process's own memory footprint.
The scene: Pivotal NYC
Jeff Dean walks up to Dan Podsedly, manager of Pivotal Tracker development.
Jeff: Hey Dan, you asked what features we wanted in Tracker. How about a way to automatically mark all my "Finished" stories as "Delivered", all at once?
Dan: (with a sly smile) Well, we have an API call for it ...
Thus was born my humble capistrano task which marks all your "Finished" stories as "Delivered". Hook it into your demo deploy task and save yourself some time.
As an added bonus, if you have Paul Dix's sax-machine gem installed (it's a SAX object parser that uses nokogiri, which I co-wrote with Aaron Patterson), you'll even get a brief summary report of the delivered stories in your cap output.
The code is below (you'll need to generate a Tracker API key). Now go deliver some stories!
#
# To use this task, simply set the following variables:
#
# set :pivotal_tracker_project_id, PROJECT_ID
# set :pivotal_tracker_token, TOKEN
#
# Then, inside the task for your demo platform, add
#
# task :demo do
# ...
# after :deploy, 'pivotal_tracker:deliver_stories'
# end
#
namespace :pivotal_tracker do
desc "deliver your project's 'finished' stories"
task :deliver_stories do
require 'rubygems'
require 'activeresource'
class Story < ActiveResource::Base
self.site = "http://www.pivotaltracker.com/services/v2/projects/:project_id"
end
Story.headers['X-TrackerToken'] = pivotal_tracker_token
puts "* delivering tracker stories ..."
response = Story.put(:deliver_all_finished, :project_id => pivotal_tracker_project_id)
begin
require 'sax-machine'
class Story
include SAXMachine
element :name
element :story_type
element :estimate
element :description
end
class Stories
include SAXMachine
elements :story, :as => :stories, :class => Story
end
doc = Stories.parse(response.body)
puts "* delivered #{doc.stories.length} stories"
doc.stories.each do |story|
puts " - #{story.story_type}: #{story.name} (#{story.estimate} points)"
end
rescue LoadError => e
puts "* stories delivered."
end
end
end







