Good technique for code reviews
At SupportBee, we are building a Gmail like single page app for customer support. Naturally, this is extremely javascript heavy. To organize our code, we use Backbone.js and Jasmine for testing (if you are looking for an introduction to Backbone, you can also checkout my Backbone.js talk at RubyConf India). Over the last few months, we have written (and rewritten) a lot of code using Backbone.js and during this time, several patterns have emerged that were not obvious from reading the docs/examples available on the net when we started out.
Models should not know about Views
This may seem like an obvious one but unfortunately the official Todo example preaches this by saving a reference to the associated view in the model and using it to remove the view when the list is cleared. The problem with this pattern is that it does not scale well when you have a complex app with several views associated with a model. A better way is to create a view and bind it to the model’s destroy event and remove itself whenever the model is destroyed. This way, the model does not have to worry about view cleanup. The same goes for re-rendering/updating the view on changes to the model. Don’t make the model do the book-keeping.
Collections should keep track of their views
The most common backbone pattern is to have a model and a collection of models and initialize a view that initializes this collection. The collection then fetches and parses the response and stores the model. It also fires an add event that you can bind to. In fact it fires a remove event too when models are removed
this.ticketList.bind("add", this.addOne); this.ticketList.bind("remove", this.removeOne)In the callback, you can create views for that model (in our case that means a ticket summary row for every ticket)
addOne: function(ticket) { var indexOfTicket, prevTicket, view; view = new SB.Views.TicketSummary({ model: ticket }); this._viewPointers[ticket.cid] = view; }In the above code snippet, we are storing a reference to the view corresponding to the model in the collection’s _viewPointers variable.
This way, everytime a model is removed from the collection (for example, when a ticket is deleted), you can handle the remove event easily
removeOne: function(ticket) { this._viewPointers[ticket.cid].remove(); }No need to store the view in the model. This is very similar to how Backbone stores a reference to the model in a collection internally.
Events are you friend
This follows from the first two.
There is a good reason why Backbone Model, Collection and View all include the Event module (pardon the ruby terminology). Raising and binding to events is the clean way to do things in Backbone (and Javascript). Apart from the events already provided by backbone, you can raise custom events very easily. Given the fact that all model events are propagated up by the collection, you can really simplify your View update logic.
Make sure jQuery isn’t unloading your events when you don’t want it to
If you are building an app where you create views on the fly and attach/remove them to the dom, you may have a problem. Everytime you remove a view from the dom, jQuery unloads all the events. So you can’t have a reference to a view and remove it from the dom and then re-attach it later. All your events would have been unloaded. If you wanna keep the views around, a better idea is to hide them using display:none. However, you should not abuse this and recycle views that you are not going to use for a while (and prevent memory leaks)
Models for objects not persisted on the server
Almost all of the examples on the net talk about Backbone models that are persisted on the server or using local storage. However, you should look into creating models (and corresponding collections) for objects that will not be saved on the database. For example, in case of SupportBee, we have a Screen model that is used for adding, well screens to the page. We also have a global Screens collection that creates a view everytime a screen is added to it. The screen objects are never persisted on the server but creating a model and collection helps us use events and other backbone patterns to simplify our code. So we are able to do something like
mainView.screens.add(new SB.Models.Screen({ title: 'Unassigned', href: '#unassigned', buttons: [SB.Views.ArchiveButton, SB.Views.AssignButton], default: true, displayTotal: true, listings: [ { title: 'Unassigned', listingName: 'unassigned_and_unanswered', displayTotal: true } ] }))mainView is the global top level view object and screens is the collection instance. Adding the new screen model fires an add event which then goes and setups up the route for #unassigned url and renders the ticket listings etc. This way, we can dynamically add screens anytime we want.
Finally, always test your javascript code. We extensively use Jasmine and Sinon.js. As the client side logic grows, you want good test coverage to be able to refactor mercilessly. More on that in another post. If you have spotted any patterns or have any feedback on the post, please leave a comment.
Nice collection of Backbone.js tips
Is
#tryreally so bad?In response to my recent post about
#trybeing a code smell, a lot of people made the reasonable objection that the example I used—of using#tryon a a Hash—was a pathological case. A much more typical usage of#trylooks like this:
- def user_info(user)
- "Name: #{user.name}. Dept: #{user.department.try(:name)}"
- end
def user_info(user) "Name: #{user.name}. Dept: #{user.department.try(:name)}" end
usermay or may not have an associated department, so the call toDepartment#nameis wrapped in a#try. If there is an associated department, its name will be returned. If not, the result will benil.Straightforward enough. Is there anything wrong with this code?
I can think of a a few things. For one thing, it’s ugly. For my money, one of the hallmarks of beautiful code is that it’s visually consistent: similar operations have similar appearance. In the code above, we access one attribute with simple dot syntax (
.name), and another with a very different-looking.try(:name), even though in both cases the concept we are trying to express is the same: “get the ‘name’ attribute”.It’s a variety of ugliness that tends to proliferate, too. Starting with the code above, it’s not a very big leap to get to this:
- def user_info(user)
- "Name: #{user.name}. Boss: #{user.department.try(:head).try(:name)}"
- end
def user_info(user) "Name: #{user.name}. Boss: #{user.department.try(:head).try(:name)}" endYuck.
And then there are the tests. They’ll probably look something like this:
- describe '#user_info' do
- subject { user_info(user) }
- let(:user) { stub('user', :name => "Bob", :department => stub(:name => "Accounting")) }
- specify {
- subject.should match(/Dept: Accounting/)
- }
- end
describe '#user_info' do subject { user_info(user) } let(:user) { stub('user', :name => "Bob", :department => stub(:name => "Accounting")) } specify { subject.should match(/Dept: Accounting/) } endMetastasizing mocks
This, again, doesn’t seem so bad. But give that test suite six months of active development, and chances are the tests wind up looking more like this:
- describe '#user_info' do
- subject { user_info(user) }
- let(:user) {
- stub('user',
- :name => "Bob",
- :department =>
- stub(:name => "Accounting",
- :head =>
- stub(:name => "Jack",
- :position => stub(:title => "Vice President"))),
- :division => stub(:name => "Microwave Oven Programming")),
- :position => stub(:title => "Senior Bean Counter"))
- }
- # examples...
- end
describe '#user_info' do subject { user_info(user) } let(:user) { stub('user', :name => "Bob", :department => stub(:name => "Accounting", :head => stub(:name => "Jack", :position => stub(:title => "Vice President"))), :division => stub(:name => "Microwave Oven Programming")), :position => stub(:title => "Senior Bean Counter")) } # examples... endNot only that, the same tree of stubs will probably be duplicated, with subtle differences, for every test group that interacts with a User—because no one has time to sort out the specific subset of stubs that a given test actually needs in order to function.
At some point the client will decide that users really need to be associated with zero or more departments instead of just one. At that point some unlucky programmer will spend a late night fixing the 300 tests this “small change” breaks because of all the stubs that model the old behavior. Then the next day he’ll write an angry rant about how mock objects are a bad idea.
Structural coupling
The seed of this all-too-common predicament is structural coupling. What’s structural coupling? To define it, let’s start with a review of the DRY principle:
Every piece of knowledge must have a single, unambiguous, authoritative representation within the system.
It’s easy to think about DRYness just in terms of data: e.g., there should be only one place in the system for API keys; they shouldn’t just be copy-and-pasted willy-nilly throughout the codebase. But DRY applies equally to structural knowledge: knowledge about the composition of and relationships between your objects.
Let’s take a look at the code we started out with:
- def user_info(user)
- "Name: #{user.name}. Dept: #{user.department.try(:name)}"
- end
def user_info(user) "Name: #{user.name}. Dept: #{user.department.try(:name)}" endThis seemingly innocuous code makes the following assumptions:
userwill have a name property.usermay or may not have a single department.user‘s department, in turn, has a name propertyBy going two levels deep into
user‘s associations, we’ve made a structural coupling between this code and the models it works with. We’ve duplicated knowledge about a User’s associations—canonically located in the User and Department classes—in the#user_infomethod.And the
#trymethod was an enabler. By papering over the uglyuser.department && user.department.nameconstruct we’d otherwise have had to use,#trymade the coupling an easier syntactical pill to swallow.This would be bad enough if we made a habit of it, because we’d have to change every method with a similar structural coupling whenever the innards of User or Department changed. But because we’re good Test-Driven developers, we then proceeded to couple dozens of test suites to a specific model structure, in the form of stubs and mock objects.
This is clearly an undesirable outcome. Wouldn’t it be handy to have a simple rule that helps us avoid structural coupling?
The Law of Demeter
Back in the 1980s, a group of programmers working on a project called the Demeter system realized that certain qualities in their object-oriented code led to the code being easier to maintain and change. Qualities such as low coupling; information hiding; localization of information, and narrow interfaces between objects. They asked themselves: “Is there a simple heuristic that humans or machines can apply to code to determine whether it has these positive qualities?”.
The answer they came up with came to be known as the “Law of Demeter”. It is stated as follows:
For all classes C. and for all methods M attached to C, all objects to which M sends a message must be instances of classes associated with the following classes:
- The argument classes of M (including C).
- The instance variable classes of C.
(Objects created by M, or by functions or methods which M calls, and objects in global variables are considered as arguments of M.)
WikiWiki explains the law like this:
- Your method can call other methods in its class directly.
- Your method can call methods on its own fields directly (but not on the fields’ fields).
- When your method takes parameters, your method can call methods on those parameters directly.
- When your method creates local objects, that method can call methods on the local objects.
If that still seems confusing, here’s an alternative explanation from Peter Van Rooijen:
- You can play with yourself.
- You can play with your own toys (but you can’t take them apart),
- You can play with toys that were given to you.
- And you can play with toys you’ve made yourself.
The Demeter programmers wrote up their experiences in a paper called Object-Oriented Programming: An Objective Sense of Style. What they found was that when methods were written in a form which complied with the Law of Demeter, the resulting codebase was easier to maintain and evolve.
It’s important to understand that the Law of Demeter is a heuristic, not an end in and of itself. It is not a law in the sense that you “must” write your code in a certain way. Rather, it is a law in the sense that it has been consistently observed that if code complies with the Law of Demeter, it almost certainly has a number of the qualities—encapsulation, loose coupling, etc.—desirable in an OO system.
Laying down the law
With that in mind, let’s take one more look at our example code:
- def user_info(user)
- "Name: #{user.name}. Dept: #{user.department.try(:name)}"
- end
def user_info(user) "Name: #{user.name}. Dept: #{user.department.try(:name)}" endThis code does not comply with the Law of Demeter. In addition to calling methods on its parameter,
user, it also calls a method on the result of one of those methods: (department.name).Assuming this is a Rails program, it is extremely easy to change the code to satisfy the law. First, we make a one-line addition to the User class:
- class User
- delegate :name, :to => :department, :prefix => true, :allow_nil => true
- # ...
- end
class User delegate :name, :to => :department, :prefix => true, :allow_nil => true # ... endThe
#delegatemacro, provided by ActiveSupport, generates a new methodUser#department_namewhich delegates to the user’s#department. By supplying:allow_nil => true, we ensure that the method will simply return nil in the case when there is no department associated with the user.Here’s our code again, updated to use the new method:
- def user_info(user)
- "Name: #{user.name}. Dept: #{user.department_name}"
- end
def user_info(user) "Name: #{user.name}. Dept: #{user.department_name}" endThe code now respects the Law of Demeter: it is coupled only to the immediate interface of the
userparameter.The updated test suite now has only one stub object:
- describe '#user_info' do
- subject { user_info(user) }
- let(:user) { stub('user', :name => "Bob", :department_name => "Accounting") }
- specify {
- subject.should match(/Dept: Accounting/)
- }
- end
describe '#user_info' do subject { user_info(user) } let(:user) { stub('user', :name => "Bob", :department_name => "Accounting") } specify { subject.should match(/Dept: Accounting/) } endAlready we have a simpler test suite. But the real benefit comes when it is time to change the models. Let’s consider the case when a User changes from being linked to just one department, to having a list of zero or more departments. How we re-implement
User#department_namedepends on the needs of the domain. Let’s say the department name should now be a comma-separated list:
- class User
- def department_name
- departments.join(", ")
- end
- end
class User def department_name departments.join(", ") end endWe replace our delegate method with a method implementing the new semantics. And that’s the only change! The
#user_infomethod remains the same, as does every test suite that referencesUser#departmentname.By adhering to the Law of Demeter, we have decreased coupling, and increased the velocity with which we can make changes to the business logic.
Objection #1: What about method chains?
“But Avdi” you may object, “it sounds like a good guideline, but clearly it’s not something to be rigidly adhered to in Ruby code. If we followed it all the time we could never do method chaining!”
Method chains are a core Ruby idiom, to be sure. As an example, here’s a method which takes a string and generates a “slug” for use as an identifier or as a a URL component:
- def slug(string)
- string.strip.downcase.tr_s('^[a-z0-9]', '-')
- end
def slug(string) string.strip.downcase.tr_s('^[a-z0-9]', '-') endThat’s one, two, three levels of method call. That can’t comply with the Law of Demeter, but it surely is concise and convenient!
Look again at the definition of the Law: it never says anything about the number of methods called, or the number of objects a method uses. It is strictly concerned with the number of types a method deals with.
The
#slugmethod expects a String, and calls three methods, each one returning… another String. In fact, because it only calls methods for the type of object (String) passed into it as parameters, we find that this method complies perfectly with the Law of Demeter.Likewise with another common Ruby pattern, chains of Enumerable methods like
#mapand#select. Because each returns another Enumerable object, there is no violation.Objection #2: Delegation explosion
Another objection to Demeter is that strictly following it results in objects which are full of attributes which aren’t a direct part of their responsibility. Quoting Mark Wilden in the comments on my previous article:
Why should a Human have to know whether a Country has a name? Or any other attribute (unless it needs them itself)? If a Human is associated with multiple Countries (birth, residence, voting, vacation, etc.) does it then have to duplicate this delegation for each method of each country?
What about attributes that clearly have nothing to do with Human? Yes, one might say that a Human has a country_name. But does a Human have a country_population? A country_mortality_rate? I would say it does not, but Demeter insists that it must.
In the Object-Oriented view of the world, objects are not merely bags of attributes. They are entities to which you send messages and from which you receive replies. The classic example is a financial transaction: if I am a shopkeeper and you buy something from me, I don’t ask you for your wallet, rummage around until I find a credit card, and then copy down the information I need. Instead, I ask you for your credit card number and expiration date.
Putting this in object terms, a payment system which calls
person.wallet.credit_cards.first.numberexhibits tight structural coupling, and is closer in spirit to the data-structure-oriented programming which preceded OO. From an objects-sending-messages standpoint, it is perfectly reasonable for a Person to have a creditcard_number.An important and often neglected point to hit on, before we move on: in an Object-Oriented system, it is prfectly allowable (and even encouraged) for objects to have personas or facets. A doctor deals with a patient’s physical symptoms while the reception desk deals with her wallet and insurance info. You wouldn’t walk into a doctor’s office, step up to the receptionist, and take off your shirt (unless you were on very good terms with the receptionist!).
Likewise, an object can have a large API, but only expose subsets of that API to different collaborators. Some languages enforce these subset relationships quite strictly; e.g. C++ with its private inheritance, and interfaces in Java. In other languages, such as Ruby, the restriction may be more about convention than something the language enforces. There’s nothing wrong with having a large API, so long as individual collaborators only talk to well-defined subsets of it.
But what about Mark’s example
human.country_mortality_rate? Surely that’s pushing it a bit far?Perhaps it is. But Demeter doesn’t prevent us from interacting with an objects second- and third-order associations; it simply asserts that we can’t interact with all of those objects in the same method. Look again at the formulation of the law:
…all objects to which M sends a message…Demeter is a rule about methods only; it does not limit the set of types a class can interact with.
So this is perfectly legal:
- class StatPresenter
- def human_stats(human)
- "Age: #{human.age}.\nCountry stats:\n#{country_stats(human.country)}"
- end
- def country_stats(country)
- " Mortality rate: #{country.mortality_rate}"
- end
- end
class StatPresenter def human_stats(human) "Age: #{human.age}.\nCountry stats:\n#{country_stats(human.country)}" end def country_stats(country) " Mortality rate: #{country.mortality_rate}" end endOf course, you could completely violate the spirit of Demeter by taking this too far; something the authors of the Demeter paper note. Realistically, we’d probably want to break that
StatPresenterclass up into smaller classes once it started interacting with many different types of object.The important thing, from the standpoint of Demeter, is to avoid tying a single method to a deep hierarchy of types, as well as limiting the number of types one method deals with.
One of the most basic ways we can limit the number of types a given method must be aware of is to eliminate the common case of “maybe nil” parameters. Remember,
NilClassis a type too, and when a parameter might be nil we’ve increased the number of types the method has to know about by one.As an example, the following version of the code above, while technically Demeter-compliant, is once again riddled with
#trycalls:
- class StatPresenter
- def human_stats(human)
- "Age: #{human.age}.\nCountry stats:\n#{country_stats(human.country)}"
- end
- def country_stats(country) # country may be nil
- " Population: #{country.try(:population)}\n" +
- " Mortality rate: #{country.try(:mortality_rate)}\n"
- end
- end
class StatPresenter def human_stats(human) "Age: #{human.age}.\nCountry stats:\n#{country_stats(human.country)}" end def country_stats(country) # country may be nil " Population: #{country.try(:population)}\n" + " Mortality rate: #{country.try(:mortality_rate)}\n" end endThe set of types
#country_statsdeals directly with is:StatPresenter(self),Country, andNilClass.We can’t always get rid of switching on
nilentirely, but what Demeter-influenced code gives us the opportunity to do is to easily confine that switch to a single location. Let’s rewrite the code above:
- class StatPresenter
- def human_stats(human)
- "Age: #{human.age}." + (human.country ?
- "\nCountry stats:\n#{country_stats(human.country)}" :
- "\n(No Country Stats)")
- end
- def country_stats(country)
- " Population: #{country.population}\n" +
- " Mortality rate: #{country.mortality_rate)\n"
- end
- end
class StatPresenter def human_stats(human) "Age: #{human.age}." + (human.country ? "\nCountry stats:\n#{country_stats(human.country)}" : "\n(No Country Stats)") end def country_stats(country) " Population: #{country.population}\n" + " Mortality rate: #{country.mortality_rate)\n" end endWith this final edit, we’ve reduced the coupling of each method to a minimal point.
StatPresenter#human_statsdeals only withHumanobjects, and all it knows about#countryis that it may or may not be there.StatPresenter#human_statsonly knows aboutCountryobjects.Bringing Demeter to work
“OK, fine. I can see that the Law of Demeter is a great guideline, at least in theory. But who has time to do all that refactoring? I have deadlines to meet!”
While refactoring code to comply with Demeter can certainly improve its design, I don’t think Demeter becomes truly practical until you incorporate it consistently into your coding style. Like many low-level “code construction” techniques—such as good variable naming—its value lies less in coming in and applying it after the fact, and more in practicing it until it becomes second nature.
Let’s take a look at how we’d add the department name to the
#user_infomethod using TDD and the Law of Demeter. Here’s the code before adding the new functionality:
- def user_info(user)
- "Name: #{user.name}"
- end
def user_info(user) "Name: #{user.name}" endNow let’s add the department name.
We write our test first:
- describe '#user_info' do
- subject { user_info(user) }
- let(:user) { stub('user', :name => "Bob", :department_name => "Accounting") }
- specify {
- subject.should match(/Dept: Accounting/)
- }
- end
describe '#user_info' do subject { user_info(user) } let(:user) { stub('user', :name => "Bob", :department_name => "Accounting") } specify { subject.should match(/Dept: Accounting/) } endWe know that nested mock/stub objects is a smell indicating structural coupling, so we force ourselves to write a stub for the
userobject we wish we had. TheUser#department_namemethod doesn’t exist yet; we make a mental note to implement it. If we forget, the omission will be caught by our acceptance and/or integration tests.- We run the spec. It fails, because we haven’t implemented it yet
We write enough code to make the test pass:
- def user_info(user)
- "Name: #{user.name}. Dept: #{user.department_name}"
- end
def user_info(user) "Name: #{user.name}. Dept: #{user.department_name}" end- We run the tests again, and this time they pass.
The final step is to implement the
User#department_namemethod. We could write a test asserting that the method delegates toDepartment; personally, I find this a little redundant and would just write the delegation and call it done:
- class User
- delegate :name, :to => :department, :prefix => true, :allow_nil => true
- # ...
- end
class User delegate :name, :to => :department, :prefix => true, :allow_nil => true # ... endRevisiting your code to make it Demeter-compliant after the fact will indeed slow you down. By incorporating the rule into your habits, to the point that it becomes second nature, you reduce the impact (if any) to the point where it becomes insignificant. This is especially true in Ruby and Rails, where techniques such as composition-and-delegation, viewed as “heavyweight patterns” in some languages, become one-liners. And any fractional slowdown you do experience from an extra test run here and there will be more than made up for by the ease of changing your loosely-coupled code as requirements change. With discipline and practice, it is possible to be both fast and good.
Conclusion
To summarize:
#tryis more often than not indicative of structural coupling. Structural coupling, in turn, violates the DRY principle.- Structural coupling, left unchecked, can substantially slow the evolution of a project.
- The Law of Demeter, which sets limits on the number of types a single method can interact with, is a heuristic for identifying code that (among other positive properties) has low structural coupling.
- When refactor our code to comply with the Law of Demeter, it tends to reduce structural coupling both in application code and in tests. As a side effect, it tends to eliminate the need for
#trycalls and similar constructs.- Contrary to popular belief, Demeter does not limit the number of of dots in a method call chain. It also doesn’t limit the number of classes a class can interact with.
- The best way to incorporate Demeter into your work is to make it a habit, rather than a cleanup chore.
Do you look for Demeter violations in your code? Do you think there are still some instances where a
#trymakes sense? Do you have more questions about the Law of Demeter or structural coupling? As always, I welcome feedback in the comments!P.S. It’s my birthday! To celebrate, for 24 hours I’m offering 50% off on my book, Exceptional Ruby. Use code
HAPPY0X1Fto get the discount.
This work, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.Related posts:
Blammo
If you want to run pow alongside the MacOS X built-in apache this page will instruct you on how to setup pow so that it doesn't takeover port 80 and instead apache will reverse proxy requests to pow.
For the Impatient
$ curl get.pow.cx/uninstall.sh | sh #if you have pow installed $ echo 'export POW_DST_PORT=88' >> ~/.powconfig $ curl get.pow.cx | sh $ sudo curl https://raw.github.com/gist/1058580/zzz_pow.conf -o /etc/apache2/other/zzz_pow.conf $ sudo apachectl restartFull Installation Instructions
1. Before installing
Before you install pow, add the following line to your ~/.powconfig
export POW_DST_PORT=88Here it is as a one-liner
$ echo 'export POW_DST_PORT=88' >> ~/.powconfigThis will cause pow's firewall run to redirect all traffic from port 88 instead of port 80. You can pick any port you like for this if you use 88 for something else. It doesn't effect the remainder of these instructions. If you have already installed pow, you will need to uninstall it before you you continue.
2. Install pow as normal
If you need those instructions, you can find them here: Installation
3. Configure Apache
You'll need to drop the file in this gist into
/etc/apache2/other/zzz_pow.confHere it is as a one-liner
$ sudo curl https://raw.github.com/gist/1058580/zzz_pow.conf -o /etc/apache2/other/zzz_pow.confThis sets up apache to act as a reverse proxy to pow. It goes directly at pow's default listen port, not the firewall port we configured in the previous step. The file name zzz_pow.conf to ensure that apache picks up the vhost for pow last, allowing you to put vhosts for other apps in front of it.
4. Start/Restart Apache
You'll need to turn on "Websharing Sharing" in the MacOS X system preferences if you haven't already. You can find it in System Preferences -> Sharing -> Web Sharing. NOTE: This will make it so anyone with your machines IP Address can access your apache server. This is the default setting from apple. If you want to change this, you will want to change the IP address that apache binds to in /etc/apache2/httpd.conf.
If Apache is already running, you can restart by either turning Web Sharing off and then back on, or you use this on the command line
$ sudo apachectl restartAfter Installation
Leveraging pow's DNS for other apps
If you now configure your vhosts for the apps you want to run in apache with *.dev hostnames, pow will provide the DNS resolution for you, which means you get to keep the benefit of not mucking with your hosts file. For example, if you have a php app, you can setup it's vhost entry to use
Using ssl with pow
This setup can be extended to allow using ssl with pow. You simply need to configure the pow vhost we setup for apache for ssl by following he apache documentation. The implementation is left as and exercise for the reader, and is ruled by the normal limitations of using ssl with apache and a name-based vhost configuration.
Troubleshooting
Apache is servicing a request that I expect to be passed to pow
Make sure that you don't have another virtual host that is configured with the same name
Pow is servicing a request that I expect to be passed to Apache
Make sure that you have another (default) virtual host configured. For example, something like this:
/etc/apache2/other/aaa_default.conf <VirtualHost *:80> #no need for settings, use apache defaults. </VirtualHost>You also need to ensure VirtualHost's are enabled, that is this line needs to be in your config somewhere
NameVirtualHost *:80You could put it into your httpd.conf or uncomment the vhosts extra line in it:
Include /private/etc/apache2/extra/httpd-vhosts.confI suggest you comment out the sample vhosts specified in that file.
Very handy if you do lots of PHP and Rails work.
I’ve just finished reading Accidental Genius by Mark Levy (second edition) on my Kindle. I’ve really, really enjoyed the book, so I decided to write a detailed summary of it, capturing all the “Accidental Genius” ideas. Enjoy!
I would say that alternative name of this book could be “How to think”. It is a must read.
The subtitle of the book is “Using Writing to Generate Your Best Ideas, Insight, and Content”, and it is a very appropriate one. Additionally, the techniques described can be used to solve any problem, whether business or personal. I believe that the ideas found in “Accidental Genius” are extremely powerful.
Accidental Genius will rock your world if you need to do any of the following:
- Think deeply
- Create great content (article, book, speech)
- Solve any problem
- Come up with awesome ideas
- Get unstuck
- Get creative
- Organize your mind and thoughts
- Help someone with any of these things
The core idea of the book is Freewriting
Freewriting is a fast method of thinking onto paper. It is basically about spilling your mind onto paper or computer, without any judgment or stopping – just fast continuous writing, going with the flow. You can even talk to yourself in your writing, or write “blah blah blah” repeatedly when stuck. It is the quantity that is important. Quality can be distilled later by editing.
Writers block? Pffff. That doesn’t exist in the realm of Freewriting. Just write – even if it’s crap. You are thinking already, so write the thoughts. Even if you have to write about how you don’t have anything to write, you can write.
The Freewriting technique in allows you to access and exploit your subconscious mind – that’s where the “genius” lives. It enables you access the raw stream of thoughts directly from your sub consciousness. The results are almost always incredible.
And it works for everyone. Even if you hated school writing assignments, you will love Freewriting.
About this summary
I will summarize all chapters of the book here, so you will get all the main ideas.
If this summary intrigues you, I wholeheartedly recommend you to get the book, as it contains many stories and additional material that vividly illustrate the techniques presented and their application.
The book is divided into 3 parts:
- Part 1 talks about the six secrets of Freewriting.
- Part 2 adds additional ideas, tools, and techniques.
- Part 3 talks about publicizing the written content (as blog posts, speeches, books, etc).
Part 1 – Six Secrets of Free Writing
Secret #1: Try Easy
You do not have to try hard. In fact, it is best when you give only 90% effort.
Begin your writing by reminding yourself to try easy. Remind yourself that you do not want produce perfect prose that will be cherished and treasured for generations to come; you just want to write some decent words. You just want to dump your brain.
Lower your expectations, and be pleasantly surprised.
Secret #2: Write Fast and Continuously
When you write fast and continuously, you will adopt easy and accepting attitude, which unblocks your subconscious mind.
If you write slowly, your talking mind will creep in. We don’t want that. The talker in your head sucks. There is only so much mental resources, and the talker will eat too much of them. The talker shuts down your genius sub-conscious computer, which can access all your memories, thoughts, experiences, ideas.
Writing continuously is also important. You want to suppress the editor (a.k.a. censor) in your mind, so the idea-producer can do its job unchained. The “inappropriate” thoughts can often be the key, so you want to make sure they can appear in your writing.
When you get stuck, you can:
- Babble nonsense onto the page
- Repeat the last word again and again
- Repeat the last letter you’ve pressed on your keyboarddddddddd
- Write anything that comes to your mind, even if it is completely unrelated to the writing
Your mind will quickly get unstuck and come with a new thought to write about.
It’s all about the quantity, uncensored. Think of yourself as word/thought producing factory.
Secret #3: Work Against a Time Limit
Pomodoro technique works brilliantly with Freewriting.
When you are Freewriting, set a short time limit for yourself – e.g. 10 or 20 minutes. You can use a kitchen timer or some software for this.
The time limit is important for two reasons:
- The limit energizes your writing effort by giving you parameters. You can go “all in”. Deadlines motivate.
- The limit keeps you writing, so you’ll have a chance for a genius moment. You will write even if you’re feeling uninspired, which is a very good thing: Paradoxically, you can get the best ideas when writing the worst junk.
Secret #4: Write the Way You Think
You need to get your raw thoughts.
Don’t write the way you speak, write the way you think. Your speech is already censored.
If you really write the way you think, your writing will probably make no sense to other people. That’s a sign you’re doing it right. (Don’t show it to other people, though. Write for yourself. Knowing that someone else might see the text would activate the little totalitarian censor in your mind.)
To achieve this kind of writing, do:
- Use your language.
- Keep quiet about things that need no explanation.
- Jump around just as your mind does.
You’re the only person that needs to understand what you write.
Freewriting isn’t really writing, it’s a means of watching yourself think.
Secret #5: Go with the Thought
When doing an improv theater, it is a golden rule to always accept the situation and agree with what other actors say. Think “YES, AND”.
Go with what you’re given. Always go with the thought that you’ve just written.
It’s all about “agreeing and extending”. It’s a great way to get “far”.
When you go with a thought, you assume that the thought is true, and you can take a series of logical steps. Just like this: If A is true, that means B is true. And if B is true, that means C is true. And if C is true, …
Secret #6: Redirect Your Attention
When you run out of things to say, you can use “focus changer”. Focus changer is a question you ask yourself on paper that requires you to comment on something you’ve just written. It keeps you moving, and helps you focus on the yet unexplored parts of a situation.
Examples of focus changers include:
- What was I thinking here?
- How else can I say that?
- What am I missing here?
- What I am wrong about here?
- What I am doing right?
- What does this remind me of?
- How would I describe this to my grandmother?
Etc. The book includes many more such example questions. You can (and should) also invent your own focus changers.
Part 2 – Powerful Refinements
The second part of the book talks about Freewriting applications, exercises, and techniques that allow you to get the most out of it.
Idea as Product
This chapter talks about why is the conversion of your thoughts into a paper product important.
Having your ideas written is important because as the written product shows you where you’ve been, it also suggests where you haven’t been. It’s a map of your mind.
Prompt Your Thinking
Prompts are Freewriting exercise. When doing this exercise, you begin the Freewriting session with a pre-determined prompt.
Prompts are open-ended phrases to warm you up and to send your mind into unanticipated directions.
Prompts allow you to find many hidden jewels that you wouldn’t otherwise discover.
Some examples of prompts include:
- The best part of my workday is…
- Yesterday I saw a curious thing…
- If I didn’t have to work, I’d…
- I threw a stone and it landed…
- I remember….
- I’d love to learn about…
- If I did the opposite of everything I normally do, my day would look like this…
- I love…
- I hate…
- I should do more…
- You know what I’d like to do again?
- If I were guaranteed success, the project I’d take on would be…
The very generic ones can work wonders:
- The storm
- It was getting dark…
- The birds were singing…
- I opened the door…
- Three days from now…
Prompts are somewhat similar to one of my long-time favorite techniques – question answered on paper. Basically, you write a question, and then start answering it. That’s it. It’s like querying your mind.
When you make the question very open-ended, you are not looking for specific answers, and it’s a prompt. The result will be a surprise. On the other hand, when the question is very specific, so will be the answers.
Open Up Words
When you “open up a word”, you redefine it and give it a personal meaning. The way the world sees that word and its meaning might not be the same way you see it.
- The first step is to pick the word. You’ll often come across words whose definitions are taken for granted, no-brainers. These are great words to open up. Some words might always seem negative and others positive, and yet, the true meaning might be entirely different.
- The second step is to write the common definitions of the word. What the world sees.
- In the third step, you ask yourself if you agree or disagree with that definition, and go into detail about your opinion. Write your personal definition.
- In the fourth final step, you summarize what you’ve learned. Just a couple of sentences should do. This helps you integrate the newly gained knowledge.
Exercise to try is to make a list of 5 common jargon words from your industry, and open up each for five minutes.
Escape Your Own Intelligence
When you are Freewriting, you should reach for obvious facts. Don’t overlook the truth hidden behind complex mental constructs of the problem you are trying to solve.
We humans have the tendency to make things really, really complicated. Sometimes, this is a bad thing.
When you write all the obvious facts about the situation, you will quickly see what makes sense and what is important. The ridiculous bullshit will be naked in the lights of the obvious.
The obvious facts can help you cut through the fog. Let one fact suggest the next.
The Value in Disconnecting
When you write long enough, your handwriting will really relax, and the internal editor will completely disappear. It’s the total uncramping of the hand and brain. The raw mind can finally shine through.
What is long enough? When you start getting tired, it is long enough. 30-minute session of fast non-stop writing should do the trick.
Never be afraid to turn your back on what you’ve already written. Disconnecting from the material can be just as valuable as connecting to it. Feel free to write “All what I’ve written up to this point is bullshit. The truth is that…”Using Assumptions to Get Unstuck
One way of making a breakthrough is by trashing a paradigm. For example, people once believed that Earth is flat. That was a paradigm. All that was needed to ditch that paradigm was a perspective shift.
The perspective shift allows you to approach your problem using an entirely different perspective.
A structured approach to do this is to ask yourself:
- What problem am I trying to solve? (Be as general as possible. Nothing specific. For example – “How do I build a fan base for something unknown?”)
- Who has solved it?
- How have they solved it?
- How can I apply their solution to my problem?
(This might require some research.)
Getting a Hundred Ideas Is Easier Than Getting One
The name of this chapter pretty much sums it up. When you have to come up with an idea, don’t try to come up with a single perfect fabulous idea. Instead of one 100% idea, go for hundred ideas of varying quality.
When you go for a single idea, you need to create and judge at the same time. When you go for hundred ideas, the quality is a non-issue. This allows you to focus entirely on the creation. One idea leads to the next – but not if you judge it and discard it.
You can’t create a killer idea from thin air anyway. You need material to work with.
You want to look for ALL the ideas first, and only then for the ONE single best idea.
Learn to Love Lying
Problem situations can often seem like closed environments. Everything is set, there are no options. But this is usually an illusion; things are seldom set in stone.
One way to overcome this rigidity which often prevents you from seeing the solution is to simply tell yourself a lie about the situation. A single lie can lead to a chain of thinking that will solve your problem.
Some examples:
- If an element in your situation is small, think of it as micro or huge
- Tall, think skyscraper-high or subbasement-low
- Clever, think genius or completely stupid
- Nuisance, think of it as intolerable or a blessing
- Abnormal, think freakish or natural
Lying can help you discover the false limits that we set for ourselves in order to be “realistic”.
Hold a Paper Conversation
This one is fun.
You can talk to anyone. Steve Jobs, your local grocer, your grandmother, Oprah, talking owl, yourself 20 years ago or 20 years in the future. Or, you can remove yourself entirely from the situation and produce a discussion between others.
There are two rules to make these conversations successful:
First, when holding a paper conversation with a character, it’s important to make the character as real as possible. Before holding a paper conversation with a character, take a couple of minutes to write about the character. Be as specific as possible. Go as far into detail as you want. Also, don’t forget to think about the place where the conversation is taking place.
Second, get the characters to make you speak. Let yourself do the majority of the talking during the conversation. The character should act mainly as a gentle guide which draws fresh observations from your own words.
Drop Your Mind on Paper
Freewriting is a component-based system.
Mix the techniques, exercises and session lengths.
Talk to yourself on paper about any resistance you might be feeling about your problem or the Freewriting method.
Save all your written material, so you can re-read it or continue later.
The Writing Marathon
A ten minute session of Freewriting might not sometimes quite cut it.
Sometimes, you might even need six hours or more.
The bad part is that you might feel physically uncomfortable at the end of the marathon. The good part is that you might discover answers that have been hiding from you for a lifetime.
Very long stretches of continuous writing certainly work for some. For example, popular blogger Steve Pavlina often writes very lengthy posts that take up to 10 hours of continuous writing to create.
The Freewriting marathon is not just 6 hours of non-stop Freewriting, it works a bit differently.
First, you need a starter thought. The process is then as follows:
- Write for 20 minutes non-stop
- Go through what you’ve written and underline or bold the most interesting parts.
- Ask yourself – what do I want to explore next? (It should send you in a new direction. If you get what you expect, it isn’t good enough.)
- Repeat.
Doubt Yourself
Recognize when you are stuck, and get unstuck.
Not everything you write will be brilliant. There will be a lot of great stuff, but you will undoubtedly find many bad or wrong thoughts in your writing. Also, there will be thoughts that you will become tired of. You can recognize them because it will seem that you come across them far too often.
These thoughts might be important indicators of areas where you need to change.
If you always come to same thoughts (“I really should finish X”), or the same people (“Peter is the key to finding a way out of this”), or the same language, or you are continually critical of yourself or others, and in many other cases where you repeat yourself, your thinking is clearly stuck.
However, that’s fine! Even if you are stuck in life, you can describe just exactly the way you’re stuck. Once you do that, you will immediately realize that you can’t go on like that anymore, and gain a new way to freedom.
The Magic of Exact Writing
Exact writing allows you to get to the core of any problem or situation.
The case with exact writing is similar to the concept of “100 ideas”. It is impossible to exactly describe the situation in a few brief
sentences outright. You need to flood the page with words. Include lot of detail and use your own language.If even that doesn’t help, try to describe the situation to a certain person. Just begin your writing using salutation, for example, you can use “Dear John” and describe the situation to your best friend John as accurately as possible.
Extract Gold from a Business Book
When you come over to a business book and have a hunch that you could get a lot of value from it, try this Freewriting technique.
As you read the book, add underlining and write notations inside it. Customize the book, make it your own. Write insights and questions into the book, even if you need to write over the existing text. Don’t be stingy with your customizations.
Now, once you finish reading the book, go through it again, and write the most interesting underlined parts and notations into your computer. Then, do a 20-minute Freewriting session about these ideas and questions. This will allow you to add real personal meaning to the most important ideas in the book, and internalize the knowledge presented.
It makes no sense to be lazy here. It took you a couple of hours to read the book anyway, so why not do the 20-minute Freewriting session, to get ten times more value from the book than you would normally get?
You Are What You Focus On
There is a great example in this chapter:
Without looking up from your computer, create a mental list of all the red objects in the room. Do this now.
Now look around. How many additional red objects do you see? Before you started reading, you weren’t focused on red objects, so you’ve probably missed a lot of them in the initial list.Now, if I tell you that I’ll pay you $1000 for a list of 100 red objects in the room, you will get really creative! Perhaps you will think “If I unscrew my telephone receiver, I see red wires. If I cut my finger, I see red blood. If I break this red bookcase into its parts, I have six red shelves.”
The objects are extremely “evasive”, even if they are very close to you. Focus is everything.
What you focus on determines how you lead your life. Therefore, you should use Freewriting to focus on things that are important for you in your life. Don’t get overwhelmed by the grind of the daily existence.Part 3 – Going Public
When Freewriting, you must always assume that no one will see what you’ve written. It should be a strictly private matter. This is necessary so you can really let go and let the inner censor recede.
However, you still can (and should) use the fruits of your Freewriting effort for public documents – books, blogs, articles, anything. This chapter talks about the ways of doing that.
Share Your Unfinished Thoughts
Sometimes, no polishing or editing is necessary or desired. You can just take a part of your writing and send it to someone.
If you are blogger, this could work great when querying for an interest in a guest post. If the host blogger says yes to your thoughts, or gives you extra thoughts of his own, you can deliver a piece that he and his audience will be happy with. Another advantage is that the chance you will procrastinate is much lower. You can just sit down and Freewrite.
You can also create a large collage with all your ideas and thoughts about the subject, and send it to someone. This works in many situations, even when writing a proposal for a book.
It’s good to share what you are thinking and feeling, even if you are not sure about what you are thinking and feeling yourself. “Talking” document allows you to share these thoughts. Just think aloud on the paper.
You can either write a letter, or assemble a collage. Don’t forget to tell the recipient what kind of feedback you would like.
Help Others Do Their Best Thinking
When working with a client as a consultant, or when helping a friend or relative with a difficult problem, you can teach them Freewriting, and any related techniques useful in their situation. They will thank you later :-)
Notice Stories Everywhere
Stories happen only to people who can tell them. Or, I would say, to people who want to tell them.
Always keep your eyes and mind open for stories and material as you go about your day. Become a walking library of stories. Write the stories down, you never know when they will come in handy.
Build an Inventory of Thoughts
This chapter fascinates me.
If you write regularly, you will generate a huge amount of material. You can then chop the material up and divide it into separate documents. For example, you can have “Marketing” document “Childhood” document, “GUI Design” document, “Fitness”, document, etc. You can have as many documents as needed. The documents should be based on themes you most commonly write about.
The chunks should be complete thoughts, not just fragments. Even if you read these chunks 20 years from now, they should still make sense.
If you build such a library, you will have myriads of material to work with. Whenever you will be writing something, you can dive into the appropriate documents and fish for chunks to use.You are basically stockpiling thoughts, ideas and stories in your computer. Sure, they all reside in your head in one way or another, too, but having them already written is something completely different. At any given moment, it is impossible to recall all the relevant thoughts. However, it’s easy to go through the relevant documents.
This also helps you to write better, because you will be using material that you had time to think about, not just making stuff up as you write.
Yet another benefit is that you will remember all your stories and thoughts better, even when you are not at the computer.
Another use of chunks is to use them as a thought-starter for something entirely new.Write Your Own Rules
Many famous writers have their own rules that help them with their writing.
For example, Hemingway had a rule of never finishing the last sentence at his writing session end. He wouldn’t finish the sentence until the next day.
Another of his rules was to write 500 to 1000 words every day.
Ray Bradbury also had a lifelong habit of writing every day.
Yet another great rule is to keep the writing and editing (revising) steps separate. Generating prose is one task, making it sound good is another task. Don’t overload yourself.
It’s a good idea to give yourself a couple of rules (goals) at the start of the day, before you get overwhelmed by the daily business.
You can have rules for different parts of the process: Warm up rule, ideation rule, writing rule, etc.
Many of the exercises described in Accidental Genius can be made into rules.
Good rule enables you to write more.
The Fascination Method
This method can help you when writing a book, or preparing a speech, etc.
When writing a book, you are basically a filter. As you’ve lived your life, you’ve gained millions of experiences and thoughts that are unique to you. Your book should begin from this material.
To get to the really good stuff, create an inventory of everything that has fascinated you at any point of your life. A list of:
- Facts
- Insights
- Anecdotes
- Philosophies
- Experiences
- Memories
- Surprises
- Websites
- Dreams
- Myths
- Books
- Conversations
- People
Literally anything that has ever fascinated you! Don’t wonder why a particular item fascinates you. Don’t worry if it has any relation to a book you want to write. Items on the fascination list have energy for you.
Take your time. Divide the writing of the list to multiple Freewriting sessions spread among a couple of days.
Once the list is done, think about the audience and what you could tell to that audience. Who might the audience be? What book demands to be written? Just brainstorm, based on the material presented to you.
Group the items from the fascination list. There will be several themes.
A book that has begun through this process will be something that:
- Will be joy to write
- Will be uniquely yours (one of a kind)
- The audience will be able to use and appreciate
The main idea is that you do not look into the market first. You look into yourself first.
Freewrite Your Way to Finished Prose
There are multiple ways of using Freewriting to create finished prose (articles, blog posts, etc.)
One way is this one:
- Do several Freewriting warm-ups
- Do focused Freewriting about the relevant topic
- Delete what’s not working
- Arrange what’s left
- Write any missing thoughts and transitions
- Edit like mad
Also, if you have some written material that you haven’t used yet, check it out. Can you use something? (See: Build an Inventory of Thoughts)
So what do you think?
Have you ever come across Freewriting or similar techniques before?
Are you going to try this?
Do you think on paper?
Remember any situation when Freewriting or thinking on paper has helped you greatly?
I personally regularly think on paper and the results are wonderful. I use my own software Swift To-Do List to organize my writing (notes) in a nice tree structure.
Also, I’ve recently decided to use Freewriting to write a book, and I’m really excited about it. I’m pretty sure that Freewriting will work really well for me in this case – I plan to use The Fascination Method.
PS: Go get the book.
Tried out some freewriting and it's quite liberating.
Looks amusing.
Principles of Good Programming
The principles of good programming are closely related to principles of good design and engineering. The following programming principles have helped me over the years become a better programmer, and I believe can help any developer become more efficient and to produce code which is easier to maintain and that has fewer defects.
DRY - Don’t repeat yourself - This is probably the single most fundamental tenet in programming is to avoid repetition. Many programming constructs exist solely for that purpose (e.g. loops, functions, classes, and more). As soon as you start repeating yourself (e.g. a long expression, a series of statements, same concept) create a new abstraction. http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
Abstraction Principle - Related to DRY is the abstraction principle “Each significant piece of functionality in a program should be implemented in just one place in the source code.” http://en.wikipedia.org/wiki/Abstraction_principle_(programming)
KISS (Keep it simple, stupid!) - Simplicity (and avoiding complexity) should always be a key goal. Simple code takes less time to write, has fewer bugs, and is easier to modify. http://en.wikipedia.org/wiki/KISS_principle
Avoid Creating a YAGNI (You aren’t going to need it) - You should try not to add functionality until you need it. http://en.wikipedia.org/wiki/YAGNI
Do the simplest thing that could possibly work - A good question to ask one’s self when programming is “What is the simplest thing that could possibly work?” This helps keep us on the path towards simplicity in the design. http://c2.com/xp/DoTheSimplestThingThatCouldPossiblyWork.html
Don’t make me think - This is actually the title of a book by Steve Krug on web usability that is also relevant in programming. The point is that code should be easily read and understood with a minimum of effort required. If code requires too much thinking from an observer to understand, then it can probably stand to be simplified http://www.sensible.com/dmmt.html
Open/Closed Principle - Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. In other words, don't write classes that people can modify, write classes that people can extend. http://en.wikipedia.org/wiki/Open_Closed_Principle
Write Code for the Maintainer - Almost any code that is worth writing is worth maintaining in the future, either by you or by someone else. The future you who has to maintain code often remembers as much of the code, as a complete stranger, so you might as well always write for someone else. A memorable way to remember this is “Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.” http://c2.com/cgi/wiki?CodeForTheMaintainer
Principle of least astonishment - The principle of least astonishment is usually referenced in regards to the user interface, but the same principle applies to written code. Code should surprise the reader as little as possible. The means following standard conventions, code should do what the comments and name suggest, and potentially surprising side effects should be avoided as much as possible. http://en.wikipedia.org/wiki/Principle_of_least_astonishment
Single Responsibility Principle - A component of code (e.g. class or function) should perform a single well defined task. http://en.wikipedia.org/wiki/Single_responsibility_principle
Minimize Coupling - Any section of code (code block, function, class, etc) should minimize the dependencies on other areas of code. This is achieved by using shared variables as little as possible. “Low coupling is often a sign of a well-structured computer system and a good design, and when combined with high cohesion, supports the general goals of high readability and maintainability” http://en.wikipedia.org/wiki/Coupling_(computer_programming)
Maximize Cohesion - Code that has similar functionality should be found within the same component. http://en.wikipedia.org/wiki/Cohesion_(computer_science)
Hide Implementation Details - Hiding implementation details allows change to the implementation of a code component while minimally affecting any other modules that use that component. http://en.wikipedia.org/wiki/Information_Hiding
Law of Demeter - Code components should only communicate with their direct relations (e.g. classes that they inherit from, objects that they contain, objects passed by argument, etc.) http://en.wikipedia.org/wiki/Law_of_Demeter
Avoid Premature Optimization - Don’t even think about optimization unless your code is working, but slower than you want. Only then should you start thinking about optimizing, and then only with the aid of empirical data. "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil" - Donald Knuth. http://en.wikipedia.org/wiki/Program_optimization
Code Reuse is Good - Not very pithy, but as good a principle as any other. Reusing code improves code reliability and decrease development time. http://en.wikipedia.org/wiki/Code_reuse
Separation of Concerns - Different areas of functionality should be managed by distinct and minimally overlapping modules of code. http://en.wikipedia.org/wiki/Separation_of_concerns
Embrace Change - This is the subtitle of a book by Kent Beck, and is also considered a tenet of extreme programming and the agile methodology in general. Many other principles are based on the concept that you should expect and welcome change. In fact very old software engineering principles like minimizing coupling are related directly to the requirement of making code easier to change. Whether or not you are an extreme programming practitioner, this approach to writing code just makes sense. http://www.amazon.com/gp/product/0321278658
Good to have all these approaches in one concise list.