<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
 <title>A Hack On The Good Life</title>
 
 <link href="http://ericfarkas.com/" />
 <updated>2013-05-21T16:57:54-07:00</updated>
 <id>http://ericfarkas.com</id>
 <author>
   <name>Eric Farkas</name>
 </author>
 
 
 <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/ericfarkas" /><feedburner:info uri="ericfarkas" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:browserFriendly></feedburner:browserFriendly><entry>
   <title>How To Create Opportunity</title>
   <link href="http://ericfarkas.com/posts/how-to-create-opportunity" />
   <updated>2013-04-19T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/how-to-create-opportunity</id>
   <content type="html">&lt;p&gt;A number of related but scattered thoughts on the subject of creating opportunity for yourself.&lt;/p&gt;




&lt;h3&gt;Favor Action Over Inaction&lt;/h3&gt;


&lt;p&gt;Look at goals you have in terms of actions to be taken.  It's not enough to write down ideas and talk about them. You have to work to implement them.  Recently someone at my church suggested regularly getting together during the summer at a local beach for volleyball.  Everyone was enthusiastic about the idea.  But to make it happen, someone needs to contact the town and see if we need a permit, and if we do, to work at getting that permit. Someone else needs to purchase equipment.  There is action to be taken; who's going to take it?&lt;/p&gt;




&lt;p&gt;I think it's helpful to see yourself as a hustler, and I mean that in the sporting sense.  In baseball, someone who's a hustler will run out every ground ball, dive for every line drive, chase every ball hit anywhere in the outfield.  At the end of the game, he comes back to the locker room with a dirty uniform. To a hustler, there are no lost causes.&lt;/p&gt;




&lt;p&gt;The way this played out for me is that I spoke to everyone who wanted to work with me, everyone with an idea. I answered emails, met with people, even if I had an inclination that it would be a waste of time.  Because you never know. Every potential opportunity is worth a look. This mindset (at the time I was looking for a project to be involved in long-term) is what caused me to &lt;a href="https://twitter.com/saddington/status/4367475923"&gt;answer a random tweet&lt;/a&gt;, which led me to Radford Harrell and &lt;a href="https://talentsoup.com"&gt;TalentSoup&lt;/a&gt;. On the surface it looked like short-term work, but it turned into 3-1/2 years (and going!) of a great partnership. You never know.&lt;/p&gt;




&lt;p&gt;The nature of the hustle will change over time.  Now that I am in a situation that I worked hard for many years to find, the types of opportunities I am looking for has changed.  I want to meet people who will help me grow my business, and help me improve as a developer, whereas before I was looking for a project to be a part of. But the bias toward action will always exist.&lt;/p&gt;




&lt;h3&gt;Sometimes It's Better To Stay The Course&lt;/h3&gt;


&lt;p&gt;All of that said, being biased towards action does not necessarily mean always looking for new things.  If you're in a situation where you are really unsure of what to do, consider the possibility of just carrying on. But hopefully "carrying on" for you means continuing with some action, as opposed to doing nothing.&lt;/p&gt;




&lt;h3&gt;Remember The Sabbath&lt;/h3&gt;


&lt;p&gt;The Sabbath is a day of rest. Among it's practical benefits (naps for the whole family!), it also serves to remind us that the increase in our lives is not down to our own working.  We are led to believe that working long hours and sacrificing time with family and loved ones is what it takes to be "successful".  By allowing me to observe a day of rest from work, God is reminding me that ultimately, He is the One who provides for me.  Yes I work hard, but &lt;a href="http://www.biblegateway.com/passage/?search=James+1%3A17&amp;version=NIV"&gt;every good thing in my life comes from God&lt;/a&gt;. The apostle Paul elaborates on this idea in the first letter to the church at Corinth:&lt;/p&gt;


&lt;p&gt; &lt;blockquote&gt;
  &lt;p&gt;What then is Apollos? What is Paul? Servants through whom you believed, as the Lord assigned to each. I planted, Apollos watered, but God gave the growth. So neither he who plants nor he who waters is anything, but only God who gives the growth.&lt;/p&gt;
  &lt;small&gt;&lt;a href="http://www.biblegateway.com/passage/?search=1+Corinthians+3%3A5-8&amp;version=ESV"&gt;1 Corinthians 3:5-8&lt;/a&gt;&lt;/small&gt;
&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;In that context he's talking about his work in the ministry of the Gospel, but the principle is clear: ultimately God is the one who prospers (or does not prosper) the labor of His people. Much more can be said about this point and how it relates to contentment. It's enough for now to remember that while you are working hard, God is the one who is directing your life, opening and closing doors, and adding (or not adding) increase through your labors.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Things To Look Out For When Building A Large Application</title>
   <link href="http://ericfarkas.com/posts/things-to-look-out-for-when-building-a-large-application" />
   <updated>2013-04-02T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/things-to-look-out-for-when-building-a-large-application</id>
   <content type="html">&lt;p&gt;A while back I found &lt;a href="http://simon.incutio.com/notes/2006/summit/schachter.txt"&gt;a great set of notes&lt;/a&gt; that were taken at a presentation by &lt;a href="https://delicious.com/"&gt;del.icio.us&lt;/a&gt; creator &lt;a href="https://twitter.com/joshu"&gt;Joshua Schachter&lt;/a&gt; at the 2006 Future Of Web Apps summit.  They were really helpful to me in my growth as a developer.  Unfortunately the notes are no longer available at the original URL, so I've taken the liberty to reproduce them here.&lt;/p&gt;




&lt;p&gt;Again, these are not my own notes (credit goes to &lt;a href="https://twitter.com/simonw"&gt;Simon Willison&lt;/a&gt;) but I want to preserve them for posterity's sake. If you know of an "official" location for these notes please let me know.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Browser quirks. CSS/JavaScript/rendering. IE caching.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scaling: avoid early optimization. SQL doesn't map well to these problems - think about how to split up data over multiple machines. Understand indexing strategies, profile every SQL statement. Nagios or similar for monitoring.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tags don't map well to SQL. Sometimes you can prune based on usage - only index the first few pages for example. This keeps indexes small and fast.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Some latency in the system is OK - work out where you have leeway, e.g. RSS feeds can fall a few minutes behind without anyone minding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;People are always going to abuse your system (scraping, greasemonkey, etc.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Idiots are a lot smarter than you" - wait to see what breaks before you fix it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learn Apache - tuning can make things a lot faster. Understand headers, mod_rewrite (a dark art). Put a proxy in front of Apache e.g. Perlbal from LiveJournal - one guy on a modem can suck up lots of resources otherwise.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Images off a different server, RSS from a different server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Save site for offline use" feature in IE is particularly nasty.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The easier the API to get in and out of, the more people will use it. There's a long tail of smart developers. Stuff like SOAP discourages adoption. No API key = people can play with it faster; drives attention.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't expose your unique id's to the outside world (php?id=1 etc.) People can scrape through everything very easily. This is why del.icio.us uses MD5 hashes of links instead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Giving everything a unique ID in the database is a scaling problem in its own right.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Features: the features you put in are as important as the ones you leave out. There's no "send a note" in del.icio.us feature because e-mail already exists.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I don't add featues that are available elsewhere e.g. messaging&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When people ask for features, get to the bottom of why they are asking for that exact thing. Solve the problem, rather than doing exactly what your asked for.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With tags, people ask for "A and B and NOT C or D" - but less than 1% of queries even use more than a single tag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RSS important in del.icio.us, because it's a native way for people to access lists (of links). Put RSS everywhere you can. del.icio.us does way more RSS traffic than HTML or API stuff - partly because of poorly written readers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Understand the headers - especially if-not-modified.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make sure the URLs follow the path of the site. DON'T include session data, drop ugly details that are to do with the system, not the user (.php, .aspx, ?, &amp;, etc.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;URLs are prime real estate - respect them&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When you chose what to build, solve a problem you have yourself so you can be sure to understand it. Passion counts. It's cheap and easy to build stuff, so other people will be building it too.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A niche product with a limited audience is still good business (with how advertising and PayPal work)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every day that you don't have something properly out in the world (not on an invite only beta) you're losing a chance to gain users. Get it out there ASAP.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Aggregation is often a focus of attention (latest, most active, etc.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As the population gets larger, the bias drifts; del.icio.us/popular becomes less interesting to the original community members. Work out ways to let the system fragment in to different areas of attention.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Spam is attention theft" - that's one of the reasons del.icio.us doesn't have a top 10 links of all time - it's an attractive nuisance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When you've figured out someone is spamming, don't let them know – let them keep posting and just silently junk their stuff.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make users do the minimum amount of work. But make them do something.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Understand the user's motivation: "You have to understand the selfish user" – user #1 has to find the system useful or you won't get user #2. Systems that only become useful when lots of people are using them usually fail, because there's no incentive for people to contribute themselves. The real trick is to make the user base you have want to invite more people in to the system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manage your effort - be careful where you spend your efforts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't waste time building features nobody uses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measurement: watch your system intensely: "Intuition is ideas backed by numbers"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measure behavior rather than claims. del.icio.us doesn't have stars because why would you bookmark something that was no good? This way people bookmark things that they really care about rather than trying to tell the system things.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User Acceptance Testing (UAT) is important. Make the system suitable for the people actually using the system. Everybody on the team should see this in action. Labs are great but expensive. If you don't have a lab do ghetto testing in Starbucks. We did one day then two days user testing in a user testing lab.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Goals skew the results. People don't read, they cram crap in to boxes. Let people wander don't give them tasks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You have to speak the user's language. "Bookmarks" are what you call them if you use Netscape of Firefox - most users these days know the term "favourite" instead. Half of his population (? users) didn't know what a bookmark was.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't make users register before they can get in to your site. Maybe even give them an anonymous account to play with. A lot of users want to know what they'll get if they register - especially from fear of giving out email address, spyware etc. You can't tell them; they're not going to read it. You have to show them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use Verbs - doing words - to prompt actions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If users do have to register, send them straight back to where they were when they're done. Don't dump them on the homepage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Design Grammar" - if you're presenting a system that's different from how other things work (del.icio.us had novel tags, save your bookmarks to the web) you should still try to reflect the design patterns of the web.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Morals: You have to develop a sense of morals when you build your system. It's the user's data; it's not yours. Make sure they can remove themselves and their account if they want to.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Infection: Understand infection vectors for promoting your system. "Enable evangelism". RSS lets you get at users who don't use your system directly. Also think about iCal, M3U - anything that a desktop app can consume over HTTP. Do an inventory to get into every desktop app. possible.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>The Value Of Lifecycle Emails</title>
   <link href="http://ericfarkas.com/posts/the-value-of-lifecycle-emails" />
   <updated>2013-04-01T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/the-value-of-lifecycle-emails</id>
   <content type="html">&lt;p&gt;Since the beginning of 2013, I've focused a lot of my development efforts on so-called "growth hacking".  Whatever the connotations that term has, I have found some really good ideas reading the many blog posts and post-mortems that are floating around.  We've seen solid growth at &lt;a href="https://talentsoup.com"&gt;TalentSoup&lt;/a&gt; that's been directly related to our focus on bringing our users to the "ah-ha" moment, and clearly presenting our value prop in blog posts and other marketing content.&lt;/p&gt;




&lt;p&gt;I've started to send out post-downgrade emails a day or two after a user downgrades from our &lt;a href="https://talentsoup.com/talent_home#pricing-fee-example"&gt;Pro Webcomp Portfolio&lt;/a&gt;.  For now I don't use a template, and they are not sent automatically.  Some users downgrade after a month or two, others have had a Pro account for a year or more.  I want each email to be different depending on how long the user has been a paying customer.&lt;/p&gt;




&lt;p&gt;The content of the emails are simple: we saw that you downgraded, we're sorry to see you go, but thanks for being a Pro member, and here's a coupon for a free month if you ever decide to re-up.  The point of the email is really just to express thanks and to put some humanity behind our company.  Part of the ethos we've created at TalentSoup is that we're honest people running a legitimate business where we want our users to succeed by helping them land real work, and emails like that help reinforce the perception (which is true!).  In our industry that kind of validation from our users is solid gold and we want to nurture and promote it.&lt;/p&gt;




&lt;h3&gt;Payback&lt;/h3&gt;


&lt;p&gt;I've only heard back from two users since I started sending these emails out.  One was from a user on our site who let me know she downgraded because she's leaving the country for two years to work in the Peace Corps.  The other was from a user I sent an email to earlier this morning.  The recurring billing system we use sends out dunning emails when a user's credit card has expired, and if the user hasn't updated their billing information after 3 days, they're automatically downgraded. Apparently the user didn't realize his account has been downgraded (I am still investigating how he missed 3 dunning emails) and was eager to re-upgrade his account.&lt;/p&gt;




&lt;p&gt;If I hadn't sent out that email to him this morning, how long would it have taken for him to realize his account had been downgraded?  In that time, we would have missed out on revenue from a user who wanted to be a paying customer.  At $5 a month, you might not see that lost money as a big deal, but I do.  Developing a mentality where every dollar counts and every customer is important is how you grow a business.  If your goal is to make, say $50,000 month, you have to make $500/month first, then $1,000, then $10,000, etc. Every dollar counts.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>A (Short) Rant About Working Remotely</title>
   <link href="http://ericfarkas.com/posts/a-short-rant-about-working-remotely" />
   <updated>2013-01-31T00:00:00-08:00</updated>
   <id>http://ericfarkas.com/posts/a-short-rant-about-working-remotely</id>
   <content type="html">&lt;p&gt;I get emails on a weekly basis from recruiters looking for developers.  The first question I respond with is, "Are you open to remote workers?", to which the answer, 99% of the time, is "No".&lt;/p&gt;




&lt;p&gt;The reasoning is inevitably something along the lines of "They're an early stage/small company, so they want to have everyone in the office", as if being a young or small start-up necessarily means that you have to have everyone together, as if there's no possible way to build a company otherwise.&lt;/p&gt;




&lt;p&gt;I get the reasons why face time is important.  This is why even companies that are advocates of remote work, like GitHub and 37signals, have company-wide meetups at least once a year.  Even an arrangement where a dev can come in once a week, or when needed during crunch time for a project or a special planning meeting, seems reasonable.  But I don't see a reason why, in 2013, given the tools we have, a developer has to be on-site at a desk every day, as the normal operating procedure.  Think of how many times companies miss out on really good developers just because they're not open to remote workers.&lt;/p&gt;




&lt;p&gt;I joined &lt;a href="https://talentsoup.com"&gt;TalentSoup&lt;/a&gt; as a technical co-founder, and worked for a year and a half with my partner before we met face to face.  All communication was done on the phone, through email, Basecamp, GitHub, iChat, whatever.  We built (and continue to build) a profitable company, completely remotely.  So I just don't get the reluctance from other companies to embrace remote workers.  Are the problems you're working on that difficult, is your product that special, that everyone needs to be in the same room every working day?&lt;/p&gt;




&lt;p&gt;I'm really disappointed at the state of remote work in the startup universe.  Hopefully it will change in the coming months and years as companies realize the resources that exist outside of their small part of the world.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Setting up statsd + graphite on Ubuntu 12.04 LTS</title>
   <link href="http://ericfarkas.com/posts/statsd-graphite-ubuntu" />
   <updated>2013-01-18T00:00:00-08:00</updated>
   <id>http://ericfarkas.com/posts/statsd-graphite-ubuntu</id>
   <content type="html">&lt;p&gt;There are many great tutorials about setting up &lt;a href="https://github.com/etsy/statsd"&gt;statsd&lt;/a&gt; and &lt;a href="http://graphite.wikidot.com/"&gt;graphite&lt;/a&gt;, specifically on Ubuntu 12.04.  The best walk through I found was &lt;a href="https://github.com/janoside/ubuntu-statsd-graphite-setup"&gt;https://github.com/janoside/ubuntu-statsd-graphite-setup&lt;/a&gt;.  I'd like to add some things that were left out of that tutorial that I had to figure out.&lt;/p&gt;




&lt;h3&gt;Database Creation&lt;/h3&gt;


&lt;p&gt;The above Gist leaves out the initial database creation.  Be sure to thoroughly read &lt;a href="http://graphite.wikidot.com/installation"&gt;http://graphite.wikidot.com/installation&lt;/a&gt; so you know at which step to create the db. I did this step after configuring Apache but before restarting the server.&lt;/p&gt;




&lt;h3&gt;WSGISocketPrefix&lt;/h3&gt;


&lt;p&gt;If you see an error message similar to the following:&lt;/p&gt;




&lt;p&gt;&lt;pre&gt;(2)No such file or directory: mod_wsgi (pid=19506): Unable to connect to WSGI daemon process 'graphite' on '/etc/apache2/run/wsgi.19365.1.1.sock' after multiple attempts.&lt;/pre&gt;&lt;/p&gt;




&lt;p&gt;That's a clue to edit &lt;pre&gt;/etc/apache2/sites-available/default&lt;/pre&gt; and make sure that the configuration for &lt;code&gt;WSGISocketPrefix&lt;/code&gt; is set as follows:&lt;/p&gt;




&lt;p&gt;&lt;pre&gt;WSGISocketPrefix /var/run/apache2/wsgi&lt;/pre&gt;&lt;/p&gt;




&lt;p&gt;Thanks to &lt;a href="http://marcelo-olivas.blogspot.com/2012/06/installing-graphite-on-ubuntu-1204.html"&gt;Marcelo Olivas&lt;/a&gt; for that tip.&lt;/a&gt;

&lt;h3&gt;Target WSGI script '/opt/graphite/conf/graphite.wsgi' cannot be loaded as Python module.&lt;/h3&gt;
&lt;p&gt;If you see this error, make sure that apache owns the directory&lt;/p&gt;
&lt;p&gt;&lt;pre&gt;/opt/graphite/storage/logs/webapp&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://answers.launchpad.net/graphite/+question/178110"&gt;thread&lt;/a&gt; pointed me in the right direction.&lt;/p&gt;

&lt;p&gt;Good luck.&lt;/p&gt;

&lt;h3&gt;See Also&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/github/statsd-ruby"&gt;statsd-ruby&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/"&gt;Measure anything, measure everything&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Learning - Interesting Array Manipulation</title>
   <link href="http://ericfarkas.com/posts/learning-interesting-array-manipulation" />
   <updated>2013-01-03T00:00:00-08:00</updated>
   <id>http://ericfarkas.com/posts/learning-interesting-array-manipulation</id>
   <content type="html">&lt;p&gt;I said in the post about my &lt;a href="http://ericfarkas.com/posts/learning-plan-for-2013/"&gt;2013 learning plans&lt;/a&gt; that I'd blog about whatever I learn, even if it's dead simple or something I should have known already.  This is my first such post.&lt;/p&gt;




&lt;p&gt;Let's start with this array:&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [1, 3, 5 , 7, 9]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;p&gt;We can replace two elements with one using the following notation:&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;#=&amp;gt;a = [1, 3, 5 , 7, 9]&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt;a = [1, 3, &amp;quot;foo&amp;quot;, 9]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;p&gt;Think of &lt;code&gt;a[ 2, 2 ] = "foo"&lt;/code&gt; as meaning, "starting at index 2, replace 2 elements with the following value".  We can modify the same notation to insert a value but not replace anything in the array:&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;#=&amp;gt;a = [1, 3, &amp;quot;foo&amp;quot;, 9]&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt;a = [1, 3, &amp;quot;bar&amp;quot;, &amp;quot;foo&amp;quot;, 9]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;p&gt;The above has the same effect as doing &lt;code&gt;a.insert(2, "bar")&lt;/code&gt;.

&lt;p&gt;The range notation can also be used to replace a range of elements, like so:&lt;/p&gt;

&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;#=&amp;gt;a = [1, 9, 8, 7, &amp;quot;bar&amp;quot;, &amp;quot;foo&amp;quot;, 9]&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt;a = [&amp;quot;bar&amp;quot;, &amp;quot;foo&amp;quot;, 9]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;

&lt;p&gt;Think of this as "replace elements at indexes 0 to 3 with the following".  The interesting thing about both of these methods of array manipulation is that the number of new elements you're assigning doesn't have to equal the number of elements you're replacing.  Using our original array, I can do the following:&lt;/p&gt;

&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;#=&amp;gt;a = [1, 3, 5 , 7, 9]&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;fizz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;buzz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;fizz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;buzz&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt;a = [1, 3, &amp;quot;fizz&amp;quot;, &amp;quot;buzz&amp;quot;, &amp;quot;fizz&amp;quot;, &amp;quot;buzz&amp;quot;, 9]&lt;/span&gt;

&lt;span class="c1"&gt;#the above is equivalent to:&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt;a = [1, 3, 5 , 7, 9]&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fizz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;buzz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;fizz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;buzz&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt;a = [1, 3, &amp;quot;fizz&amp;quot;, &amp;quot;buzz&amp;quot;, &amp;quot;fizz&amp;quot;, &amp;quot;buzz&amp;quot;, 9]&lt;/span&gt;

&lt;span class="c1"&gt;# using range notation, we can do the same as above:&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt;a = [1, 3, 5 , 7, 9]&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;fizz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;buzz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;fizz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;buzz&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt;a = [1, 3, &amp;quot;fizz&amp;quot;, &amp;quot;buzz&amp;quot;, &amp;quot;fizz&amp;quot;, &amp;quot;buzz&amp;quot;, 9]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;

&lt;p&gt;In each example I replaced two elements with four elements.  The array grows automatically (see documentation on &lt;a href="http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-5B-5D-3D"&gt;[]=&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;One last thing.  If you specify a range outside of the size of the array, the intermin elements will be set to &lt;code&gt;nil&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;#=&amp;gt;a = [&amp;quot;bar&amp;quot;, &amp;quot;foo&amp;quot;, 9]&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;fizz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;buzz&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt;a = [&amp;quot;bar&amp;quot;, &amp;quot;foo&amp;quot;, 9, nil, nil, &amp;quot;fizz&amp;quot;, &amp;quot;buzz&amp;quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;

&lt;p&gt;As always in Ruby, there are multiple ways to accomplish the same thing.&lt;/p&gt;

&lt;h3&gt;See Also&lt;/h3&gt;
&lt;p&gt;&lt;a href="http://www.ruby-doc.org/core-1.9.3/Array.html"&gt;Class: Array (Ruby 1.9.3)&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Learning Plan For 2013</title>
   <link href="http://ericfarkas.com/posts/learning-plan-for-2013" />
   <updated>2012-11-28T00:00:00-08:00</updated>
   <id>http://ericfarkas.com/posts/learning-plan-for-2013</id>
   <content type="html">&lt;p&gt;I've started to think about and plan how I will improve my development skills in 2013. My goals are:&lt;/p&gt;


&lt;ul&gt;
 &lt;li&gt;&lt;p&gt;Deepen my knowledge of the Ruby language&lt;/p&gt;&lt;/li&gt;
 &lt;li&gt;&lt;p&gt;Learn CoffeeScript&lt;/p&gt;&lt;/li&gt;
 &lt;li&gt;&lt;p&gt;Become familiar with database technologies other than MySQL, especially MongoDB&lt;/p&gt;&lt;/li&gt;
 &lt;li&gt;&lt;p&gt;Consistently write about what I am learning, whether it's simple or not&lt;/p&gt;&lt;/li&gt;
 &lt;li&gt;&lt;p&gt;Write a Ruby driver for &lt;a href="https://github.com/bitly/nsq"&gt;NSQ&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I will achieve my goals in this manner:&lt;/p&gt;


&lt;ul&gt;
  &lt;li&gt;&lt;p&gt;Read &lt;a href="http://pragprog.com/book/tbcoffee/coffeescript"&gt;CoffeeScript: Accelerated JavaScript Development&lt;/a&gt; by Trevor Burnham (160 pages)&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Read &lt;a href="http://pragprog.com/book/ruby3/programming-ruby-1-9"&gt;Programming Ruby 1.9 (3rd edition): The Pragmatic Programmers' Guide&lt;/a&gt; by Dave Thomas, with Chad Fowler and Andy Hunt (944 pages)&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Read &lt;a href="http://www.amazon.com/Eloquent-Ruby-Addison-Wesley-Professional/dp/0321584104"&gt;Eloquent Ruby&lt;/a&gt; by Russ Olsen (448 pages)&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Read &lt;a href="http://pragprog.com/book/ppmetr/metaprogramming-ruby"&gt;Metaprogramming Ruby: Program Like the Ruby Pros&lt;/a&gt; by Paolo Perrotta (296 pages)&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Read &lt;a href="http://pragprog.com/book/rwdata/seven-databases-in-seven-weeks"&gt;Seven Databases in Seven Weeks&lt;/a&gt; by Eric Redmond and Jim R. Wilson (352 pages)&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Read &lt;a href="http://www.amazon.com/MongoDB-Definitive-Guide-Kristina-Chodorow/dp/1449381561/"&gt;MongoDB: The Definitive Guide&lt;/a&gt; by Kristina Chodorow and Michael Dirolf (216 pages)&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Read and thoroughly understand the code behind one of &lt;a href="http://rubyeventmachine.com/"&gt;EventMachine&lt;/a&gt;, &lt;a href="https://github.com/macournoyer/thin"&gt;Thin&lt;/a&gt;, &lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt;, &lt;a href="http://mongomapper.com/"&gt;MongoMapper&lt;/a&gt; or &lt;a href="http://rack.github.com/"&gt;Rack&lt;/a&gt;, then write about it.&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Read &lt;a href="http://pragprog.com/book/btlang/seven-languages-in-seven-weeks"&gt;Seven Languages In Seven Weeks&lt;/a&gt; by Bruce A. Tate (328 pages)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;That's approximately 2200 pages (Programming Ruby is 944 pages but the second half of the book is language reference which I won't be "reading").  So 2200/365 is approximately 6 pages a day, or 42 pages a week.  Some of the books I am reading will cover material I am already familiar with, but I personally find it helpful to consistently review the basics.&lt;/p&gt;




&lt;p&gt;I will post my progress here.  In the past I have been embarrassed to write about the things I am learning because I feel others have said it better already, and perhaps I'm posting about things I should have known already.  But that is a counterproductive mindset to have.  First of all, it betrays a conceit that anyone actually reads these posts in the first place.  Secondly, it keeps me from engaging in what is one of the best ways to complete the learning experience, and that is to write about it and teach others.&lt;/p&gt;




&lt;p&gt;I'm looking forward to firming up the knowledge I already have, and getting deeper into Ruby, the language I have come to love.  I'm also excited about digging into an open source project that I have used and admire, and learning from the masters.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>A Tale of Two Beers</title>
   <link href="http://ericfarkas.com/posts/a-tale-of-two-beers" />
   <updated>2012-11-19T00:00:00-08:00</updated>
   <id>http://ericfarkas.com/posts/a-tale-of-two-beers</id>
   <content type="html">&lt;p&gt;On the night of December 17, 2011, I brewed a clone of &lt;a href="http://rivendellcraftbrewery.com/brew-2-new-belgium-abbey-ale-clone-dubbel"&gt;New Belgium's Abbey Ale&lt;/a&gt;, which is a &lt;a href="http://beeradvocate.com/beer/style/57"&gt;dubbel&lt;/a&gt;.  On the evening of July 21, 2012, I brewed a clone of &lt;a href="http://www.austinhomebrew.com/product_info.php?products_id=11934"&gt;New Belgium's Saison Belgian Style Farmhouse Ale&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;The Dubbel&lt;/h3&gt;


&lt;p&gt;When the dubbel was meant to be "ready" (about 3-4 weeks in the bottle), I thought it was a failure.  It had a soapy aftertaste, which I identified at the time as being caused by &lt;a href="http://www.howtobrew.com/section4/chapter21-2.html"&gt;the beer being left in the fermenter too long&lt;/a&gt;.  It was in the primary for one week, and secondary for 11 days.  Eighteen total days. Not a long time by any stretch, but I was a new homebrewer, and what did I know?&lt;/p&gt;




&lt;p&gt;The problem with screwing up a beer is that now you have 50+ bottles of homebrew that you really don't want to drink, and you don't want to let your friends drink either.  So I did what any embarrassed homebrewer might do: I just let it sit in my dark, cool basement for months.&lt;/p&gt;




&lt;p&gt;It turns out this was the best thing I could have done.&lt;/p&gt;




&lt;p&gt;A couple of nights ago, I had a meeting to attend, and someone had to bring the beers.  Weeks earlier, I had thrown a couple of bottles of the dubbel in with some other beers to make a six pack for a night out.  Perhaps some of my friends who didn't have much experience with craft beer wouldn't notice the off flavor, and they might enjoy it.  Turns out the beer got positive reviews from those friends.  Buttressed by that feedback, I dared to bring a six pack of my homebrew to that meeting.  Maybe the soapy off-taste had dissipated a bit, and I could start to unload my failed homebrew.&lt;/p&gt;




&lt;p&gt;I cracked open the bottle, and poured it out.  It wasn't as carbonated as it had been when I first tried it months ago.  The head was legit, and it smalled fantastic.  I let it warm up and breathe, and took my first sip.  The flavor had totally changed. Gone was the soapy after-taste, and the beer actually had a bit of complexity about it that was absent originally.  At 7.3% ABV it had some kick, but whereas in January the alcohol slammed you in the face, now it was more subtle, masked by the dark fruit, malty flavors of the beer.&lt;/p&gt;




&lt;h3&gt;The Saison&lt;/h3&gt;


&lt;p&gt;A similar, though less dramatic thing happened with a &lt;a href="http://instagram.com/p/OkEBO-Epc7/"&gt;saison&lt;/a&gt; that I brewed in July.  With the purchase of a new boil kettle, I was dialing my process in.  Armed with what I thought I had learned from brewing the dubble, we had a nearly-flawless brew night on July 21.  Four weeks later, the saison was ready to drink.&lt;/p&gt;




&lt;p&gt;I really liked this beer initially.  My only criticism at the time was that the yeast flavors were a bit overwhelming for that style.  This will come off as snobbish, but the prominence of the yeast was something most folks wouldn't have noticed unless they were regular drinkers of farmhouse ales.  No worries, though, because it was a good tasting beer, with no discernible off-flavors.  I was proud of it but I'd probably still make a run to the beer distributor if we were low on other beers, instead of reaching out for my own homebrew.&lt;/p&gt;




&lt;p&gt;But last night, my wife and I were watching a movie, and had a huge bowl of homemade popcorn.  Naturally we had to have some beer, but we were out of everything.  There were no cold dubbels in the fridge, only one saison.  I hadn't tried the saison for a while, so I poured it into a glass, and the first sip made me think it was a different beer.  The yeast had taken a back seat to these overwhelmingly tasty fruit flavors.  The carbonation was there as well.  My wife and I both remarked that this was a beer we'd go buy in a store, and choose over other craft brews.   I was now in the position of having a couple of month's worth of delicious homebrew in my basement!&lt;/p&gt;




&lt;h3&gt;Lessons Learned&lt;/h3&gt;


&lt;p&gt;Experiencing my beers on those two occasions, after having brewed and then tasted them throughout the intervening months, was surreal.  You're holding a drink in your hand that you quickly realize is a living thing.  It's evolving.  There's yeast in that bottle that's doing &lt;i&gt;something&lt;/i&gt;.  Now, every beer has a "drink-by" date, past which you no longer realize the benefits of aging. But a huge lesson for me is that I need to give my beers time to come into their own, and let nature takes it's course.&lt;/p&gt;




&lt;p&gt;On a macro level, this is a huge lesson to learn for life in general.  There are things which I have no control over, though I wish I did.  "The recipe said the beer will be ready 4 weeks after bottling, and it's 4 weeks later, so this beer better be ready now!" But yeast doesn't really care what I want, or when I want it.  It's going to do it's thing in it's own time. There is, to borrow an idea from &lt;a href="http://www.marshillaudio.org/About/KenMyersBio.aspx"&gt;Ken Myers&lt;/a&gt;, a "givenness" to beer making that resists attempts to impose a timeline on it.  Sure you can tweak things in your process, but you're ultimately at the mercy of the yeast.&lt;/p&gt;




&lt;p&gt;One of the reasons I got into homebrewing was that I thought it'd be good for me to have a hobby that requires patience, and that does not give immediate feedback.  As a programmer, my life is all about instant feedback.  I can start with an empty app, and in a few minutes, with basic models, controller actions, and tests, have a working system. It won't be perfect, but you can push this button and stuff will happen.  Making beer is not like that.  The brewing process takes hours, the bottling process takes time and attention to detail, and then you're waiting weeks for the beer in the bottle to be ready, and even when it's "ready" it might not be truly ready (as my experience has now shown).&lt;/p&gt;




&lt;p&gt;There's a part of me that's afraid of the negative effects of technology on my soul.  Just in the act of software development, I unconsciously create habits and expectations that spill over into other areas of my life.  But unfortunately, there's not a &lt;code&gt;1:1&lt;/code&gt; relationship between software development and being a good father, for example.  In the former, I can sit down in front of my laptop and clear a bug list in a couple of hours, and those bugs will (hopefully) never appear again.  In the latter, it might take days, months, &lt;i&gt;years&lt;/i&gt; for my wife and I to work through issues and establish good habits with our children, or within our marriage. And even then, constant vigilance is required to fight against sinful relational tendencies, or to reinforce good habits through the discipline of repitition.  Does my work in and use of technology prepare my soul for that fight?  More and more I think it does not, hence my journey into homebrewing, and also a big reason why I've disciplined myself to &lt;a href="http://gymjones.com"&gt;work out strenuously&lt;/a&gt; on a regular basis.&lt;/p&gt;




&lt;p&gt;These two things (homebrewing and working out) are valuable to me for the counter-balancing practices and insights they yield relative to my day-to-day experience.  The Bible is always my source of ultimate truth and illumination.  But I have found it helpful to force myself to do things that are not comfortable precisely because they put "flesh" on the things I believe about what constitutes human flourishing.&lt;/p&gt;




&lt;p&gt;This lesson, about giving something time to evolve and mature on it's own terms, and providing the environment in which it can flourish while having the patience to let the process happen, is one I'm happy to have experienced.&lt;/p&gt;




&lt;p&gt;I'm even happier that I didn't throw out those bottles of homebrew.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Dynamic Images (Or A Lightweight Gravatar)</title>
   <link href="http://ericfarkas.com/posts/dynamic-images-or-a-lightweight-gravatar" />
   <updated>2012-10-03T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/dynamic-images-or-a-lightweight-gravatar</id>
   <content type="html">&lt;p&gt;We recently rolled out &lt;a href="https://talentsoup.com/producers"&gt;Creative Commissary&lt;/a&gt;, our project management tool for image producers.  It's a completely separate app from &lt;a href="https://talentsoup.com"&gt;TalentSoup&lt;/a&gt; in every way.  They do not share databases, and communicate via a JSON-based RESTful API.  Clients build and manage projects in Commissary, and the talent details (name, contact info, etc.) are stored in TalentSoup.&lt;/p&gt;




&lt;p&gt;In TalentSoup, talent have the option of selecting any of their photos as the main photo.  This is the photo whose thumbnail will be shown in search results and project galleries.  This main photo is the talent's first impression on the client, and therefore the talent's most important photo.&lt;/p&gt;




&lt;p&gt;Originally, when doing a search return (Commissary sending search parameters to TalentSoup, TalentSoup returning a search result to Commissary), I was including the static URL for the main photo at the time of the search as the &lt;code&gt;main_photo&lt;/code&gt; value.  This value would be stored in Commissary and would be referenced when rendering a gallery page for the client to look at.  One thing I didn't take into account, however, was that talent might change their &lt;code&gt;main_photo&lt;/code&gt; anytime after the gallery for that particular project was built.  When that happened, the image being displayed in the gallery wouldn't change, because the URL was being stored statically on the Commissary side.&lt;/p&gt;




&lt;p&gt;This is a problem because sometimes talent change their &lt;code&gt;main_photo&lt;/code&gt; based on what type of project they're being considered for (we let them know when they're added to a gallery, and what type of job it is).  For example, right now we're managing a job for a client who is going to be photographing people running.  As a result, some talent changed their &lt;code&gt;main_photo&lt;/code&gt; to show them running or doing some sort of physical exercise.  This should be reflected in the galleries, obviously!&lt;/p&gt;




&lt;p&gt;Basically what I needed was a service like &lt;a href="http://gravatar.com"&gt;Gravatar&lt;/a&gt;.  One URL I could pass around between TalentSoup and Commissary that would always render the given talent's &lt;code&gt;main_photo&lt;/code&gt;.  I came up with a simple solution.  I am not sure if this is the best way to accomplish this, but it works. Here's a controller action that lives on the TalentSoup side:&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;#app/controllers/users_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TalentController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main_photo&lt;/span&gt;
    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:include&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:main_photo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main_photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:medium&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# medium thumbnail url&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;p&gt;It takes the talent's &lt;code&gt;id&lt;/code&gt; as a parameter and will always display the &lt;code&gt;main_photo&lt;/code&gt;.  Now I can do this on the Commissary side:&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://talentsoup.com/talent/&amp;lt;%= @talent.id %&amp;gt;/main_photo&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;p&gt;And I'll always get the &lt;code&gt;main_photo&lt;/code&gt; for a given talent even when they change it.  We can make this more efficient by caching on the TalentSoup side, and invalidating when the talent changes their main photo.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Sinatra, ActiveRecord, and MySQL</title>
   <link href="http://ericfarkas.com/posts/sinatra-activerecord-and-mysql" />
   <updated>2012-09-06T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/sinatra-activerecord-and-mysql</id>
   <content type="html">&lt;p&gt;If you want to use ActiveRecord with &lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt;, the &lt;a href="https://github.com/janko-m/sinatra-activerecord"&gt;sinatra-activerecord&lt;/a&gt; gem is the way to go.  The examples in their documentation are for the &lt;code&gt;sqlite3&lt;/code&gt; adapter.  I thought it'd be helpful to show how I used this gem with MySQL instead.  I created a &lt;code&gt;models&lt;/code&gt; folder in my app root, where I store my ActiveRecord models.  You can include all of them in one file (like &lt;code&gt;models.rb&lt;/code&gt;) or give each model it's own file, and include it explicitly.  We'll use the former for this example.&lt;/p&gt;




&lt;p&gt;
&lt;pre&gt;
# config/database.yml
database: dbFoo
username: db_user
password: db_pass
host: localhost
port: 9999
&lt;/pre&gt;
&lt;/p&gt;




&lt;p&gt;Now we tell the gem about our db settings:&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# app.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sinatra&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;sinatra/activerecord&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;yaml&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;./models/models.rb&amp;#39;&lt;/span&gt;

&lt;span class="no"&gt;DB_CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;YAML&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;config/database.yml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;:database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;mysql://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;DB_CONFIG&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;DB_CONFIG&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;DB_CONFIG&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;host&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;DB_CONFIG&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;port&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;DB_CONFIG&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;database&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;
  &lt;span class="n"&gt;erb&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;p&gt;Simple, and you get all the power of ActiveRecord as well as the ability to run migrations.&lt;/p&gt;




&lt;h3&gt;See Also&lt;/h3&gt;


&lt;p&gt;&lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;&lt;a href="https://github.com/janko-m/sinatra-activerecord"&gt;sinatra-activerecord&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>On Child Rearing</title>
   <link href="http://ericfarkas.com/posts/on-child-rearing" />
   <updated>2012-09-01T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/on-child-rearing</id>
   <content type="html">&lt;p&gt;&lt;i&gt;The following was written by my pastor, &lt;a href="http://relocatingtoelfland.com"&gt;Ben Miller&lt;/a&gt;, as counsel to a family raising a young child. I've been given permission to reproduce the letter here. The only modifications have been to remove references to specific names and life situations.&lt;/i&gt;&lt;/p&gt;




&lt;p&gt;Hi, [redacted],&lt;/p&gt;




&lt;p&gt;I wanted to follow up on our brief conversation last night. I know the parenting issues you're sorting through are really difficult, and I'd like to offer whatever encouragement and counsel I can. I doubt I'll say anything here that you haven't already thought of, but maybe something will at least provide an additional perspective.&lt;/p&gt;




&lt;p&gt;1. As you know, the goal in parenting is not to bring forth a product that meets the specifications in some pre-printed blueprint somewhere. There isn't a preformed set of specifications "out there" that says, "This is exactly what it will look like for your child to be Christlike." That may be obvious, but what it means in practice is that it's okay as parents to feel (pretty much every day!) a bit unsure of the best way forward. We ourselves are still discovering what the particular "product" is supposed to be, and sometimes our methods have to be retooled significantly as we move along in that discovery process. You know from scripture that your child is to be conformed to their Lord and Savior Jesus Christ by the power of the Holy Spirit, but what you don't know is what precise form that will take (which, of course, means you're still learning every day what are the best methods to guide them toward their individualized conformity to Jesus). In short, if you feel uncertain, that doesn't mean you're blowing it!&lt;/p&gt;




&lt;p&gt;2. On a related note, your child is not a problem to be solved, but a gift to be enjoyed and a mystery to be wondered at. This doesn't take anything away from the fact that they must be trained, but there's a world of difference between (a) training a child you have come to regard (not at all maliciously, of course, but simply because you're tired, perplexed, and frustrated) as a "problem child," and (b) training a child from whom you constantly step back and say, "What a wonderfully mysterious gift God has given us here." Part of the reason you get overwhelmed in training your child is that they're beyond figuring out - and God made them that way! We need to back away as parents sometimes and simply enjoy that.&lt;/p&gt;




&lt;p&gt;3. In light of the medical issues, remember that your child is much more than a physical body; and remember also that they're much more than a will to be subdued. At the center of their being is something the Bible calls the heart, and your ultimate goal in parenting is to win their heart to you and to their Triune God. "My son, give me your heart" (Proverbs 23:26); that's parenting in a nutshell. It can be unclear, when children are very young and don't communicate well, just how to interact with their hearts; but as they get older and begin to talk, you get significant glimpses into their hearts every day, and you will have many, many opportunities every day to reach into their hearts with the love and truth of Jesus. This doesn't always mean talking to them; it means, for example, being glad (or at least acting glad!) to see them when they get out of bed in the morning, reaching out to touch them when they come near you, smiling at them, pointing out to them the good gifts of God, speaking scripture in their hearing, being careful of frustrated tones and gestures and even "vibes," being quick to restore affection after the sharpness of discipline, speaking well of them to others, noticing the little things they do that they yearn (yea, clamor) for you to notice, etc. Children need to feel drawn in by their parents, not pushed away (overtly or covertly). Again, the goal is to establish a bond of affection heart-to-heart that will then open their ears when it's time to rebuke, chasten, and instruct.&lt;/p&gt;




&lt;p&gt;4. Again in light of the medical issues, you will continue to learn every day how to balance meeting your child's needs (which are real) with the dark reality that they (like all of us) will use their needs to manipulate you to get what they want. One of your child's needs is to know that when they push against a clear (and reasonable) boundary you have set, it's not going to move. Nothing breeds insecurity in a child so much as not knowing who's in charge. As you build the bond of affection I mentioned above, you will find that your setting and enforcing of boundaries will not frustrate your child (though they may still resist, just to make sure you truly mean it), but will give them great comfort and reassurance.&lt;/p&gt;




&lt;p&gt;5. One of the hardest things to learn in parenting is when to skirt a particular battle in order to win the war. There are certain things your children are doing to do every day that are going to drive you insane, and now is simply not the time to address them. There is always a need to prioritize "battles" in the long "war" to win the hearts of our children. A parent who can't wisely prioritize, and then let go of certain things for the present, is going to be a parent who eventually gets completely overwhelmed - and heaven help the kids at that point!&lt;/p&gt;




&lt;p&gt;6. Connected with this, I would suggest that you both sit down and hammer out a set of (by this I mean two or three) simple, clear, attainable goals for your child's development (say) over the next three months (or six months), and then teach and discipline diligently, consistently, and unswervingly to those. Other stuff will have to wait. You don't build a roof at the same time you're building a foundation. For example, this summer we're working with our kids on expressing gratitude and taking jurisdiction over particular "zones" in the house.&lt;/p&gt;




&lt;p&gt;7. On the social side of things, listen to wise counsel from others, but don't be ruled in any way by the opinions of others. You are not bringing up your child for either set of in-laws, or Trinity Church, or the medical professionals who weigh in; you're bringing them up for the Triune God - and it is you who are called to bring them up, which means you need to be comfortable making wise decisions and seeing them through, regardless of what others think. This doesn't mean you're hard-headed and don't listen (I wouldn't be writing this email if I were trying to encourage that!), but it does mean you're comfortable with the fact that the God of all grace - and He alone - is your Judge in this business of parenting. Everything else is just human opinions.&lt;/p&gt;




&lt;p&gt;8. [The husband], this is directed especially to you: be very careful (notice the "very" in that phrase!) about too many outside commitments at this stage in your family's life. You don't have time for a lot of outside commitments; your marriage and your children are your first priority, and whatever doesn't serve those relationships, at this stage in the game, is questionable. You need to unplug from stuff and connect with your children, especially this child. Whatever it takes.&lt;/p&gt;




&lt;p&gt;9. Don't worry, I'm almost done! On a penultimate note, don't worry too much about visible results, especially day to day. God will bless faithful obedience. Believe that. You'll see the fruit in harvest season; right now it's planting and watering time, and it often looks like the plants aren't growing. They are, because God is at work. "Be it unto you according to your faith."&lt;/p&gt;




&lt;p&gt;10. Above all, never let a day pass without praying fervently for the work of the Holy Spirit in all of your children. "You have not because you ask not."&lt;/p&gt;




&lt;p&gt;With deep affection in Christ,&lt;/p&gt;




&lt;p&gt;Ben&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Pipe Fabrication with QuickPen 3D and CSVs</title>
   <link href="http://ericfarkas.com/posts/pipe-fabrication-with-quickpen-3d-and-csvs" />
   <updated>2012-08-26T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/pipe-fabrication-with-quickpen-3d-and-csvs</id>
   <content type="html">&lt;p&gt;&lt;i&gt;I've been told that it's good practice to blog about code you've written, and the thought process behind it.  Doing so gives people an insight into how you approach problems, which at times is more important than what exactly you wrote.  This is such a post.&lt;/i&gt;&lt;/p&gt;




&lt;p&gt;My full-time job is as a mechanical draftsman for an HVAC contractor.  Our work is mostly in high-end residential apartment buildings and hotels in the 5 boroughs of New York City.  We build steam and water-based HVAC systems (sometimes both in the same building).  At work, we use &lt;a href="http://www.quickpen.com/index.php/Products/PipeDesigner-3D-Overview.html"&gt;QuickPen Pipe Designer 3D&lt;/a&gt;, on top of AutoCAD, to draw 3D models of our mechanical piping.&lt;/p&gt;




&lt;center&gt;&lt;div&gt;&lt;img src="/assets/images/prv.png" class="img-polaroid"/&gt;&lt;p&gt;&lt;i&gt;A PRV station&lt;/i&gt;&lt;/p&gt;&lt;/center&gt;




&lt;h3&gt;Coordination&lt;/h3&gt;


&lt;p&gt;To get pipe to the field, the draftsmen first take the contract drawings from the architect and engineer, and we draw our pipe on the floor layout.  We then attend coordination meetings, where all trades (plumbers, electricians, sheetmetal, etc.) work on a set of drawings that has everyone's "stuff" on them.  This lets us resolve conflicts and hits.  Pipe moves around, duct gets raised or lowered, and so on.  When all trades sign off on the drawing for a particular floor, the draftsmen are then free to go back to their offices, update their own shop drawings, and work on fabricating their pipe.  The adoption of &lt;a href="http://usa.autodesk.com/building-information-modeling/"&gt;BIM&lt;/a&gt; and related software stacks promises to make/has made some of these steps unncessary, but many jobs are still coordinated like this.&lt;/p&gt;




&lt;h3&gt;Fabrication&lt;/h3&gt;


&lt;p&gt;The next goal for the draftsman is to hand the fitters in the shop what are called "fabrication sheets", or "cut sheets", that detail what needs to be fabricated, and what tag that piece should be given.&lt;/p&gt;




&lt;center&gt;&lt;div&gt;&lt;img src="/assets/images/cut-sheets.png" class="img-polaroid"/&gt;&lt;p&gt;&lt;i&gt;Fabrication pieces&lt;/i&gt;&lt;/p&gt;&lt;/center&gt;




&lt;p&gt;In the above image, &lt;code&gt;R-03&lt;/code&gt; is the tag number given to a piece of pipe that is 1-1/2" in diameter, 9'-6-1/4" long, and has a 1-1/2"x3/4" tee on one end.  We only want one of these pieces tagged R-03.  A fitting on the end of a piece like this is called a "make-up fitting". Usually, threaded pipe like this only has one make-up fitting. The other end of the pipe is a threaded end, and the fitters put these pieces together to construct all the piping on a given floor. We send "shop drawings" to the field that detail how these pieces fit together:&lt;/p&gt;




&lt;center&gt;&lt;div&gt;&lt;img src="/assets/images/balloon-dwg.png" class="img-polaroid"/&gt;&lt;p&gt;&lt;i&gt;Shop drawing with pipes tagged&lt;/i&gt;&lt;/p&gt;&lt;/center&gt;




&lt;p&gt;QuickPen makes it (almost) easy to do all this.  I draw my pipe in 3D, at real elevations. I then use QuickPen's ISO manager to create my fabrication pieces by literally clicking on a piece of pipe, and it's make-up fitting.  QuickPen auto-increments the counter as I create pieces.&lt;/p&gt;




&lt;h3&gt;The Problem&lt;/h3&gt;


&lt;p&gt;While QuickPen makes it easy to tag your pipe for fabrication, and make a shop drawing with ballons for each tag, it doesn't make it THAT easy to create the cut sheets.  When you ask QuickPen to export your Bill Of Materials so that you can make a spreadsheet to print out and give to the fabricators in the shop, you're given the option to export to a CSV.  Here is what QuickPen gives you, truncated for our purposes:&lt;/p&gt;




&lt;pre&gt;
"Iso Number","Size","Quantity","Length","Long Description"
"R-02","1-1/2""","1","9'-6 1/4""","PIPE, S/STD A-53 GRADE B ERW T.B.E."
"R-02","1-1/2""x3/4""","1","","TEE REDUCING, THRD, 150LB MALLEABLE IRON"
"S-13","2""","1","9'-6 1/4""","PIPE, S/STD A-53 GRADE B ERW T.B.E."
"S-13","2""x3/4""","1","","TEE REDUCING, THRD, 150LB MALLEABLE IRON"
&lt;/pre&gt;




&lt;p&gt;And so on, for each piece.  The CSV is always un-ordered by ISO number (tag number). So basically what you get is a listing of each individual component of a fabrication piece, and not the whole piece itself.  Sometimes this is OK, because there are pieces that may have more than two components.  But 95% of the time a fabrication piece is a length of pipe and a fitting (tee, elbow, valve, coupling, etc.).  I have to massage this CSV so that it's useful to the shop fabricators.&lt;/p&gt;




&lt;p&gt;For a while I did this by hand, which was tedious.  I made mistakes as well, which were costly in real dollar terms.  When union steamfitters call the office while they're standing on the 50th floor deck of a building, in the middle of winter, screaming that your pipe doesn't fit...well, that costs everyone money in wasted time and material.  In this way, drafting is different from much of the programming I've done recently.  Aside from the processing of subscriptions, bugs that occur on &lt;a href="https://talentsoup.com"&gt;TalentSoup&lt;/a&gt; don't &lt;i&gt;immediately&lt;/i&gt; result in lost money, but simply an annoyance.  Drafting is different: a mistake could cost tens of thousands of dollars.&lt;/p&gt;




&lt;p&gt;So after spending so much time on these CSVs, and making some bad mistakes, I decided to let the computer handle the CSVs for me.  This problem was always begging to be solved by a script.  The result is &lt;a href="http://github.com/cut-sheets"&gt;cut-sheets&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;Walk-Through&lt;/h3&gt;


&lt;p&gt;Now that I told you more than you wanted to know about mechanical piping, I want to quickly walk though the parsing script.&lt;/p&gt;




&lt;p&gt;First, I shamelessly steal Rails' &lt;code&gt;blank?&lt;/code&gt; method, which is useful in this context.  Then I set up some constants that map to the column indexes in the CSV.  It's more readable this way. Then I read the CSV in as an array.&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;csv&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;blank?&lt;/span&gt;
    &lt;span class="nb"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:empty?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;ISO_NUMBER&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="no"&gt;SIZE&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="no"&gt;QUANTITY&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="no"&gt;LENGTH&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="no"&gt;DESCRIPTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;

&lt;span class="n"&gt;fabrication_pieces_as_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CSV&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;isos.csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;p&gt;Sometimes, QuickPen's Bill of Materials generator brings in pieces of pipe or fittings that are not part of any fabrication piece.  I remove them from my array so I can process the CSV knowing that I am only working with fabrication pieces. The motivation for want to assume this will be clear shortly.&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;fabrication_pieces_as_array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_if&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ISO_NUMBER&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;blank?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;p&gt;Next, I change some of the descriptions of the pieces to conform to our shop standard.  Also, if the current array element is a fitting (which doesn't have a length as it's not a piece of pipe), then I put the fitting size into the description.  This is so when I make this element the make-up fitting of a piece of pipe, I get the fitting size in the description without any other manipulation.&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;fabrication_pieces_as_array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;DESCRIPTION&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;gsub!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/, 150.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;DESCRIPTION&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;gsub!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/ 150.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;DESCRIPTION&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;gsub!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; PIPE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;DESCRIPTION&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;SIZE&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;DESCRIPTION&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;LENGTH&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;blank?&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;p&gt;Now that my &lt;code&gt;fabrication_pieces_as_array&lt;/code&gt; object is cleaned up, I can start combining pipe pieces with their make-up fittings.  First I create a hash using Ruby's awesome &lt;code&gt;group_by&lt;/code&gt; method.  What I get back is a Hash whose keys are the &lt;code&gt;ISO_NUMBER&lt;/code&gt;, or tag number.

&lt;p&gt;
&lt;pre&gt;
  {"R-02"=&gt;[["R-02", "1-1/2\"", "1", "9'-6 1/4\"", "PIPE, S/STD A-53 GRADE B ERW T.B.E."],
            ["R-02", "1-1/2\"x3/4\"", "1", "", "1-1/2\"x3/4\" TEE REDUCING, THRD"]],
   "R-03"=&gt;[["R-03", "1-1/2\"", "1", "9'-6 1/4\"", "PIPE, S/STD A-53 GRADE B ERW T.B.E."],
            ["R-03", "1-1/2\"x3/4\"", "1", "", "1-1/2\"x3/4\" TEE REDUCING, THRD"]],
   "R-04"=&gt;[["R-04", "1-1/2\"", "1", "9'-6 1/4\"", "PIPE, S/STD A-53 GRADE B ERW T.B.E."],
            ["R-04", "1-1/2\"x3/4\"", "1", "", "1-1/2\"x3/4\" TEE REDUCING, THRD"]]}
&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;iso_pieces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fabrication_pieces_as_array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ISO_NUMBER&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;

&lt;p&gt;Now I can test to see how many pieces each tag number has.  As I said before, 95% of the pieces I make will will have two components: a length of pipe and a fitting.  For cases where the pieces are more complex, I handle those manually, usually by making a sketch of the piece in QuickPen.  I'll make a note on the cut sheet to "See Sketch" for that pieces.  Otherwise, I now go through my original array.  For each element I test to see if it's &lt;code&gt;ISO_NUMBER&lt;/code&gt; number has two components, and if the &lt;code&gt;LENGTH&lt;/code&gt; column is not &lt;code&gt;nil?&lt;/code&gt;, meaning this element is the pipe portion of the fabrication piece that also has one make-up fitting.  I add the fitting row to the pipe row because the pipe row has the pipe &lt;code&gt;LENGTH&lt;/code&gt;; it just "feels" more natural to do it that way.&lt;/p&gt;

&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;fabrication_pieces_as_array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;iso_pieces&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ISO_NUMBER&lt;/span&gt;&lt;span class="o"&gt;]].&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;LENGTH&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;blank?&lt;/span&gt; 
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;DESCRIPTION&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iso_pieces&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ISO_NUMBER&lt;/span&gt;&lt;span class="o"&gt;]].&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;LENGTH&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;blank?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;DESCRIPTION&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;

&lt;p&gt;Now that I've replaced the &lt;code&gt;DESCRIPTION&lt;/code&gt; column of each piece of pipe with the corresponding make-up fitting, I can remove the rows of make-up fittings, which, again, don't have a &lt;code&gt;LENGTH&lt;/code&gt; value. Then I sort the array by &lt;code&gt;ISO_NUMBER&lt;/code&gt;.  This is not necessary, as I can do this in Excel when add this information to our template, but it saves me a step.&lt;/p&gt;

&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="n"&gt;fabrication_pieces_as_array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_if&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;LENGTH&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;blank?&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;iso_pieces&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ISO_NUMBER&lt;/span&gt;&lt;span class="o"&gt;]].&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort!&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ISO_NUMBER&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ISO_NUMBER&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;

&lt;p&gt;Lastly, I generate a new CSV with all the pieces and their make-up fittings.  Pieces that have more &gt; 2 components are also listed, but they have not been "combined" together, so I'll resolve them manually. At the bottom of the CSV I also generate a bill of materials, which is a count of each fitting, so the shop knows how many of each to order from the supply house. Ruby's &lt;code&gt;group_by&lt;/code&gt; gets all the distinct pieces into a hash, and then it's as simple as getting the &lt;code&gt;size&lt;/code&gt; of each key.&lt;/p&gt; 

&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="no"&gt;CSV&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;cut-sheets.csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;wb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;csv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ISO&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;SIZE&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;QUANTITY&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;LENGTH&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;DESCRIPTION&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;fabrication_pieces_as_array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;ISO_NUMBER&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;SIZE&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;QUANTITY&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;LENGTH&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;DESCRIPTION&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;fabrication_pieces_as_array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_by&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="no"&gt;DESCRIPTION&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;

&lt;p&gt;The finished CSV looks like this:&lt;/p&gt;

&lt;p&gt;
&lt;pre&gt;
Iso Number,Size,Quantity,Length,Long Description
R-01,"1-1/4""",1,"9'-6 1/4""","PIPE, S/STD A-53 GRADE B ERW T.B.E."
R-02,"1-1/2""",1,"9'-6 1/4""","1-1/2""x3/4"" TEE REDUCING, THRD"
R-03,"1-1/2""",1,"9'-6 1/4""","1-1/2""x3/4"" TEE REDUCING, THRD"
R-04,"1-1/2""",1,"9'-6 1/4""","1-1/2""x3/4"" TEE REDUCING, THRD"
R-05,"1-1/4""",1,"9'-6 1/4""","1-1/4""x3/4"" TEE REDUCING, THRD"
R-08,"1-1/4""",1,"9'-6 1/4""","1-1/4""x3/4"" TEE REDUCING, THRD"
R-09,"1-1/4""",1,"9'-6 1/4""","1-1/4""x3/4"" TEE REDUCING, THRD"
R-10,"1-1/4""",1,"9'-6 1/4""","1-1/4""x3/4"" TEE REDUCING, THRD"
R-11,"1-1/4""",1,"9'-6 1/4""","1-1/4""x3/4"" TEE REDUCING, THRD"
...
...
"","",3,"","1-1/2""x3/4"" TEE REDUCING, THRD"
"","",8,"","1-1/4""x3/4"" TEE REDUCING, THRD"
"","",9,"","2""x3/4"" TEE REDUCING, THRD"
"","",1,"","2-1/2""x3/4"" TEE REDUCING, THRD"
"","",1,"","2-1/2""x2"" REDUCER, CONC "
"","",1,"","4""x3/4"" THREAD-OLET"
&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;I can plug that into our template, and I am good to go.  Manually, this would take up to an hour depending on how many pieces are on the drawing.  This script takes less than a second.&lt;/p&gt;

&lt;p&gt;This is a simple script but it's made my life easier, and I got to write out my thought process, which was fun.  If you have any suggestions for improvements, feel free to reach out.&lt;/p&gt;

&lt;h3&gt;See Also&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/speric/cut-sheets/"&gt;cut-sheets on GitHub&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Eager Loading With Authlogic</title>
   <link href="http://ericfarkas.com/posts/eager-loading-with-authlogic" />
   <updated>2012-08-23T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/eager-loading-with-authlogic</id>
   <content type="html">&lt;p&gt;I've used &lt;a href="https://github.com/binarylogic/authlogic/"&gt;authlogic&lt;/a&gt; for authentication and session management on a number of projects.  For one application, I wanted to eager-load an association at the time of a successful sign-in.  I thought it'd be helpful to demonstrate the code here, since it took some digging through the documentation and other examples to figure out how to accomplish this.&lt;/p&gt;




&lt;p&gt;Basically you want to create a new session within a &lt;code&gt;with_scope&lt;/code&gt; block, and pass a &lt;code&gt;find_options&lt;/code&gt; hash with whatever options you need.  Here's the &lt;code&gt;create&lt;/code&gt; action in the &lt;code&gt;UserSessions&lt;/code&gt; controller:&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserSessionsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="no"&gt;UserSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with_scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:find_options&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:include&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:photos&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="vi"&gt;@user_session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;UserSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user_session&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@user_session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;
      &lt;span class="n"&gt;flash&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:notice&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Login successful!&amp;quot;&lt;/span&gt;
      &lt;span class="n"&gt;redirect_back_or_default&lt;/span&gt; &lt;span class="n"&gt;account_url&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:action&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:new&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;h3&gt;See Also&lt;/h3&gt;


&lt;p&gt;&lt;a href="http://rdoc.info/github/binarylogic/authlogic"&gt;authlogic documentation&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>What I Learned On The Track This Morning</title>
   <link href="http://ericfarkas.com/posts/what-i-learned-on-the-track-this-morning" />
   <updated>2012-08-03T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/what-i-learned-on-the-track-this-morning</id>
   <content type="html">&lt;p&gt;Today's workout called for a 5000m row.  I do not have access to a C2, so, after some consultation, I decided to run 3 miles instead.  I was told to run it hard, and I set myself a goal of 3 miles in 21 minutes. I have never done 3 miles in that time. I have come close before, 21 minutes and change, in my running days. But that was 5 years and 20 pounds ago.  I don't train that way anymore, I use the weights a lot and get my "cardio" from brutal circuits. My workouts for the last few months have featured no running whatsoever.&lt;/p&gt;




&lt;p&gt;Immediately you see the problem.  The ego suggested a goal, and I listened, and adopted that goal as my own. I had not put in the work necessary to achieve that goal, and I thought I could just show up and, with pretty much nothing in the bank, set a new personal best.  The smarter thing would have been to set a more achievable pace, maybe 22:30 or 23 minutes.  Those times are not fantastic but at my current fitness levels, and in the context of my larger training plan, they would have represented good work.  More importantly, I could have finished the workout.&lt;/p&gt;




&lt;p&gt;By now, you know how the story goes.  I started off at about a 6:45/mile pace. Two laps in and I realized the hole I had dug for myself.  Approaching the mile mark, I had "the moment". Faced with the consequences of my decisions, there were two paths: continue on and live with myself with integrity and suffer, or give up.&lt;/p&gt;




&lt;p&gt;I chose to bail. One mile done, and I was done too.&lt;/p&gt;




&lt;p&gt;In the initial moments after quitting, I tried to rationalize my decision. "My lungs hurt", "my legs hurt", "it's humid out", "one mile is good for not having run in a while".  But the truth is, I made a choice.  I chose to quit, I chose to stop putting one foot in front of the other. I did not have enough integrity to face the consequences of my actions. I lied to myself.&lt;/p&gt;




&lt;p&gt;The walk back to the car from the track was tough. I took stock of other areas in my life where I had dug myself a hole that I cheated my way out of. It's not easy to come face to face with yourself as you really are, not as you imagine yourself to be.&lt;/p&gt;




&lt;p&gt;It's interesting to me how I first thought that what I learned today was that I need to pace myself and fight against my ego. In fact, I learned that you cannot expect to achieve what you did not work for.  And the answer is not always to work harder, to run yourself into the ground.  Sometimes the answer is to just starting working, period.&lt;/p&gt;


&lt;p&gt; &lt;p&gt;Second, when have dug yourself a hole and find yourself in an uncomfortable situation that is of your own making, stand your ground and live with yourself.  Sometimes it means suffering. In fact, it almost always does. But you come out on the other side with integrity.&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;I did not have that integrity today.  I will have it on Monday, and every day after that. And not just on the track, or in the gym, but in all of life, with the help of God.&lt;/p&gt;




&lt;h3&gt;See Also&lt;/h3&gt;


&lt;p&gt;&lt;a href="http://station515.blogspot.com/2011/10/moment.html"&gt;the moment&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;&lt;a href="http://www.gymjones.com/knowledge/article/23/"&gt;Will And Suffering&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Global Exception Handling With Rails 3 and Bugsnag</title>
   <link href="http://ericfarkas.com/posts/global-exception-handling-with-rails-3-and-bugsnag" />
   <updated>2012-07-09T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/global-exception-handling-with-rails-3-and-bugsnag</id>
   <content type="html">&lt;p&gt;At &lt;a href="https://talentsoup.com"&gt;TalentSoup&lt;/a&gt;, we recently launched a new application privately to a number of our trusted customers.  For exception handling, I decided to give &lt;a href="http://bugsnag.com"&gt;bugsnag&lt;/a&gt; a try.  In the past I've used the &lt;a href="https://github.com/rails/exception_notification"&gt;exception_notification&lt;/a&gt; gem, and have even rolled my own solution for handling exceptions, but for this new app, I thought I'd do something new. I'm a big fan of leveraging existing "solutions" for common tasks, so I can focus on the core business of the application (see &lt;a href="http://fhwang.net/2012/02/19/Buy-vs-build-for-an-early-stage-startup"&gt;Buy-vs-build for an early stage startup&lt;/a&gt;).&lt;/p&gt;




&lt;p&gt;bugsnag has nice &lt;a href="https://bugsnag.com/docs/notifiers/ruby"&gt;documentation&lt;/a&gt; to help you get started. What I wanted to do was just catch every exception that's generated by the app, regardless of specific type.  As the app matures and we decide what we want to do in a particular case, we'll handle each exception type differently, but for now, a global catch-all will suffice:&lt;/p&gt;




&lt;p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="c1"&gt;# config/initializers/bugsnag.rb&lt;/span&gt;
&lt;span class="no"&gt;Bugsnag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;API_KEY&amp;quot;&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ignore_classes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/controllers/application.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="ss"&gt;ActionController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="ss"&gt;:Base&lt;/span&gt;
  &lt;span class="n"&gt;rescue_from&lt;/span&gt; &lt;span class="no"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:with&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:handle_public_excepton&lt;/span&gt;
  
  &lt;span class="kp"&gt;protected&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_public_excepton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;:template&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;shared/exception&amp;quot;&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;




&lt;p&gt;So we catch every exception, and display a generic error page that lives in &lt;code&gt;app/views/shared/&lt;/code&gt;. This wound up being trivial but it was a good excuse to dig into Rails 3 internals and learn about how exceptions are handled within the framework.&lt;/p&gt;




&lt;h3&gt;See Also&lt;/h3&gt;


&lt;p&gt;&lt;a href="http://m.onkey.org/rescue-from-dispatching"&gt;rescue_from dispatching&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;&lt;a href="https://github.com/rails/rails/blob/master/activesupport/lib/active_support/rescuable.rb"&gt;rails/activesupport/lib/active_support/rescuable.rb&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Stuff DHH Says</title>
   <link href="http://ericfarkas.com/posts/stuff-dhh-says" />
   <updated>2012-07-06T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/stuff-dhh-says</id>
   <content type="html">&lt;p&gt;One of the things that makes being a developer interesting (or maddening, depending on your perspective) is the ever-present need to learn new "stuff".  Even if time does not allow you to play with certain technologies or frameworks, you should still be aware of their existence, and have a general idea of what they do and where you might use them.&lt;/p&gt;




&lt;p&gt;The problem, as I see it, is that it's difficult to separate the signal from the noise.  With so many frameworks, methodologies, data stores, servers, etc. one can feel overwhelmed quickly. A few prominent voices start touting this or that approach, and you're left with the question: do I jump on board?  Or do I stick with what I know and what has worked, and potentially get left behind?  These questions can affect careers.  You have to learn to balance the desire to stay up-to-date with the temptation to drink the Kool-Aid and take your eye off the ball, which is the fact that, in the &lt;a href="http://www.codersatwork.com/"&gt;words&lt;/a&gt; of &lt;a href="http://www.jwz.org/"&gt;jwz&lt;/a&gt;, you're not here to write code, you're here to ship products. What approach or technology or framework will help you actually ship code?&lt;/p&gt;




&lt;p&gt;Over time, I have found &lt;a href="http://david.heinemeierhansson.com/"&gt;DHH&lt;/a&gt; to be a sane voice of reason in what can sometimes be an insane Rails community.  The framework he created has basically furnished me a career and lifestyle, so I have a high degree of respect for him.  Not only did he create the Rails framework, but 37signals has created some of, if not the most, successful Rails applications to date.  When he talks about development, I listen.&lt;/p&gt;




&lt;p&gt;In the last few weeks I've watched DHH's &lt;a href="https://twitter.com/dhh"&gt;Twitter feed&lt;/a&gt;
with great interest, as he's been offering his thoughts on some of the current Rails development trends that are relevant to my projects.  His approach resonates with me, as he seems to advocate a sane, simple, common-sense approach to Rails development.  I've taken the time to aggregate some of his recent Tweets into paragraph form. This is helpful to me, and maybe it will be helpful to you.&lt;/p&gt;


&lt;br/&gt;


&lt;h3&gt;On Testing and Testing Frameworks&lt;/h3&gt;


&lt;p&gt;&lt;blockquote&gt;Spurred by this debate on rails-core &lt;a href="https://groups.google.com/forum/?fromgroups#!topic/rubyonrails-core/_lcjRRgyhC0"&gt;https://groups.google.com/forum/?fromgroups#!topic/rubyonrails-core/_lcjRRgyhC0&lt;/a&gt;, I'd like to see some more real FactoryGirl code gisted.  All the tests I've seen written with FactoryGirl, or other factory approaches, have been worse than the equivalent fixture approach. I like &lt;a href="http://twitter.com/tenderlove"&gt;@tenderlove&lt;/a&gt;'s theory that people got hooked on factories before foxy fixtures with named associations and never revisited.  That's really the sad part about both rspec and FactoryGirl. Newcomers are lead astray to these tar pits instantly.  It's like most Rails books can't wait to pimp the framework ride. Hey yo, that chariot needs some spinning rims, yo! Ugh.&lt;/blockquote&gt;&lt;/p&gt;


&lt;hr&gt;


&lt;p&gt;&lt;blockquote&gt;&lt;a href="http://twitter.com/rgreen"&gt;@rgreen&lt;/a&gt;: everone is pushing Rspec. Where do we go for better instructions?&lt;br/&gt;
&lt;a href="http://twitter.com/dhh"&gt;@dhh&lt;/a&gt;: See Agile Web Development with Rails and stick to test/unit as it comes out of the box.&lt;/blockquote&gt;&lt;/p&gt;


&lt;hr&gt;


&lt;p&gt;&lt;blockquote&gt;&lt;a href="http://twitter.com/paulvolpato"&gt;@paulvolpato&lt;/a&gt;: if you don't mock often, do you tend to write unit tests where the object under test talks to real collaborators?&lt;br/&gt;
&lt;a href="http://twitter.com/dhh"&gt;@dhh&lt;/a&gt;: Yes. I don't believe much in the "complete isolation" idea. My unit tests are more like model tests and do hit the db.&lt;/blockquote&gt;&lt;/p&gt;




&lt;p&gt;Sources: &lt;a href="http://bitly.com/bundles/o_t794esdva/5"&gt;http://bitly.com/bundles/o_t794esdva/5&lt;/a&gt;&lt;/p&gt;


&lt;br/&gt;


&lt;h3&gt;On Software Architects and Architecture&lt;/h3&gt;


&lt;p&gt;&lt;blockquote&gt;It's easy to forget that there are still people who proudly call themselves software architects, use UML, and clamor for repeatable process! I was only reminded by reading &lt;a href="http://www.javacodegeeks.com/2012/07/architects-need-pragmatic-software.html"&gt;http://www.javacodegeeks.com/2012/07/architects-need-pragmatic-software.html&lt;/a&gt;. Ugh. Here's a process for you: Software architects who don't implement a substantial amount of what they design will fuck shit up. Badly.&lt;/blockquote&gt;&lt;/p&gt;


&lt;hr&gt;


&lt;p&gt;&lt;blockquote&gt;Following up on the discussion from &lt;a href="http://twitter.com/rubyrogues"&gt;@rubyrogues&lt;/a&gt;, this is a great example of SRR pattern wankery: &lt;a href="http://www.naildrivin5.com/blog/2012/06/10/single-responsibility-principle-and-rails.html"&gt;http://www.naildrivin5.com/blog/2012/06/10/single-responsibility-principle-and-rails.html&lt;/a&gt;.  Premature extraction!  The over-architecture of injecting the mailer is particularly offensive. Reminds me of that thread where someone got canned for such code. Test is simple: Look at the original code and compare to the concoction that follows. Forget the authoritative pattern name. Is it better? This coordination work is exactly what the controller is there to perform. It's completely fine for controllers to be more than 1 line. (Just to be clear, I think moving the mailing action into the model is an abomination. Leave it in the controller. That's what it's for!) Preemptive programming is what happens when you just can't wait to apply your patterns until they're actually needed. I didn't think it was possible, but Single Responsibility Principle appears to be producing even more bullshit code than Law of Demeter. Love how these patterns are not supposed to bear critique in any of their examples. It's always about MAINTAINABILITY. You have to believe! If you can't provide a convincing before/after code for your pattern, you're selling snake oil. Fuck your faith-based programming bullshit.&lt;/blockquote&gt;&lt;/p&gt;


&lt;hr&gt;


&lt;p&gt;&lt;blockquote&gt;Another example where SRR went hog wild: &lt;a href="https://gist.github.com/2838490"&gt;https://gist.github.com/2838490&lt;/a&gt;. Every line of activity becomes a class instead of just a method. Fuck me. (The roar of the "proper OO" crowd in that thread going rah-rah-rah is hilarious, btw.) I think having the mailer call inline in the controller is a better fit until you have more real problems tugging at it.&lt;/blockquote&gt;&lt;/p&gt;


&lt;hr&gt;


&lt;p&gt;&lt;blockquote&gt;I'm a little late to the party, but unless you're actually reusing this PostComment logic in multiple places, I agree with your senior developer. Here's how I'd swing it in the regular controller:&lt;br/&gt;&lt;br/&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&lt;/span&gt;
 &lt;span class="n"&gt;before_filter&lt;/span&gt; &lt;span class="ss"&gt;:set_entry&lt;/span&gt;
 &lt;span class="n"&gt;before_filter&lt;/span&gt; &lt;span class="ss"&gt;:reject_spam&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
   &lt;span class="vi"&gt;@comment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

   &lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new_comment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@comment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deliver&lt;/span&gt;

   &lt;span class="no"&gt;TwitterPoster&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;              &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;share_on_twitter?&lt;/span&gt;
   &lt;span class="no"&gt;FacebookPoster&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:comment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@comment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;share_on_facebook?&lt;/span&gt;    
 &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="kp"&gt;private&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_entry&lt;/span&gt;
     &lt;span class="vi"&gt;@entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reject_spam&lt;/span&gt;
     &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="ss"&gt;:bad_request&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;SpamChecker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spammy?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;br/&gt;I'm assuming that the language detection is something that needs to happen in the comment, so I would keep it in that model -- but not sure what it does. (&lt;i&gt;In response to this thread: &lt;a href="https://gist.github.com/2838490"&gt;https://gist.github.com/2838490&lt;/a&gt;&lt;/i&gt;)&lt;/blockquote&gt;&lt;/p&gt;




&lt;p&gt;Sources: &lt;a href="http://bitly.com/bundles/o_t794esdva/6"&gt;http://bitly.com/bundles/o_t794esdva/6&lt;/a&gt;&lt;/p&gt;


&lt;br/&gt;


&lt;h3&gt;On Rails Application Design&lt;/h3&gt;


&lt;p&gt;&lt;blockquote&gt;&lt;a href="http://twitter.com/seandevineinc"&gt;@seandevineinc&lt;/a&gt;: Any advice on when to use AR observers instead of AR callbacks?&lt;br/&gt;
&lt;a href="http://twitter.com/dhh"&gt;@dhh&lt;/a&gt;: I basically do not use Observers any more. I find them too indirect. I use callbacks all the time, though. But a general rule would be that callbacks should only be used for model concerns, observers can trigger external services. So if saving a model should send an email, I'd use an observer if I couldn't do it in the controller. Not a callback.&lt;/blockquote&gt;&lt;/p&gt;


&lt;hr&gt;


&lt;p&gt;&lt;blockquote&gt;&lt;a href="http://twitter.com/rdetert"&gt;@rdetert&lt;/a&gt;: what about observers for sending notifications?&lt;br/&gt;
&lt;a href="http://twitter.com/dhh"&gt;@dhh&lt;/a&gt;: I'm not a big fan in general. It makes the flow opaque. Just triggering from the controller is much simpler in most cases.&lt;/blockquote&gt;&lt;/p&gt;




&lt;p&gt;Sources: &lt;a href="http://bitly.com/bundles/o_t794esdva/7"&gt;http://bitly.com/bundles/o_t794esdva/7&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;P.S. - The new &lt;a href=”http://bitly.com”&gt;bitly&lt;/a&gt; is awesome.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Full-Time/Part-Time Co-Founder Dynamics</title>
   <link href="http://ericfarkas.com/posts/full-time-part-time-co-founder-dynamics" />
   <updated>2012-06-01T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/full-time-part-time-co-founder-dynamics</id>
   <content type="html">&lt;p&gt;I received a lot of feedback, via &lt;a href="http://news.ycombinator.com/item?id=4044044"&gt;Hacker News&lt;/a&gt;, Twitter, and email, from my post titled &lt;a href="http://ericfarkas.com/posts/how-to-do-a-startup-on-the-side-and-not-lose-your-family/"&gt;How To Do A Startup On The Side And Not Lose Your Family&lt;/a&gt;.  Based on that feedback, I have a lot of ideas for future posts.  There are a lot of people out there trying to do the "startup on the side" thing while leading a full family life, and perhaps there hasn't been a lot written specifically for that group.  I do not claim to be an expert, but my experience, limited though it may be, might be helpful for those folks.  This post is about co-founder dynamics, particularly in the case of one co-founder being full-time, the other being part-time.&lt;/p&gt;




&lt;p&gt;As with my last post, this is simply my &lt;a href="http://epistemocrat.blogspot.com/"&gt;n=1&lt;/a&gt; experience.  What follows is not meant to be prescriptive but rather descriptive.  It might seem crazy and foolish but it works for me.&lt;/p&gt;




&lt;h3&gt;School of Hard Knocks&lt;/h3&gt;


&lt;p&gt;In 2005, 3 of my friends and I created a social networking startup. My 3 co-founders were working on the project full-time.  I had just gotten married, and was one year into a great job as a Java developer at a large bank in NYC, with a baby on the way.  There was no way I was leaving that situation to chase the dream full-time.  So our setup was 3 full-time guys, me part-time, with the expectation that through either revenue or investment, I'd one day be able to work full-time.  I wound up leaving before I had that opportunity, after about 18 months.  I was terrible at balancing work/life/startup, and the strain on my young marriage caused me to neglect working on the startup, which was not fair to my co-founders.  A graceful exit was the best option for all parties.&lt;/p&gt;




&lt;p&gt;In 2009 I joined &lt;a href="http://talentsoup.com"&gt;TalentSoup&lt;/a&gt;.  My co-founder, Rad Harrell, had started the company in 2006, and brought me on board as a technical co-founder.  The setup there was similar: Rad does TalentSoup full-time, it's how he feeds his family.  I do my work part-time.&lt;/p&gt;




&lt;p&gt;So that's my experience.  Two startups, both done part-time while my co-founders were full-time.  The first was unsustainable for me, my involvement in the second is still going strong after 2 1/2 years.&lt;/p&gt;




&lt;h3&gt;Qualities To Look For In A co-founder&lt;/h3&gt;


&lt;p&gt;Before discussing the dynamics of how co-founders work together, let's take a step back and talk about beginning that relationship in the first place.  Much has been written about the qualities to look for in a co-founder, technical or otherwise.  To me, the most important thing is character.  Do they value what I value?  Are their priorities, even if they don't have a family like me, the same as mine?  Do we have common ground on which to relate to one another, and understand each other?  Is this person not only trustworthy, but open and honest?  These are not trivial concerns.  Doing a startup is very similar to a marriage in that, in many ways, you are sharing a part of your soul with another person.  Anything that you find mildly annoying or grating about the other person will certainly show up, magnified 1,000 times, in a co-founder relationship, especially one that takes place in the context of having a family and other external pressures.&lt;/p&gt;




&lt;p&gt;Trusthworthiness, which is the part of character that's the most important to me, is a tricky thing to test for.  I've lived and worked in New York my whole life, and as a result I've developed stereotypical New York cynicism.  I default to "I'm not buying it".  This cynicism, while sometimes harmful in other areas, is absolutely an asset for testing trustworthiness.  When someone speaks to me, does my instinct incline me to believe them, or dismiss what they're saying?  I think I have a good instinct for that kind of stuff, developed over time, having heard dozens of startup ideas and talked to countless people.&lt;/p&gt;




&lt;p&gt;This came in handy when I first spoke to Rad.  We had a phone conversation for an hour, and he pitched me on the project.  He was open about everything: the past, the current state of the company in all areas,  what his vision for the future was, and what he was willing to give to get me on board.  There was a recognition that I would be the technology lead, that his expertise was limited in that area, and I'd have freedom to do what I felt needed to be done.  You might think that's too much information to give in a first discussion, but his openness, as well as my gut telling me that this guy was being absolutely real with me, endeared me to him.  I remember telling my wife that I liked Rad so much after that call, I felt like I wanted to do the startup just to see him succeed, never mind what would be in it for me.&lt;/p&gt;




&lt;p&gt;This whole trust thing is the cornerstone to the whole co-founder relationship.  As you'll see shortly.&lt;/p&gt;




&lt;h3&gt;The Dirty Details&lt;/h3&gt;


&lt;p&gt;So now you've found a co-founder.  If both of you are going to work full-time, it's pretty easy to decide you'll split the equity somewhere around 50/50.  But what if one of you works full-time while the other works part-time?  How do you split equity? This is where things get tricky.&lt;/p&gt;




&lt;p&gt;Keep this in mind: your number one goal in a startup is to ship something people want.  Especially in the early stages, work has to happen at a fast and furious pace.  You are in constant shipping mode.  Whenever you're actually sitting down, working on your startup, a couple of hours wasted on anything other than shipping code is a disaster. Keeping this in mind will help in two ways when deciding equity splits:&lt;/p&gt;


&lt;ul&gt;
    &lt;li&gt;&lt;p&gt;Even though figuring this split out is very important, it's not the most important thing.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;When you're spending energy on anything in an early startup (including equity splits), you're asking: "How will this help me ship code?"&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;That second point often gets lost in the mix when these discussions take place.  I've seen things like this: &lt;i&gt;"I'm full-time and I get X%, my part-time co-founder gets Y%, and when he goes full-time, I'll have X-10% and he'll get Y+10%"&lt;/i&gt;.  I've been the guy on the other side of that equation, and I can tell you that it doesn't motivate me to know that I get more equity if I am full-time.  In fact, I think knowing that there's more % waiting for me if I can just cross the full-time chasm actually stresses me out, which affects me emotionally, which affects me mentally, which negatively impacts my ability to...wait for it...ship code.  Call me weak-minded but that's how I'm wired.  You might be different; n=1.  Maybe the aforementioned approach works for your personality type, in which case, do it!&lt;/p&gt;




&lt;p&gt;So whatever you are thinking the percentage splits are, might I suggest just making them that way from the start?  Of course everyone should be vested, and the appropriate contractual steps should be taken to ensure everyone's backs are covered.  But if, as the full-time guy, you really trust your co-founder, and really trust that he's working as hard as he can to make the company a success, then why not start with whatever your "when you're full-time" percentage is?  If you can't trust your co-founder, then maybe you should find another one.&lt;/p&gt;




&lt;p&gt;Notice I am not suggesting hard and fast percentage amounts.  There are alot of considerations: which one is the technical co-founder, and therefore actually building the product?  Are both?  Are neither?  Has either partner invested money into the company for bootstrapping?  Is the business existing and profitable already? In these discussions, be open and honest with your wants.&lt;/p&gt;




&lt;p&gt;It comes down to this: the equity split shouldn't be used a carrot on a stick, but rather as a tool to create good morale for the people involved in the startup.  By all means, vest, and CYA with contracts, etc., but at the end of the day, you either trust your partner or you don't.&lt;/p&gt;




&lt;h3&gt;Actual Working&lt;/h3&gt;




&lt;p&gt;You have a co-founder, and you have all the equity stuff worked out so you'll know how many millions you'll get when Google acquires you.  You want your Ferrari in red, right?  With black leather?  Sure, we can do that.&lt;/p&gt;




&lt;p&gt;Now you get to work.  You hustle and make the most of all the time you have, balancing as you go.  You make use of things like &lt;a href="http://basecamp.com"&gt;Basecamp&lt;/a&gt;, &lt;a href="http://campfirenow.com/"&gt;Campfire&lt;/a&gt;, iChat, text messages, etc. to communicate asynchronously.  And you don't IM your partner about a bug, you open a new issue in &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt; so he can look at it when it's convenient for him, and you both have a log to refer back to.  If you're a non-technical co-founder, you learn basic HTML so you can do copy work on the site, and you learn how git works so you can be involved.  Spencer Fry wrote a &lt;a href="http://spencerfry.com/whats-a-non-programmer-to-do"&gt;great post&lt;/a&gt; about this a while back.  If you're the technical guy, you look at how you can make your co-founder's life easier through automating common admin tasks. Most of all, you...chill out.  If your co-founder takes a night, or two, or three off, it's ok.  Because you trust him.  He's not "deceiving me by not working for his fair share", he's doing what he needs to do, and when he gets back you know he's going to kill it.&lt;/p&gt;




&lt;p&gt;TalentSoup was an existing business making money when I joined, but it was not a freestanding entity.  That's changed.  We've automated many of the administrative tasks around the site, things Rad was doing manually, or tasks he wanted to do but could not.  This increased efficiencies, meaning we could do more work in less time, and therefore make more money because we could handle more jobs simultanenously.  We could focus on serving clients and not on the tedious stuff.  I also conceived and implemented our paid subscription service, &lt;a href="https://talentsoup.com/signup"&gt;Webcomp Pro&lt;/a&gt;, which created a new revenue stream.  In other words, I am not just building features that Rad dreamt up.  I am actively involved in product development, short-term and long-term, always on the lookout to create revenue through increasing our efficiency, or introducing a new product.  I am a partner in the business, with an equity state that makes me happy and frees me to think about shipping code, not chasing a carrot.&lt;/p&gt;




&lt;p&gt;Flexibility in the when and where is also very important.  Don't set a "minimum expected hours per week".  If you're working with someone who has a full-time job and family, and you add the expectation of X amount of hours per week on top of that to justify an equity percentage, it's going to be more harmful than helpful.  I speak from personal experience, being that family guy.  Now of course, if over the long term your partner is not pulling their weight, then you say what needs to be said, or do what needs to be done.  But I like the idea of judging based on results only.  If I am consistently shipping code that customers are using, and we are making money, does it matter how many hours I worked last week, or that I worked those hours in my boxers in bed late at night while everyone is sleeping?  No, it does not.  Ship code.  Everything else is details.&lt;/p&gt;




&lt;p&gt;You also celebrate success, however small.  New feature launched?  Let's have some beers together over iChat.  First paying customer? Alright!  Our 1,000th user just signed up?  Let's take the night off and take our wives out.  Working asynchronously, in different locations (Rad is in beautiful Savannah, GA and I am on Long Island), means you have to be intentional about building the camraderie that being in the same room would normally bring.  It's not easy, but it can be done.  Sometimes I laugh at myself for the ridiculous things I celebrate, like fixing an annoying bug or changing the layout of a page, but then I remember these small celebrations are necessary because they give me an emotional boost, which helps me ship code.&lt;/p&gt;




&lt;h3&gt;Choose Your Own Adventure&lt;/h3&gt;


&lt;p&gt;I was on a job interview once, and the person interviewing me asked what I consider success to be, just generally, not necessarily with relation to work, though it could be that.  Whatever came into my mind.  My honest answer was that success to me meant having a strong marriage, and children who grow up to love me and love God as well.  I was kind of surprised by my answer.  Before any business achievement, that's how I define success.  And given that, my relationship with my co-founder makes perfect sense.  And he is the same way (step 1: same values).  We want TalentSoup to succeed financially.  I want to make a lot of money; I am not embarrassed about that.  But more than that I want to have a strong family, I want to live a good life and enjoy it.  So I work hard, yes, but I also remember to relax and slow down.  I don't stress about what Rad is or isn't doing, because I trust him, and he trusts me.  If something needs to be said, either of us would say it.  And if things completely broke down, there are legal structures in place to protect both of us.  But I don't operate out of that.  I operate on the assumption that we're both trying to build something beautiful together, in good faith, and if over a short period of time life gets in the way, that's ok.&lt;/p&gt;




&lt;p&gt;If you're doing a startup on the side while you have a family, you're already choosing a difficult path.  Don't make it worse by partnering up with someone you can't trust.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>How To Do A Startup On The Side And Not Lose Your Family</title>
   <link href="http://ericfarkas.com/posts/how-to-do-a-startup-on-the-side-and-not-lose-your-family" />
   <updated>2012-05-30T00:00:00-07:00</updated>
   <id>http://ericfarkas.com/posts/how-to-do-a-startup-on-the-side-and-not-lose-your-family</id>
   <content type="html">&lt;p&gt;In October 2009, I joined &lt;a href="https://talentsoup.com"&gt;TalentSoup&lt;/a&gt;, as the technical co-founder and lead developer.  There's a really crazy story about how I wound up finding Rad Harrell and TalentSoup, but in short, I answered a call for help in &lt;a href="https://twitter.com/#!/human3rror/status/4367475923"&gt;this tweet&lt;/a&gt; and the rest is my history.  Oh, and the first thing I did when I joined TalentSoup was ditch Drupal for Rails.&lt;/p&gt;




&lt;h3&gt;My So-Called Life&lt;/h3&gt;


&lt;p&gt;I was at the time, and still am, gainfully employed as a mechanical draftsman for a large and well respected HVAC contractor whose work is mainly in the 5 boroughs of NYC.  The story of how a guy who has been programming since the age of 12, has a BS in Comp Sci and spent the early part of his career as a developer in the financial industry ended up doing mechanical drafting is also a long story, but one for another day.&lt;/p&gt;




&lt;p&gt;As of this writing, I am 29 years old.  I am the married father of three great kids.  I have a mortgage.  In addition to all of that, and my full-time job as a draftsman, and TalentSoup on nights and weekends, I have the following obligations and interests:
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Elder at &lt;a href="http://trinitychurchlongisland.com"&gt;Trinity Church&lt;/a&gt;, where I also help teach Sunday School&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I enjoy &lt;a href="http://rivendellcraftbrewery.com"&gt;homebrewing&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I try to read as much as time allows on wide range of subjects&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Watching Chelsea FC (an emotionally traumatic experience lately)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Work out regularly (&lt;a href="http://www.flexcart.com/members/elitefts/default.asp?pid=2976"&gt;5/3/1&lt;/a&gt; or &lt;a href="http://gymjones.com"&gt;Gym Jones&lt;/a&gt;-style)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not to mention generally hanging out and enjoying "the good life".  I also have very strong convictions about not doing or thinking about work on Sundays.  So if a day is 24 hours, and I need to sleep at least 6-8 of them, and I only really have 6 days of the week in which to do all of the aforementioned "stuff", how do I fulfill all my obligations?&lt;/p&gt;

&lt;p&gt;What follows is my own &lt;a href="http://epistemocrat.blogspot.com/"&gt;n=1&lt;/a&gt; experience.  Every person, family, and life situation is different. Here's what's worked for me and helped me bootstrap a debt free, profitable company while having a day job, a family, and a life outside of programming.&lt;/p&gt;

&lt;h3&gt;Buy-In&lt;/h3&gt;
&lt;p&gt;By far the biggest win in my situation is the support of my wife.  She not only tolerates my involvement in a startup, she actively supports me and roots for me.  She wants to see us succeed.  She knows and loves my co-founder and his family.  She asks about the business, unprompted.  All of these things together make the situation workable.  Without her support, there's no way I could do a startup and have a healthy marriage and family life.&lt;/p&gt;

&lt;h3&gt;Priorities: How Do They Work?&lt;/h3&gt;
&lt;p&gt;Spousal/SO support is the first step.  The second step is just as important, and closely related: establishing priorities.  Every post I've ever read about doing startups with a family talks about establishing priorities, but the actual process of doing so is taken for granted.  I don't think it should be.  Again, this is n=1 for me, but I've learned to go about making priorities by thinking about my life in this following manner:&lt;/p&gt;

&lt;code&gt;inner life -&gt; relationships -&gt; provision -&gt; employment -&gt; obligations -&gt; startup -&gt; enjoyment&lt;/code&gt;

&lt;h3&gt;Inner Life&lt;/h3&gt;
&lt;p&gt;The book of Proverbs says "Keep your heart with all vigilance, for from it flow the springs of life" (&lt;a href="http://www.biblegateway.com/passage/?search=Proverbs+4%3A23&amp;version=ESV"&gt;4:23&lt;/a&gt;).  Everything I do in life is affected by the state of my heart.  Am I depressed?  Happy?  Anxious?  Angry?  All of these heart conditions affect the way I interface with my wife, kids, friends, co-workers; they affect the way I do my work and my outlook on life in general.  It's a virtuous/vicious cycle: the happier (or more depressed) I am, the better (or worse) I work and live, which makes me happier (or more depressed).  Therefore my first priority is to "keep my heart".  I do this through prayer, meditation, and Scripture reading, and by regularly being part of worship.&lt;/p&gt;

&lt;h3&gt;Relationships&lt;/h3&gt;
&lt;p&gt;In reality, those categories mentioned above are not water-tight.  They spill over into each other, going up and down the chain.  My inner life affects everything that comes after it, but that doesn't mean my work doesn't affect my inner life.  With that said, I next consider relationships, the most important being with my wife, and after that with my children.  Even with my wife's support, there are times when she needs personal time with me.  A common text message I send to my business partner is "Nikki needs a night, I'll see you tomorrow".  We'll get some good Belgian ale, make popcorn, and watch a movie.  A simple night, but we reconnect, and that's important.  There has to be a give and take.&lt;/p&gt;

&lt;p&gt;Time with my kids, especially at their young ages, is important as well.  I have a lot of room to improve in this area, but the best advice I can give when it comes to time with your kids is "be present".  It's so easy to be with my kids physically, but mentally be thinking about a bug that needs fixing, or a feature I'm in the middle of working on.  It takes intentional effort to be mentally present.  One thing that's helped is taking notes throughout the day, either with &lt;a href="http://fieldnotesbrand.com/"&gt;Field Notes&lt;/a&gt; or in &lt;a href="http://basecamp.com/"&gt;Basecamp&lt;/a&gt;.  Being diligent in this regard means I'm less stressed about losing an idea or thought while I'm away from my computer.  When the kids are asleep, I can just check on the notes I've left myself throughout the day.&lt;/p&gt;

&lt;h3&gt;Provision &amp; Employment&lt;/h3&gt;
&lt;p&gt;These two are so closely related, they warrant being discussed together. As a man, with a family depending on me, I have to be sure I am providing for them, materially.  This means food on the table, clothes on their backs, heat in the winter, etc.  The primary means of doing this is via a regular job, which I have.  Being that my day job provides most of the money my family depends on, I need to make sure my startup pursuits do not affect my day job.  When I am in the office, I am working hard.  That means not working on startup work on my boss' dime.  It also means being mentally present at work as well. &lt;/p&gt;

&lt;h3&gt;Obligations&lt;/h3&gt;
&lt;p&gt;Responsibilities that I have that fall outside of the employment/provision spectrum would be something like be an elder at Trinity.  Right now, being an elder is the only thing I do that falls in this category, and most of my elder work (besides a meeting once a month) can take place at home through email or on the phone.  But this work is something my family and I highly value, and are committed to.&lt;/p&gt;

&lt;h3&gt;Startup&lt;/h3&gt;
&lt;p&gt;Turns out that the thing which occupies my mind (and my dreams) most of the time is, in reality, further down on the priority chain than you'd have thought.  Not much really to say here.  If you've read this far, you know what doing a startup is like.&lt;/p&gt;

&lt;h3&gt;Enjoyment&lt;/h3&gt;
&lt;p&gt;This is a catch-all for any activity I enjoy that, while personally fulfilling, is spent apart from my family and/or does not contribute to the provision need mentioned earlier.  Working out, reading, etc.  Again, enjoyments are one way to improve my inner life, so even though this is last on the list, it's also important, but not as important.&lt;/p&gt;

&lt;h3&gt;Tying It Together&lt;/h3&gt;
&lt;p&gt;So now I have my priorities, listed in my order of importance.  Realizing that they are all related, I nevertheless run every opportunity through the priority framework, and ask the question: where does it fit?  Given what's been going on in my life in the last couple of days/weeks/months, can I pursue this opportunity without harming something higher up the priority chain?  For example, I enjoy playing soccer.  My brother and his buddies regularly play on Saturday mornings at a high school not far from my house.  Most weeks, given how busy things normally are, I would not be able to justify spending a Saturday morning away from my kids doing something like that.  It doesn't contribute to anything above "enjoyment" in the priority chain, and whatever small "inner life" benefit I'd get by playing soccer would be negated by the harm it'd do to my relationship with my wife and children, knowing that dad had a free Saturday morning but spent it kicking a ball around instead of taking us out to breakfast.&lt;/p&gt;

&lt;p&gt;There are times, however, when for some reason I've been able to spend a good amount of time with my kids during the week, and taking a few hours to play soccer on a Saturday isn't a big deal.  It's a play-it-by-ear situation.  But most of the time, if I have free time, I try and spend it with my family instead of for myself.&lt;/p&gt;

&lt;p&gt;I also have to turn down some legitimate provision opportunities as well because of the harm they'd do to my relationships.  A full-time job and part-time startup take up a lot of energy, and adding something like a short-term consulting project to the mix, even though it may pay well, is something I can't do.  In the last year I've turned down 3 such opportunities.&lt;/p&gt;

&lt;p&gt;Likewise, there are some jobs I wouldn't take because of the impact it'd have on my family life.  Things like consistently long hours (in a day job) are a deal breaker for me.  That might disqualify me for a job at many web startups, and that's OK with me.  Marco Arment recently had a great post titled &lt;a href="http://www.marco.org/2012/05/24/old-farts"&gt;Old Farts Know How To Code&lt;/a&gt; that sums up my feelings on the matter.&lt;/p&gt;

&lt;h3&gt;Practical Tips&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;p&gt;Get your spouse or SO's buy-in.  Until that happens, I'd suggest not going forward.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;Take time to focus on your inner life, in whatever way makes sense to you.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;When you're with your family or friends, "be there" mentally.  Chances are, that bug or feature can wait.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;Beg, borrow, and steal...time.  I've started bringing my laptop to my day job, so I can work on TalentSoup during lunch.  That's 5 extra hours a week.  Sometimes I wake up early and work before my day job.  Many nights I stay up late so I can work when everyone's asleep, without taking time away from my family.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;Something has to give, and it's usually your hobbies.  I've made a rule that if I do not workout in the morning before my day job, then I can't work out for the rest of the day.  This forces me to pursue my "hobby" on my time, so I don't take time away from my family or startup.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;On a regular basis, leave the laptop on your desk and take your kids out for ice cream, or go for a walk around the neighborhood.  Life is short, and you don't get these years back. Be flexible.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt;Turn off the iPhone.  Easier said than done, and I guarantee I am a worse offender than you.  But it needs to be done.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I certainly do not keep these guidelines as well as I should.  But I am learning, and I'm a lot better at balancing my time now than I have been in the past.&lt;/p&gt;

</content>
 </entry>
 
</feed>
