<?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:blogger='http://schemas.google.com/blogger/2008' 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-7192097691707306287</id><updated>2024-09-04T23:17:15.698-04:00</updated><category term="howto"/><category term="opinion"/><category term="testing"/><category term="JS"/><category term="rant"/><category term="ruby"/><category term="cucumber"/><category term="email"/><category term="extjs"/><category term="git"/><category term="rails"/><category term="rspec"/><category term="selenium"/><category term="svn"/><title type='text'>Random Software Inklings</title><subtitle type='html'>I tend to do a lot of random research for various packages for use in projects.  Most of the time I just make the package work and move on.  This means that if I need to do the same thing again I have to go find all my sources again, or remember what I did.  This is my attempt to actually document what I have done in a useable way.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>22</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-3102848516317073567</id><published>2012-12-29T21:22:00.003-05:00</published><updated>2012-12-29T21:22:38.605-05:00</updated><title type='text'>I have moved.</title><content type='html'>The blog has moved to: &lt;a href=&quot;http://jkamenik.github.com/&quot;&gt;http://jkamenik.github.com&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The full details of why are here:&amp;nbsp;&lt;a href=&quot;http://randomsoftwareinklings.blogspot.com/2012/11/the-long-away.html&quot;&gt;http://randomsoftwareinklings.blogspot.com/2012/11/the-long-away.html&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/3102848516317073567/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2012/12/i-have-moved.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3102848516317073567'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3102848516317073567'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2012/12/i-have-moved.html' title='I have moved.'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-5678251702419347384</id><published>2012-11-12T19:12:00.001-05:00</published><updated>2012-11-12T19:12:42.588-05:00</updated><title type='text'>The long away</title><content type='html'>&lt;h2&gt;
The Reason&lt;/h2&gt;
First, let me&amp;nbsp;apologize&amp;nbsp;to my 3 readers that I have been away for so long. &amp;nbsp;There are a lot of reasons why I have been gone, but mainly it boils down to how much I hate the blogger interface for dealing with code.&lt;br /&gt;
&lt;br /&gt;
Sure, blogger is a simple platform and does exactly what it says. &amp;nbsp;Sure, it integrates with google plus, twitter, etc... &amp;nbsp;Sure, it was easy to configure, setup and deploy. &amp;nbsp;Sure, it has a lot of features.&lt;br /&gt;
&lt;br /&gt;
But, it sucks for code! &amp;nbsp;And I mean Sucks! &amp;nbsp;For code there are a few very specific hot button items that are must haves:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Monospaced font and significant whitespace (&amp;lt;pre&amp;gt; in HTML)&lt;/li&gt;
&lt;li&gt;Line numbers&lt;/li&gt;
&lt;li&gt;Syntax highlighting&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Not that much to ask, but those features are ONLY ever going to be needed by a programmer. &amp;nbsp;So I can see why they don&#39;t get that much attention.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Both the old and the new blogger interface have a way of dealing with this, but it is time consuming and annoying:&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;Use gist.github.com as a dumping place for all your code samples&lt;/li&gt;
&lt;ol&gt;
&lt;li&gt;Create a separate file in the gist for each sample (makes it easier later)&lt;/li&gt;
&lt;li&gt;Make sure the file name has the right&amp;nbsp;extension&amp;nbsp;so that syntax highlighting happens&lt;/li&gt;
&lt;li&gt;Copy the embed codes for each gist&lt;/li&gt;
&lt;/ol&gt;
&lt;li&gt;When editing the blog only use HTML mode!&lt;/li&gt;
&lt;ol&gt;
&lt;li&gt;The gist code is a javascript so it will only render in preview&lt;/li&gt;
&lt;li&gt;Switching between HMTL and compose sometimes rearranges non-visible items, which means when your page renders your code could be anywhere&lt;/li&gt;
&lt;li&gt;If I am going to write raw HTML I might as well use Markdown, which sucks less and converts to HTML&lt;/li&gt;
&lt;/ol&gt;
&lt;li&gt;Hope that nobody views your blog with JS disabled!&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
The Solution&amp;nbsp;&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
After playing with many blogging solution and giving up on Wordpress entirely (Sorry I just cannot get what I want without a LOT of php hacking), I have decided to give a Octopress a serious shot. &amp;nbsp;I use github all the time and even host a few things using github pages.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In case you don&#39;t know, Github pages can render Jekyll sites. &amp;nbsp;And Jekyll is a blog aware static site generator, which can read markdown. &amp;nbsp;And Markdown is a much more human friendly markup language then HTML, but compiles into HTML; also, Github uses it everywhere in their site (as do a lot of other places).&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
So things started to fall into place with Jekyll. &amp;nbsp;I just needed to install a syntax highlighting gem, and design the site. &amp;nbsp;In the process, I&amp;nbsp;stumbled&amp;nbsp;across Octopress, which is a wrapper around a Jekyll site, works with github, has syntax highlighting, provides a clean theme, and has a programmer friendly workflow.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
So Octopress is the blog engine for me. &amp;nbsp;And it will reside at &lt;a href=&quot;http://jkamenik.github.com/&quot;&gt;http://jkamenik.github.com&lt;/a&gt;. &amp;nbsp;I will slowly be transferring all my entries from here, but I doubt I will retire this site entirely.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/5678251702419347384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2012/11/the-long-away.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/5678251702419347384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/5678251702419347384'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2012/11/the-long-away.html' title='The long away'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-4744707535827733251</id><published>2012-03-09T10:05:00.000-05:00</published><updated>2012-03-09T10:05:02.746-05:00</updated><title type='text'>Factory Girl Automatic Tests</title><content type='html'>&lt;span style=&quot;font-size: x-large;&quot;&gt;Introduction&lt;/span&gt;&lt;br /&gt;
Early in a project I started to use factory girl without fully understanding it. &amp;nbsp;After many months of creating steps like &quot;&lt;b&gt;Given /^(\d+) blog exists$/&lt;/b&gt;&quot; and &quot;&lt;b&gt;Given /^the following blogs exist:$/&lt;/b&gt;&quot; I started to come up with generic functions that would build those steps.&lt;br /&gt;
&lt;br /&gt;
Stupid me for not checking that factory girl already does something like that. &amp;nbsp;All you have to do is include factory girl&#39;s &lt;a href=&quot;http://github.com/thoughtbot/factory_girl/blob/master/lib/factory_girl/step_definitions.rb&quot;&gt;step_definitions.rb&lt;/a&gt; file:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/2003858.js?file=env.rb&quot;&gt;
&lt;/script&gt;

Once you start using FactoryGirl correctly there are a world of new features that can make your steps both cleaner and more concise. &amp;nbsp;Here are some tips I have found via trial and error.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: x-large;&quot;&gt;Tip 1: Do not reinvent the wheel&lt;/span&gt;&lt;br /&gt;
Factory girl will create steps for all&amp;nbsp;factories&amp;nbsp;that you have&amp;nbsp;register.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/2003858.js?file=example_steps.feature&quot;&gt;
&lt;/script&gt;

&lt;span style=&quot;font-size: x-large;&quot;&gt;Tip 2: Use association, Do not add more steps then are needed&lt;/span&gt;&lt;br /&gt;
Factory girl&amp;nbsp;association are automatically created before the factory is created and they are automatically linked. &amp;nbsp;It only supports the &lt;b&gt;belongs_to&lt;/b&gt; behavior, so keep that in mind.&lt;br /&gt;
&lt;br /&gt;
Using the &quot;&lt;b&gt;Given the following XXX exist&lt;/b&gt;&quot; step you can define attributes, on the the&amp;nbsp;association, in the table. &amp;nbsp;If we leave the association out then a default is created. &amp;nbsp;If we define an attribute then it will be found or created using that attribute.&lt;br /&gt;
&lt;br /&gt;
Lets say you have a product and it can belog to a category. &amp;nbsp;You do not need to create a category.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/2003858.js?file=product_category.rb&quot;&gt;
&lt;/script&gt;
&lt;script src=&quot;https://gist.github.com/2003858.js?file=product_category.feature&quot;&gt;
&lt;/script&gt;

&lt;span style=&quot;font-size: x-large;&quot;&gt;Tip 3: Attaching files via CarrierWave&lt;/span&gt;&lt;br /&gt;
Since cucumber is a text file it doesn&#39;t make much sense for you to define full files in steps. &amp;nbsp;It also doesn&#39;t really make sense to embed full file paths in the tests. &amp;nbsp;Instead, you can use a Transient attribute and some code so that in cucumber you define a file name and in the factory it converts to an actual file.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/2003858.js?file=file_product.rb&quot;&gt;
&lt;/script&gt;
&lt;script src=&quot;https://gist.github.com/2003858.js?file=file_product.feature&quot;&gt;
&lt;/script&gt;

&lt;span style=&quot;font-size: x-large;&quot;&gt;Tip 4: Fixing a circular dependency between two models&lt;/span&gt;&lt;br /&gt;
Lets say you have a Store model and User model. &amp;nbsp;And a User can both work at and own a Store. &amp;nbsp;If you put associations in both the User and the Store model then each will try to create the other, infinitely. &amp;nbsp;We can reuse the transient method as before to break the circle.&lt;br /&gt;
&lt;br /&gt;
The trick is to avoid defining an association in both factories, but instead use a transient attribute in one factory to simulate the behavior of an association. &amp;nbsp;Also, since transient attributes are not likely to have the same level of sophistication as the associations you should use the association to define the more complex of the two models.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/2003858.js?file=user_store.rb&quot;&gt;
&lt;/script&gt;
&lt;script src=&quot;https://gist.github.com/2003858.js?file=user_store.feature&quot;&gt;
&lt;/script&gt;

&lt;span style=&quot;font-size: x-large;&quot;&gt;Tip 5: Fixing a circular dependency between the same model&lt;/span&gt;&lt;br /&gt;
Let say you have a Category model, and that model can belong to another Category (a tree) then you cannot use an association or you get the same infinite recursion issue as before. &amp;nbsp;Here we can use a transient attribute along with an &lt;b&gt;after_create&lt;/b&gt; hook to simulate the behavior we want.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/2003858.js?file=category.rb&quot;&gt;
&lt;/script&gt;
&lt;script src=&quot;https://gist.github.com/2003858.js?file=category.feature&quot;&gt;
&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/4744707535827733251/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2012/03/factory-girl-automatic-tests.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/4744707535827733251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/4744707535827733251'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2012/03/factory-girl-automatic-tests.html' title='Factory Girl Automatic Tests'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-8450260322059106020</id><published>2011-11-30T08:36:00.001-05:00</published><updated>2011-11-30T10:22:06.108-05:00</updated><title type='text'>CI server using Firefox</title><content type='html'>If you are like me, you always setup a CI to ensure things that you already finished are not broken by things you are currently doing. &amp;nbsp;And if you are like me, you use &lt;a href=&quot;http://github.com/jnicklas/capybara&quot;&gt;capybara&lt;/a&gt;, &lt;a href=&quot;http://github.com/lanej/selenium-webdriver&quot;&gt;selenium-webdriver&lt;/a&gt;, and &lt;a href=&quot;http://www.mozilla.org/en-US/firefox/new/&quot;&gt;firefox&lt;/a&gt; to actually test the results in a browser. &amp;nbsp;The rub comes when the CI server (running headless) needs to run firefox, which needs a screen.&lt;br /&gt;
&lt;br /&gt;
Virtual Frame Buffer (xvfb) is a great package. &amp;nbsp;All it does is provide an X11 interface in memory instead of requiring a screen. &amp;nbsp;Below I detail what I do on Ubuntu, but it should work the same on any Linux distro.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Installing Firefox&lt;/span&gt;&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1409172.js?file=install_firefox.sh&quot;&gt;
&lt;/script&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Testing Firefox&lt;/span&gt;&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1409172.js?file=test_firefox.sh&quot;&gt;
&lt;/script&gt;
Once firefox is installed it can be tested on a X11-forwarded ssh connection. &amp;nbsp;X11-forwarding has to be enabled on the server and client and can be tested by checking DISPLAY.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Installing Xvfb&lt;/span&gt;&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1409172.js?file=install_xvfb.sh&quot;&gt;
&lt;/script&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Testing Firefox again
&lt;/span&gt;&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1409172.js?file=test_firefox_again.sh&quot;&gt;
&lt;/script&gt;
The easiest way to test is to use image magick to take a screen shot of the VFB and save it to an image that can be opened in Firefox. &amp;nbsp;If the image opens and it looks like a firefox browser is running on the google page then you did it correctly.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Create a xvfb startup script&lt;/span&gt;&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1409172.js?file=xvfb.sh&quot;&gt;
&lt;/script&gt;
On ubuntu no start script is provided, but a simple one put in &lt;i&gt;/etc/init.d/xvfb&lt;/i&gt; is enough.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Getting the CI server to use the VFB&lt;/span&gt;&lt;br /&gt;
At this point getting a CI server to use Xvfb will depend on how it is started. &amp;nbsp;If you use the Xvfb startup script then the display is &lt;i&gt;:1&lt;/i&gt;. &amp;nbsp;All you have to do is &lt;i&gt;export DISPLAY=:1&lt;/i&gt; before the CI server starts.</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/8450260322059106020/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/11/ci-server-using-firefox.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/8450260322059106020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/8450260322059106020'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/11/ci-server-using-firefox.html' title='CI server using Firefox'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-3612141657345818625</id><published>2011-11-21T16:38:00.001-05:00</published><updated>2012-05-20T01:27:40.788-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ruby"/><title type='text'>Ruby module instance and class methods</title><content type='html'>A very common idiom especially when code starts to become complicated is to put functions in modules and mix in that behavior to several classes. &amp;nbsp;And a common expansion is to have a single &quot;include&quot; add both instance and class methods.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Here is how to do it:&lt;/div&gt;
&lt;script src=&quot;https://gist.github.com/1384045.js?file=include.rb&quot;&gt;
&lt;/script&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&quot;include&quot; is used to add a module&#39;s instance methods as instance methods to the including class, but it doesn&#39;t traverse sub modules. &amp;nbsp;&quot;extend&quot; is used to add a module&#39;s instance methods as class methods to a class.&lt;br /&gt;
&lt;br /&gt;
When a module is &quot;included&quot; it can register a callback which is passed the class that is doing the including. &amp;nbsp;And calling &quot;extend&quot; on the base and passing a submodule will cause it to put those methods at the class level. &amp;nbsp;This causes a chain reaction of loading both instance and class methods using a single &quot;include&quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Pro Tip&lt;/b&gt;: It is possible to cause the same behavior using &quot;extend&quot; by using the &quot;extended&quot; callback; however, this is not common and &quot;include &#39;x&#39;&quot; reads better then &quot;extend &#39;y&#39;&quot;.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/3612141657345818625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/11/ruby-module-instance-and-class-methods.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3612141657345818625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3612141657345818625'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/11/ruby-module-instance-and-class-methods.html' title='Ruby module instance and class methods'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-6919045839991825878</id><published>2011-08-16T10:48:00.000-04:00</published><updated>2011-08-16T10:48:47.516-04:00</updated><title type='text'>The 3N Rule for deployment</title><content type='html'>As a programmer I am lazy, but in a productive way.  It is not that I don&#39;t do anything.  It is that I only do things a set number of times.  I have a simple rule, which I call the &quot;3N rule&quot;, which states &quot;I will doing something once, script it the second time, and use the script from then on&quot;.  Therefore, if I have 3 to N devices to manage then I have a script that takes minimal input and replicates the change on all devices.&lt;br /&gt;
&lt;br /&gt;
I also have a 3N rule for programming, but that is a matter for another time.&lt;br /&gt;
&lt;br /&gt;
By using a script I eliminate a lot of the human error and also increase my productivity because computers are, now days, faster at responding to stimuli then I am.  But computers are &quot;swift idiots&quot; so for this to work the environment has got to be controlled.  I recommend doing the following:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Use VMs&lt;/li&gt;
&lt;li&gt;Run headless, enable SSH&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;http://sourceforge.net/apps/mediawiki/clusterssh/index.php?title=Main_Page&quot;&gt;ClusterSSH&lt;/a&gt; or &lt;a href=&quot;http://code.google.com/p/csshx/&quot;&gt;csshX&lt;/a&gt;&amp;nbsp;for learning what to script&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://github.com/capistrano/capistrano/wiki&quot;&gt;Capistrano&lt;/a&gt;&amp;nbsp;or &lt;a href=&quot;http://docs.fabfile.org/en/1.2.0/index.html&quot;&gt;Fabric&lt;/a&gt; for automating change&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;By using a VM and running headless you remove the temptation to sit at a physical terminal. &amp;nbsp;It also eliminates the fear associated with screwing something up as it is trivial to snapshot a machine and restore its state.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;By using SSH and ClusterSSH you enable yourself to start thinking about the cluster as a whole, and more importantly start acting on the cluster as a whole. &amp;nbsp;If some of the servers get out of sync then it makes management more difficult, so by using ClusterSSH you eliminate that problem.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;By using Capistrano, or its like, then you eliminate the human error entirely. &amp;nbsp;This is just good policy. &amp;nbsp;Also, a benefit of a deployment script is that it is self documenting (assuming you know how to read).&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/6919045839991825878/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/08/3n-rule-for-deployment.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/6919045839991825878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/6919045839991825878'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/08/3n-rule-for-deployment.html' title='The 3N Rule for deployment'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-8607903754959629642</id><published>2011-06-26T17:36:00.001-04:00</published><updated>2011-08-01T09:25:37.164-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="extjs"/><category scheme="http://www.blogger.com/atom/ns#" term="howto"/><category scheme="http://www.blogger.com/atom/ns#" term="JS"/><title type='text'>ExtJS 3 Views and Controllers</title><content type='html'>In a previous &lt;a href=&quot;http://randomsoftwareinklings.blogspot.com/2011/06/ext4-impressions.html&quot;&gt;post&lt;/a&gt; I wrote about my impressions of Ext4. &amp;nbsp;The overall impression were that the ideas were sound, but the implementation was still buggy. &amp;nbsp;So instead of waiting (which is not possible in a business sense) I have back-ported the ideas to Ext3. &amp;nbsp;I have not yet figured out how to get stores and models working well, but I have gotten views and controllers works very well.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Layout&lt;/span&gt;&lt;br /&gt;
Views basically work as is, because that is how ExtJS was designed. &amp;nbsp;Any subclass of a UI widget will work as a view. &amp;nbsp;However, to make the concept of a view work well they should only be&amp;nbsp;instantiated&amp;nbsp;once and have their modification logic in a controller. &amp;nbsp;For this reason I recommend using the following directory layout:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;app.js&lt;/li&gt;
&lt;li&gt;ext/&lt;/li&gt;
&lt;li&gt;app/&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;controllers/&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;Controller.js&lt;/li&gt;
&lt;li&gt;ContentController.js&lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;lib/&lt;/li&gt;
&lt;li&gt;view/&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;layout/&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;Content.js&lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;Viewport.js&lt;/li&gt;
&lt;li&gt;OtherView.js&lt;/li&gt;
&lt;/ul&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;app.js&lt;/span&gt;&lt;br /&gt;
This should just a Ext.onRead function that instantiates the &lt;span class=&quot;code&quot;&gt;Viewport.js&lt;/span&gt; class. &amp;nbsp;It should also setup any globals that will be used throughout the app and should be included last.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;lib/&lt;/span&gt;&lt;br /&gt;
Since views should only be instantiated once this is the place to put reusable code. &amp;nbsp;Items here will not usually have an xtype because they will be extended by view items.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Controller.js&lt;/span&gt;&lt;br /&gt;
This is the base class for all controllers. &amp;nbsp;It has the helper to bind to views and look them up afterwards. &amp;nbsp;A small override to &lt;span class=&quot;code&quot;&gt;Ext.ComponentMgr&lt;/span&gt; is required to make everything work.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1118084.js?file=Controller.js&quot;&gt;
&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Content.js&lt;/span&gt;&lt;br /&gt;
This needs to be a simple card layout so that you can add new items to it.&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1118109.js?file=content.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;ContentController.js&lt;/span&gt;&lt;br /&gt;
This class binds to a something that would change the content panel, like a TreePanel, and then using &lt;span class=&quot;code&quot;&gt;Ext.ComponentMgr&lt;/span&gt; creates a card, adds it to the content, and shows it.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1118111.js?file=ContentController.js&quot;&gt;&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/8607903754959629642/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/06/extjs-3-views-and-controllers.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/8607903754959629642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/8607903754959629642'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/06/extjs-3-views-and-controllers.html' title='ExtJS 3 Views and Controllers'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-4661709048887491758</id><published>2011-06-18T20:58:00.000-04:00</published><updated>2011-06-18T20:58:10.009-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="opinion"/><title type='text'>Minimum Rcov</title><content type='html'>&lt;a href=&quot;http://en.wikipedia.org/wiki/Robert_Cecil_Martin&quot;&gt;Uncle Bob&lt;/a&gt;, has stated that all code should be 100% covered, but 100% is an&amp;nbsp;asymptote. &amp;nbsp;Therefore he expects something less then 100%. &amp;nbsp;This might be true in the Java, or C version of Rcov, but not for Ruby Rcov. &amp;nbsp;Also, I personally think that 100% is a bit low.&lt;br /&gt;
&lt;br /&gt;
All 100% coverage means is that at least one test touches every line. &amp;nbsp;And in truly simple code that is usually enough. &amp;nbsp;But I work on systems, not simple code. &amp;nbsp;Rarely do I write single use code. &amp;nbsp;In most cases I write code that does one thing well, but is used by a lot of different part of the system.&lt;br /&gt;
&lt;br /&gt;
Errors are very rarely in core modules, but rather in the runtime combination of core modules. &amp;nbsp;This is covered by integration testing which is not usually pulled into rcov. &amp;nbsp;I think this is a mistake.&lt;br /&gt;
&lt;br /&gt;
The following is how I think about a minimum level of Rcov:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;b&gt;Exclude libraries from rcov metrics&lt;/b&gt;. That is unless you decide to include their unit testing within your own. &amp;nbsp;Either way this will give you a more accurate reading of coverage. &amp;nbsp;Seeing rcov at 90%, but thinking it is 100% because rcov can&#39;t see libraries isn&#39;t a valid assumption, IMHO.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Include integration testing in rcov&lt;/b&gt;. &amp;nbsp;Rcov has an option to&amp;nbsp;aggregate several runs into a final number. &amp;nbsp;You should include both unit and integration testing in the final number.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Expect greater then 100% coverage&lt;/b&gt;. &amp;nbsp;Actually, including edge cases, positive, and negative testing the number will be closer to 1000% percent covered. &amp;nbsp;You read that right, every line of code should be covered by at least 10 tests.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Include the rcov threshold test in CI&lt;/b&gt;. &amp;nbsp;You should, before committing, always run all unit tests, integration tests, and rcov threshold. &amp;nbsp;At first set the threshold low (about 80%) and move it to 100% ASAP. &amp;nbsp;And by including threshold test in CI then the code can&#39;t go out the door without the correct amount of coverage.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Review tests first and last&lt;/b&gt;. &amp;nbsp;If your teams doesn&#39;t already do code reviews, it should. &amp;nbsp;If it does then start with the tests and then review the code. &amp;nbsp;This lets you ensure the obvious edge cases are covered without being swayed by the code. &amp;nbsp;After reviewing the code re-review the tests to ensure there are no new edge cases need to be covered.&lt;/li&gt;
&lt;/ol&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/4661709048887491758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/06/minimum-rcov.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/4661709048887491758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/4661709048887491758'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/06/minimum-rcov.html' title='Minimum Rcov'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-3611009301082026148</id><published>2011-06-06T21:21:00.000-04:00</published><updated>2011-06-06T21:21:49.202-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="opinion"/><category scheme="http://www.blogger.com/atom/ns#" term="rant"/><title type='text'>Ext4 Impressions</title><content type='html'>I have been using Ext for years, starting with 2.0. &amp;nbsp;In most cases this is paired with Ruby on Rails on the server and has generally been nice. &amp;nbsp;There were some growing pains in understanding that Ext is not like jQuery. &amp;nbsp;It isn&#39;t meant to enhance the page it is meant to control it. &amp;nbsp;Once it is understood that ExtJS is an event driven UI layer much like MFC (Microsoft) then things are rather easy to grasp.&lt;br /&gt;
&lt;br /&gt;
In fact, Ext (pre 4) is almost just like MFC in that it is primarily a collection of UI widgets that you can put together in any way you wish. &amp;nbsp;This is great for very small UIs that do not have a lot of interconnected parts, but once the number of interconnected item reaches more then a few (say 5 or 6) then it is nearly impossible to map all the emergent properties mentally. &amp;nbsp;Enter MVC.&lt;br /&gt;
&lt;br /&gt;
MVC is a design patter created over 30 years ago, whose sole purpose is the separation of concerns. &amp;nbsp;Business logic (model) is separated from UI (view) and they communicate via a defined set of actions (controller). &amp;nbsp;Anyone familiar with UI programming should be familiar with this pattern. &amp;nbsp;It is used in almost all major UI libraries except ExtJS, until now.&lt;br /&gt;
&lt;br /&gt;
Ext4 aims to fix this flaw by retooling the framework to be MVC. &amp;nbsp;It is almost successful. &amp;nbsp;There is a major problem in separating models from collections (which is understandable given Javascript&#39;s nature) but there are more elegant solutions then the one Ext4 uses (see SproutCore and Backbone). &amp;nbsp;There are far too many annoyances at the model level for me to detail here, but as an example the LocalStore and MemoryStore are broken as of this writing and cannot update or remove models since they do not fire events. &amp;nbsp;This problem can be seen is a number of their own grid demos.&lt;br /&gt;
&lt;br /&gt;
I also tried to use the compatibility mode to convert an older Ext3 app to Ext4. &amp;nbsp;I was successful in removing all of the errors and most of the warning, but could not get the page to render. &amp;nbsp;Each component thought it was rendered, but no DOM was presented to the browser. &amp;nbsp;Lots of digging later and it turns out you can&#39;t mix MVC Ext4 objects with non-MVC Ext3 like objects. &amp;nbsp;Not sure why, and without errors it is hard to tell what went wrong.&lt;br /&gt;
&lt;br /&gt;
Finally I started from scratch and created&amp;nbsp;&lt;a href=&quot;https://github.com/jkamenik/extjs4-frame&quot;&gt;extjs4-frame&lt;/a&gt;&amp;nbsp;so that I could dig in without having to worry about Ext3 compatibility screwing things up. &amp;nbsp;Thing did not go much better. &amp;nbsp;I watched the videos and then dove right in. &amp;nbsp;As expected what was shown on the videos (obviously dumbed down) worked as expected, but as I started to add complexity things would break in mysterious ways. &amp;nbsp;Turning to the documentation was only marginally helpful, since it appears that it has not been full updated. &amp;nbsp;Also, some of the documentation does not match the JDoc comments in the source (probably human error in generating the docs). &amp;nbsp;This is an annoyance to be sure, but not a show stopper since I know how to read Javascript (as anyone that would be using ExtJS should). &amp;nbsp;However, documentation error (especially when generated from embedded comments) are indicative of a lack of attention to detail and since they do not provide their unit tests I can only guess that their storage engines are not well tested either.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Pros&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Documentation videos are a nice touch&lt;/li&gt;
&lt;li&gt;Simplified code due to the MVC nature&lt;/li&gt;
&lt;li&gt;Sensible file structure (forced)&lt;/li&gt;
&lt;li&gt;Auto-loader (turn off in production, but great for development)&lt;/li&gt;
&lt;li&gt;Better stack and error handling (as compared with Ext 2/3)&lt;/li&gt;
&lt;li&gt;Trees use real models (meaning the server can respond with any hierarchical objects)&lt;/li&gt;
&lt;li&gt;Non-flash based charts&lt;/li&gt;
&lt;li&gt;Smaller size (as compared to Ext 2/3)&lt;/li&gt;
&lt;/ul&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Cons&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Difficult to understand (even for those with MVC and ExtJS experience)&lt;/li&gt;
&lt;li&gt;No backwards compatibility (the compatibility mode is a joke)&lt;/li&gt;
&lt;li&gt;4.0.X versions are not&amp;nbsp;guaranteed&amp;nbsp;compatible (there are still a lot of internal changes going on)&lt;/li&gt;
&lt;li&gt;Documentation does not always match source comments and is not always correct (save some time and just start with the code)&lt;/li&gt;
&lt;li&gt;Inconsistant Model/Container behavior&lt;/li&gt;
&lt;li&gt;No generator scripts provided for aiding design patterns&lt;/li&gt;
&lt;li&gt;No unit testing framework provided&lt;/li&gt;
&lt;li&gt;No JS compiler/minifier provided (one that can read the requires directive is almost a must now that they exist)&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Overall&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Wait for 4.1 to see if things stabilize. &amp;nbsp;Or switch to SproutCore.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/3611009301082026148/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/06/ext4-impressions.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3611009301082026148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3611009301082026148'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/06/ext4-impressions.html' title='Ext4 Impressions'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-7174726300139878295</id><published>2011-05-11T09:12:00.001-04:00</published><updated>2011-08-17T08:44:14.923-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cucumber"/><category scheme="http://www.blogger.com/atom/ns#" term="howto"/><category scheme="http://www.blogger.com/atom/ns#" term="rails"/><category scheme="http://www.blogger.com/atom/ns#" term="rspec"/><category scheme="http://www.blogger.com/atom/ns#" term="ruby"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><title type='text'>Cucumber with using Rspec should_receive</title><content type='html'>I recently came across a case where I wanted to use a &lt;span class=&quot;code&quot;&gt;should_receive&lt;/span&gt; test in cucumber.  The basic idea is that I was attempting to prevent Apache library code from calling out to the system, while simultaneously testing that the correct function was actually called.  In Rspec (which backs cucumber in my setup) it is simply:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151447.js?file=test.rb&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
At the end of the test if &lt;span class=&quot;code&quot;&gt;restart&lt;/span&gt; has not been called then the entire test fails.  Because of the way that cucumber works with rspec 1.3 (rails 2.x, which is required by the project) registering a &lt;span class=&quot;code&quot;&gt;should_receive&lt;/span&gt; has no effect (seems this is fixed in the cucumber/rspec versions for rails 3).  Even if it were to work it would mean putting a &lt;span class=&quot;code&quot;&gt;Then&lt;/span&gt; before the &lt;span class=&quot;code&quot;&gt;When&lt;/span&gt; or putting a test in a &lt;span class=&quot;code&quot;&gt;Given&lt;/span&gt;.  To me that just seems wrong.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151447.js?file=apache.feature&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
What I had to do to simulate a &lt;span class=&quot;code&quot;&gt;should_receive&lt;/span&gt; but still maintain the flow of the scenario was break it up so that I stub the function and when that function is called I set a flag.  Later I test that the flag is set.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151447.js?file=apache-after.feature&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
The step that sets the flag is &lt;span class=&quot;code&quot;&gt;Apache can restart&lt;/span&gt; and the one that tests is &lt;span class=&quot;code&quot;&gt;Apache should restart&lt;/span&gt;.  Here are the step definitions:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151447.js?file=apache_steps.rb&quot;&gt;&lt;/script&gt;&lt;br /&gt;
</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/7174726300139878295/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/05/cucumber-with-using-rspec-shouldreceive.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/7174726300139878295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/7174726300139878295'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/05/cucumber-with-using-rspec-shouldreceive.html' title='Cucumber with using Rspec should_receive'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-6745783963293301120</id><published>2011-04-13T10:35:00.000-04:00</published><updated>2011-04-13T10:35:31.203-04:00</updated><title type='text'>Heroku</title><content type='html'>At first it was not clear to me that Heroku was free for very small apps, but it is. &amp;nbsp;There are also a few restrictions that Heroku has:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;The project has to use Git&lt;/li&gt;
&lt;li&gt;The project has to be a Rails App (or have a config.ru)&lt;/li&gt;
&lt;li&gt;The project has to be in the repository root (good practice)&lt;/li&gt;
&lt;li&gt;The project has to list all the dependent gems (good practice)&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;If you meet all these requirements it is as simple as installing the heroku gem, creating a new app, and pushing the to heroku.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Setup a heroku ENV&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;div&gt;$ mkdir heroku&lt;/div&gt;&lt;div&gt;$ cd heroku&lt;/div&gt;&lt;div&gt;$ rvm use 1.9.2@heroku --create&lt;/div&gt;&lt;div&gt;$ gem install heroku&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Pushing to Heroku&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;code&quot;&gt;&lt;div&gt;$ git clone&amp;nbsp;git@github.com:jkamenik/Laser-Landmarks-Prototype.git&lt;/div&gt;&lt;div&gt;$ cd Laser-Landmarks-Prototype&lt;/div&gt;&lt;div&gt;$ heroku add laser-landmarks&lt;/div&gt;&lt;div&gt;$ git push heroku master&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/6745783963293301120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/04/heroku.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/6745783963293301120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/6745783963293301120'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/04/heroku.html' title='Heroku'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-3446673574426442327</id><published>2011-02-28T11:01:00.011-05:00</published><updated>2011-02-28T15:52:41.553-05:00</updated><title type='text'>Continuous Integration with CIJoe</title><content type='html'>&lt;a href=&quot;https://github.com/defunkt/cijoe&quot;&gt;CIJoe&lt;/a&gt;&amp;nbsp;lacks a great many things that I usually expect in a CI server. &amp;nbsp;But it is now unclear if they are really all that needed, or just added by CI marketing. &amp;nbsp;Here is now I usually get CIJoe working with the least Effort.&lt;br /&gt;
&lt;br /&gt;
The abridged version:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;code&quot;&gt;# rvm&lt;br /&gt;
$ bash &amp;lt; &amp;lt;( curl http://rvm.beginrescueend.com/releases/rvm-install-head )&lt;br /&gt;
$ rvm install 1.8.7-p330&lt;br /&gt;
$ rvm use 1.8.7-p330@foo --create&lt;br /&gt;
$ gem install bundler&lt;br /&gt;
$ gem install cijoe&lt;br /&gt;
$ git clone git://github.com/XXX/foo.git&lt;br /&gt;
$ nohup cijoe -p 4000 foo &amp;amp;&lt;br /&gt;
$ git config --add cijoe.runner &quot;bundle install 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; rake ci&quot;&lt;/div&gt;&lt;br /&gt;
&amp;nbsp;  &lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;1. Install RVM&lt;/span&gt;&lt;br /&gt;
Docs on installing RVM are &lt;a href=&quot;http://rvm.beginrescueend.com/rvm/install/&quot;&gt;here&lt;/a&gt;. &amp;nbsp;I use this to isolate my version of Ruby and Gems from other CIJoe servers that I will setup on the same machine. &amp;nbsp;This reduces the headaches later&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;1.1. Install Ruby&lt;/span&gt;&lt;br /&gt;
With RVM installing new versions of ruby is easy with RVM. &amp;nbsp;For example I use ruby 1.8.7-p330 on one of my projects so I run &lt;span class=&quot;code&quot;&gt;rvm install 1.8.7-p330&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;1.2. Create a Gemset&lt;/span&gt;&lt;br /&gt;
For every CI Server I run I use a different gem set. &amp;nbsp;Yes it uses more disk space but it saves so many headaches if two repos need different versions of different gems it is worth it. &amp;nbsp;You can run it with the &quot;rvm use&quot; command. &amp;nbsp;Lets say my project is called &quot;FOO&quot; I would run &lt;span class=&quot;code&quot;&gt;rvm use 1.8.7-p330@foo --create&lt;/span&gt; to switch to ruby &lt;b&gt;1.8.7-p330&lt;/b&gt; using gemset &lt;b&gt;foo&lt;/b&gt;&amp;nbsp;and create the gemset if it doesn&#39;t already exist.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;1.3. Install Bundler&lt;/span&gt;&lt;br /&gt;
Bundler is a manager for RubyGems that aids in getting the correct version of the correct gems installed. &amp;nbsp;I install it so that before every make I run &quot;bundle install&quot; to make sure that the CI server has all the gems it needs for the test to pass. &amp;nbsp;And if it fails because of a gem problem then it means the correct gems were not noted in the GemFile and therefore it will break in production.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;2. Install CIJoe&lt;/span&gt;&lt;br /&gt;
CIJoe is a gem and it should be installed in the same gemset that the repo will be run out of. &amp;nbsp;So if the code to test is Foo then run &lt;span class=&quot;code&quot;&gt;rvm use 1.8.7-p330@foo --create; gem install cijoe&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;2.1. Clone your repo&lt;/span&gt;&lt;br /&gt;
&lt;span class=&quot;code&quot;&gt;git clone git://github.com/XXX/foo.git&lt;/span&gt; or something like that&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;2.2. Run CIJoe&lt;/span&gt;&lt;br /&gt;
I run it with nohup and &amp;amp; so it is in the background and always running. &amp;nbsp;I also give it a known port so that I blow a hole in the firewall for it (see below for why).&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;code&quot;&gt;nohup cijoe -p 4000 foo &amp;amp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;3. Configure your repo&lt;/span&gt;&lt;br /&gt;
This is both a strength and weakness of CIJoe: all the configuration for what to run and how is stored in the Git repo. &amp;nbsp;The strength is that it is stored with the repo, the weakness is that .git/config and .git/hooks are not pushed to remote repos so if you need to start over you will need to recreate that config from scratch.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;3.1. Add the runner command&lt;/span&gt;&lt;br /&gt;
CIJoe only allows running of a single command but it executes it in &quot;sh&quot; so you can use &quot;&amp;amp;&amp;amp;&quot; to chain them. &amp;nbsp;However, to make things easier on myself I always create a rake task called &quot;ci&quot; and execute that. Also, to make sure that all the gems are correctly installed I run bundler first.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-small;&quot;&gt;&lt;i&gt;Note: When chaining commands via &amp;amp;&amp;amp; make sure you redirect stderr to stdout since CIJoe only reads stdout, otherwise you will get nothing in the output file. &amp;nbsp;The last command will already have stderr redirected so do not do it twice.&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;code&quot;&gt;git config --add cijoe.runner &quot;bundle install 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; rake ci&quot;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;3.2. Adding Notification&lt;/span&gt;&lt;br /&gt;
CIJoe just executes .git/hook/build-failed on a failure and .git/hook/build-worked on a success. &amp;nbsp;The &lt;a href=&quot;https://github.com/defunkt/cijoe/tree/master/examples&quot;&gt;examples&lt;/a&gt; show how to setup these up.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;4. Auto build&lt;/span&gt;&lt;br /&gt;
Any HTTP PUSH to CIJoe will cause a build to be kicked off. &amp;nbsp;This is useful for auto-kicking a build when a new commit is pushed.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;4.1. Blow a hole in the Firewall&lt;/span&gt;&lt;br /&gt;
This is so that github can get into your build server, since I assume you have your build server behind a firewall. &amp;nbsp;Your config will depend on Firewall/Router.&lt;br /&gt;
&lt;br /&gt;
4.2. Configure Github&lt;br /&gt;
Just add a &lt;a href=&quot;http://help.github.com/post-receive-hooks/&quot;&gt;Post-Receive&lt;/a&gt; hook via the github admin screen.</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/3446673574426442327/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/02/continuous-integration-with-cijoe.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3446673574426442327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3446673574426442327'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/02/continuous-integration-with-cijoe.html' title='Continuous Integration with CIJoe'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-2952741361961610778</id><published>2011-02-25T13:46:00.000-05:00</published><updated>2011-02-25T13:46:21.797-05:00</updated><title type='text'>BDD and TDD</title><content type='html'>Apparently I have been doing TDD wrong for years (who knew?). &amp;nbsp;There are plenty of resources out there to help, but here are the ones I found useful in the order of most to least useful.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;http://lizkeogh.com/2007/06/13/bdd-tdd-done-well/&quot;&gt;BDD - TDD done well?&lt;/a&gt;&amp;nbsp; Basically explained away my misconceptions about BDD. &amp;nbsp;I thought it a replacement to TDD (unit testing) and it is not.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.youtube.com/watch?v=VLEgp1189dk&quot;&gt;Roman Numeral Kata in Ruby&lt;/a&gt;. &amp;nbsp;I had thought the point of testing was to test working code and never really bridged to understand how good code comes from bad code that is tested. &amp;nbsp;The video shows by adding tests that define the &quot;correct&quot; code 1 at a time and coding only to the current tests that&amp;nbsp;elegant&amp;nbsp;solutions are likely.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://sites.google.com/site/tddproblems/&quot;&gt;TDD Problem.&lt;/a&gt;&amp;nbsp; It is nice to read articles and watch movies, but until you have actually attempted to solve a real life TDD problem things are not going to make sense. &amp;nbsp;This site is full of problems.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aslakhellesoy/cucumber/wiki/gherkin&quot;&gt;Gherkin&lt;/a&gt;. &amp;nbsp;This is the language used by Cucumber to define BDD.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://vimeo.com/2305392&quot;&gt;How I Learned To Love Testing&lt;/a&gt;. &amp;nbsp;Demo of by testing up a test env things are easier.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/questions/2341998/how-to-adopt-tdd-and-ensure-adherence&quot;&gt;How to adopt TDD and ensure adherence?&lt;/a&gt;&amp;nbsp; The question posed is strikingly similar to oppositions that I have heard in the past; mostly stemming from misconceptions about testing. &amp;nbsp;The selected answer is well formed, and is the main reason to read the question.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.rubykoans.com/&quot;&gt;Ruby Koans&lt;/a&gt;. &amp;nbsp;Helps people learn ruby by presenting tests that all fail and requiring the user to code until they pass. &amp;nbsp;The tests are very basic, but it is a good&amp;nbsp;foray&amp;nbsp;into TDD&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aslakhellesoy/cucumber/wiki/tutorials-and-related-blog-posts&quot;&gt;Cucumber Tutorials and Related Blog Posts&lt;/a&gt;. &amp;nbsp;Just a list of other resources.&lt;/li&gt;
&lt;/ol&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/2952741361961610778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/02/bdd-and-tdd.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/2952741361961610778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/2952741361961610778'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/02/bdd-and-tdd.html' title='BDD and TDD'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-3459057924810224667</id><published>2011-02-09T09:06:00.001-05:00</published><updated>2011-08-17T09:04:29.570-04:00</updated><title type='text'>Sinatra Proxy</title><content type='html'>Anyone that has developed for the web has come accross the &lt;a href=&quot;http://en.wikipedia.org/wiki/Same_origin_policy&quot;&gt;Same Origin Policy&lt;/a&gt; problem. &amp;nbsp;Basically it says that for an AJAX call to be valid it must come from the same host as the page, using the same port, subdomain, and protocol. &amp;nbsp;I find there are many cases where I need to gather information from remote hosts, which leaves me two choices: proxy the request through my server, or use an iframe.&lt;br /&gt;
&lt;br /&gt;
In general since it is JSON/XML data I am interested in then an iframe will not work so I have to create a proxy. &amp;nbsp;Below is what I do using Sinatra.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Required Gems&lt;/span&gt;&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Sinatra&lt;/li&gt;
&lt;li&gt;Net::HTTP&lt;/li&gt;
&lt;li&gt;CGI&lt;/li&gt;
&lt;li&gt;URI&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Directory Structure&lt;/span&gt;&lt;/div&gt;&lt;div&gt;This is just the default that I use for all sinatra apps because it makes things easier. &amp;nbsp;Sinatra will automatically serve files in the public directory if a file by that name exists&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;index.html - static file that includes all other files&lt;/li&gt;
&lt;li&gt;public - place where other static files will go&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;stylesheets&lt;/li&gt;
&lt;li&gt;images&lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;server.rb - the sinatra app&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Server.rb&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;script src=&quot;https://gist.github.com/1151485.js?file=server.rb&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;/remote/*&lt;/b&gt;&lt;br /&gt;
The * tells sinatra to blindly take everything up to the &quot;?&quot; and put it in &lt;span class=&quot;code&quot;&gt;params[:splat]&lt;/span&gt;. &amp;nbsp;I then split out the first item for the server name and port. &amp;nbsp;You could also split out username/password if you want, but I rarely use HTTP Basic Auth so I never do.&lt;br /&gt;
&lt;br /&gt;
I delete the key &#39;splat&#39; and anything I do not want to forward so that they are not URL encoded to the remote.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;urlencode&lt;/b&gt;&lt;br /&gt;
A simple function that re-encodes params that were placed in the params hash. &amp;nbsp;Sinatra does a good job of taking params that look like &lt;span class=&quot;code&quot;&gt;color[]=red&amp;amp;color[]=blue&lt;/span&gt; and turning them into &lt;span class=&quot;code&quot;&gt;{&quot;color&quot; =&amp;gt; [&quot;red&quot;,&quot;blue&quot;]}&lt;/span&gt;. &amp;nbsp;Therefore I check the value&#39;s class for Array and if so I add the &quot;[]&quot; to the param name.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Proxy&lt;/b&gt;&lt;br /&gt;
The proxy code is a call to Net::HTTP returning the response body. &amp;nbsp;I could also have returned the headers, but in this case I do not care.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151485.js?file=proxy.rb&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;In Action&lt;/span&gt;&lt;br /&gt;
&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt; &lt;td&gt;/remote/medic/this&lt;/td&gt;&lt;td&gt;-&amp;gt;&lt;/td&gt;&lt;td&gt;/medic:80/this&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;/remote/medic:4567/this&lt;/td&gt;&lt;td&gt;-&amp;gt;&lt;/td&gt;&lt;td&gt;/medic:4567/this&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;/remote/medic:4567/this?_dc=1&lt;/td&gt;&lt;td&gt;-&amp;gt;&lt;/td&gt;&lt;td&gt;/medic:4567/this&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;/remote/medic/this?foo=bar&lt;/td&gt;&lt;td&gt;-&amp;gt;&lt;/td&gt;&lt;td&gt;/medic:80/this?foo=bar&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt; &lt;td&gt;/remote/medic/this?foo[]=bar&amp;amp;foo[]=baz&lt;/td&gt;&lt;td&gt;-&amp;gt;&lt;/td&gt;&lt;td&gt;/medic:80/this?foo[]=bar&amp;amp;foo[]=baz&lt;/td&gt; &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/3459057924810224667/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/02/sinatra-proxy.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3459057924810224667'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3459057924810224667'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/02/sinatra-proxy.html' title='Sinatra Proxy'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-8242989216509026134</id><published>2011-01-28T20:10:00.000-05:00</published><updated>2011-01-28T20:10:49.164-05:00</updated><title type='text'>RVM</title><content type='html'>It looks like I am the last hold out for the use of RVM. &amp;nbsp;What prompted my move? &amp;nbsp;Well Macs come with ruby 1.8.7, but I always use ruby 1.9 in my projects. &amp;nbsp;I replaced the standard system ruby with my own 1.9 version. &amp;nbsp;The problem came when starting a new job since they used ruby 1.8.7 (a patch level greater then supplied my Apple), and JRuby.&lt;br /&gt;
&lt;br /&gt;
I spend a few hours just trying to get JRuby to play nice with the 1.9 version that I had installed. &amp;nbsp;Things where not going well. &amp;nbsp;The problems were almost entirely related to path-ing, but I am ruby programer not a system administrator. &amp;nbsp;I was almost ready to give up when I remembered a lightning talk given at the &lt;a href=&quot;http://bmoreonrails.org/&quot;&gt;B&#39;more on Rails&lt;/a&gt; group on &lt;a href=&quot;http://rvm.beginrescueend.com/&quot;&gt;RVM&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
In case you, like me, have been hiding under a rock for the last few years RVM is a Ruby Version Manager. &amp;nbsp;In a nutshell it lets you install any or all versions of ruby in their own sandbox&amp;nbsp;(you would be insane to install &quot;all&quot;, but you can!). &amp;nbsp;It solves all the problems associated with having different ruby versions installed and allows you to easily switch ruby versions with &lt;span class=&quot;code&quot;&gt;rvm use XXX&lt;/span&gt; where XXX is the version of ruby of you want.&lt;br /&gt;
&lt;br /&gt;
More then just being able to switch ruby versions; you are able to create arbitrary sandboxes for gems called GemSets. &amp;nbsp;This is most useful for me in trying to figure out what gems are used in what projects. &amp;nbsp;In most of my projects now I have a &lt;span class=&quot;code&quot;&gt;.rvmrc&lt;/span&gt; files containing the version of ruby and the GemSet to use for that project: &lt;span class=&quot;code&quot;&gt;echo &quot;rvm use XXX@YYY --create&quot; &amp;gt; .rvmrc&lt;/span&gt;, where XXX is the ruby version and YYY is the GemSet to use. &amp;nbsp;&lt;span class=&quot;code&quot;&gt;--create&lt;/span&gt; just creates a GemSet if it doesn&#39;t exist.&lt;br /&gt;
&lt;br /&gt;
Your results may vary, but for me all I had to do was:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Follow the quick install:&amp;nbsp;&lt;span class=&quot;code&quot;&gt;bash &amp;lt; &amp;lt;( curl http://rvm.beginrescueend.com/releases/rvm-install-head )&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;List the rubies I could install:&amp;nbsp;&lt;span class=&quot;code&quot;&gt;rvm list known&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Install a few rubies:&amp;nbsp;&lt;span class=&quot;code&quot;&gt;rvm  install XXX&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Switch to a ruby and test it: &lt;span class=&quot;code&quot;&gt;rvm use XXX&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Setup and a .rvmrc file: &lt;span class=&quot;code&quot;&gt;echo &quot;rvm use XXX@YYY --create&quot; &amp;gt; .rvmrc&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;I have not yet played around enough to see if I could use a GemSet to create a &lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler&lt;/a&gt; package. &amp;nbsp;It seems that they are made to &lt;a href=&quot;http://rvm.beginrescueend.com/integration/bundler/&quot;&gt;play nice with each other&lt;/a&gt;, so I foresee no reason it will not be as straight forward as this.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/8242989216509026134/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/01/rvm.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/8242989216509026134'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/8242989216509026134'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/01/rvm.html' title='RVM'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-6380031586176582200</id><published>2011-01-10T09:19:00.000-05:00</published><updated>2011-01-28T20:27:02.267-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="opinion"/><category scheme="http://www.blogger.com/atom/ns#" term="rant"/><title type='text'>Problems with Agile Implementation</title><content type='html'>I really like agile programming. &amp;nbsp;It keeps me close to the action, and makes me have to think about my next moves. &amp;nbsp;It also keeps me informed as to what is going on around me. &amp;nbsp;But in my many years of using agile I realize that, though the process itself is very nice, its implementations can tend not to be.&lt;br /&gt;
&lt;br /&gt;
Problem don&#39;t arise from agile itself, but who and how it was implemented. &amp;nbsp;If the&amp;nbsp;implementer&#39;s&amp;nbsp;goals do not match&amp;nbsp;the &lt;a href=&quot;http://en.wikipedia.org/wiki/Agile_software_development&quot;&gt;Agile Manifesto&lt;/a&gt;&amp;nbsp;there is little chance of success. &amp;nbsp;I have used&amp;nbsp;&lt;a href=&quot;http://en.wikipedia.org/wiki/Scrum_(development)&quot;&gt;scrum&lt;/a&gt;&amp;nbsp;many times and the most common problems I see are:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Agile as Micromanagement&lt;/li&gt;
&lt;li&gt;Agile as a Whip&lt;/li&gt;
&lt;li&gt;Agile as an Excuse&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Agile as Micromanagement&lt;/span&gt;&lt;/div&gt;&lt;div&gt;It looks like:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Having to break task down into hourly segments of work&lt;/li&gt;
&lt;li&gt;Having to break task that logically have to be done by a single person&lt;/li&gt;
&lt;li&gt;Having to account for ALL time taken, even time not related to code like attending meetings&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;Agile tenet misused:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Individuals and interactions over process and tools&lt;/li&gt;
&lt;li&gt;Working software over comprehensive documentation&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;This happens when a manager (&lt;a href=&quot;http://en.wikipedia.org/wiki/Scrum_(development)#.E2.80.9CChicken.E2.80.9D_roles&quot;&gt;chicken role&lt;/a&gt;) is the Scrum Master or when the Product Owner has say over implementation specifics. &amp;nbsp;It is a confusion of roles, which in turn leads to a confusion of goals, which in turn leads to over documentation.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Agile as a Whip&lt;/span&gt;&lt;/div&gt;&lt;div&gt;It looks like:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Filtering a&amp;nbsp;burn-down&amp;nbsp;on an individual basis&lt;/li&gt;
&lt;li&gt;Placing more in the sprint then can be done (but still requiring it all to be done)&lt;/li&gt;
&lt;li&gt;Associating points with people (publicly)&lt;/li&gt;
&lt;li&gt;Associating number of tasks done with effort&lt;/li&gt;
&lt;li&gt;Associating points with hours&lt;/li&gt;
&lt;li&gt;Basically anything where measured output is more important then people&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;Agile tenet misused:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Individuals and interactions over process and tools&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Anytime you associate numbers with people you have created a &lt;a href=&quot;http://en.wikipedia.org/wiki/Crab_mentality&quot;&gt;crab mentality&lt;/a&gt;. &amp;nbsp;Their focus will stop being on software, but on making their numbers better. &amp;nbsp;Those that are better at number games will succeed, those that are better at software will fail.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Anytime you put your people under undo pressure then simple mistakes are made. &amp;nbsp;This is going to later erode confidence in the team. &amp;nbsp;It is going to happen like this: &quot;you missed a comma in a Javascript file which causes it not to work in IE. &amp;nbsp;That was such a simple mistake to have tested for that I am not sure you are testing any of your code.&quot; &amp;nbsp;The problem was caused by 4 hours of sleep in 72 hours of coding at the end of an over-extended sprint. &amp;nbsp;The programmer was nearly delirious. &amp;nbsp;It is shocking it was the only mistake, not that it was a simple mistake!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Unfortunately I have seen this situation start innocent enough, with comments like &quot;we don&#39;t want to over work the staff&quot; or &quot;we want to make sure they always have something to do&quot; or &quot;we want them working on the correct things&quot;. &amp;nbsp;If the &quot;we&quot; in question is management (&lt;a href=&quot;http://en.wikipedia.org/wiki/Scrum_(development)#.E2.80.9CChicken.E2.80.9D_roles&quot;&gt;chicken role&lt;/a&gt;) then there is probably already micromanagement going on, and Agile is being used as whip to solve the problems created by the bad implementation.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Agile as an Excuse.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;It looks like:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;&quot;You said it would take XXX. &amp;nbsp;It took YYY. &amp;nbsp;You need to make up the difference out of your own time&quot;&lt;/li&gt;
&lt;li&gt;&quot;We cannot slip these date, and you have already pared back the release N sprints ago. &amp;nbsp;You need to put in extra effort&quot;&lt;/li&gt;
&lt;li&gt;&quot;Agile is about being agile. &amp;nbsp;Even though we are mid sprint we are radically changing direction, but we are not canceling the sprint or doing sprint planning. &amp;nbsp;We are just swapping out some tasks for others.&quot;&lt;/li&gt;
&lt;li&gt;&quot;You picked the language. &amp;nbsp;It is now your problem to bring this project to conclusion and under budget.&quot;&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;Agile tenet misused:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Individuals and interactions over process and tools&lt;/li&gt;
&lt;li&gt;Responding to change over following a plan&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
These are all excuses I have heard. &amp;nbsp;Each time given by a person in&amp;nbsp;&lt;a href=&quot;http://en.wikipedia.org/wiki/Scrum_(development)#.E2.80.9CChicken.E2.80.9D_roles&quot;&gt;chicken&amp;nbsp;role&lt;/a&gt;&amp;nbsp;(managers) because they are ignoring changes in the field (military term). &amp;nbsp;Every choice has a set of outcomes: some good, some bad. &amp;nbsp;The attempt with agile is not to mitigate bad outcomes, but to allow those outcomes to contribute to the overall direction.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Sometimes the bad outcome will be that something took to long, or that one language/tool was not the correct choice given the problem set. &amp;nbsp;If for every problem that happens the developer has to take their own time, or face&amp;nbsp;embarrassment, to solve the problem then they will stop making choices. &amp;nbsp;Not just choices that might have bad outcomes, but choices altogether. &amp;nbsp;At which point someone in a &lt;a href=&quot;http://en.wikipedia.org/wiki/Scrum_(development)#.E2.80.9CChicken.E2.80.9D_roles&quot;&gt;chicken role&lt;/a&gt; will start making more choices then they should.&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/6380031586176582200/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/01/problems-with-agile-implementation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/6380031586176582200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/6380031586176582200'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2011/01/problems-with-agile-implementation.html' title='Problems with Agile Implementation'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-1260324603444475373</id><published>2010-12-15T15:00:00.001-05:00</published><updated>2011-08-17T08:49:56.202-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="howto"/><category scheme="http://www.blogger.com/atom/ns#" term="JS"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><title type='text'>JS Unit Testing using YUI</title><content type='html'>Ok, Selenium was a partial success that you can read about &lt;a href=&quot;http://randomsoftwareinklings.blogspot.com/2010/11/learning-selenium.html&quot;&gt;here&lt;/a&gt;, but it really was harder then I wanted it to be and it required a lot of setup for only a little bit of testing. &amp;nbsp;I noticed that a project that I use a lot ExtJS uses a modified version of YUI Test. &amp;nbsp;So I converted my Selenium test example to YUI Test.&lt;br /&gt;
&lt;br /&gt;
My initial impression was confusion because you have to download the entire YUI 2 suite just get to get the &lt;a href=&quot;http://developer.yahoo.com/yui/yuitest/&quot;&gt;test tools&lt;/a&gt;. &amp;nbsp;Also, when you download the entire suite they are many different copies of the same file in various stages of minification. &amp;nbsp;But following the documentation I boiled down exactly what I needed and threw away the rest. &amp;nbsp;I put all the code for my test &lt;a href=&quot;https://github.com/jkamenik/yui-test-example&quot;&gt;here&lt;/a&gt; so you can follow along.&lt;br /&gt;
&lt;br /&gt;
You will need the following files from the YUI archive:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;build/logger/assets/logger.css&lt;/li&gt;
&lt;li&gt;build/logger/logger.js&lt;/li&gt;
&lt;li&gt;build/yuiloader-dom-event/yuiloader-dom-event.js&lt;/li&gt;
&lt;li&gt;build/yuitest/assets/testlogger.css&lt;/li&gt;
&lt;li&gt;build/yuitest/yuitest.js&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;Create an HTML file that includes the css and js files:&lt;/div&gt;&lt;script src=&quot;https://gist.github.com/1151464.js?file=index-header.html&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;The HTML will also need to instantiate the test logger and run the test runner global. &amp;nbsp;I bound the test run to a button so I could control when it ran:&lt;/div&gt;&lt;script src=&quot;https://gist.github.com/1151464.js?file=index-body.html&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
Now that we have a &lt;span class=&quot;code&quot;&gt;YAHOO.tool.TestRunner&lt;/span&gt; we need to add a test that can be run.  Instanciate a &lt;span class=&quot;code&quot;&gt;new YAHOO.tool.TestCase&lt;/span&gt; and add it to the TestRunner. &amp;nbsp;All a TestCase needs is a name and a bunch of functions that start with &quot;test&quot;. &amp;nbsp;Everything else is automatic. &amp;nbsp;Below is a simplified version of the &lt;a href=&quot;https://github.com/jkamenik/yui-test-example/blob/master/test.js&quot;&gt;full file&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151464.js?file=test.js&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Conclusion&lt;/span&gt;&lt;br /&gt;
Though Selenium is more automated that comes at the cost of being more complicated then testing should be. &amp;nbsp;If I were a team of programmers then sure setting up and maintaining Selenium Remote controls would be a small part of the overall effort, but since I am not a team of programmers I think it is overkill.&lt;br /&gt;
&lt;br /&gt;
What I really need is an easy to run, easy to write, repeatable, unit testing framework in JS. &amp;nbsp;I do the leg work on pointing my various browsers at the html and reviewing the results. &amp;nbsp;When things get big enough that I need to setup a continuous integration server, or I have a QA department, then I will give Selenium another go. &amp;nbsp;For now YUI test is the way to go.</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/1260324603444475373/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/12/js-unit-testing-using-yui.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/1260324603444475373'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/1260324603444475373'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/12/js-unit-testing-using-yui.html' title='JS Unit Testing using YUI'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-4317933392938211590</id><published>2010-12-12T13:15:00.000-05:00</published><updated>2010-12-13T20:18:44.618-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="email"/><category scheme="http://www.blogger.com/atom/ns#" term="howto"/><category scheme="http://www.blogger.com/atom/ns#" term="opinion"/><title type='text'>Dealing with Email overload</title><content type='html'>This isn&#39;t&amp;nbsp;strictly&amp;nbsp;software related, but a lot of us have to deal with the horror that is email. &amp;nbsp;Email is not a good solution to any problem, but it is&amp;nbsp;ubiquitous so it is used for all things: personal&amp;nbsp;correspondence, commit tracking, defect notification, task notification, etc... &amp;nbsp;Email is all to often used as a mechanism to pass-the-buck.&lt;br /&gt;
&lt;br /&gt;
The only way to deal with this email overload is to set&amp;nbsp;boundaries&amp;nbsp;on email usage. &amp;nbsp;A lot of people will find these&amp;nbsp;boundaries&amp;nbsp;annoying, if not unworkable. &amp;nbsp;Just stick with it and lead by example. &amp;nbsp;Eventually, in a time of high stress, you will be able to get to important messages fast and they will be left floundering.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;My results&lt;/span&gt;&lt;br /&gt;
I am putting the summary first in hopes that you might actually try some of these suggestions. &amp;nbsp;On average I get 50 to 200 work related emails a day. &amp;nbsp;In any given day (saturday and sunday included) I will need to respond to 5 to 10 of them. &amp;nbsp;That means that I get between 40 and 195 junk emails that pass (and should pass) my junk mail filters.&lt;br /&gt;
&lt;br /&gt;
By focusing on making sure my inbox only includes import emails I have reduced my inbox to 3 to 5 message each check (and I check twice a day). &amp;nbsp;On busy days I only ever check my inbox, relying on others to contact me via a different medium if they really need me. &amp;nbsp;When there is a less busy day I go through the other inbox and deal with those messages.&lt;br /&gt;
&lt;br /&gt;
At the time I am writing this (on sunday) I have 1 email in my inbox and 91 emails in my other inbox that were all sent saturday. &amp;nbsp;A quick read of the 1 email and I will have to spend 5 minutes reviewing code, but the rest can wait until monday.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Use the right messaging system&lt;/span&gt;&lt;br /&gt;
Use email for messages that do not need an immediate response. &amp;nbsp;Since there is no message size limit, make sure you use the most of it. &amp;nbsp;Write emails that are well detailed and specific. &amp;nbsp;If at any point a message can be answered with &quot;Ok&quot; or some other monosyllabic word then email was not the correct choice.&lt;br /&gt;
&lt;br /&gt;
Use Instant Messages (IM,IRC,etc...) for conversations that need semi immediate responses, and a&amp;nbsp;possible&amp;nbsp;record. &amp;nbsp;Many IM packages can log conversations for later viewing, which is useful when you forget things. &amp;nbsp;There are message length and formatting restrictions so this forces the messages to be&amp;nbsp;brief&amp;nbsp;and specific.&lt;br /&gt;
&lt;br /&gt;
Use Phone, Skype, or face to face contact when a response is needed immediately.&lt;br /&gt;
&lt;br /&gt;
Use Twitter, or some other global message posting service to track commits, continuous integration fails, defects, etc...&lt;br /&gt;
&lt;br /&gt;
Use a wiki, or blog to track generic instructions or documentation.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Check email twice a day (only)&lt;/span&gt;&lt;br /&gt;
The easiest way to train&amp;nbsp;people&amp;nbsp;not to use email as a crutch is not use it as one yourself. &amp;nbsp;Only check email twice a day, and give a concerted effort to reduce that to once a day within a month, and once a week within 6 months. &amp;nbsp;Setup the other messaging systems so that email is used only for what it needs to be. &amp;nbsp;And when people step out of line, correct them.&lt;br /&gt;
&lt;br /&gt;
Email should not be the first thing that you check in the morning, as you should not be working out of email. &amp;nbsp;It should also not be the last thing you check as it will disrupt your already stressful commute/home life. &amp;nbsp;Instead check email 2 hours after the start of the day, and 2 hours before the end of the day. &amp;nbsp;If you work a 9-to-5 that means once at 11am and once at 3pm.&lt;br /&gt;
&lt;br /&gt;
The day you start doing this, tell the people that send you the most email and the ones that you will most affect by the change, and no one else. &amp;nbsp;You are not trying to be sneaky, but if you blast an email to everyone then you are going to make it a big deal; which it is not. &amp;nbsp;The others will learn over time.&lt;br /&gt;
&lt;br /&gt;
When someone tells you that you need to check your email and read their message do it, but only check their email, ignore all other messages.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Don&#39;t use Inbox as an archive&lt;/span&gt;&lt;br /&gt;
Once you are finished reading an email, either deal with the email and delete it or archive it. &amp;nbsp;Create a&amp;nbsp;separate&amp;nbsp;archive folder and move emails there if you need to save them. &amp;nbsp;Your goal is aways to reduce the inbox to zero&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Create an other inbox&lt;/span&gt;&lt;br /&gt;
Create a filter that will dump any messages where you are not the direct recipient into a different folder. &amp;nbsp;This should be the first filter you create, but the last filter that is applied to any message. &amp;nbsp;Create as many other filters and folders as you can to move useless messages round. &amp;nbsp;The goal is to remove any message that you do not need to act on so that what is left is just what is important. &amp;nbsp;The trash can is a good folder to dump things that are truly useless.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Use the server&#39;s filtering mechanism&lt;/span&gt;&lt;br /&gt;
When possible have server do all the filtering that way you can use multiple email clients and will not be&amp;nbsp;dependent&amp;nbsp;on leaving an email client on. &amp;nbsp;All Exchange servers and many online email services can do server filtering. &amp;nbsp;When using Outlook, be careful, the ease of creating filters is sometimes offset by the fact that it lies about what filters can be saved to the server. &amp;nbsp;Though harder, since MS didn&#39;t spend much time on the&amp;nbsp;server-side&amp;nbsp;features of Exchange, it is better to build the filters on the server directly.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Reduce the inbox to zero (in one sitting)&lt;/span&gt;&lt;br /&gt;
The goal is always to reduce your inbox to zero. &amp;nbsp;Once all the filters are in place, the only thing left to actually deal with all the important emails (at least that is the hope). &amp;nbsp;When dealing with email decide if the email should be dealt with, deferred, or archived.&lt;br /&gt;
&lt;br /&gt;
If time is really an issue then find the messages to archive and defer first, and move them. &amp;nbsp;That way what is left is just the stuff to deal with now.</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/4317933392938211590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/12/dealing-with-email-overload.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/4317933392938211590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/4317933392938211590'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/12/dealing-with-email-overload.html' title='Dealing with Email overload'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-804548736477533961</id><published>2010-12-05T23:00:00.000-05:00</published><updated>2010-12-13T20:18:25.578-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="howto"/><category scheme="http://www.blogger.com/atom/ns#" term="opinion"/><title type='text'>How to be a bad boss</title><content type='html'>Being a boss is a complicated thing. It is your job to get people to do things, sometimes things they do not want to do. And it is especially complicated in the software industry, where it is like herding cats.&lt;br /&gt;
&lt;br /&gt;
I see a lot of posts on how to be a good boss, but the problem there is that they often forget to mention the things that can and will immediately erode any success you might have had. I am going to assume that as a reader you strive to be a person who others are willing to follow.&lt;br /&gt;
&lt;br /&gt;
Nobody is perfect, so you will probably have done at least one of these things in the past. &amp;nbsp;Or you do then without even knowing. &amp;nbsp;Now is your chance to stop, and be a constructive boss who people want to work for.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Be smarmy&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Yep, this is number one.&amp;nbsp;Smarmy people are falsely friendly (think used car dealers). &amp;nbsp;Notice I did not say do not &quot;be mean.&quot; As a boss sometimes you will need to be mean, or at least not very nice.  And even that should be kept to a minimum, but it is never OK to be smarmy.  All it does is make people uncomfortable confiding in you, which means they will either find better employment (if they can) or they will sabotage you.&lt;br /&gt;
&lt;br /&gt;
Here are some examples of what I mean by smarmy:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Suggest that a problem can be solved by the employee working overtime&lt;/li&gt;
&lt;li&gt;Using scrum (or any general meeting) to point out an employee&#39;s failure&lt;/li&gt;
&lt;li&gt;Joke about firing an employee&lt;/li&gt;
&lt;li&gt;Make any sarcastic comment about an employee in the presence of others&lt;/li&gt;
&lt;li&gt;Only talking to an employee when you need them to do something (i.e., I am only talking to you because I want something)&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;Some ways to tell that you have already failed at this:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;All laughter stops when you enter the room&lt;/li&gt;
&lt;li&gt;Conversations between you and an employee die quickly&lt;/li&gt;
&lt;li&gt;Employees no longer tell you about issues they are having&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Be a&amp;nbsp;hypocrite&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
A hypocrite is someone who says one thing and thinks or does another. &amp;nbsp;It can be as overt as being prejudiced (racist, sexist, etc...) or as covert as being passive aggressive. &amp;nbsp;Being a hypocrite is risky because when other people find out, you are just being deceitful, and no one wants to deal with that.&lt;br /&gt;
&lt;br /&gt;
One example is saying that code needs to be high quality but not leaving time for testing. &amp;nbsp;Overtly this often resembles&amp;nbsp;explicitly&amp;nbsp;adding a testing phase at the end of coding and then cutting it if coding goes long. &amp;nbsp;Covertly this is often assuming that testing is done as part of coding, but&amp;nbsp;scoffing&amp;nbsp;at longer estimates. &amp;nbsp;Speed and quality have a tenuous relationship. &amp;nbsp;Something done slowly may or may not be of quality, but something quickly is almost never of quality.&lt;br /&gt;
&lt;br /&gt;
The best way not to be a hypocrite is not to bring your values and prejudices into the equation. &amp;nbsp;To do this though you have to understand what your values and prejudices are. &amp;nbsp;In the case of the no testing, quality is&amp;nbsp;explicitly&amp;nbsp;stated as the important part, but speed is implicitly stated as the important factor. &amp;nbsp;More specifically speed at the cost of quality is stated as the import factor. &amp;nbsp;To avoid this, it is important to explicitly state the importance of all three tenets of the project&amp;nbsp;triangle: that way you avoid the&amp;nbsp;embarrassing&amp;nbsp;case where one&amp;nbsp;tenet goes to 0 unintentionally. &amp;nbsp;See:&amp;nbsp;&lt;a href=&quot;http://en.wikipedia.org/wiki/Project_triangle&quot;&gt;Project Triangle&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Be arrogant&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
I am not talking about being hard headed or&amp;nbsp;stubborn. &amp;nbsp;I am talking about being truly arrogant. &amp;nbsp;Stubborn people will listen to reason, assuming the&amp;nbsp;opposing&amp;nbsp;argument is good enough; arrogant people cannot be directly convinced. &amp;nbsp;There are two forms of arrogance to watch out for: arrogance of idea, and arrogance of presenter. &amp;nbsp;Arrogance of idea is simply dismissing an idea because it is in direct conflict with your internal ideas or values. &amp;nbsp;Arrogance of presenter is rejecting an idea because of who presented it.&lt;br /&gt;
&lt;br /&gt;
Two engineers approach you with different solutions to the problem of releasing on time: one says to keep the feature set and stop doing unit testing, and the other says reduce the feature set but keep the unit testing. &amp;nbsp;Which do you choose? &amp;nbsp;If you choose &quot;keep the feature set&quot; then you wrong. &amp;nbsp;If you choose &quot;keep the unit testing&quot; you are also wrong. &amp;nbsp;If you chose either (based on only the information provided) then the choice you made was based on you internal values only and not based on the problem being presented.&lt;br /&gt;
&lt;br /&gt;
In the context of work the truth is that new hires probably know more about a given framework than you, and certainly will have a different perspective on how things should be done. &amp;nbsp;They are probably offering you this knowledge, because they have already encountered the problem you are now seeing and have found an appropriate solution. &amp;nbsp;It is tempting to dismiss this simply because a new hire presented it and they don&#39;t know the full business impact of their suggestion. &amp;nbsp;But ask yourself: if the system architect presented you with this idea would you accept it? &amp;nbsp;If yes, then you are suffering from arrogance of presenter and should tread lightly.&lt;br /&gt;
&lt;br /&gt;
I suggest instead that you judge an idea (never a presenter) on its merits in the context of only the problem (never on facts you assume to be true). &amp;nbsp;And make sure you are consciously and actively judging the idea, and you are not adding your own problem or values.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Offer post hoc rewards&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
A post hoc reward is a reward given after work is complete. &amp;nbsp;In physical work the effects of post hoc rewards are well known and lead to better results, but in knowledge work offering post hoc rewards has a distracting effect. Work will take longer and be of lower quality. It will make employees very good at performing work that maximises their incentives, and not at developing a quality product. &amp;nbsp;Be aware that offering reward for work done defines a tangible value for that work, which may&amp;nbsp;demoralize&amp;nbsp;an employee who feels under compensated as a result.&lt;br /&gt;
&lt;br /&gt;
http://www.youtube.com/watch?v=u6XAPnuFjJc&lt;br /&gt;
&lt;br /&gt;
Intrinsic&amp;nbsp;rewards can and should be used. &amp;nbsp;These are things that are given universally and without reservation. &amp;nbsp;Basic examples would be vacation, and health benefits. &amp;nbsp;Other examples would be a free day to work on any project the employee wanted, or free soda/tea/coffee. &amp;nbsp;These intrinsic benefits help endear you and the company to the employee.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Accept estimates less than 4 hours&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
This may only be true in software, but tasks never take less than 4 hours. &amp;nbsp;A&amp;nbsp;task is really an atomic&amp;nbsp;complete chunk of work, so to be considered complete it must be coded, tested, reviewed, and committed. &amp;nbsp;However, some employees will&amp;nbsp;claim they finish tasks sooner. &amp;nbsp;Often these people are cutting corners (not testing, or not getting a review) or they are doing a bunch of tasks that should have logically been considered one task.&lt;br /&gt;
&lt;br /&gt;
Either way they are playing a game with the numbers to get their counts higher, which means that you are probably rewarding over aggressive estimates, and have a huge defect backlog. &amp;nbsp;Well done!&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Assume research is intrinsic&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
I have seen this bite so many bosses, where they say something like &quot;tasks cannot take longer then 4 days.&quot; &amp;nbsp;The problem is that now you have employees trying to split tasks that have little or no real research done. &amp;nbsp;So when it comes time to implement you have a bunch of small tasks that are too small to warrant research, but no real clear direction because no real research was done.&lt;br /&gt;
&lt;br /&gt;
Estimates of several weeks or months are fine early in the process. &amp;nbsp;But enough time needs to be given to investigate feasibility prior to high level scoping. &amp;nbsp;Prior to implementation, time should be given to research an&amp;nbsp;implementation&amp;nbsp;and split the tasks out. &amp;nbsp;All of this time should have been blocked in as a single unit during high level scoping.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Require meetings&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Meetings cost time and money. &amp;nbsp;And in addition to the meeting itself there is time before the meeting where people ramp down, and time after the meeting when people need to ramp back up again. &amp;nbsp;Both ramping phases are about 10 minutes each.&lt;br /&gt;
&lt;br /&gt;
To&amp;nbsp;monetize this for you, let&#39;s say you have 12 employees going to a meeting, and on average they make $24 an hour, and the meeting is 15 minutes (a scrum). &amp;nbsp;The total chunk of wasted time is 35 minutes or about $14 per employee. &amp;nbsp;For the company that means you wasted $168. &amp;nbsp;And that does not include any prep time employees had to take.&lt;br /&gt;
&lt;br /&gt;
In&amp;nbsp;general, meetings need to be kept to a minimum and kept on track because they will end up being the single most costly event both in terms of money and productivity.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Watch your employees work&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Nobody is comfortable being scrutinized. &amp;nbsp;Do not set up your office so that you are looking into your employees&#39; cubes, and don&#39;t wonder around aimlessly, and never under any&amp;nbsp;circumstances&amp;nbsp;hover. &amp;nbsp;By doing this&amp;nbsp;you are showing your employees that the most important thing is to look busy (a function of the keyboard and finger), not to solve problems (a function of the brain). &amp;nbsp;At the point where your employees feel scrutinized they will work just hard enough to not get fired, and will partake in CYA games.&lt;br /&gt;
&lt;br /&gt;
The best example is Office Space the movie. &amp;nbsp;Little more needs to be said.</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/804548736477533961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/11/how-to-be-bad-boss.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/804548736477533961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/804548736477533961'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/11/how-to-be-bad-boss.html' title='How to be a bad boss'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-1837712945471756797</id><published>2010-12-04T18:47:00.000-05:00</published><updated>2011-01-28T21:03:14.317-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="git"/><category scheme="http://www.blogger.com/atom/ns#" term="howto"/><category scheme="http://www.blogger.com/atom/ns#" term="svn"/><title type='text'>GIT with SVN</title><content type='html'>Normally, I would just use GIT without a bridge to another control system, but many companies use SVN.  There are just so many benefits to using GIT that, for me, I am going to use it if there is a bridge to repository type the company uses.  I certainly don&#39;t hate SVN or CVS or Perforce, but GIT allows me to work the way that I know I am most productive; which is commit early, commit often.&lt;br /&gt;
&lt;br /&gt;
What I mean by commit early, commit often is that I commit even if I only have part of the solution. &amp;nbsp;As I find the other parts of the solution I commit those as well. &amp;nbsp;That way when I am 2 or 3 days into a fix I already have the commit messages saved in GIT, so I don&#39;t have to remember what I did for those 2 or 3 days. &amp;nbsp;When the solution is shippable only then do I push it up to the company&#39;s repository.&lt;br /&gt;
&lt;br /&gt;
Benefits of GIT include:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Being able to share non production ready code (Peer to Peer)&lt;/li&gt;
&lt;li&gt;Being able to have many local branches&lt;/li&gt;
&lt;li&gt;Being able to logically group commits (via local branches) and push all at once&lt;/li&gt;
&lt;li&gt;Fantastic branch switching/merging&lt;/li&gt;
&lt;ol&gt;&lt;li&gt;Rarely will you ever have to fix a merge conflict yourself&lt;/li&gt;
&lt;/ol&gt;&lt;li&gt;Rebase as well as merge&lt;/li&gt;
&lt;ol&gt;&lt;li&gt;Rebase is SVNs style of linear commits&lt;/li&gt;
&lt;li&gt;Merge is non-linear and tries to keep commits sequentially ordered by date.  So if two branches are merged and both were actively worked on then the commits are intermixed.  (makes a lot more sense in practice then in writing)&lt;/li&gt;
&lt;/ol&gt;&lt;/ol&gt;&lt;br /&gt;
With GIT&#39;s power comes a little bit more complexity, and here I will detail the method that I have developed over months of fits and starts.  That way you can experience the benefits of using GIT for day to day work, but still use SVN when dealing with corporate.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Settings up the SVN Bridge&lt;/span&gt;&lt;br /&gt;
You can have GIT manage an entire SVN repository, branches and all. &amp;nbsp;However, for GIT to do this it must checkout every revision of the SVN repo. &amp;nbsp;This can be very painful when there are a lot of commits. &amp;nbsp;Instead I recommend only managing a single branch starting a specific revision near HEAD. &amp;nbsp;You will lose history older then that revision, but it does save a lot of time for large SVN repositories.&lt;br /&gt;
&lt;br /&gt;
For the below example I am going to assume we have a standard SVN repository at http://example.com/svn, the latest revision is 400, and the SVN username is test.&lt;br /&gt;
&lt;br /&gt;
Find the latest revision of the repository:&lt;br /&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;svn log http://example.com/svn/trunk | head
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The latest revision will start with an &quot;r&quot; and be within the first 5 lines.&lt;br /&gt;
&lt;br /&gt;
Setting up a git repo:&lt;br /&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;git svn init --username=test http://example.com/svn/trunk dev
git svn fetch -r 400
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now you have a local GIT repo in dev that is synced to SVN trunk at revision 400. &amp;nbsp;And when you &lt;span class=&quot;code&quot;&gt;git svn dcommit&lt;/span&gt; the user &lt;span class=&quot;code&quot;&gt;test&lt;/span&gt; will be used.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Always work in a branch&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
In my company before anything is allowed to be checked into SVN it must go through a peer review.  In any given day I might work on 2 or 3 bugs/tasks.  For each, I create a GIT branch with the bug/task ID and work there.  When I am done I use &lt;span class=&quot;code&quot;&gt;git log -p&lt;/span&gt; to list the diffs that I submit for review. Then I move on to the next item, while the fix is being reviewed.  When the first bug/task is reviewed and accepted I jump back to master, rebase it, jump to the branch, rebase master, jump back to master and merge the branch, and finally svn dcommit everything.  After I mark the bug/task complete I also delete the branch. &amp;nbsp;If the code is not accepted then I still have a branch where I can make the required corrections and repeat the process.&lt;br /&gt;
&lt;br /&gt;
It might sound complicated, but it really isn&#39;t. &amp;nbsp;The only reason for all this rebasing is so that GIT&#39;s native merge tools deal with SVN merge conflicts. &amp;nbsp;I am not sure why, but they are far better then what the SVN bridge can do, and will ultimately lead to few headaches for you.&lt;br /&gt;
&lt;br /&gt;
In code it looks like:&lt;br /&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;git&amp;gt; git svn rebase
git&amp;gt; git checkout -b task1
... work on task 1
git&amp;gt; git log -p -n X &amp;gt; task1.diff
... create diff of all (X) changes needed for task1
... email the diff for review
git&amp;gt; git checkout master
git&amp;gt; git svn rebase
git&amp;gt; git checkout -b task2
... work on task 2
... when task 1 is approved commit what you have for task 2 and dcommit task1
... return to task 2 when task 1 is committed
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
This style also works well if your boss comes over at the 11th hour and assignes you a new emergency assignment.  When you are done with the emergency switching gears is as easy as switching branches.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;DCommitting your changes&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
As states above I use the checkout, rebase, checkout, rebase, checkout, merge, dcommit style.  This seems cumbersome until you understand the purpose.&lt;br /&gt;
&lt;br /&gt;
From any branch always checkout into master.  This allows master to stay pure of your changes and makes its less likely that git svn will fail.&lt;br /&gt;
&lt;br /&gt;
Once master is up-to-date checkout the branch again and rebase the master changes into the branch.  Fixing any rebase conflicts there might be.  By fixing them on the branch we keep master clean so we are using git&#39;s rebase/merge capabilities, not the SVN bridge&#39;s.  There are times this step can be skipped, but once you have to deal with your first rebase conflict from the bridge you will wish you had branched.&lt;br /&gt;
&lt;br /&gt;
Now that the branch holds the latest code to be dcommitted to SVN: checkout master, merge the branch, and dcommit master.  You could rebase the changes from the branch if you prefer, it really makes no difference because your changes are on top of SVN now either way.  Once done all of master&#39;s log messages will be rewritten to what is in SVN.&lt;br /&gt;
&lt;br /&gt;
All together it looks like this:&lt;br /&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;git&amp;gt; git checkout master
git&amp;gt; git svn rebase
git&amp;gt; git checkout task1
git&amp;gt; git rebase master
... SVN needs your changes to be rebased so rebase master
... by rebasing onto to a branch it is easier to deal with rebase failures
... fix any rebase issues
git&amp;gt; git checkout master
git&amp;gt; git merge task1
... could merge or rebase, doesn&#39;t matter here
git&amp;gt; git svn dcommit
... mark the task closed
git&amp;gt; git brach -D task1
... svn doesn&#39;t always mark the branch merged properly so use -D instead of -d
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Pro Tips&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Different user names&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Use a different username for SVN and GIT.  That way it is easy to see in the log what is committed to SVN since the bridge will rewrite the commit log from what SVN says.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;For SVN I use jkamenik.&lt;/li&gt;
&lt;li&gt;For GIT I use jkamenik at gmail dot com.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Commits that are in SVN also have the SVN revision number in the log message, but I find it easier to use usernames since it is at the top of the log message.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Setup Aliases&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Git allows more commands to be added via aliases. &amp;nbsp;An alias can be a shortening of a git command: &lt;span class=code&gt;st = status&lt;/span&gt;. &amp;nbsp;Or it can be a shell command that git will execute &lt;span class=code&gt;test = !sh -c &#39;echo &quot;it works!&quot;&#39;&lt;/span&gt; (notice the leading !).&lt;br /&gt;
&lt;br /&gt;
Here is the alias part of my &lt;span class=code&gt;~/.gitconfig&lt;/span&gt; file looks like:&lt;br /&gt;
&lt;div class=code&gt;&lt;pre&gt;[alias]
  # Old SVN aliases
  ci = commit
  co = checkout
  st = status
  stat = status
  # stuff I find useful
  br = branch
  df = diff
  rm-all = !git st | grep deleted | awk &#39;{print $3}&#39; | xargs git rm
  add-all = !git st | grep modified | awk &#39;{print $3}&#39; | xargs git add
  st-br = &quot;!f(){ git co master &amp;&amp; git svn rebase &amp;&amp; git co -b $1 }; `f`&quot;
  up-br = &quot;!f(){ git co master &amp;&amp; git svn rebase &amp;&amp; git co $1 }; `f`&quot;
  co-br = &quot;!f(){ git up-br $1 &amp;&amp; git co master &amp;&amp; git merge $1 &amp;&amp; git svn dcommit }; `f`&quot;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Notice that &lt;span class=code&gt;st-br&lt;/span&gt;, &lt;span class=code&gt;up-br&lt;/span&gt;, and &lt;span class=code&gt;co-br&lt;/span&gt; are basically all the set of commands I noted above in single command style.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Stashing changes&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The stash is a hidden place that git can keep changes that are not yet ready to commit.  This is very useful if you get switched to another task and really don&#39;t have time to fully vet a change on the current task.  You can stash the outstanding change and later replay them.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;code&quot;&gt;git stash&lt;/span&gt; keeps a queue of changes so you can stash more then one thing, but you can only replay them top down.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;code&quot;&gt;&lt;pre&gt;git&amp;gt; git stash
... hides all modified files
git&amp;gt; git stash list
... lists all stashes
git&amp;gt; git stash apply
... applies the top stash, but does not remove it
git&amp;gt; git stash pop
... applies the top stash, and remove it&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Resources&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;GIT &amp;lt;-&amp;gt; SVN workflow: http://www.biostat.jhsph.edu/~rpeng/biostat776/workflow.html&lt;/li&gt;
&lt;li&gt;Effectively using GIT with SVN: http://www.viget.com/extend/effectively-using-git-with-subversion/&lt;/li&gt;
&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/1837712945471756797/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/12/git-with-svn.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/1837712945471756797'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/1837712945471756797'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/12/git-with-svn.html' title='GIT with SVN'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-5101777311618576016</id><published>2010-11-18T21:29:00.001-05:00</published><updated>2011-08-17T09:00:28.731-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="selenium"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><title type='text'>Learning Selenium</title><content type='html'>&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Background&lt;/span&gt;&lt;br /&gt;
My basic need is to find a platform where I can test FF, IE, and Safari on Windows, Linux, and OS X.  I use OS X as my platform, and Safari or Webkit as my environment.  I don&#39;t like Windows or IE.  Linux is OK, but I like OS X because it just works the way I want.  And I find FF to be slow, and Firebug which is needed to debug we pages causes rendering changes and timing issues (most notably causing FF to crash). &lt;br /&gt;
&lt;br /&gt;
Ideally I want the testing environment:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;To be developed in Safari on OS X&amp;nbsp;&lt;/li&gt;
&lt;li&gt;To be able to test both internal libraries and rendered UI&amp;nbsp;&lt;/li&gt;
&lt;li&gt;To use unit tests to test internal libraries&amp;nbsp;&lt;/li&gt;
&lt;li&gt;To use interactive tests to test rendered UI&amp;nbsp;&lt;/li&gt;
&lt;li&gt;To use the unit/interactive tests as regression system moving forward&amp;nbsp;&lt;/li&gt;
&lt;li&gt;To write the tests once on my browser of choice&amp;nbsp;&lt;/li&gt;
&lt;li&gt;To run the tests on all combinations of browser and platform.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;To not be bogged down by the testing framework&amp;nbsp;&lt;/li&gt;
&lt;li&gt;To be free, or very cheap&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;From my research it looked like Selenium did basically exactly what I needed. And unfortunately was the only real option.  There were plenty of options for taking screen shots of public sites (which mine isn&#39;t, yet) and comparing those between browsers.  And there are several options for unit testing javascript, but only Selenium did both and could be run on my own hardware.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Implementation&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
From my reading it looked like I wanted to use the IDE to create the tests, and remote controls to run the browsers.  Eventually I need to scale to Selenium Grid, but that is for later discussion.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Test code&lt;/span&gt;&lt;br /&gt;
This is the sample file that I created to test.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151478.js?file=code.html&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Using the IDE&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
It took a long time to understand this tool, since I had no background in Selenium.  The basic premiss is that each file is a single test case, which is a set of tests.  Each test is a grouping of three items: the action, the target, and the expected result.  It natively creates a 3 column HTML table, which it can also run, but personal preference is that use the IDE to export the basic test into a different language.&lt;br /&gt;
&lt;br /&gt;
I am a rails developer, and am familar with rspec so I use the IDE to run the tests to make sure they work, but then I transfer it to rspec since it is a more expressive test framework.  The downside is that you have to use a Remote Control to run the test, which adds an extra level of complications.  We will get to the Remote Control later.&lt;br /&gt;
&lt;br /&gt;
A basic HTML test looks like this:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151478.js?file=test.html&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
There really is nothing more to it then that.  The one thing to note is that the test uses &lt;code&gt;verifyEval&lt;/code&gt; which takes a JavaScript string.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;gotcha&quot;&gt;In all tests the &lt;code&gt;this&lt;/code&gt; object is the base Selenium object so if you want to get to the &lt;code&gt;window&lt;/code&gt; object you have to traverse up the stack via &lt;code&gt;this.browserbot.getUserWindow()&lt;/code&gt;.&lt;/div&gt;&lt;br /&gt;
&lt;div class=&quot;gotcha&quot;&gt;Unfortunately everything tested in Selenium is converted to a string before testing.  So if I need to ensure that an integer parsing function actually produces a number I need to use &lt;code&gt;typeof&lt;/code&gt;.&lt;/div&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Using RSpec&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The IDE is a great way to test scripts live, but for any programmer it is going to be easier to use a testing framework doing it programmatically.  As a rails guy I prefer RSpec so that is what I use.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Installing&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
This package requires ruby-gems, rspec, and selenium-client.  I am going to assume you have ruby-gems installed already.  The others are installed like this:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151478.js?file=install.sh&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Converting&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
When using Rspec the only real thing to remember is that there are no assert* or verify* methods.  The reason is that Rspec itself is a testing framework so it will have it own version of assert and verify (in this case should).&lt;br /&gt;
&lt;br /&gt;
The IDE has a great feature in that it converts the HTML test into a rspec test for you.  It isn&#39;t a great format, but it is better then nothing and is a good place to start.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151478.js?file=selenium.rb&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;gotcha&quot;&gt;The browser being shown is &lt;code&gt;*chrome&lt;/code&gt;.  This actually means Firefox, not Google Chrome.  For Google Chrome use &lt;code&gt;*googlechrome&lt;/code&gt;.&lt;/div&gt;&lt;br /&gt;
&lt;div class=&quot;gotcha&quot;&gt;For Safari use &lt;code&gt;*safari&lt;/code&gt;, but remember that you will need to disable the popup blocker manually and close Safari (for this user) otherwise it will just sit there forever.&lt;/div&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Remote Controls&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
A remote control is what Selenium uses to execute the test.  The IDE comes with it built-in, but it is tied to FireFox.  To use IE, Safari, or Chrome you need to download the remote control software: &lt;a href=&quot;http://seleniumhq.org/projects/remote-control&quot;&gt;http://seleniumhq.org/projects/remote-control&lt;/a&gt;.  This software is just a Java server that opens your machine on port 4444 (by default) to allow Selenium clients to run tests.  Each client gets its own browser instance to run the tests in.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;gotcha&quot;&gt;The server must be run by a user that has access the browser and has a screen to render to.&lt;/div&gt;&lt;br /&gt;
&lt;div class=&quot;gotcha&quot;&gt;Firefox will only run a single profile per user.  If you need to run Firefox concurrently on the same machine you need to fake a second profile.  Don&#39;t do it, just create a VM; you will be happier.&lt;/div&gt;&lt;br /&gt;
&lt;div class=&quot;gotcha&quot;&gt;Google Chrome does not works on OS X.  This is because OS X doesn&#39;t add application executables to the path, and the server code isn&#39;t smart enough to use the executable directly.  The fix is supposedly &lt;a href=&quot;http://groups.google.com/group/selenium-users/browse_thread/thread/7205089285e92973?hide_quotes=no&amp;amp;fwc=1&quot;&gt;here&lt;/a&gt;, but I was not able to get it to work.  If I do I will probably write another blog entry and link it here.&lt;/div&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Putting it all together&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
By default RSpec provides no runner code and the code the IDE produces is not standalone.  This is not a problem since installing RSpec into a rails app installs script/spec.  I have copied the runner code here so make it easier.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/1151478.js?file=runner.rb&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
I am going to assume the RSpec runner code is called spec and the test file is called test.rb.  To run this test from the command line do the following:&lt;br /&gt;
ruby spec test.rb &lt;br /&gt;
&lt;br /&gt;
Assuming you followed all the steps the test should have opened Firefox, executed a page, run the tests, closed Firefox, and returned the results.  Now you can add more tests and have Selenium execute them.&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: x-large;&quot;&gt;Related Research&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Using Selenium Grid&lt;/li&gt;
&lt;li&gt;Using Chrome, or IE&lt;/li&gt;
&lt;li&gt;Using a Grid to run the same test in all browsers on all OSs&lt;/li&gt;
&lt;/ol&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/5101777311618576016/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/11/learning-selenium.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/5101777311618576016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/5101777311618576016'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/11/learning-selenium.html' title='Learning Selenium'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7192097691707306287.post-3170511180547733077</id><published>2010-11-18T09:09:00.000-05:00</published><updated>2010-12-04T18:56:43.479-05:00</updated><title type='text'>Topics</title><content type='html'>&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;The following are topics that I will be writing about in the coming weeks (or months)&lt;/span&gt;&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&lt;strike&gt;&lt;a href=&quot;http://randomsoftwareinklings.blogspot.com/2010/11/learning-selenium.html&quot;&gt;Selenium testing&lt;/a&gt;&lt;/strike&gt;&lt;/span&gt;&lt;/li&gt;
&lt;ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Multi-browser repeat testing&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Selenium RSpec proper format&lt;/span&gt;&lt;/li&gt;
&lt;ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;settings up the before/after filter&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Organizing&amp;nbsp;the tests so they can be run individually&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Integration tests&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Selenium Grid&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;ExtJS I18N&lt;/span&gt;&lt;/li&gt;
&lt;ol&gt;&lt;/ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Auditing in Rails&lt;/span&gt;&lt;/li&gt;
&lt;ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Using observers?&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;CouchDB&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;NodeJS&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Authentication in Rails&lt;/span&gt;&lt;/li&gt;
&lt;ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Authlogic&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Cancan&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Search in Rails&lt;/span&gt;&lt;/li&gt;
&lt;ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Sphinx?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;Something else&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&lt;a href=&quot;http://randomsoftwareinklings.blogspot.com/2010/12/git-with-svn.html&quot;&gt;&lt;u&gt;&lt;s&gt;Using Git&lt;/s&gt;&lt;/u&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;</content><link rel='replies' type='application/atom+xml' href='http://randomsoftwareinklings.blogspot.com/feeds/3170511180547733077/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/11/topics.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3170511180547733077'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7192097691707306287/posts/default/3170511180547733077'/><link rel='alternate' type='text/html' href='http://randomsoftwareinklings.blogspot.com/2010/11/topics.html' title='Topics'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/07582858158334682358</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>