<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2178120640661923784</id><updated>2011-12-03T14:35:44.871-08:00</updated><category term='vim tip'/><category term='technology'/><category term='taglist'/><category term='best-practice'/><category term='design patterns'/><category term='lambdas'/><category term='news'/><category term='php'/><category term='technology headlines'/><category term='programming'/><category term='perl'/><category term='oop'/><category term='role'/><category term='conference'/><category term='lambda'/><category term='technology news'/><category term='listfile'/><category term='perl oasis'/><category term='ctags'/><category term='vim plugin'/><category term='moose'/><category term='closure'/><category term='vim script'/><category term='roles'/><category term='self-improvement'/><category term='design'/><category term='traits'/><category term='vim'/><category term='productivity'/><category term='_why'/><category term='code'/><category term='architecture'/><category term='attitude'/><category term='closures'/><category term='rant'/><title type='text'>A Grumpy Young Man</title><subtitle type='html'>rants and raves on web software development</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>21</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-4836124338066026601</id><published>2011-08-12T17:43:00.000-07:00</published><updated>2011-08-12T17:45:42.322-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='technology headlines'/><category scheme='http://www.blogger.com/atom/ns#' term='technology news'/><category scheme='http://www.blogger.com/atom/ns#' term='technology'/><category scheme='http://www.blogger.com/atom/ns#' term='news'/><title type='text'>Technosplode - A New Technology News Site</title><content type='html'>Check out my new site for &lt;a href="http://technosplode.com"&gt;trending technology news headlines&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-4836124338066026601?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/4836124338066026601/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=4836124338066026601' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/4836124338066026601'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/4836124338066026601'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2011/08/technosplode-new-technology-news-site.html' title='Technosplode - A New Technology News Site'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-5991638858582211198</id><published>2011-07-25T18:59:00.000-07:00</published><updated>2011-07-25T19:13:39.984-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><title type='text'>Singleton Abuse</title><content type='html'>I know this is a tired topic, but when explaining how &lt;i&gt;not&lt;/i&gt; to abuse the singleton pattern, I explain it this way:&lt;br /&gt;&lt;br /&gt;Many developers ask themselves this question: "Is there any reason anyone would want to have two instantiations of this class?" This is the wrong question and leads to an over-abundance of singletons. Really, they should be asking, "If I instantiate more than one object of this class, &lt;i&gt;will everything break?&lt;/i&gt;"&lt;br /&gt;&lt;br /&gt;An additional benefit of approaching the pattern this way is that an answer of "yes" leads to the questions, "&lt;i&gt;Why&lt;/i&gt; would my code break? Can I refactor and generalize so that the singleton pattern won't be necessary?"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-5991638858582211198?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/5991638858582211198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=5991638858582211198' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/5991638858582211198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/5991638858582211198'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2011/07/singleton-abuse.html' title='Singleton Abuse'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-1739064534224683793</id><published>2011-03-05T07:37:00.000-08:00</published><updated>2011-03-07T11:31:13.567-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><title type='text'>Programming: What Exactly Is It</title><content type='html'>Computer Science is a misnomer. A science is the study of a particular subject with the aim of building a body of knowledge through observation, experimentation and prediction. Physics is a science; we've worked to understand the natural laws that govern material objects, their movement and their interaction with each other.&lt;br /&gt;&lt;br /&gt;In contrast, a programmer does not study and try to understand a set of laws inherent to an existing, mysterious system. A programmer creates. The computer does not cease to function if you put everything into the global namespace or create a &lt;a href="http://en.wikipedia.org/wiki/God_object"&gt;God object&lt;/a&gt;. It's just a bad &lt;i&gt;judgement call&lt;/i&gt;. Science doesn't have judgement calls. It's either true and testable, or it isn't.&lt;br /&gt;&lt;br /&gt;So programming is a &lt;i&gt;craft&lt;/i&gt;. A craft is something that requires skilled labor. Unlike art, it generally produces something practical and usable. Unlike unskilled labor, one of the worker's responsibilities is to make judgement calls that greatly affect the quality of the result.&lt;br /&gt;&lt;br /&gt;A programmer named Dan North wrote a winding and controversial &lt;a href="http://dannorth.net/2011/01/11/programming-is-not-a-craft/"&gt;article&lt;/a&gt; defending his opinion that programming is not a craft but a &lt;i&gt;trade&lt;/i&gt;. I don't feel like picking apart his entire article, but here are a couple points:&lt;ul&gt;&lt;li&gt;He doesn't argue in terms of concepts. Depending on your take on the words "craft" and "trade" (which, according to some dictionaries, are synonyms), you may agree or disagree with him. That's a shaky basis for an argument.&lt;/li&gt;&lt;li&gt;He focuses on the &lt;i&gt;attitude&lt;/i&gt; of some programmers who claim to be craftsmen. He has more of an issue with conceited posers than good programmers who consider their work a craft.&lt;/li&gt;&lt;li&gt;He says that clients don't care how well-crafted the code is; they simply want it to do what they want it to do. That's true, but they also want to save time and money in the long-run.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Unskilled labor can be unskilled because they've been given a design formulated by someone else; there are few or no judgement calls left to be made. E.g., an assembly line worker who bolts pre-stamped parts to a car or a worker in an electronics factory who solders components to a board according to a schematic. Unless you have a software architect who spells out every class, every package, every method and every interaction, you make hundreds of judgement calls every day as you code, and those decisions affect the long-term viability of the software.&lt;br /&gt;&lt;br /&gt;No matter what features our bosses or clients want from us, we're the programmers. We're responsible for knowing how to implement those features in a way that will benefit the company or client, not just right now, but in the long-run. If your only focus is to get the code to do what the the client wants, I pity the developer who has to maintain it and the client who has to foot the bill to extend it. That's where craftsmanship comes in. We're paid by clients to craft a quality product that they will &lt;i&gt;continue&lt;/i&gt; to be pleased with.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-1739064534224683793?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/1739064534224683793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=1739064534224683793' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/1739064534224683793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/1739064534224683793'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2011/03/tired-subject-programming-is-craft.html' title='Programming: What Exactly &lt;i&gt;Is&lt;/i&gt; It'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-5682393696824751023</id><published>2011-03-04T08:57:00.001-08:00</published><updated>2011-03-05T06:42:59.953-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><title type='text'>Architectural Notes</title><content type='html'>These are just a few things related to programming I've been thinking about.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;There's rarely a right answer; if you're fortunate, there will be an obvious choice with fewer trade-offs.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Design is very subjective. If you give a problem to five programmers, you will get five different implementations, and it's quite possible that all of them are good. Every carpenter knows that a dovetail is the strongest joint, but the curve of the chair's back, the spread of its legs and the finish of its grain are all judgement calls that go into making something elegant but practical.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Programming is a craft, not a science.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Expect to do one major refactor of any project or feature. It's OK---you had no way of knowing that what you thought you needed at first wasn't what you actually needed, and your first attempt will finally give you a standard for comparison to create what you actually needed to begin with.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Code something with the simplest solution possible without ignoring extensibility or flexibility. Worst case, you have to do a major refactor, which is quite nearly inevitable, anyway (see above point). Best case, the solution works and you're done. Either way, you didn't spend more time than was necessary.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;All things considered, this means we're the blue-collar workers of the future. I hope you're OK with that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-5682393696824751023?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/5682393696824751023/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=5682393696824751023' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/5682393696824751023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/5682393696824751023'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2011/03/architectural-notes.html' title='Architectural Notes'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-1801257039929218417</id><published>2010-12-13T18:46:00.001-08:00</published><updated>2011-07-25T20:09:11.775-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='listfile'/><category scheme='http://www.blogger.com/atom/ns#' term='vim plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='vim script'/><title type='text'>List File Plugin for Vim</title><content type='html'>A while back, I had a post about &lt;a href="http://lucasoman.blogspot.com/2009/10/nested-lists-in-vim.html"&gt;managing lists in vim&lt;/a&gt;. Since then, I've found greater and greater use for the script and have invested more time into it. I've decided to put it on &lt;a href="http://www.vim.org/scripts/script.php?script_id=3368"&gt;vim.org&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'm going to reproduce the documentation in the plugin itself with the addition of some in-depth explanation.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Check this post often, as I will keep the documentation up-to-date with the latest features and tweaks.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lucasoman.com/images/listfile.png"/&gt;&lt;br /&gt;&lt;br /&gt;The plugin helps me manage a plain-text file of nested lists. I use it to manage to-do lists, shopping lists, URLs---all in the same file. It manages priority, status, date, custom folding, among other things. It supports tagging, timestamping and searching with vim's quickfix list. Aside from the shortcuts and commands below, indentation and folding are managed by the standard vim mappings. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-size:1.2em;"&gt;&lt;br /&gt;&lt;b&gt;First Step&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;:Lcreate &amp;lt;name&amp;gt;&lt;/b&gt; - create new list file in current buffer with &amp;lt;name&amp;gt; (".list" is added automagically)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Creating Items&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;,n&lt;/b&gt; - create new item&lt;br /&gt;&lt;b&gt;&amp;lt;enter&amp;gt;&lt;/b&gt; - (insert or normal) create new item&lt;br /&gt;&lt;b&gt;,s&lt;/b&gt; - create sub item&lt;br /&gt;&lt;b&gt;&amp;lt;tab&amp;gt;&lt;/b&gt; - (insert or normal) create sub-item&lt;br /&gt;&lt;b&gt;,u&lt;/b&gt; - create super item&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Marking Items&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;:Lmark &amp;lt;mark&amp;gt;&lt;/b&gt; - (normal or visual line) mark item(s) with &amp;lt;mark&amp;gt;&lt;br /&gt;&lt;b&gt;,p&lt;/b&gt; - mark item with '=' (in progress)&lt;br /&gt;&lt;b&gt;,x&lt;/b&gt; - mark item with 'x' (completed)&lt;br /&gt;&lt;b&gt;,o&lt;/b&gt; - mark item with 'o'&lt;br /&gt;&lt;b&gt;,?&lt;/b&gt; - mark item with '?'&lt;br /&gt;&lt;b&gt;,-&lt;/b&gt; - mark item with '-' (default, incomplete)&lt;br /&gt;&lt;b&gt;,N&lt;/b&gt; - set priority as N, where N is 1-5&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tagging Items&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;:Ltag &amp;lt;tag&amp;gt; [tag ...]&lt;/b&gt; - (normal or visual line) add tag(s) to line(s) (has auto complete)&lt;br /&gt;&lt;b&gt;:Ltagr &amp;lt;tag&amp;gt; [tag ...]&lt;/b&gt; - (normal or visual line) remove tag(s) from line(s) (has auto complete)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Searching For Items&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;:Lsearch mark &amp;lt;mark&amp;gt;&lt;/b&gt; - find all items with &amp;lt;mark&amp;gt; (e.g.: =, 1, -, etc.) using location list&lt;br /&gt;         &lt;b&gt;tag &amp;lt;tag&amp;gt;&lt;/b&gt; - find all items with &amp;lt;tag&amp;gt; using location list&lt;br /&gt;         &lt;b&gt;due [date]&lt;/b&gt; - find all items due on [date]. Today is the default.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Setting Due Dates&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;:Ldue [date]&lt;/b&gt; - (normal or visual line) set due date. Today is the default.&lt;br /&gt;&lt;b&gt;:Lduer&lt;/b&gt; - (normal or visual line) remove due date&lt;br /&gt;Due dates are in the format YY-MM-DD or any of: yesterday, today, tomorrow,&lt;br /&gt;                                                N day[s], N week[s] (where N is a number)&lt;br /&gt;                                                N:M day[s], N:M week[s] (where N and M are numbers and N &amp;lt;= M)&lt;br /&gt;E.g.: To see all items due this week or next week: &lt;b&gt;:Lsearch due 0:1 week&lt;/b&gt;&lt;br /&gt;      To see all items due next week: &lt;b&gt;:Lsearch due 1:1 week&lt;/b&gt;&lt;br /&gt;      To see all items due last, this, or next weeks: &lt;b&gt;:Lsearch due -1:1 week&lt;/b&gt;&lt;br /&gt;      To see all items due tomorrow or the next day: &lt;b&gt;:Lsearch due 1:2 day&lt;/b&gt;&lt;br /&gt;      To make an item due end of next week: &lt;b&gt;:Ldue 1:1 week&lt;/b&gt;&lt;br /&gt;      To make an item due one week from today: &lt;b&gt;:Ldue 1 week&lt;/b&gt;&lt;br /&gt;      To see all items due in exactly four days: &lt;b&gt;:Lsearch due 4 days&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Sorting Items&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;,r&lt;/b&gt; - (visual line) sort highlighted items &lt;br /&gt;&lt;b&gt;,r&lt;/b&gt; - (normal) sort entire file&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Et Cetera&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;,t&lt;/b&gt; - add/update last-modified timestamp on item&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Configuration&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;listFile_timestamp = 0&lt;/b&gt;&lt;br /&gt;Should timestamps be added to each item by default? Set to &lt;b&gt;1&lt;/b&gt; to show a timestamp by default.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;listFile_indent = 4&lt;/b&gt;&lt;br /&gt;Standard indent for nesting.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;listFile_ranks = ['=','1','2','3','4','5','o','-','?','x']&lt;/b&gt;&lt;br /&gt;Rank of marks for sorting. Note that your own, custom marks can be added to this list and used with the &lt;b&gt;:Lmark&lt;/b&gt; command above.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;listFile_mark = '-'&lt;/b&gt;&lt;br /&gt;Default mark for new items.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;listFile_dateFormat = '%y-%m-%d'&lt;/b&gt;&lt;br /&gt;Default format for due dates.&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Advanced Configuration&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Here's an example of something you may put in your .vimrc file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;" add new "!" mark to ranking&lt;br /&gt;let g:listFile_ranks = ['=','1','2','3','4','5','!','o','-','?','x']&lt;br /&gt;&lt;br /&gt;" open local projects list file&lt;br /&gt;nmap &amp;lt;Leader&amp;gt;l :60vsplit ~/projects.list&lt;CR&gt;&lt;br /&gt;&lt;br /&gt;" add my mappings when a list file is loaded&lt;br /&gt;autocmd BufNewFile,BufRead *.list call MyListFileStuff()&lt;br /&gt;fun! MyListFileStuff()&lt;br /&gt;  " add "!" mark for normal and visual modes&lt;br /&gt;  nmap &amp;lt;buffer&amp;gt; ,! :Lmark !&amp;lt;CR&amp;gt;&lt;br /&gt;  vmap &amp;lt;buffer&amp;gt; ,! :Lmark !&amp;lt;CR&amp;gt;&lt;br /&gt;&lt;br /&gt;  " tag lines with "quick" tag&lt;br /&gt;  nmap &amp;lt;buffer&amp;gt; ,tq :Ltag quick&amp;lt;CR&amp;gt;&lt;br /&gt;  vmap &amp;lt;buffer&amp;gt; ,tq :Ltag quick&amp;lt;CR&amp;gt;&lt;br /&gt;&lt;br /&gt;  " find all lines tagged as "quick"&lt;br /&gt;  nmap &amp;lt;buffer&amp;gt; ,sq :Lsearch tag quick&amp;lt;CR&amp;gt;&lt;br /&gt;&lt;br /&gt;  " set an item as due today&lt;br /&gt;  nmap &amp;lt;buffer&amp;gt; ,dt :Ldue today&lt;br /&gt;&lt;br /&gt;  " find all items due this week&lt;br /&gt;  nmap &amp;lt;buffer&amp;gt; ,sw :Lsearch due 0:0 weeks&lt;br /&gt;endfunction&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In this example, I've added a new mark for me to use: "!". I've also added it to the mark ranks so that sorting will put "!" items in their proper order. I created a shortcut ("\l") to open my projects.list file in a narrow sidebar in vim. I added some things to help me manage items that are quick tasks---a mapping to easily tag items as "quick" and another to search for them. I also added a way to set a due date of today and to find all items due this week.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If you'd like this simple tool in your belt, &lt;a href="http://www.vim.org/scripts/script.php?script_id=3368"&gt;go get it&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-1801257039929218417?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/1801257039929218417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=1801257039929218417' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/1801257039929218417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/1801257039929218417'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2010/12/list-file-plugin-for-vim.html' title='List File Plugin for Vim'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-4169320207722427678</id><published>2010-09-02T16:47:00.000-07:00</published><updated>2010-09-02T16:58:44.876-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='taglist'/><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><category scheme='http://www.blogger.com/atom/ns#' term='vim plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Vim, PHP, TagList, and Ctags</title><content type='html'>I spent a good bit of time figuring out how to get vim to play nice with the spectacular &lt;a href="http://www.vim.org/scripts/script.php?script_id=273"&gt;TagList&lt;/a&gt; plugin, Ctags and PHP, so I've decided to sum up everything I did to get it working perfectly for the next person who comes along.&lt;br /&gt;&lt;br /&gt;Installing TagList itself is simple---just follow the instructions, standard to installing any vim plugin. However, getting &lt;a href="http://ctags.sourceforge.net/"&gt;Exuberant Ctags&lt;/a&gt;, which is TagList's workhorse, to work properly with PHP is another matter. Ctags doesn't understand PHP very well. It picks up on variables (which is sorta useless in PHP, anyway) but not functions whose definitions specify access ("public", "protected", etc.), interfaces or abstract classes. How useless!&lt;br /&gt;&lt;br /&gt;Creating a useful Ctags file for PHP is not difficult, thanks to the info at &lt;a href="http://weierophinney.net/matthew/archives/134-exuberant-ctags-with-PHP-in-Vim.html"&gt;these&lt;/a&gt; &lt;a href="http://nerdsdiscoveries.com/?p=21"&gt;two&lt;/a&gt; resources. Just use these flags:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;ctags -R --exclude=.svn --tag-relative=yes --PHP-kinds=+cf-v --regex-PHP='/abstract\s+class\s+([^ ]+)/\1/c/' --regex-PHP='/interface\s+([^ ]+)/\1/c/' --regex-PHP='/(public\s+|static\s+|abstract\s+|protected\s+|private\s+)function\s+\&amp;?\s*([^ (]+)/\2/f/' lib/&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Notice that I exclude variables via the --PHP-kinds option. This isn't C, tyvm.&lt;br /&gt;&lt;br /&gt;What I didn't understand, at first, is that TagList calls Ctags every single time it's opened so that it generates an up-to-date temporary tags file. This makes perfect sense, but when Ctags is called by TagList, it doesn't have all of our required command-line options. Enter the Ctags config file. Put the following in your ~/.ctags file:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;-R&lt;br /&gt;--exclude=.svn&lt;br /&gt;--tag-relative=yes&lt;br /&gt;--PHP-kinds=+cf-v&lt;br /&gt;--regex-PHP=/abstract\s+class\s+([^ ]+)/\1/c/&lt;br /&gt;--regex-PHP=/interface\s+([^ ]+)/\1/c/&lt;br /&gt;--regex-PHP=/(public\s+|static\s+|abstract\s+|protected\s+|private\s+)function\s+\&amp;?\s*([^ (]+)/\2/f/&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And, amazingly, TagList becomes useful for PHP development. The last step is to tweak TagList to work in a way that's most convenient for you. Personally, I like these options:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;" set the names of flags&lt;br /&gt;let tlist_php_settings = 'php;c:class;f:function;d:constant'&lt;br /&gt;" close all folds except for current file&lt;br /&gt;let Tlist_File_Fold_Auto_Close = 1&lt;br /&gt;" make tlist pane active when opened&lt;br /&gt;let Tlist_GainFocus_On_ToggleOpen = 1&lt;br /&gt;" width of window&lt;br /&gt;let Tlist_WinWidth = 40&lt;br /&gt;" close tlist when a selection is made&lt;br /&gt;let Tlist_Close_On_Select = 1&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;TagList has a ton of &lt;a href="http://vim-taglist.sourceforge.net/manual.html#taglist-options"&gt;options&lt;/a&gt;, so check them out. You may also be interested in my most up-to-date &lt;a href="http://github.com/lucasoman/Conf/blob/master/.ctags"&gt;.ctags&lt;/a&gt; and &lt;a href="http://github.com/lucasoman/Conf/blob/master/.vimrc"&gt;.vimrc&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-4169320207722427678?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/4169320207722427678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=4169320207722427678' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/4169320207722427678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/4169320207722427678'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2010/09/vim-php-taglist-and-ctags.html' title='Vim, PHP, TagList, and Ctags'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-5495817266576222402</id><published>2010-07-30T12:52:00.000-07:00</published><updated>2010-07-30T13:02:20.246-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='self-improvement'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='attitude'/><title type='text'>My Health As a Programmer</title><content type='html'>People often separate general health into categories--physical, emotional, mental, congenital, hexagonal, Portugal. Although some degree of health in one area or another is possible while ignoring the others, I firmly believe each category is dependent on the others.&lt;br /&gt; &lt;br /&gt;Good programmers are proud of their capacity for resolving issues and breaking down problems into their salient parts. Quips about co-workers aside, mental and intellectual health are crucial, and I'll bring that up again later. But that's a very small part of a very large picture.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Physical&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I had an undiagnosed medical condition for a couple years that affected my energy, motivation and self-image. I didn't have enough energy to keep a routine of physical exercise. On some days, I struggled to stay awake. Focus was hard to come by.&lt;br /&gt; &lt;br /&gt;After a successful diagnosis, proper medication helped tremendously. I still felt, though, that my work performance was suffering. I began exercising.&lt;br /&gt; &lt;br /&gt;A note about exercise: I hear people say "Oh, I'm not the exercising type." These are always people who have never had an exercise routine before. Anyone I've ever met who has had a routine and fell out of it (for whatever reason) has spoken fondly of the days when they had a routine and of how good they felt--not just physically, but about themselves. So don't give me that "not the exercising type" crap. Unless you have a doctor's note, get out there, even if you have to start small, like, walking-two-blocks small. You will not regret it.&lt;br /&gt; &lt;br /&gt;Anyway, so I started exercising. And it was awful. I was exhausted. Panting, tired, and the next day, painfully sore. But after a couple weeks, I started to really look forward to my exercise. Then I started setting goals. Then I started running barefoot. Now I'm six seconds away from beating my best-ever mile time (I ran my best mile in eighth grade).&lt;br /&gt; &lt;br /&gt;More importantly, I feel I'm more productive than ever at work. I've started exercising in the morning before I go into the office, which means I'm alert and ready long before I get there. My body is fully awake, my blood is pumping and my brain is active. I walk into the office with a plan, confident and proud of myself for sticking to my routine.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Family&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I also try to resolve issues at home. As a married man, I have to occasionally resolve conflicts, of course, with my wife. And bills have to be paid, the car has to be detailed and the trash has to be taken out. I like to keep up with these things, not letting them pile up, go unresolved, or otherwise weigh on my mind. This prevents me from being distracted during the day by stress.&lt;br /&gt; &lt;br /&gt;A note about motivations: Of course, I don't do all of these things to be a better programmer. I do them to have a better life in general. I have noted, however, that they have a huge effect on my performance at work and as a programmer.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Intellectual&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I've also started caring for my intellectual health. I've had an addiction to certain web sites that waste my time. Considering the &lt;a href="http://en.wikipedia.org/wiki/Garbage_In,_Garbage_Out"&gt;GIGO principle&lt;/a&gt;, I shouldn't be reading them, anyway.&lt;br /&gt; &lt;br /&gt;I've noted how I used to have far more original ideas years ago. In fact, I used to deliberately avoid reading certain things because I thought they'd spoil my creativity by filling my head with other people's ideas. At some point, I started questioning that theory and started reading obsessively. Now I'm finding that a balance between these is best. Say no to &lt;a href="http://news.ycombinator.com/"&gt;Hacker News&lt;/a&gt;. Say yes to &lt;a href="http://www.schneier.com/"&gt;Schneier on Security&lt;/a&gt; and &lt;a href="http://blogs.msdn.com/b/oldnewthing/"&gt;The Old New Thing&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Attitude&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;One of the most effective things I've done to be a healthy programmer is made an effort to be more humble:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Programming is hard.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Neither I nor anyone else is perfect.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There exists someone who is smarter than I am.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There exists a programmer who could do my project better.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I will never be done improving my craft.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I find that, when I remember these things, I am more open to advice, willing to accept constructive criticism, and curious to learn more, and I feel like I get along with my coworkers better. If I'm willing to listen to what others around me have to say, their knowledge is added to my knowledge. Imagine taking the knowledge from five, ten, fifty good programmers and sticking it in your own head. How can that be a bad thing?&lt;br /&gt; &lt;br /&gt;I think a lot of this could be applied to any craft or profession. I, however, am only a programmer. I can say, from first-hand experience, that living and thinking this way has made me a better programmer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-5495817266576222402?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/5495817266576222402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=5495817266576222402' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/5495817266576222402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/5495817266576222402'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2010/07/my-health-as-programmer.html' title='My Health As a Programmer'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-1587933294856923483</id><published>2010-05-26T22:06:00.000-07:00</published><updated>2010-05-27T09:11:00.003-07:00</updated><title type='text'>What Facebook Needs: Respect, Not Settings</title><content type='html'>Unless you're like the Unibomber and live in a remote cabin, you're probably already bored of the security and privacy news surrounding Facebook.&lt;br /&gt;&lt;br /&gt;Since Facebook has realized that they're precariously close to losing millions of users over the bad press, they've made some changes to the privacy settings in an attempt to simplify them and rebuild the trust that has been lost.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What It's Really About&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;But it's not about settings. I appreciated their efforts to make the settings more granular some time ago. As I see it, there are actually two major hurdles for Facebook, neither related to the current set of privacy settings but to their lack of respect for users and their data:&lt;br /&gt;&lt;br /&gt;1) Obviously, settings should not be changed automatically for any reason, as Facebook has done with previous updates. Facebook should also show respect for its users' data by erring on the side of privacy. If an advertiser does not absolutely need a piece of data, it should not be shared, even if Facebook's privacy policy allows it.&lt;br /&gt;&lt;br /&gt;2) In addition, there is currently no easy way to close an account. You can only "deactivate" it--meaning that all of your data is still in Facebook's servers, linked to other users' accounts and ready for your inevitable (ahem) return to their social network, at which time your account can be conveniently reactivated.&lt;br /&gt;&lt;br /&gt;These are the ways that Facebook has betrayed the trust of its users. It's not about what settings can be configured. Facebook just needs to show some respect for the users and data that have made the social network successful.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Why Your Data Isn't Safe&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Recently, Mark Zuckerburg had a &lt;a href="http://www.readwriteweb.com/archives/the_half_truths_of_mark_zuckerberg.php"&gt;phone call for the press&lt;/a&gt; where he explained Facebook's mistakes, its goals, and the changes that are being made. The way I see it, there are only two possibilities here:&lt;br /&gt;&lt;br /&gt;1) Mark Zuckerberg is a liar. He doesn't want to really change the way Facebook does things because this will result in a loss of revenue. The revamped user settings are just a red herring.&lt;br /&gt;&lt;br /&gt;2) Mark Zuckerberg is a bothersome, idealist figurehead to those who actually run Facebook and must appease him. He may not have made these business decisions for the sake of revenue, but those responsible for increasing revenue convinced him that these offending choices were best for other reasons, e.g., that openness is the way of the future and a way for Facebook to continue changing the world. This would also suggest that Zuckerburg's lofty goals and hubris will be his downfall, like so many ancient Greek protagonists.&lt;br /&gt;&lt;br /&gt;Regardless of Zuckerburg's motives, your data is not safe with Facebook.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;How to Demonstrate Respect&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To turn this situation around, they need to demonstrate an unflagging respect for their users' personal data, even at the loss of revenue. This means not unnecessarily sharing data and not changing the way data is shared, even when settings are added or removed. This will take time. They will not be able to gain their users' trust as quickly as they lost it.&lt;br /&gt;&lt;br /&gt;They also need to provide a way to completely close an account without any caveats or persistent data. This would be a huge overhaul; it would require removing photos, updates, comments on other users' walls and posts. It would also require removing other users' actions relating to the cancelling user's account. This is a huge loss of data, but it is the only way to thoroughly remove an account from the system.&lt;br /&gt;&lt;br /&gt;Even if someone never cancels his account, he can take comfort in the fact that if, at any time, he is uncomfortable with Facebook's use of his data, it can be quickly and thoroughly wiped. This would not only decrease users' reluctance to share personal data on the site, it would also give the users an effective way to hold Facebook accountable for its decisions, as any bad decision could result in a permanent loss of valuable data.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-1587933294856923483?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/1587933294856923483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=1587933294856923483' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/1587933294856923483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/1587933294856923483'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2010/05/what-facebook-needs-respect-not.html' title='What Facebook Needs: Respect, Not Settings'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-661024541010921874</id><published>2010-01-19T07:28:00.001-08:00</published><updated>2010-01-19T17:14:05.579-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='moose'/><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='perl oasis'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Orlando Perl Workshop (OPW) / Perl Oasis</title><content type='html'>I had a really great time this weekend with all the guys at &lt;a href="http://perloasis.org"&gt;OPW&lt;/a&gt;! Although I'm obviously not a Perl guy, it was neat to see some of Perl's capabilities and some of the cool things this community is doing to improve web development. Some of these ideas could even be applied to PHP development. I'll have to give that more thought.&lt;br /&gt;&lt;br /&gt;I was also graciously invited to speak about &lt;a href="http://perloasis.org/opw2010/talk/2510"&gt;roles in PHP&lt;/a&gt; (since they were inspired by Perl's Moose::Roles module). I'll throw up another post with more details about the talk later.&lt;br /&gt;&lt;br /&gt;There were some great speakers; I really enjoyed the talks and getting to meet fellow programmers. &lt;a href="http://stevan-little.blogspot.com"&gt;Stevan Little&lt;/a&gt; gave a great talk on everything, particularly wiring up a new app with URL routing and Bread::Board. The colorful &lt;a href="http://www.shadowcat.co.uk/blog/matt-s-trout/"&gt;Matt Trout&lt;/a&gt; told an epic poem about database management, and &lt;a href="http://martian.org/marty/"&gt;Marty Pauley&lt;/a&gt; give an interesting talk on functional programming in Perl, which actually refreshed a lot of the basics I'd forgotten from school. &lt;a href="http://sartak.org/"&gt;Shawn Moore&lt;/a&gt; revealed even more geek than I thought possible in his talk about conquering NetHack with an artificially intelligent bot.&lt;br /&gt;&lt;br /&gt;Of course, there were more good things that happened there, but these are the highlights that came to mind. Many thanks to &lt;a href="http://chris.prather.org/"&gt;Chris Prather&lt;/a&gt; and his very organized and hard-working wife Jamie for their putting this shindig together. Well done! I look forward to next year. Also, I wanted to mention a particular &lt;a href="http://plackperl.org"&gt;Perl web server named Plack&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-661024541010921874?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/661024541010921874/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=661024541010921874' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/661024541010921874'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/661024541010921874'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2010/01/orlando-perl-workshop-opw-perl-oasis.html' title='Orlando Perl Workshop (OPW) / Perl Oasis'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-7984962607981031861</id><published>2009-10-08T18:08:00.000-07:00</published><updated>2009-10-08T18:26:12.973-07:00</updated><title type='text'>Nested Lists in Vim</title><content type='html'>A while back, I worked on a project called &lt;a href="http://lucasoman.blogspot.com/2008/05/simplist-launches.html"&gt;Simplist&lt;/a&gt;. It was a little web app for creating, managing and sharing infinitely nestable lists.&lt;br /&gt;&lt;br /&gt;It was really handy, and I was very proud of myself, until I realized that this is something you can do in Notepad with little effort and in vim with even less.&lt;br /&gt;&lt;br /&gt;So I created a short script to duplicate Simplist's basic features in vim:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lucasoman.com/images/vimlist.gif"/&gt;&lt;br /&gt;&lt;br /&gt;Notice that some items are marked as completed ("x") and some have priorities (numbers). The last item is actually a list itself with sub-items and is folded.&lt;br /&gt;&lt;br /&gt;With a few simple commands, it's pretty handy:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;,n - create a [n]ew list item&lt;br /&gt;,x - mark item as done ("x")&lt;br /&gt;,p - mark item as in [p]rogress ("=")&lt;br /&gt;,- - mark item as undone ("-")&lt;br /&gt;,N - mark item with priority N where N is 1-5&lt;br /&gt;,t - update item's [t]imestamp&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;If you combine these with a few vim keys:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt; - indent line (make it a sub-item of item above)&lt;br /&gt;&lt;&lt; - outdent line (make it a super-item)&lt;br /&gt;zo - open a folded list&lt;br /&gt;zc - fold an open list&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;you get a pretty decent way to manage lists. I use this all the time--every day at work and for personal lists.&lt;br /&gt;&lt;br /&gt;Here's the vim script. Just paste it into your .vimrc:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;if version &gt;= 700&lt;br /&gt; autocmd BufNewFile,BufRead *.list call ListFile()&lt;br /&gt; autocmd TabEnter *.list call ListFile()&lt;br /&gt;&lt;br /&gt; " 'install' list features&lt;br /&gt; function ListFile()&lt;br /&gt;  setlocal foldmethod=expr&lt;br /&gt;  setlocal foldexpr=ListFoldLevel(v:lnum)&lt;br /&gt;  setlocal shiftwidth=4&lt;br /&gt;  setlocal tabstop=4&lt;br /&gt;  setlocal foldtext=ListFoldLine(v:foldstart)&lt;br /&gt;  setlocal noshowmatch&lt;br /&gt;  setlocal cindent&lt;br /&gt;  " add [n]ew item below current&lt;br /&gt;  map &lt;buffer&gt; ,n o- &lt;C-R&gt;=ListTimestamp()&lt;CR&gt;&lt;ESC&gt;^la&lt;br /&gt;  " mark item as [x]&lt;br /&gt;  map &lt;buffer&gt; ,x mz^rxf[hdf]$a&lt;C-R&gt;=ListTimestamp()&lt;CR&gt;&lt;ESC&gt;`z&lt;br /&gt;  " mark item as [-]&lt;br /&gt;  map &lt;buffer&gt; ,- mz^r-f[hdf]$a&lt;C-R&gt;=ListTimestamp()&lt;CR&gt;&lt;ESC&gt;`z&lt;br /&gt;  " mark item as = (in [p]rogress)&lt;br /&gt;  map &lt;buffer&gt; ,p mz^r=f[hdf]$a&lt;C-R&gt;=ListTimestamp()&lt;CR&gt;&lt;ESC&gt;`z&lt;br /&gt;  " mark item with a rank&lt;br /&gt;  map &lt;buffer&gt; ,1 mz^r1f[hdf]$a&lt;C-R&gt;=ListTimestamp()&lt;CR&gt;&lt;ESC&gt;`z&lt;br /&gt;  map &lt;buffer&gt; ,2 mz^r2f[hdf]$a&lt;C-R&gt;=ListTimestamp()&lt;CR&gt;&lt;ESC&gt;`z&lt;br /&gt;  map &lt;buffer&gt; ,3 mz^r3f[hdf]$a&lt;C-R&gt;=ListTimestamp()&lt;CR&gt;&lt;ESC&gt;`z&lt;br /&gt;  map &lt;buffer&gt; ,4 mz^r4f[hdf]$a&lt;C-R&gt;=ListTimestamp()&lt;CR&gt;&lt;ESC&gt;`z&lt;br /&gt;  map &lt;buffer&gt; ,5 mz^r5f[hdf]$a&lt;C-R&gt;=ListTimestamp()&lt;CR&gt;&lt;ESC&gt;`z&lt;br /&gt;  " add/update [t]imestamp&lt;br /&gt;  map &lt;buffer&gt; ,t mz$a [&lt;ESC&gt;^f[hd$a&lt;C-R&gt;=ListTimestamp()&lt;CR&gt;&lt;ESC&gt;`z&lt;br /&gt; endfunction&lt;br /&gt;&lt;br /&gt; " return properly formatted timestamp&lt;br /&gt; function ListTimestamp()&lt;br /&gt;  return ' ['.strftime('%Y-%m-%d %T').']'&lt;br /&gt; endfunction&lt;br /&gt;&lt;br /&gt; " return fold line format&lt;br /&gt; function ListFoldLine(linenum)&lt;br /&gt;  let s:count = 1&lt;br /&gt;  let s:spaces = ''&lt;br /&gt;  while s:count &lt;= &amp;shiftwidth&lt;br /&gt;   let s:spaces = s:spaces.' '&lt;br /&gt;   let s:count = s:count + 1&lt;br /&gt;  endwhile&lt;br /&gt;  return substitute(getline(a:linenum),"\t",s:spaces,'g')&lt;br /&gt; endfunction&lt;br /&gt;&lt;br /&gt; " foldexpr function&lt;br /&gt; function ListFoldLevel(linenum)&lt;br /&gt;  let s:prefix = ''&lt;br /&gt;  let s:myline = getline(a:linenum)&lt;br /&gt;  let s:nextline = getline(a:linenum+1)&lt;br /&gt;  let s:mynumtabs = match(s:myline,"[^\t]",0)&lt;br /&gt;  let s:nextnumtabs = match(s:nextline,"[^\t]",0)&lt;br /&gt;  if s:nextnumtabs &gt; s:mynumtabs " if this item has sub-items&lt;br /&gt;   let s:level = s:nextnumtabs&lt;br /&gt;  else " next item is either same or higher level&lt;br /&gt;   let s:level = s:mynumtabs&lt;br /&gt;   if s:nextnumtabs &lt; s:mynumtabs " if next item has higher level, close this fold&lt;br /&gt;    let s:prefix = '&lt;'&lt;br /&gt;    let s:level = s:nextnumtabs+1&lt;br /&gt;   end&lt;br /&gt;  endif&lt;br /&gt;  if a:linenum &gt; 1&lt;br /&gt;   s:pline = getline(a:linenum-1)&lt;br /&gt;   s:pnumtabs = match(s:pline,"[^\t]",0)&lt;br /&gt;   if s:level &lt; s:pnumtabs&lt;br /&gt;   " if this is higher level than prev, start a new fold&lt;br /&gt;    let s:prefix = '&gt;'&lt;br /&gt;   endif&lt;br /&gt;  endif&lt;br /&gt;  return s:prefix.s:level&lt;br /&gt; endfunction&lt;br /&gt;endif&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-7984962607981031861?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/7984962607981031861/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=7984962607981031861' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/7984962607981031861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/7984962607981031861'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2009/10/nested-lists-in-vim.html' title='Nested Lists in Vim'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-8163912313717998831</id><published>2009-09-24T06:54:00.000-07:00</published><updated>2009-09-25T07:48:48.625-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='moose'/><category scheme='http://www.blogger.com/atom/ns#' term='closures'/><category scheme='http://www.blogger.com/atom/ns#' term='roles'/><category scheme='http://www.blogger.com/atom/ns#' term='lambdas'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='traits'/><category scheme='http://www.blogger.com/atom/ns#' term='oop'/><title type='text'>A Different Way: Traits in PHP</title><content type='html'>For a while now I've had &lt;a href="http://tozt.net/"&gt;some&lt;/a&gt; &lt;a href="http://sartak.org/"&gt;friends&lt;/a&gt; who are Perl fanatics. Perl's OOP support is minimal, which makes it extremely flexible and powerful. One module that takes advantage of that is &lt;a href="http://search.cpan.org/dist/Moose/lib/Moose.pm"&gt;Moose&lt;/a&gt;, which provides an easy-to-use framework for OO design. Basically, Moose gives Perl the OO capabilities that most languages take for granted, if that's what you're looking for. In addtion to such capabilities are a few that most languages don't have, such as &lt;a href="http://search.cpan.org/dist/Moose/lib/Moose/Role.pm"&gt;Roles&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Moose Roles are:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;like multiple-inheritance, except that a Role handles method and attribute collisions&lt;/li&gt;&lt;br /&gt;&lt;li&gt;like interfaces, except that a Role is more than a contract--it's fully-working code&lt;/li&gt;&lt;br /&gt;&lt;li&gt;like mixins, except that Roles can have requirements for being applied to an object&lt;/li&gt;&lt;br /&gt;&lt;li&gt;like duck typing, except that a Role is a named collection of methods and attributes, so the state of &lt;i&gt;being&lt;/i&gt; something is more concrete&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;These ideas really inspired me, but I'm stuck with PHP. Fortunately, PHP 5.3 comes with a &lt;a href="http://lucasoman.blogspot.com/2009/08/php-is-dead.html"&gt;crippled implementation&lt;/a&gt; of lambdas and closures, which gives just enough functionality to implement something similar to Moose::Role. I also read these &lt;a href="http://lucasoman.com/schaerli-phd.pdf"&gt;two&lt;/a&gt; &lt;a href="http://lucasoman.com/stateful-traits.pdf"&gt;papers&lt;/a&gt;, which gave me some solid ideas, although I didn't read them as gospel.&lt;br /&gt;&lt;br /&gt;You can &lt;a href="http://github.com/lucasoman/TObj"&gt;get the code&lt;/a&gt; from GitHub.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;How It Works&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First, you have to define some traits:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;TObj::Trait('Sortable',&lt;br /&gt;    'sort',function($obj) {&lt;br /&gt;      ...&lt;br /&gt;    },&lt;br /&gt;    'sortAttribute','',&lt;br /&gt;    'setSortAttribute',function($obj,$attrName) {&lt;br /&gt;      $obj-&gt;sortAttribute = $attrName;&lt;br /&gt;    }&lt;br /&gt;  );&lt;br /&gt;&lt;br /&gt;TObj::Trait('Categorizable',&lt;br /&gt;    'category','',&lt;br /&gt;    'getCategory',function($obj) {&lt;br /&gt;      return $obj-&gt;category;&lt;br /&gt;    }&lt;br /&gt;  );&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Now we create some other class:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;class Something extends TObj {&lt;br /&gt;  private $foo;&lt;br /&gt;&lt;br /&gt;  public function __construct($baz) {&lt;br /&gt;    $this-&gt;foo = $baz;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;And now we apply our traits to Something object:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;$something = new Something('lolwut');&lt;br /&gt;$something-&gt;apply(Sortable);&lt;br /&gt;$something-&gt;apply(Categorizable);&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;You can now call those traits' methods as if they belonged to the Something class! Note that an object's methods and attributes will override any methods or attributes in the traits, even within the context of the calling trait (see part about aliasing below). This makes traits as flexible as extended super classes.&lt;br /&gt;&lt;br /&gt;It's a fairly simple concept, though there are some hang-ups when you get down to the nitty-gritty. For instance, what if two traits have methods or attributes with the same name? Well, you can simply exclude it from one of the traits:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;$something-&gt;apply(Categorizable,array(&lt;br /&gt;      except=&gt;array('getCategory')&lt;br /&gt;      )&lt;br /&gt;    );&lt;br /&gt;&lt;/blockquote&gt;&lt;/pre&gt;&lt;br /&gt;or alias it:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;$something-&gt;apply(Categorizable,array(&lt;br /&gt;      alias=&gt;array('getCategory'=&gt;'getCategoryName')&lt;br /&gt;      )&lt;br /&gt;    );&lt;br /&gt;&lt;/blockquote&gt;&lt;/pre&gt;&lt;br /&gt;But what if &lt;code&gt;getCategory()&lt;/code&gt; is used all over in other Categorizable methods? Now they'll actually be calling the &lt;code&gt;getCategory()&lt;/code&gt; from the other trait, right?&lt;br /&gt;&lt;br /&gt;Wrong! Since TObj has to handle all trait method dispatch, it does a little magic. To resolve method and attribute names, it checks for the original name ("getCategory") within the trait whose method made the call, then moves outside and checks aliased and unaliased names. It works the same way for trait attributes, too. This prevents aliases from breaking trait code.&lt;br /&gt;&lt;br /&gt;To know if some random object you've received has a certain trait:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;if ($something-&gt;applied(Categorizable)) {&lt;br /&gt;  echo 'It is categorizable!';&lt;br /&gt;} else {&lt;br /&gt;  echo 'This object marches to the beat of a different drummer.';&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;To require that the object have a certain trait method:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;TObj::Trait('TraitThatRequires',&lt;br /&gt;    required,array('aRequiredMethod'),&lt;br /&gt;    ...&lt;br /&gt;    );&lt;br /&gt;&lt;br /&gt;$something-&gt;apply(TraitThatRequires);&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;The last line would throw an exception because &lt;code&gt;aRequiredMethod()&lt;/code&gt; is not yet implemented by a trait applied to &lt;code&gt;$something&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;TObj traits do other things, and you're welcome to check out its &lt;a href="http://github.com/lucasoman/TObj/blob/master/test/tobjtest.php"&gt;unit tests&lt;/a&gt; to see its full functionality.&lt;br /&gt;&lt;br /&gt;This project is very new, and it has little or no documentation and I'm sure it has bugs (though all tests pass, of course). If you're interested in the project, feel free to pull it and contact me if you have any interesting ideas.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-8163912313717998831?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/8163912313717998831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=8163912313717998831' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/8163912313717998831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/8163912313717998831'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2009/09/different-way-traits-in-php.html' title='A Different Way: Traits in PHP'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-5684506997899258197</id><published>2009-09-02T08:21:00.000-07:00</published><updated>2009-09-02T08:38:48.060-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='vim tip'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='vim script'/><title type='text'>Vim Tip: Debug Path</title><content type='html'>I work for a company whose code is in thousands of files in hundreds of directories. We have multiple VCS repositories. Function libraries, class files, HTML, XSL--a convoluted maze to navigate, especially when trying to debug something.&lt;br /&gt;&lt;br /&gt;Whenever I'm debugging, I may follow the rabbit hole from function to function, through various libraries and classes, only to find that I'd taken a wrong turn somewhere and come to a dead end. I then either have to sift through the multiple tabs I've opened to find where I should pick up the search again, or, if I've been closing files along the way, I have to start the search from the beginning. This is annoying and time-consuming.&lt;br /&gt;&lt;br /&gt;I decided to write a simple vim script to save my cursor's current line, along with the file name and line number, to a file in my home directory. I can then continually save lines as I follow a path through the code. If, at any time, I hit a dead end, I simply go through this "trace" file and find where to pick up the search again. Combined with vim's &lt;a href="http://vimdoc.sourceforge.net/htmldoc/editing.html#gf"&gt;"gf" command&lt;/a&gt;, this file is very useful!&lt;br /&gt;&lt;br /&gt;Here's the code you can add to your own .vimrc file:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;nmap &amp;lt;F8&gt; &amp;lt;ESC&gt;:call WriteTrace()&amp;lt;CR&gt;&lt;br /&gt;&lt;br /&gt;function WriteTrace()&lt;br /&gt;  let lineNum = line('.')&lt;br /&gt;  let lineFile = bufname('%')&lt;br /&gt;  let lineVal = getline(lineNum)&lt;br /&gt;                        &lt;br /&gt;  let allLines = readfile("$HOME/trace.txt")&lt;br /&gt;  let allLines = add(allLines,lineFile.":".lineNum)&lt;br /&gt;  let allLines = add(allLines,lineVal)&lt;br /&gt;  let allLines = add(allLines,"")&lt;br /&gt;                        &lt;br /&gt;  call writefile(allLines,"$HOME/trace.txt")&lt;br /&gt;endfunction             &lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;In the code above, F8 will save the line under the cursor, but the nmap can be changed to map to any key, of course.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-5684506997899258197?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/5684506997899258197/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=5684506997899258197' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/5684506997899258197'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/5684506997899258197'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2009/09/vim-tip-debug-path.html' title='Vim Tip: Debug Path'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-7596265295563844943</id><published>2009-08-26T08:13:00.000-07:00</published><updated>2009-08-26T09:08:11.852-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='moose'/><category scheme='http://www.blogger.com/atom/ns#' term='lambda'/><category scheme='http://www.blogger.com/atom/ns#' term='closure'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><category scheme='http://www.blogger.com/atom/ns#' term='role'/><title type='text'>PHP Is a Husk</title><content type='html'>I thought there was hope. I really did. Maybe it was naivete, or simply denial because it's the language I use day-in and day-out. But there is no hope for PHP.&lt;br /&gt;&lt;br /&gt;No, I don't think the project will die; I'm sure it will continue to grow. It's no new revelation that the language has a lot of problems. Even when we finally got prepared statements with the MySQLi interface, there were a lot of problems with the way it worked such that writing a thorough DBAL for it was nearly impossible. But lately I've been pretty positive about the direction PHP has been going--with 5.3 came namespaces, lambdas and closures, among other things.&lt;br /&gt;&lt;br /&gt;And then I started playing around with these new features. The namespace delimiter is odd, but big deal. Lambdas and closures, though, are broken and useless for anything but the most basic applications.&lt;br /&gt;&lt;br /&gt;For instance, I've been messing around a little with Perl and an awesome OO extension for it called Moose. Moose has a ton of features, but one particular feature is Roles. Roles, as defined by the Moose devs, are somewhere between abstract classes and mixins, with multiple inheritance--but more flexible and easier to develop with. You know, pure awesome.&lt;br /&gt;&lt;br /&gt;So I set out to try my hand at making something similar to Roles with PHP's new lambdas and closures. That should give me all the power I need to hack something together, right? This is the best I could come up with:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;// class used to enable roles for other classes&lt;br /&gt;class Role {&lt;br /&gt; // used by the class the role is applied to&lt;br /&gt; // must be in that class's __call() method&lt;br /&gt; static public function call($obj,$funcName,$args) {&lt;br /&gt;  $func = $obj-&gt;$funcName;&lt;br /&gt;  array_unshift($args,$obj);&lt;br /&gt;  call_user_func_array($func,$args);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; // applies a role to the object.&lt;br /&gt; // for best results, use in the constructor.&lt;br /&gt; static public function apply($obj,$name) {&lt;br /&gt;  $mixin = new $name();&lt;br /&gt;  foreach ($mixin as $funcName=&gt;$func) {&lt;br /&gt;   $obj-&gt;$funcName = $func;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// my example role that I want to apply to other classes&lt;br /&gt;class MyRole {&lt;br /&gt; public $yar = 'role\'s test string';&lt;br /&gt;&lt;br /&gt; public function __construct() {&lt;br /&gt;&lt;br /&gt;  // prints a string, including a var from the object&lt;br /&gt;  // that is using this role.&lt;br /&gt;  $this-&gt;roleFunction = function($obj) {&lt;br /&gt;   print('This is a role function! '.$obj-&gt;blah."\n");&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  // prints the object that is using this role.&lt;br /&gt;  // this will also show the methods it has received&lt;br /&gt;  // from the role.&lt;br /&gt;  $this-&gt;printr = function($obj) {&lt;br /&gt;   print_r($obj);&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  // a function that gets overridden by Test class's&lt;br /&gt;  // native method by the same name.&lt;br /&gt;  $this-&gt;overriddenFunction = function($obj) {&lt;br /&gt;   print("You'll never see this.\n");&lt;br /&gt;  };&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// my class that I want to apply a role to. Note the&lt;br /&gt;// call to Role::apply() in the constructor.&lt;br /&gt;class Test {&lt;br /&gt; public $blah = 'test string';&lt;br /&gt;&lt;br /&gt; public function __construct() {&lt;br /&gt;  Role::apply($this,'MyRole');&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; // overrides MyRole's version, as it should&lt;br /&gt; public function overriddenFunction() {&lt;br /&gt;  print("I beat the role's method!\n");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; // necessary mess to get $object-&gt;roleMethod() to work&lt;br /&gt; public function __call($funcName,$args) {&lt;br /&gt;  Role::call($this,$funcName,$args);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;$test = new Test();&lt;br /&gt;$test-&gt;printr();&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The above will print_r the Test object, as it should, including the public attributes that hold the lambdas from the Role. Neat, right?&lt;br /&gt;&lt;br /&gt;Well, not really. This is so messy, so impractical. The limitations are ridiculous. For example, the following works:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;$someFunc = function() { print('Hello, blog!'); }&lt;br /&gt;$someFunc();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;But this doesn't:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;$obj = new StdClass();&lt;br /&gt;$obj-&gt;someFunc = function() { print('Hello, blog!'); }&lt;br /&gt;$obj-&gt;someFunc();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Whoops! That object method doesn't exist! And what about this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;class MyNeatoClass {&lt;br /&gt; public $someVar = 'Hello, blog!';&lt;br /&gt;&lt;br /&gt; public function __construct() {&lt;br /&gt;  $this-&gt;someMethod = function() { print($this-&gt;someVar); }&lt;br /&gt;  $this-&gt;otherMethod = function() use ($this) { print($this-&gt;someVar); }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Neither the lambda nor the closure works. &lt;code&gt;$this&lt;/code&gt; cannot be used in lamdas or closures. So, when it comes to using these new features with objects, they're quite nearly completely useless. These are just a couple examples of what makes my above attempt at creating Roles broken and useless.&lt;br /&gt;&lt;br /&gt;And as I mentioned before, this isn't the first time the PHP devs have botched up new features. They seem to be committed to adding "big boy" features, which should be a good thing, but not to building them &lt;i&gt;correctly&lt;/i&gt;. This leaves developers--who actually know what these features are and how to use them in other languages--frustrated and disgusted. For what audience are they coding? Do they think Grandpa Jo's 14-year-old nephew, who "builds websites", is going to use closures? No, real developers are going to &lt;i&gt;try&lt;/i&gt; to use them, and then give up, because they're useless.&lt;br /&gt;&lt;br /&gt;This trend is obviously going to continue. PHP had the opportunity to recover from past mistakes with recent new features. They have not learned a thing from past mistakes. PHP, you are dead to me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-7596265295563844943?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/7596265295563844943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=7596265295563844943' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/7596265295563844943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/7596265295563844943'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2009/08/php-is-dead.html' title='PHP Is a Husk'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-1523754347536424611</id><published>2009-08-23T17:20:00.000-07:00</published><updated>2009-08-23T17:51:35.416-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='_why'/><category scheme='http://www.blogger.com/atom/ns#' term='best-practice'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Chaos vs. Best Practice: Let Them Eat Cake</title><content type='html'>Even if you didn't know who _why was a couple weeks ago, most likely you've gathered enough since then to know that he was a very bright and artistic programmer (or a very bright and logical artist?) who inexplicably erased his digital presence.&lt;br /&gt;&lt;br /&gt;But enough about _why, as that dead horse has been and is currently being beaten to horse pudding. However, in looking through some of _why's archives (things are rarely &lt;i&gt;truly&lt;/i&gt; erased), I found this insightful and interesting &lt;a href="http://74.125.95.132/search?q=cache:http://hackety.org/2007/12/24/thisHackWasNotProperlyPlanned.html"&gt;blog post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;To sum up, _why feels like so called "best practices" are dead, boring, and stifling. Good, artistic and revolutionary code comes from chaos. Diagrams and TDD are simply scripted banality. I see his point, but I responded to his post thus:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Refusing to be stifled by best practices is one thing, and it’s not a new thing; to touch on the gagging cliches, everyone who’s ever caused a shift in perception or the field of possibilities--Beethoven, Lennon, Newton, DaVinci--questioned the “best practices” of their field in their day. But you mentioned yourself that we can’t possibly understand every avenue, every corner, of the systems we work with. Isn’t that why we have best practices? To invent the wheel without understanding friction coefficients is foolish. Even though you disagree with their choice, maybe your predecessors decided a sleigh made more sense because you didn’t realize that you live in North Dakota.&lt;br /&gt;&lt;br /&gt;Chaos is for after-hours. Chaos is a bunch of possibilities and fewer probabilities, with only one or two sure things. Once you’ve polished all that and have one sure thing in your hands, use it at your day job. At that point, it’s a best practice. Best practices have an origin. Chaos has a conclusion; to claim to subscribe to one and reject the other is short-sighted. To accept both as part of the process shows your passion and dedication to your field.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;To expand on this response, there are three types of programmers with respect to chaos and best practice:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Great programmers who can dabble in chaos, confident that a best practice will rise from the ashes of many smart--but failed--attempts. They then share and use these practices. These are the French elite of programmers.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Decent programmers who dabble in chaos but do not develop best practices. Such dabbling helps them to see the wisdom in best practices and use them wisely to make solid software. These are the French &lt;i&gt;bourgeoisie&lt;/i&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Programmers who shouldn't be programmers--the peasant farmers.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I appreciate _why's contributions to programming-as-art. And I really believe that he is brilliant enough to develop best practices from chaos. But &lt;b&gt;his advice to abandon best practice for chaos is saying to the rest of us, "Let them eat cake!"&lt;/b&gt; I'm sure his elitism is unintentional, and I certainly don't mean to villianize him by any means. But cake is a luxury and an artistic tribute to the beauty of food. Buildings are built by men eating meat and potatoes. Solid, robust and maintainable software is developed by programmers who use best practices.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-1523754347536424611?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/1523754347536424611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=1523754347536424611' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/1523754347536424611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/1523754347536424611'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2009/08/chaos-vs-best-practice-let-them-eat.html' title='Chaos vs. Best Practice: Let Them Eat Cake'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-5725565746959148418</id><published>2009-08-20T08:14:00.000-07:00</published><updated>2009-08-20T08:18:09.906-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim tip'/><title type='text'>Vim Tip: Session Management</title><content type='html'>Instead of having another gigantic essay "Vim Tips, part 2," I decided to just address interesting tid-bits of my .vimrc file one at a time. First up: sessions!&lt;br /&gt;&lt;br /&gt;Vim sessions are rudimentary but can easily be enhanced to make working on a project or multiple projects more convenient. I'm a big fan of tabs, so I'll often have several tabs open at a time. When I return to work the next day, I'd like to pick up where I left off as seamlessly as possible. Also, if I get pulled away on something else temporarily, I'd like to be able to save my current state, jump to the task at hand, and save its session, as well, in case I have to go back to it again after it's complete.&lt;br /&gt;&lt;br /&gt;To this end, I've put the following in my .vimrc file:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;nmap &amp;lt;F3&amp;gt; &amp;lt;ESC&amp;gt;:call LoadSession()&amp;lt;CR&amp;gt;&lt;br /&gt;" don't store any options in sessions&lt;br /&gt;if version &amp;gt;= 700&lt;br /&gt;  set sessionoptions=blank,buffers,curdir,tabpages,winpos,folds&lt;br /&gt;endif&lt;br /&gt;&lt;br /&gt;" automatically update session, if loaded&lt;br /&gt;let s:sessionloaded = 0&lt;br /&gt;function LoadSession()&lt;br /&gt;  source Session.vim&lt;br /&gt;  let s:sessionloaded = 1&lt;br /&gt;endfunction&lt;br /&gt;function SaveSession()&lt;br /&gt;  if s:sessionloaded == 1&lt;br /&gt;    mksession!&lt;br /&gt;  end&lt;br /&gt;endfunction&lt;br /&gt;autocmd VimLeave * call SaveSession()&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;To start a session in vim, use the following:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;:mksession&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;This will create a Session.vim file in your CWD. Now, next time you open vim, you can simply hit F3 and your session will be restored. Of course, this little snippet does more than just ":source Session.vim".&lt;br /&gt;&lt;br /&gt;When you hit F3, my script sets a flag that means we're currently using a saved session. When you quit vim, it will automatically re-save your session, so when you restore it next time (by hitting F3 again), vim will be returned to the exact state it was in when you quit. It's a very simple hack, but it saves a lot of time.&lt;br /&gt;&lt;br /&gt;One improvement I'd like to make is to be able, somehow, to select which session file to use. Suppose I have Project1.vim and Project2.vim ("Session.vim" is the default name, but isn't required). I'd like to be able to select which project I'm working on, and the appropriate session file would be loaded.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-5725565746959148418?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/5725565746959148418/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=5725565746959148418' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/5725565746959148418'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/5725565746959148418'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2009/08/vim-tip-session-management.html' title='Vim Tip: Session Management'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-7975944824303340962</id><published>2008-07-18T15:41:00.000-07:00</published><updated>2008-07-18T15:50:52.121-07:00</updated><title type='text'>Vim Tips, part 1</title><content type='html'>Because I'm such a fan of &lt;a href="http://vim.org"&gt;Vim&lt;/a&gt;, and because I have people asking me about it quite often (for tips, general questions, etc.), I figured I'd write a two-part article on the greatest text editor in the world.&lt;br /&gt;&lt;br /&gt;There are innumerable key combinations in Vim (well, they are in fact finite, but numerous), so I thought I'd describe the ones that I use most often and find the most useful:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Navigation&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;h, j, k and l - Move left, down, up, or right.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;gg and G - Move to the top or the bottom of the file.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;ctrl-u and ctrl-d - Move up or down one half-screen.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;w and b - Move forward or backward, one word at a time.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;fX, FX - Move the cursor over the next or previous occurrence of X in the current line, where X is any character.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;tX, TX - Move the cursor &lt;i&gt;to&lt;/i&gt; (not over!) the next or previous occurrence of X in the current line.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;0 and $ - Move the cursor to the beginning or end of the line.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;^ - Move the cursor to the first non-whitespace character of the line.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;/[string] and ?[string] - Searches forward or backward for [string].&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Tabs&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I love tabs. They're a new feature in Vim 7, and I instantly became dependent on them. Here are the commands for using them:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;:tabe [file] - Open a new tab editing [file]. Don't specify [file] to open a blank tab.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;gt and gT - Move to the next or the previous tab. If already at the last or first tab, wraps&lt;/li&gt; around.&lt;br /&gt;&lt;li&gt;Ngt - Move to the Nth tab.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;:q - Closes current tab.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;:qa - Closes all tabs and exits Vim.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Editing&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;d&lt;movement&gt; - Deletes in the direction of your movement. E.g.: d$ deletes to the end of the line, dw deletes to the end of the current word, dF[ deletes backward to and including the next '[' character.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;dd - Deletes current line.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;rX - Replaces current character with character X.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;~ - Toggles capitalization of current character. If in visual mode, toggles capitalization of highlighted block.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;s - Deletes current character and enters insert mode.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;i - Enters insert mode.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;a - Moves one character forward and enters insert mode.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Folding&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Folding is incredibly useful. It's great for getting large blocks of code out of your way in annoyingly long class files.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;zf[movement] - Folds lines in direction of [movement]. E.g.: zfG folds all lines from the current line to the end of the file. For [movement], you can also use &lt;a href="http://vimdoc.sourceforge.net/htmldoc/motion.html#text-objects"&gt;text objects&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;zf - In Visual mode, will fold the currently selected lines.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;zo - Open the fold under the cursor.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;zc - Close the fold the cursor is in.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Other Tips&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;:% - Replaced by current file. You'll see how this is useful below.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;:![command] - Execute [command] in the shell. E.g.: !php % - runs current PHP script, !ls - lists files in current directory, !php -l % - checks syntax of current PHP script, !cp % %.bak - creates a backup of current file.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;:nohl - Turns off highlighting, if it's on. Depending on your Vim's configuration, doing searches (with / and ?) highlight all matches. This will un-highlight all search matches.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;This may seem overwhelming if you're new to Vim, but it becomes second-nature as you get used to it. I did not have to check the Vim documentation at all to write this blog post. It is in fact possible to become accustomed to using so many (useful!) Vim commands. Of course, these aren't all I use, but just the most common.&lt;br /&gt;&lt;br /&gt;Next post, I'll discuss my .vimrc file, which contains tons of configuration that makes using Vim a lot easier for me. Perhaps you'll be able to use some of my settings to help you, too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-7975944824303340962?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/7975944824303340962/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=7975944824303340962' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/7975944824303340962'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/7975944824303340962'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2008/07/vim-tips-part-1.html' title='Vim Tips, part 1'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-4077690690955272010</id><published>2008-05-09T12:00:00.000-07:00</published><updated>2008-05-09T12:12:51.320-07:00</updated><title type='text'>Simplist Launches</title><content type='html'>So I've been working on a new project lately. I threw it out there for a few friends and on a social news site I frequent for suggestions. I got some great feedback and tweaked many things that made it easier to use and more powerful. The site is live and there are already many active accounts, though I'm still making feature additions and changes as I figure out what works and what doesn't. I'd &lt;b&gt;love&lt;/b&gt; to get some more feedback. Check it out:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://mysimplist.com"&gt;http://mysimplist.com&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-4077690690955272010?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/4077690690955272010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=4077690690955272010' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/4077690690955272010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/4077690690955272010'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2008/05/simplist-launches.html' title='Simplist Launches'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-4121751749432491885</id><published>2008-04-17T11:09:00.000-07:00</published><updated>2008-04-17T11:17:22.390-07:00</updated><title type='text'>Profiling by Command History</title><content type='html'>I read a &lt;a href="http://perlbuzz.com/mechanix/2008/04/what-commands-do-you-run.html"&gt;blog post&lt;/a&gt; about seeing what commands you use most in your shell. So I went ahead and executed the convoluted awk command to give me a pretty list:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;[lucas@sarpedon ~]$ history|awk '{a[$2]++} END{for(i in a){ \&lt;br /&gt;&gt; printf "%5d\t%s \n",a[i],i}}'|sort -rn|head&lt;br /&gt;  193   vim&lt;br /&gt;  143   ssh&lt;br /&gt;  129   ls&lt;br /&gt;  108   clear&lt;br /&gt;   91   cd&lt;br /&gt;   69   screen&lt;br /&gt;   65   ruby&lt;br /&gt;   34   time&lt;br /&gt;   22   scp&lt;br /&gt;   19   top&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And I got to thinking about how this can say a lot about how I use my computer and how I do my programming. First of all, it's obvious what text editor I use to code--Vim. Second, I obviously do a lot of my coding on other servers, since ssh is the second-ranked command. I use clear a lot because sometimes my CPU gets hot (Thunderbird has an annoying bug that causes it to top off my CPU for minutes at a time), which prints warning messages in my terminal window.&lt;br /&gt;&lt;br /&gt;Anyway, sort of a silly diversion, but interesting nonetheless. What commands do you use most often? What does it indicate about your habits?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-4121751749432491885?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/4121751749432491885/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=4121751749432491885' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/4121751749432491885'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/4121751749432491885'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2008/04/profiling-by-command-history.html' title='Profiling by Command History'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-6396372109283970248</id><published>2008-01-29T10:11:00.000-08:00</published><updated>2008-01-29T10:12:03.995-08:00</updated><title type='text'>Why Use OO design?</title><content type='html'>There are several paradigms for organizing and writing code for a web application. I hate to use the word "paradigm", though, since it's a two-bit word, and some common paradigms I've seen aren't worth twenty-five cents.&lt;br /&gt;&lt;br /&gt;Hopefully, if you're reading this article, you've already read the others, especially the one on &lt;a href="http://lucasoman.blogspot.com/2007/12/basics.html"&gt;The Basics&lt;/a&gt;. In that article, I give a list of important things to keep in mind when developing a web application. I give some superficial advice on how to adjust your design to meet these goals, but now I'm going to give you the best solution that exists at this moment, and it will probably remain so for a long time--the object-oriented (OO) paradigm.&lt;br /&gt;&lt;br /&gt;For many of you, especially those who have no formal training in software development, the subject of object-oriented programming (OOP) may automatically turn you off. I can promise that it's not as difficult as the old Java dinosaurs would like you to think (they're looking for job security, you know), and any effort you put into learning OO principles will be repaid ten-fold when you write your next app. Let's see why.&lt;br /&gt;&lt;br /&gt;Parallel to my Basics article, I'll cover each point and explain how OO design applies.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;OO design makes it easier to code additional functionality in the future.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Everything&lt;/i&gt; in a well-designed OO application will be modular and separated from code that does a different task. It also makes following the "black box" principle a breeze. Once you code something that works and has a well defined interface--like public functions (methods) and what those functions get and give back in return--you don't have to look at the internals again. In fact, the rest of your code that may use this "black box" doesn't care what the internals are, either.&lt;br /&gt;&lt;br /&gt;This also has the implication that you could code support for a certain feature into your application &lt;i&gt;before you even code the feature itself&lt;/i&gt;. This is a great way to force yourself to stick to the "black box" principle and sketch out well-defined interfaces before starting to code a module.&lt;br /&gt;&lt;br /&gt;- &lt;i&gt;OO design follows the DRY principle by default.&lt;/i&gt; The easiest and most obvious way to follow the DRY principle is to factor your code into functions. OOP forces you to do this, to an extent. At any rate, learning OO principles will also help you think in this direction, even if you're not forced by the paradigm to factor your code.&lt;br /&gt;&lt;br /&gt;- &lt;i&gt;OO design requires that you correctly identify relationships in your data.&lt;/i&gt; Yes, it's true--sometimes OO programmers get it wrong. It's easy to do. But if you're thinking about everything in terms of objects, sometimes real-world ones, then you'll have a better perspective for accurately seeing these relationships.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;OO design makes it easier to maintain your code.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;As I mentioned above, using OO design turns your application into a bunch of "black boxes". If you change something in a module, as long as you &lt;i&gt;leave the interface the same&lt;/i&gt;, no other code will be affected by your changes. This is a huge time saver. You don't have to lay awake in bed wondering if you've broken some completely unrelated part of your application.&lt;br /&gt;&lt;br /&gt;- &lt;i&gt;OO design helps you separate data from code.&lt;/i&gt; If your models are the only objects that are tightly related to your data, then most of your code will satisfy this requirement automatically.&lt;br /&gt;&lt;br /&gt;- &lt;i&gt;OO design helps you keep things organized.&lt;/i&gt; I've gotten confused trying to figure out a 10-page site coded using the "page-by-page" method (which is dreadfully popular). If an OO app is well-designed, even if it has a hundred features, you will be able to navigate the code and find what you're looking for quickly.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;OO code is often usable in other projects.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;This is an area where OO design really shines. How complicated is the idea of a category? Really, you could code something like this once, and use this code in every project that uses categories. Modular code is reusable, and everything in an OO application is modular.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;To conclude...&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I hope that I've convinced you that OOP is worth a careful look. Even if you never use it, after learning it, you will be a better programmer. Of course, I can't teach OOP in a single article. There are many, many helpful resources out there for learning about OO design, and I won't dare to try to trump them on my blog. Google is your personal research assistant, and he's just itching for something to do.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-6396372109283970248?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/6396372109283970248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=6396372109283970248' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/6396372109283970248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/6396372109283970248'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2008/01/why-use-oo-design.html' title='Why Use OO design?'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-4749215356668525515</id><published>2007-12-19T10:10:00.000-08:00</published><updated>2007-12-19T10:18:46.802-08:00</updated><title type='text'>Web Application Security</title><content type='html'>Before expounding the topics in my last post, I thought I'd take a detour to explain something that is &lt;i&gt;very&lt;/i&gt; important and often overlooked: &lt;b&gt;web application security&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;This is a broad topic, one that is the subject of innumerable mailing lists and that has spawned innumerable consulting firms. I will hardly be able to treat the topic thoroughly in this article. I can, however, give some advice that can knock out the most common and devastating vulnerabilities in web applications. This short list covers some of the biggest &lt;i&gt;non-SQL related&lt;/i&gt; vulnerabilities. SQL vulnerabilities and attack prevention are very important, as well, and are a topic for a future article.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Cross-Site Scripting (XSS)&lt;/b&gt; - This vulnerability can affect any application that accepts and displays user input. If this input is not properly validated, an attacker can input HTML or Javascript code that is then displayed to every user who comes to the site. This code can do a range of damage, from changing the display/style of the webpage to reaping user login information with an AJAX control using the Javascript DOM.&lt;br /&gt;&lt;b&gt;Solution:&lt;/b&gt; Validate your data! Strip all HTML tags from your user input. If you must, allow a well-defined subset of harmless HTML tags (text formatting only, like bold, italics, etc.), and strip the rest.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Cross-Site Request Forgeries (CSRF)&lt;/b&gt; or &lt;b&gt;"session riding"&lt;/b&gt; - Attackers can fool your unwitting users who have logged in to your application to execute arbitrary site functionality. For instance, say someone has logged into your web forum. Later, he receives a malicious email containing a link like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;a href="http://yourwebforum.tld/post.php?body=I'm%20a%20loser!"&amp;gt;Check out this funny picture!&amp;lt;/a&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Or worse:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;img src="http://yourwebforum.tld/post.php?body=I'm%20a%20loser!"/&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The user wouldn't have to click on the above example--only open the email--for the request to be made. In this example, a post could be made using a user's credentials without his knowledge.&lt;br /&gt;&lt;b&gt;Solution:&lt;/b&gt; There are several easy things you can do to mitigate the chance that an attack like this would be successful, like using POST instead of GET or making user credentials expire quickly. However, to thoroughly prevent this type of attack, the solution requires even more. It helps to associate a unique key with each form submission, a key that the attacker could never know. The key could be generated randomly for the form when it is displayed to the user and saved to match to the future POST when the form is submitted back to the server.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;"Session hijacking"&lt;/b&gt; - An attacker can discover the ID of a user's session and send the session ID in the URL or in the HTTP headers as his own, thereby "hijacking" the user's credentials. This gives any access your user had to the attacker.&lt;br /&gt;&lt;b&gt;Solution:&lt;/b&gt; You can try associating the user's IP address with the session, which you would check against the client's IP every time a request were made. This, though, can knock out valid users whose ISPs use load-balancing proxies for internet traffic; their IP addresses may change with every request. It could also allow an attacker behind the same router as the user to hijack a session undetected. Instead, you can set a cookie on the client's machine that contains nothing important or private, say, a unique key generated when the session is created. This key is also saved in the session, and is checked against the cookie at every request.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Lack of URL access restriction&lt;/b&gt; - Sometimes a web application will only authenticate a user when trying to access an entry point to a restricted portion of the site. For instance, if a user tries to view his personal dashboard, his credentials are checked and he is redirected to a login page before he can continue. However, if this user were to type in a direct URL to a page other than the dashboard, he would bypass the login procedure altogether. This is a glaring oversight in many web applications. All an attacker has to discover is the the URLs to non-entry pages, to which he would have full access.&lt;br /&gt;&lt;b&gt;Solution:&lt;/b&gt; Validate credentials on &lt;i&gt;every&lt;/i&gt; page that should have restricted access.&lt;br /&gt;&lt;br /&gt;A good rule of thumb when it comes to web application security: Don't &lt;i&gt;ever&lt;/i&gt; assume that an attacker won't try or won't figure out how. Ideally, you should be comfortable allowing an attacker to see your source code. It should be apparent that your security is so air-tight that he is forced to play by the rules.&lt;br /&gt;&lt;br /&gt;Of course, there are many other types of attacks, and, as I mentioned, I will do another article on SQL vulnerabilities and exploits, which are also very important. For more information on the topics in this article, here are some great resources:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.owasp.org"&gt;The Open Web Application Security Project&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.owasp.org/index.php/Top_10_2007#Summary"&gt;OWASP's top ten web app exploits&lt;/a&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.webappsec.org"&gt;Web Application Security Consortium&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.webappsec.org/lists/"&gt;Join their mailing list&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://cookies.lcs.mit.edu/pubs/webauth:tr.pdf"&gt;MIT paper on secure sessions&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-4749215356668525515?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/4749215356668525515/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=4749215356668525515' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/4749215356668525515'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/4749215356668525515'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2007/12/web-application-security.html' title='Web Application Security'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2178120640661923784.post-9218873173794794140</id><published>2007-12-10T12:55:00.000-08:00</published><updated>2007-12-10T12:56:47.874-08:00</updated><title type='text'>The Basics</title><content type='html'>From the perspective of most people, web software development is much simpler than compiled software development. Coding a desktop application should require far more planning than coding a web application, right? On the other hand, compiled software developers see most web applications as silly toys and most web application developers as silly people who don't know good software from a hole in Vista.&lt;br /&gt;&lt;br /&gt;So how can these two perspectives be reconciled? The fact is, web software development and compiled software development are exactly the same. Both have attracted different types of developers and, in some cases, different types of users, but the same principles apply. By opening the eyes of web software developers to the beauty of good software design, their opinion that web software doesn't require much planning will change. At the same time, with the increase in quality in the design of web software, compiled software developers may actually begin to show a little respect for "silly" web software developers. Well, maybe.&lt;br /&gt;&lt;br /&gt;There are some very basic questions you should ask yourself when designing ANY software:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Can I make it easier to code additional functionality in the future?&lt;/b&gt;&lt;br /&gt;Some develop with the sole purpose of checking items off the list of requirements. True, this may help you meet your deadline faster (though even that is not likely). But what happens when, six months from now, your client asks for another feature to be added? Did you anticipate that feature request? More realistically, did you code a framework to support quick addition of any request? Here are some ways you can do so:&lt;br /&gt;- &lt;i&gt;Follow the DRY (Don't Repeat Yourself) principle.&lt;/i&gt; For example, do you find that your code is similar or even identical in different parts of your software? It would make far more sense to generalize the code and put it in a single function. Later, when you add a feature that requires similar code, you simply call this function. This works especially well for pulling and massaging data from your database.&lt;br /&gt;- &lt;i&gt;Anticipate changes in data.&lt;/i&gt; Sure, your client may say that "red" and "blue" are the only options, so you figure an enum in the database will work just fine. Don't be so trusting. There's an N% chance that your client will remove or add colors in the future, where N is large. Create a separate table for these options, and don't feel silly when there are only two rows in the table at your initial launch.&lt;br /&gt;- &lt;i&gt;Correctly identify relationships in your data.&lt;/i&gt; Does a shopping cart have an item, or is it the other way around? Can a cart have more than one item? Unless the alternative is absurd, don't ever assume that a cart won't have multiple items, or a user won't have multiple profiles, or a post won't have multiple comments, even if your client swears that they want to "keep it simple" and only have a one-to-one relationship.&lt;br /&gt;- &lt;i&gt;Separate anything that looks remotely like an option from your logic&lt;/i&gt; (i.e., don't "hard-code" anything). The number of pictures per row in a photo gallery, the number of file upload fields in a form, the maximum dimensions of a profile photo--all of these should be variables or constants in your code. Initially, before the client asks for the ability to modify these options, they can be set in a separate config file for the application. As the client realizes that these options need to be configurable through the web interface, it will be easy-cheesy to accommodate that.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Can I make it easier to maintain this code?&lt;/b&gt;&lt;br /&gt;Even if the client never asks for additional features, he may want "tweaks" to the software. Are tweaks going to be just that, or are they going to require an overhaul?&lt;br /&gt;- &lt;i&gt;Separate design from content.&lt;/i&gt; I can't stress how important this is. For instance, in a web application, have just a few source files (like a header, a footer, and a stylesheet) that define all design elements. If your client wants a re-design, you won't have to modify every single page of the website. This seems like common sense, but it's amazing how many people will forgo this huge time saver because "it's just a small site."&lt;br /&gt;- &lt;i&gt;Separate data from code.&lt;/i&gt; What if your database schema changes? Don't spend hours modifying eighty-two source files because you add or change one column, or worse, avoid making changes to the software that would improve functionality or efficiency because it's too much of a pain to make them. Your database queries should be quarantined in functions. All your application should know about is functions like getUsers, addUser, getCart, removeAccount etc. To demonstrate how flexible this can be, imagine converting a web application from MySQL to CSV flat files (don't actually do this, or I'll shoot you). None of your page-to-page code would have to change; you'd only have to re-work a few functions responsible for accessing data.&lt;br /&gt;- &lt;i&gt;Keep things organized.&lt;/i&gt; This is a broad point, and touches on things like consistent tabbing of your code blocks, sane file naming conventions, sane directory structure, and grouping library functions in files. There's a place for everything, and everything should be in its place. All files should be included at the top of a file, and, ideally, included in only one file. Don't include a source file in the middle of your code just because you need to call a function defined in that file on the line below. How in the world are you going to follow that logic later?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;How can I make this code usable in other projects?&lt;/b&gt;&lt;br /&gt;It's really nice to have a battle-hardened library of code that you can plug into a client's site for oft-requested functionality. Really, by taking the advice above, this takes care of itself. But just to stress a couple important points:&lt;br /&gt;- &lt;i&gt;Separate data from code.&lt;/i&gt; You wrote an awesome photo gallery module for a client, but your new client uses a different type of database. It shouldn't matter. Just rewrite your data access functions.&lt;br /&gt;- &lt;i&gt;Options, options, options.&lt;/i&gt; One client may want four photos per row, but the other may only want three. Even if you don't have a web interface for them, separate all options from your code and keep them in a config file. By changing only a few config variables or constants, you can completely customize an application for a client.&lt;br /&gt;- &lt;i&gt;Separate design from code.&lt;/i&gt; How's your client going to feel when he sees an identical gallery on a competitor's site? Allowing your clients to skin their application to fit their branding gives them warm fuzzies inside.&lt;br /&gt;&lt;br /&gt;This is a huge topic, and I've only scratched the surface. I will expound on some of these points in later articles. Please leave additional points in the comments.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2178120640661923784-9218873173794794140?l=lucasoman.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lucasoman.blogspot.com/feeds/9218873173794794140/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2178120640661923784&amp;postID=9218873173794794140' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/9218873173794794140'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2178120640661923784/posts/default/9218873173794794140'/><link rel='alternate' type='text/html' href='http://lucasoman.blogspot.com/2007/12/basics.html' title='The Basics'/><author><name>Lucas Oman</name><uri>http://www.blogger.com/profile/11133152482095049863</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_MV_mXbD6CDQ/SsS0PFUJXII/AAAAAAAAABU/IePQYcygus8/S220/3902147829_2c9f1ff522.jpg'/></author><thr:total>0</thr:total></entry></feed>
