tag:blogger.com,1999:blog-15390460832689261172009-07-06T00:42:02.890-07:00Michael Greenlyfree software, agile development, ubuntu, ruby, rails and what ever else.Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.comBlogger63125tag:blogger.com,1999:blog-1539046083268926117.post-38478989650673669252009-05-10T09:34:00.000-07:002009-05-10T09:37:06.512-07:00Onion News Network Has Fun With Star Trek MovieI haven't seen the movie but I stumbled across this and got a good laugh.<br /><br /><object width="480" height="430"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://www.theonion.com/content/themes/common/assets/onn_embed/embedded_player.swf?image=http%3A%2F%2Fwww.theonion.com%2Fcontent%2Ffiles%2Fimages%2FSTAR_TREK_article.jpg&amp;videoid=94844&title=Trekkies%20Bash%20New%20Star%20Trek%20Film%20As%20%27Fun%2C%20Watchable%27" /><param name="wmode" value="transparent" /><embed src="http://www.theonion.com/content/themes/common/assets/onn_embed/embedded_player.swf"type="application/x-shockwave-flash" allowScriptAccess="always" allowFullScreen="true" wmode="transparent" width="480" height="430"flashvars="image=http%3A%2F%2Fwww.theonion.com%2Fcontent%2Ffiles%2Fimages%2FSTAR_TREK_article.jpg&videoid=94844&title=Trekkies%20Bash%20New%20Star%20Trek%20Film%20As%20%27Fun%2C%20Watchable%27"></embed></object><br /><a href="http://www.theonion.com/content/video/trekkies_bash_new_star_trek_film?utm_source=videoembed">Trekkies Bash New Star Trek Film As 'Fun, Watchable'</a><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-3847898965067366925?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-21687780480665780392009-05-10T06:31:00.000-07:002009-05-10T06:41:30.639-07:00Writing the Reader's Stories for the First BDD Blog ReleaseI continue with my plan to <a href="http://blog.michaelgreenly.com/2009/05/writing-user-stories-for-first-bddblog.html">write user stories</a> for my <a href="http://blog.michaelgreenly.com/2009/05/planning-features-for-first-bddblog.html">first release</a>. In this post I'll cover features from the readers perspective.<br /><ol><li>Must be able to Create, Edit and View posts.</li><li>Must support per post permanent URLs</li></ol>Here's the first draft of the readers browsing feature. We'll see where it evolves to in the future.<br /><br /><pre>Feature: browsing<br />As a reader<br />I want to browse the site<br />So that I can read it's content<br /><br /><br />#<br /># scenarios that involve the reader navigating to the<br /># home page as the number of available posts vary<br />#<br />Scenario: 0 of 0<br /> Given that 0 posts exist<br /> When I go to the home page<br /> Then I should be on the error page<br /> And I should see "page not found"<br /><br />Scenario: 1 of 1<br /> Given that 1 post exists<br /> When I go to the home page<br /> Then I should be on post page 1<br /> And I should not see a link to the next post<br /> And I should not see a link to the previous post<br /><br />Scenario: 2 of 2<br /> Given that 2 posts exist<br /> When I go to the home page<br /> Then I should be on post page 2<br /> And I should see a link to the previous post<br /> And I should not see a link to the next post<br /><br /><br />#<br /># Scenarios that involve the reader navigating to a specific<br /># post; the first, middle and last. These primarily demonstrate<br /># the behavior of the navigation links to previous and next<br /># post pages<br />#<br />Scenario: 1 of 2<br /> Given that 2 posts exist<br /> When I go to post page 1<br /> Then I should not see a link to the previous post<br /> And I should see a lnk to the next post<br /><br />Scenario: 2 of 3<br /> Given that 3 posts exist<br /> When I go to post page 2<br /> Then I should see a link to the previous post<br /> And I should see a link to the next post<br /><br /><br />#<br /># navigation to specific posts is done through post<br /># page permalinks. The permalinks are derived from<br /># the title of the post<br />#<br />Scenario: navigate to an non-existant post<br /> Given that no posts have been created<br /> When I go to the path /posts/get-rich-overnight<br /> Then I should be on the error page<br /> And I should see "page not found"<br /> And I should see a link to the home page<br /><br />Scenario: navigate to an existing post<br /> Given that post 1 exists<br /> And that post 1 has the title "Get Rich Overnight"<br /> And that post 1 has the body lorem ispum<br /> When I go to /posts/get-rich-overnight<br /> Then I should see "Get Rich Overnight"<br /> And I should see lorem ipsum</pre><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-2168778048066578039?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-33904123355422947412009-05-10T06:28:00.001-07:002009-05-10T13:35:16.556-07:00Writing User Stories for the First BDDBlog ReleaseIn a <a href="http://blog.michaelgreenly.com/2009/05/planning-features-for-first-bddblog.html">previous post</a> I shared the basics of my release plan, crude as it was. Next I'm going to create user stories for the entire first release once that's done I'll plan and execute regular weekly iterations to complete all the release stories. The one thing I'm going to do a bit different is that I'm going to rough out my user stories directly to cucumber features then I will maintain a list of uncompleted features that can be prioritized as I burn it down.<br /><br />The first task at hand is to turn my first release's outline into Cucumber features. As I do this I'm not going to get overly concerned about the wording of my features. I expect that I'll be massaging them as I implement them.<br /><br />With all that out of the way It's time to burn down the items on my release list and create some features. The first two items in the list is the most basic;<br /><ol><li>Must be able to Create, Edit and View posts.</li><li>Must support per post permanent URLs</li></ol><br />Next I'll look at this from each type of users role; <a href="http://blog.michaelgreenly.com/2009/05/writing-readers-stories-for-first-bdd.html">Reader</a>, Author and Admin but I'm going to do that in seperate posts.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-3390412335542294741?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-28460962834345849492009-05-03T07:59:00.000-07:002009-05-03T08:06:20.969-07:00My First BDDBlog Design DecisionsWebsite navigation should just work from the users perspective. It should seem simple and intuitive they should never have to think about it. That of course means the burden to 'think about it' is on the developer. Today I spent some time trying to decide how I wanted navigation to work in my new blog. It wasn't as easy to make these decisions as I would have expected before hand. <br /><br />Specifically I was trying to figure out exactly what a reader expects to see when they navigate to the home page and where they expect to find older posts. I spent a fair bit of time looking at other blogs and in the end I found two basic approaches. <br /><br />In one case the blogs tend to provide some number of recent posts one below the next on the home page. Typically the posts are separated by dates as appropriate. This style seems to be more appropriate for blogs with frequent small posts. <br /><br />The other common approach was to only display the most recent post on the homepage and to provide navigation links to previous posts. This style seems to be more appropriate for blogs with longer posts.<br /><br />This left me a bit unsure at first. Quite a few of my posts are much to big for Blogger's format. On the other hand It's not entirely uncommon for me to do micro posts. What to do?<br /><br />Well after spending quite a bit of time thinking about it, for now, my decision is that I'll use the latter style. My blog will feature a single post per page and provide links to older or newer posts as appropriate but this means I can't easily roll a twitter or delicious summary into my blog without it displacing the most recent post.<br /><br />After spending some time pondering that problem I decided that I may not be able to roll those other feeds into my blog but it most certianly doesn't prevent me from splicing them into my feed.<br /><br />So it looks like I've already stumbled on to another requirement; the ability to splice multiple RSS/Atom feeds into one. Feature creep already =(<br /><br />On the other hand maybe I've stumbled on to a useful service? Atom/RSS Feed splicing with format conversion? Does anyone already offer that? Hmmm... I'll have to think about that one.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-2846096283434584949?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-32401820043789319892009-05-02T10:50:00.000-07:002009-05-10T06:22:15.009-07:00Planning Features for the First BDDBlog ReleaseIn a <a href="http://blog.michaelgreenly.com/2009/03/project-within-project.html">previous post</a> I talked a bit about my motivations and just what I was up to. In this post I'm going to start to define some specifics for the inner blog project.<br /><br />I'm planning on making at least three releases. The first release is intended to replicate the basic functionality of Blogger. The second release will expand on the first, provide better tools for managing multi-part articles and some wiki like editing features. The third release, which is being developed for a different domain and not this blog, will incorporate the shopping cart functionality.<br /><br />Here's my laundry list of functionality in the order I intend to implement it in the first release:<ol><br /><li>Must be able to Create, Edit and View posts</li><br /><li>Must support per post permanent URLs</li><br /><li>Must allow posts to be tagged</li><br /><li><del>Must support monthly archiving</del></li><br /><li>Must support authenticated admin/author logins</li><br /><li>Must have admin editing of site title and description</li><br /><li>Must have admin editing of site CSS and HTML header/footers</li><br /><li>Must have admin editing of side panel html/javascript</li><br /><li>Must be able to import Blogger's exported content</li><br /><li>Must provide full text search functionality</li><br /><li>Must provide tag search functionality</li><br /><li>Must provide an Atom 1.0 full content feed</li><br /><li>Must be able to ping blog search engines</li><br /></ol><br />Now on to the coding....<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-3240182004378931989?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-19682270719950274172009-05-01T06:41:00.000-07:002009-05-01T07:28:31.313-07:00Dissident Movments Of The Future<a href="http://en.wikipedia.org/wiki/Anonymous_(group)">Anonymous</a>' target may be Scientology but it's interesting to think about how these tactics maybe applied more broadly in the future by other interests.<br /><br /><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/JCbKv9yiLiQ&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/JCbKv9yiLiQ&hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-1968227071995027417?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-11190935032672071032009-05-01T06:08:00.000-07:002009-05-01T07:32:46.089-07:00How Long Before TV Programming Follows Print Media?You'd pretty much have to be living under a rock not be aware of the changes the 21st century is imposing on the old media industries. Print news papers have been especially hard hit recently. Still, it really sunk in today, that television is next and when their financial problems start, it will happen just as rapidly and be just as significant.<br /><br />What made this especially clear today was watching this:<br /><embed src="http://blip.tv/play/7FD+gQaCiiA" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="390"></embed><br />I'm not entirely new to <a href="http://www.epicfu.com/">EPIC FU</a>, I first ran across it quite a while back, but what what really struck me today was the high quality of the production. If this is what you can get for free and on demand why would I want to pay to have crap streamed to me?<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-1119093503267207103?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-46209430765888903592009-04-26T09:51:00.000-07:002009-04-26T11:25:33.633-07:00Some Progress On My BDD Blog ProjectI finally got around to doing some more work on my BDD Blog project over the weekend. Up to this point I had mostly been just experimenting (<a href="http://blog.michaelgreenly.com/2009/03/baby-step-1-with-rails-rspec-and.html">here</a>, <a href="http://blog.michaelgreenly.com/2009/03/baby-step-2-with-rails-rspec-and.html">here</a> and <a href="http://blog.michaelgreenly.com/2009/03/rails-authentication.html">here</a>), but this time I decided I was going to try and write and implement a real scenario.<br /><br />My goal though is not so much to implement code. Instead I'm still trying to learn how to write features and specs. I want to get to the point where I'm comfortable enough with Cucumber, Webrat and RSpec that I'm confident I can express all the features for a release without wasting my time.<br /><br />So lets start with the feature I'm going to try and implement. The feature it self is very straight forward. It walks through the process used by an author when creating a new post.<br /><pre><code>Scenario: save a draft post<br /> Given I am logged in as John Doe<br /> And I am on the post creation page<br /> And I have filled in the title with "Get Rich Overnight"<br /> And I have filled in the body with "lorem ipsum"<br /> When I click the "Create" button<br /> Then I should be on the post index page<br /> And I should see "Post successfully created"<br /> And I should see "1 to 1 of 1 posts"<br /> And there should only be 1 post listed<br /> And the first post's title should be "Get Rich Overnight"<br /> And the first post's author should be "John Doe"<br /> And the first post's status should be "draft"<br /></code></pre><br />I've already <a href="http://blog.michaelgreenly.com/2009/03/rails-authentication.html">played around</a> with <a href="http://wiki.github.com/binarylogic/authlogic">AuthLogic</a> and demonstrated it's use but there's a minor change here. I'm no longer testing authentication so I needed to reduce the login process to a single given statement.<br /><pre>Given I am logged in as John Doe</pre><br />The code I used to do that is pretty straight foward.<br /><pre class="ruby"><code><span class="constant">Given</span> <span class="punct">/^</span><span class="constant">I</span> <span class="ident">am</span> <span class="ident">logged</span> <span class="keyword">in</span> <span class="ident">as</span> <span class="punct">(.+)</span><span class="global">$/</span> <span class="ident">do</span> <span class="punct">|</span><span class="ident">login</span><span class="punct">|</span><br /> <span class="constant">Given</span> <span class="punct">&quot;</span><span class="string">I am the registered user <span class="expr">#{login}</span></span><span class="punct">&quot;</span><br /> <span class="ident">visit</span> <span class="ident">path_to</span><span class="punct">(&quot;</span><span class="string">the login page</span><span class="punct">&quot;)</span><br /> <span class="ident">fill_in</span><span class="punct">('</span><span class="string">Login</span><span class="punct">',</span> <span class="symbol">:with</span> <span class="punct">=&gt;</span> <span class="ident">login</span><span class="punct">)</span><br /> <span class="ident">fill_in</span><span class="punct">('</span><span class="string">Password</span><span class="punct">',</span> <span class="symbol">:with</span> <span class="punct">=&gt;</span> <span class="punct">&quot;</span><span class="string">password</span><span class="punct">&quot;)</span><br /> <span class="ident">click_button</span><span class="punct">(&quot;</span><span class="string">Login</span><span class="punct">&quot;)</span><br /><span class="keyword">end</span><br /></code></pre><br />The only thing even remotely interesting here is that I'm calling one of my previously defined 'Given' statements from within this one.<br /><br />The next few statements are very straight forward and were implemented entirely in cucumber with the existing scaffold for the Post resource.<br /><pre>And I am on the post creation page<br />And I have filled in the title with "Get Rich Overnight"<br />And I have filled in the body with "lorem ipsum"<br />When I click the "Create" button<br />Then I should be on the post index page<br />And I should see "Post successfully created"<br /></pre>The next line in this scenario:<br /><pre>And I should see "1 to 1 of 1 posts"</pre>I now realize didn't belong in this scenario (I'll refactor that out later) but since I learned something while implementing it I'm going to share.<br /><br />Specifically, what I learned was this statement has no real purpose. I wanted to make sure that only the new post was present but all this really does it check to see if the "<code>1 to 1 of 1 posts</code>" text is present. It doesn't provide any real validation of the number of posts on the page. In the future I'll move this out to a new feature that's specifically written to test the pagination summary.<br /><br />This line was not originally in my feature:<br /><pre>And there should only be 1 post listed</pre>I added it when I realized my mistake with the previous line. This statement directly tests the number of posts on the page by counting the table rows in the post index table. It's implementation is very straight forward:<br /><pre class="ruby"><code><span class="constant">Then</span> <span class="punct">/^</span><span class="ident">there</span> <span class="ident">should</span> <span class="ident">only</span> <span class="ident">be</span> <span class="punct">(\</span><span class="ident">d</span><span class="punct">+)</span> <span class="ident">posts?</span> <span class="ident">listed</span><span class="global">$/</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">count</span><span class="punct">|</span><br /> <span class="ident">response</span><span class="punct">.</span><span class="ident">should</span> <span class="ident">have_tag</span><span class="punct">(&quot;</span><span class="string">tr</span><span class="punct">&quot;</span> <span class="punct">,</span> <span class="symbol">:count</span> <span class="punct">=&gt;(</span><span class="ident">count</span><span class="punct">.</span><span class="ident">to_i</span> <span class="punct">+</span> <span class="number">1</span><span class="punct">))</span><br /><span class="keyword">end</span><br /></code></pre><br />The next three lines:<pre>And the first post's title should be "Get Rich Overnight"<br />And the first post's author should be "John Doe"<br />And the first post's status should be "draft"<br /></pre><br />These lines were all very similar. They are simply looking to make sure that the contents of specific columns in the post index table have the correct information. This statement is implemented with the following code:<br /><pre class="ruby"><code><span class="constant">Then</span> <span class="punct">/^</span><span class="ident">the</span> <span class="punct">(.+)</span> <span class="ident">post</span><span class="punct">'</span><span class="string">s (.+) should be &quot;([^\&quot;]*)&quot;$/ do |index, column, text|<br /> response.should have_selector(&quot;table&gt;tr:nth-child(#{position_to(index) + 1})&quot;) do |tr|<br /> tr.should have_selector(&quot;td[class=</span><span class="punct">'</span><span class="comment">#{column}']&quot;) do |td|</span><br /> <span class="ident">td</span><span class="punct">.</span><span class="ident">should</span> <span class="ident">contain</span><span class="punct">(</span><span class="ident">text</span><span class="punct">)</span><br /> <span class="keyword">end</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span><br /></code></pre>There's not much special there but it was my first attempt at really trying to use the CSS selector notation. Maybe after I'm more comfortable with it I'll devote a post to just that but for now I'll just explain this method.<br /><br />In a nutshell it simply looks to make sure that nth row in the post index table has a td tag with a class matching the column name and that it's content matches the text passed in.<br /><br />You may also notice I have a poorly named method called position_to in this method that simply turns the english words; first, second, third, etc... into the appropriate fixnum. Hopefully down the road I can find a library that does this but if not maybe I'll expand my approach into a full fledged gem.<br /><br />This pretty much covered what I did with Cucumber and Webrat to fully implement this scenario but I did dip down into some view, controller and model specs to get the job done. I'll save that for another post.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-4620943076588890359?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-26793469161010316232009-04-26T06:28:00.000-07:002009-04-26T06:37:23.114-07:00Can't Use Ruby 1.9.1 Just YetI had wanted to <a href="http://blog.michaelgreenly.com/2009/04/multiple-versions-of-ruby-on-ubuntu-3.html">switch to Ruby 1.9</a> for my current project. I'm using a fairly minimal number of dependencies and didn't expect any problems. Unfortunately for what ever reason RSpec fails silently under Ruby1.9.1 but works properly on Ruby1.8.7 (at least on my machine). Since I don't have the time to track it down right now it looks like I'm reverting back to Ruby1.8.7 for the time being.<br /><br />Hopefully I'll have time to dig into it later in the week.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-2679346916101031623?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-22839767926029110752009-04-24T06:05:00.000-07:002009-04-24T07:02:23.212-07:00The 'sudo' Command's EnvironmentI'm sure most people are aware the '<span class="blsp-spelling-error" id="SPELLING_ERROR_0">sudo</span>' command sterilizes the user's environment variables before executing the command passed to it. The most common issue I've run into with this is $PATH not being preserved. The usual scenario is that I have an application installed into /opt and I've appended it's location to $PATH, which works just fine until you try to execute the application with '<span class="blsp-spelling-error" id="SPELLING_ERROR_2">sudo</span>' and it fails because the command is no longer in the current path.<br /><br />The fix to this is really straight forward and I'm surprised I hadn't thought of it earlier. Simply add the following to your $HOME/.<span class="blsp-spelling-error" id="SPELLING_ERROR_3">bashrc</span> file.<br /><pre>alias <span class="blsp-spelling-error" id="SPELLING_ERROR_4">sudo</span>="<span class="blsp-spelling-error" id="SPELLING_ERROR_5">sudo</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_6">env</span> PATH=$PATH"</pre>What this does is use '<span class="blsp-spelling-error" id="SPELLING_ERROR_7">sudo</span>' to run '<span class="blsp-spelling-error" id="SPELLING_ERROR_8">env</span>' which is passed the provided command but sets the target environment's PATH to match the current users before executing it.<br /><br />Don't forget that you can always run the <span class="blsp-spelling-error" id="SPELLING_ERROR_9">unaliased</span> version of a command simply by prefixing it with a backslash.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-2283976792602911075?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com2tag:blogger.com,1999:blog-1539046083268926117.post-24661028162134860692009-04-12T05:53:00.000-07:002009-04-28T04:15:52.322-07:00Multiple Versions of Ruby on Ubuntu #3I decided it's time to switch to Ruby 1.9 but I still have a couple of Ruby 1.8 projects I need to maintain. That means I need a simple technique for switching back and forth between multiple versions of Ruby. I've experimented with different approaches in the past but I'm not completely satisfied with any of them.<br /><ul><li><a href="http://blog.michaelgreenly.com/2007/12/multiple-ruby-version-on-ubuntu.html">Multiple Versions of Ruby on Ubuntu #1</a></li><li><a href="http://blog.michaelgreenly.com/2008/08/multiple-versions-of-ruby-on-ubuntu-2.html">Multiple Versions of Ruby on Ubuntu #2</a></li></ul>This time I've decided to use a shell script to update a symbolic link that points to the active version. It's extremely fast and simple.<br /><br />I started by purging my system of any apt installed Ruby packages.<pre>sudo apt-get remove --purge ruby1.8 ruby1.9</pre><br />Then I installed Ruby 1.8 from source.<pre>sudo apt-get build-dep ruby1.8<br />wget -c ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz<br />tar -xvzf ruby-1.8.7-p72.tar.gz<br />cd ruby-1.8.7-p72<br />./configure --prefix=/opt/ruby-1.8.7-p72 --enable-pthread --enable-shared --enable-openssl --enable-readline --enable-zlib<br />make<br />sudo make install<br />wget -c http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz<br />tar -xvzf rubygems-1.3.1.tgz<br />cd rubygems-1.3.1<br />sudo /opt/ruby-1.8.7-p72/bin/ruby setup.rb</pre><br />Then I installed Ruby 1.9 from source.<pre>sudo apt-get build-dep ruby1.9<br />wget -c ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p0.tar.gz<br />tar -xvzf ruby-1.9.1-p0.tar.gz<br />cd ruby-1.9.1-p0/<br />./configure --prefix=/opt/ruby-1.9.1-p0 --enable-pthread --enable-shared<br />make<br />sudo make install</pre><br />Then I add the following line to the bottom of $HOME/.bashrc<pre>export PATH=/opt/ruby/bin:$PATH</pre><br />Then I saved the following script in /usr/local/bin/select_ruby<br /><pre>#!/bin/sh<br /><br />while : # Loop forever<br />do<br />cat << !<br /><br />current = $(/opt/ruby/bin/ruby --version)<br /><br />Select Option<br /><br />1. ruby-1.8.6-p368<br />2. ruby-1.8.7-p72<br />3. ruby-1.8.7-p160<br />4. ruby-1.9.1-p0<br />5. exit<br /><br />!<br /><br />echo -n " Your choice? : "<br />read choice<br /><br />case $choice in<br />1) rm -rf /opt/ruby; ln -s /opt/ruby-1.8.6-p368 /opt/ruby;;<br />2) rm -rf /opt/ruby; ln -s /opt/ruby-1.8.7-p72 /opt/ruby;;<br />3) rm -rf /opt/ruby; ln -s /opt/ruby-1.8.7-p160 /opt/ruby;;<br />4) rm -rf /opt/ruby; ln -s /opt/ruby-1.9.1-p0 /opt/ruby;;<br />5) exit;;<br />*) echo "\"$choice\" is not valid "; sleep 2 ;;<br />esac<br />exit<br />done</pre><br />Now to switch I just run the script and select the version I want.<pre>sudo select_ruby</pre><br />There's a couple of things to be aware of using this approach. Each ruby installation has it's own separate instance of rubygems so gems will have to be installed multiple times. Also since sudo doesn't preserve the users $PATH you will need to use the full path with the gem command; example:<pre>sudo /opt/ruby/bin/gem install rails</pre> Or this <a href="http://blog.michaelgreenly.com/2009/04/sudo-commands-environment.html">fix</a>.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-2466102816213486069?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com6tag:blogger.com,1999:blog-1539046083268926117.post-36700906317993562009-04-05T08:26:00.000-07:002009-04-05T09:56:44.496-07:00As The Prouduct Owner What's My Goal?I've been away from rails for a bit and to catch up I've decided to write an application to re-familiarize myself with the frame work and at the same time try and apply what I think I've learned about behavior driven development. There's an <a href="http://blog.michaelgreenly.com/2009/03/project-within-project.html">earlier post</a> with more details.<br /><br />Today, in this post, I'm going to be the <a href="http://en.wikipedia.org/wiki/Scrum_%28development%29#.22Pig.22_roles">product owner</a> and attempt to define my goals for this project.<br /><br />I want to create a blog with built in shopping cart functionality. The intention is that the original content generated for the blog will generate traffic to the site that can be converted into sales. I realize that if this was my only goal it would be smarter to use an existing blog framework that has an available shopping cart plugin, but it's not my only goal.<br /><br />I want to break development into two major parts. The first phase will be to develop the blog and the second the shopping cart functionality. I'm going this route because it will allow the blog to start building traffic while the shopping cart is being developed.<br /><br />This means I need to define what I want in a blog....<br /><ul><li>It should support a write to draft, edit, preview then publish work flow. When drafts are previewed they should be seen exactly as they will appear on the finished page. Nothing annoys me more about blogger than the fact that it's preview does not use the blog's styles sheets.</li><li>It should provide a sane default permalink based on the title but it should also allow the permalink to be overridden.</li><li>It should allow additional alias permalinks. This is specifically to make importing the content of other blogs with different permalink formats easier.</li><li>The markup should be a subset of html. I don't need a WYSIWYG editor or a markdown syntax.</li><li>It should handle tags for blog articles and be able to display a tag cloud on the side bar.</li><li>It should support interactive editing of javascript widgets for the side bar.<br /></li><li>It should handle article grouping to create a series of articles. When an article is made part of a series it should automatically include links to the previous and next articles in the series.</li><li>It obviously needs to generate rss/atom feeds.</li><li>It should support search but it can be an external service (google site search) if it's incorporated really well.</li><li>It should support online editing of the site wide css. It shouldn't be necessary to change sources code, commit and deploy it just for a CSS tweak.</li><li>It should support comments from authenticated users (OpenID and Facebook authentication) and anonymous comments after review.<br /></li><li>It should be able to import a blog from Blogger.</li><li>It should support syntax highlighting for ruby, xml, yaml, css, html and json code.</li><li>It should be able to compile a daily digest of Twitter comments.</li><li>It shoudl be able to compile a daily digest of Delicious links.<br /></li></ul>That spells out pretty much what I'm after.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-3670090631799356?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-31617409648329502292009-04-04T18:54:00.000-07:002009-04-04T19:39:09.766-07:00Python's DVCS ComparisonI ran across this comparison of <a href="http://www.python.org/dev/peps/pep-0374/">distributed version control systems</a>. It also appears that Python has <a href="http://mail.python.org/pipermail/python-dev/2009-March/087931.html">selected</a> <a href="http://www.selenic.com/mercurial/wiki/">Mercurial</a> after doing this comparison. I don't really have a comment on that but I do find it interesting how different communities gravitated in different directions. With Ruby users firmly entrenched in the world of Git and now Python going with Mercurial.<br /><br />Still while reading through it I found at least one thing I wanted to share. Towards the end of the comparison there's a section that evaluates <a href="http://www.python.org/dev/peps/pep-0374/#updating-a-checkout">how long it takes to update a stale repository</a>. In that section there's a comment that indicates that git was not able to checkout a repository at a specific commit but I'm not sure how that effected that test?<br /><br />To perform this test I would have used this...<br /><pre># fetch a copy of the repository<br />git clone [url]<br /># roll everything back 700 commits<br />git reset --hard HEAD~700<br /># pull the head from remote master<br />git pull</pre><br /><span style="font-style: italic; color: rgb(0, 0, 102);">I think I misunderstood what they were trying to test. It looks like they wanted to test the performance of the fetch, merge, and commit and what I described above doesn't re-fetch the 700 commits.</span><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-3161740964832950229?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-48220366374491204712009-03-31T03:30:00.000-07:002009-05-02T10:06:51.252-07:00Testing Rails Authlogic with CucumberIn early rails projects I rolled my own authentication system, later I used <a href="http://github.com/technoweenie/restful-authentication/tree/master">restful-authentication</a>. This time I'm going to try out <a href="http://wiki.github.com/binarylogic/authlogic">AuthLogic</a> which seems to be fairly popular, cleanly packaged and actively developed. I may walk through an AuthLogic <a href="http://www.binarylogic.com/2008/11/3/tutorial-authlogic-basic-setup">installation</a> in a future post but today I just want to make sure it can play nicely with cucumber.<br /><br />First I wrote a fairly basic login scenario. You'll notice I stuck with the <a href="http://http//www.benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories/">declarative</a> style in writing this and I followed the advice provided in <a href="http://www.pragprog.com/titles/achbd/the-rspec-book">"The RSpec Book"</a>. I use direct model access to create the registered user in the 'Given' but then use simulated browser access to do the login and verify the actions success.<pre>Scenario: successful login<br />Given I am the registered user John Doe<br />And I am on the login page<br />When I login with valid credentials<br />Then I should be on the account page<br />And I should see "Login successful!"</pre><br /><br />This left me with three steps to implement:<br /><br /><pre class="ruby"><span class="constant">Given</span> <span class="punct">/^</span><span class="constant">I</span> <span class="ident">am</span> <span class="ident">the</span> <span class="ident">registered</span> <span class="ident">user</span> <span class="punct">(.+)</span><span class="global">$/</span> <span class="ident">do</span> <span class="punct">|</span><span class="ident">login</span><span class="punct">|</span><br /><span class="ident"> params</span> <span class="punct">=</span> <span class="punct">{</span><br /><span class="punct"> "</span><span class="string">login</span><span class="punct">"=&gt;</span> <span class="ident">login</span><span class="punct">,</span><br /><span class="punct"> "</span><span class="string">password</span><span class="punct">"=&gt;"</span><span class="string">password</span><span class="punct">",</span><br /><span class="punct"> "</span><span class="string">password_confirmation</span><span class="punct">"=&gt;"</span><span class="string">password</span><span class="punct">"</span><br /><span class="punct"> }</span><br /><span class="attribute"> @user</span> <span class="punct">=</span> <span class="constant">User</span><span class="punct">.</span><span class="ident">create</span><span class="punct">(</span><span class="ident">params</span><span class="punct">)</span><br /><span class="keyword">end</span><br /><br /><span class="constant">When</span> <span class="punct">/^</span><span class="constant">I</span> <span class="ident">login</span> <span class="ident">with</span> <span class="ident">valid</span> <span class="ident">credentials</span><span class="global">$/</span> <span class="keyword">do</span><br /><span class="ident"> fill_in</span><span class="punct">('</span><span class="string">Login</span><span class="punct">',</span> <span class="symbol">:with</span> <span class="punct">=&gt;</span> <span class="attribute">@user</span><span class="punct">.</span><span class="ident">login</span><span class="punct">)</span><br /><span class="ident"> fill_in</span><span class="punct">('</span><span class="string">Password</span><span class="punct">',</span> <span class="symbol">:with</span> <span class="punct">=&gt;</span> <span class="punct">"</span><span class="string">password</span><span class="punct">")</span><br /><span class="ident"> click_button</span><span class="punct">("</span><span class="string">Login</span><span class="punct">")</span><br /><span class="keyword">end</span><br /><br /><span class="constant">Then</span> <span class="punct">/^</span><span class="constant">I</span> <span class="ident">should</span> <span class="ident">be</span> <span class="ident">on</span> <span class="punct">([^\"</span><span class="string">]*)$/ do |page_name|<br />response.request.path.should == path_to(page_name)<br />end<span class="normal"><br /></span></span></pre><br />All fairly straight forward. I don't expect any problems using Cucumber with Authlogic.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-4822036637449120471?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com3tag:blogger.com,1999:blog-1539046083268926117.post-27205623266674954662009-03-27T11:23:00.000-07:002009-03-28T06:42:45.474-07:00Herding TigersI took the time last night to skim through most of the <a href="http://mwrc2009.confreaks.com/">videos</a> from the <a href="http://www.mtnwestrubyconf.com/2009/index">Mountain West Ruby Conference</a> on <a href="http://www2.confreaks.com/"><span class="blsp-spelling-error" id="SPELLING_ERROR_0">Confreaks</span></a>. Without question my favorite in the bunch was <a href="http://www.danielphilpott.com/about.htm"><span class="caption">Daniel <span class="blsp-spelling-error" id="SPELLING_ERROR_1">Philpott</span></span>'s</a> - "<a href="http://mwrc2009.confreaks.com/14-mar-2009-10-30-herding-tigers-software-development-and-the-art-of-war-daniel-philpott.html">Herding Tigers - Software Development and the Art of War</a>".<br /><br />If agile project <span class="blsp-spelling-corrected" id="SPELLING_ERROR_2">management</span> is something that interests you this video is worth watching.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-2720562326667495466?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-76374788213067123472009-03-26T12:56:00.000-07:002009-03-27T11:33:57.418-07:00The Facebook UI ChangesLots of people have been talking about the UI changes on Facebook but I wasn't <span class="blsp-spelling-corrected" id="SPELLING_ERROR_2">immediately</span> sure of how I felt. I was more than willing to give the new look a chance. After all the old <span class="blsp-spelling-error" id="SPELLING_ERROR_3">UI</span> really sucked.<br /><br />Well, I've decided. The new <span class="blsp-spelling-error" id="SPELLING_ERROR_4">UI</span> sucks even worse!<br /><br />Oddly it took this change for me to understand what I was using <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Facebook</span> for. Which has primarily been to watch status changes, photo uploads and exchange private messages.<br /><br />The problem is that since the change everyone has started having much longer <span class="blsp-spelling-corrected" id="SPELLING_ERROR_6">conversations</span> on the "what's on you mind" post and unlike wall-to-wall conversations you can't opt-out of them.<br /><br />I'm stuck reading them all! It sucks!<br /><br />If I want to read through my updates to see things I'm interested in I have to wade through tons and tons of spam and it's not just conversation spam it's all the noise from the hundreds of absolutely retarded <span class="blsp-spelling-error" id="SPELLING_ERROR_7">facebook</span> applications.<br /><br />The signal to noise ratio has dropped to the point where I've pretty much given up.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-7637478821306712347?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-11667819838727973652009-03-26T12:37:00.000-07:002009-05-02T10:11:59.001-07:00A Project Within A ProjectSo I've decided that I've learned what I need to from my <a href="http://wiki.github.com/aslakhellesoy/cucumber">Cucumber</a> experiments[<a href="http://blog.michaelgreenly.com/2009/03/baby-step-1-with-rails-rspec-and.html">1</a>,<a href="http://blog.michaelgreenly.com/2009/03/baby-step-2-with-rails-rspec-and.html">2</a>] and now it's time to dive in for real but to do that I need to be sure I understand my objective and of course it's complicated like an <span class="blsp-spelling-corrected" id="SPELLING_ERROR_0">onion</span>. So I'm going to<a href="http://blog.michaelgreenly.com/2009/03/pop-why-stack.html"> pop the why stack</a> a few times.<br /><br />My outer goal is to provide myself with a realistic Rails project that I can apply Agile, Behavior Driven Development techniques to. I'm writing the code so I can continue my education. I'm blogging about it so that I can generate some self promotion. I may benefit from the self promotion if I choose to pursue this career path again someday.<br /><br />Of course it's necessary for the inner project to have legitimate driving factors as well, otherwise I can't really practice managing an agile process. So, here's a bit of background on the goals of the inner project...<br /><br />The inner project is a <a href="http://en.wikipedia.org/wiki/Do_it_yourself"><span class="blsp-spelling-error" id="SPELLING_ERROR_1">DIY</span></a>/<a href="http://en.wikipedia.org/wiki/HOWTO"><span class="blsp-spelling-error" id="SPELLING_ERROR_2">HOWTO</span></a> blog that's also an online store. The goal is to provide quality original content. So that it drives traffic to the site. So that related merchandise is exposed to reader traffic. So that stuff can be sold. So that I can make money.<br /><br />So now with the stake holder's position explained it's time to start planning the project!<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-1166781983872797365?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-52620243727881544442009-03-26T10:29:00.000-07:002009-03-26T12:42:30.962-07:00Pop The Why StackI think one of the most important idea's in lean/agile development is the attention devoted to only doing work where it provides value. One of the best expressions of this I've seen is on <a href="http://wiki.github.com/aslakhellesoy/cucumber">Cucumber's wiki page</a>. Basically it says...<br /><br />Before doing something pop the why stack recursively until you end up with one of the following business values; protect revenue, increase revenue or manage cost.<br /><br />I think that's a powerful razor to cut things with!<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-5262024372788154444?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-13529386844612318632009-03-25T14:37:00.000-07:002009-05-02T10:08:21.237-07:00Baby Step #2 with Rails, RSpec and CucumberIn my <a href="http://blog.michaelgreenly.com/2009/03/baby-step-1-with-rails-rspec-and.html">previous post</a> I took my first stab at Cucumber; first writing a failing Scenario, then getting it to pass, then writing a failing Spec and getting it to pass. The scenario in the previous post was overly simple, in fact I was able to complete it without writing any code at all. My goal this time is to continue my education with a slightly more realistic scenario.<br /><br />One way to attack an application is in the same order it's used. In the case of a blog the author will need to add content before the site has any value, so that seems like a reasonable starting point.<br /><pre># RAILS_ROOT/features/author_publish.feature<br />Feature: author publish<br />As an author<br />I want to publish a post<br />So that it can be read<br /><br />Scenario: publish a post<br /> Given I am on the post creation page<br /> When I add a new post<br /> Then I should see a page for the new post</pre><br />The narrative for this feature was easy but deciding on how to approach the first scenario wasn't. In the end after browsing around the web and reading tons of material it was another post from <a href="http://www.benmabey.com/">Ben Maybe</a> called <a href="http://www.benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories/">"Imperative vs Declarative Scenarios in User Stories"</a> that seemed to help me decide.<br /><br />With that feature in place running cucumber:<pre>./script/cucumber features/author_publish.feature</pre><br />Produces the following:<pre>Feature: author publish<br />As an author<br />I want to publish a post<br />So that it can be read<br /><br />Scenario: publish a post # features/author_publish.feature:6<br /> Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6<br /> Can't find mapping from "the post creation page" to a path. (RuntimeError)<br /> /home/mgreenly/Projects/blog/features/support/paths.rb:11:in `/^I am on (.+)$/'<br /> features/author_publish.feature:7:in `Given I am on the post creation page'<br /> When I add a new post # features/author_publish.feature:8<br /> Then I should see a page for the new post # features/author_publish.feature:9<br /><br />1 scenario<br />1 failed step<br />2 undefined steps<br /><br />You can implement step definitions for missing steps with these snippets:<br /><br />When /^I add a new post$/ do<br />pending<br />end<br /><br />Then /^I should see a page for the new post$/ do<br />pending<br />end</pre><br />The problem here is cucumber doesn't know what to map the phrase 'the post creation page' to. This can be fixed by adding the following code to the navigation helper.<pre class="ruby"><span class="comment"># RAILS_ROOT/features/support/paths.rb</span><br /><span class="keyword">def </span><span class="method">path_to</span><span class="punct">(</span><span class="ident">page_name</span><span class="punct">)</span><br /> <span class="keyword">case</span> <span class="ident">page_name</span><br /><br /> <span class="keyword">when</span> <span class="punct">/</span><span class="regex">the homepage</span><span class="punct">/</span><br /> <span class="ident">root_path</span><br /><br /> <span class="keyword">when</span> <span class="punct">/</span><span class="regex">the post creation page</span><span class="punct">/</span><br /> <span class="ident">post_path</span><span class="punct">(&quot;</span><span class="string">new</span><span class="punct">&quot;)</span><br /><br /> <span class="keyword">else</span><br /> <span class="keyword">raise</span> <span class="punct">&quot;</span><span class="string">Can't find mapping from <span class="escape">\&quot;</span><span class="expr">#{page_name}</span><span class="escape">\&quot;</span> to a path.</span><span class="punct">&quot;</span><br /> <span class="keyword">end</span><br /><span class="keyword">end</span></pre><br />Now we run cucumber again:<pre>./script/cucumber features/author_publish.feature</pre><br />And it produces the following:<br /><pre>Feature: author publish<br />As an author<br />I want to publish a post<br />So that it can be read<br /><br />Scenario: publish a post # features/author_publish.feature:6<br /> Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6<br /> undefined method `post_path' for #<actioncontroller::integration::session:0xb6d7b354> (NoMethodError)<br /> /home/mgreenly/Projects/blog/features/support/paths.rb:9:in `/^I am on (.+)$/'<br /> features/author_publish.feature:7:in `Given I am on the post creation page'<br /> When I add a new post # features/author_publish.feature:8<br /> Then I should see a page for the new post # features/author_publish.feature:9<br /><br />1 scenario<br />1 failed step<br />2 undefined steps<br /><br />You can implement step definitions for missing steps with these snippets:<br /><br />When /^I add a new post$/ do<br />pending<br />end<br /><br />Then /^I should see a page for the new post$/ do<br />pending<br />end</actioncontroller::integration::session:0xb6d7b354></pre><br />Now it's complaining that it can't find a mapping for the Post resource. That makes sense since in my previous round I only created a controller. Instead of laboring through the process to create the Post model why not cheat and use the built in generator.<br /><pre>./script/generate rspec_scaffold -f Post title:string body:text</pre><br />Obvious, going this route means I've broken my previously completed feature, so out of curiosity lets check it out.<br /><br />Running cucumber on the original feature:<pre>./script/cucumber features/reader_browse.feature</pre><br />Produces the following:<pre>Feature: browsing<br />As a reader<br />I want to browse the site<br />So that I can read it's content<br /><br /> Scenario: visit the homepage # features/reader_browse.feature:6<br /> When I go to the homepage # features/step_definitions/webrat_steps.rb:10<br /> Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93<br /> expected the following element's content to include "Hello world!":<br /> Posts: index<br /> Listing posts<br /> Title<br /> Body<br />New post<br /> (Spec::Expectations::ExpectationNotMetError)<br /> features/reader_browse.feature:8:in `Then I should see "Hello world!"'<br /><br />1 scenario<br />1 failed step<br />1 passed step</pre><br /><br />Broken as expected. The problem is that the index page no longer has the "Hello world!" text on it. No problem we'll fix that later, for now lets keep going on the new feature. <br /><br />When I run cucumber now:<pre>./script/cucumber features/author_publish.feature</pre><br />I get:<pre>Feature: author publish<br />As an author<br />I want to publish a post<br />So that it can be read<br /><br />Scenario: publish a post # features/author_publish.feature:6<br /> Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6<br /> When I add a new post # features/author_publish.feature:8<br /> Then I should see a page for the new post # features/author_publish.feature:9<br /><br />1 scenario<br />2 undefined steps<br />1 passed step<br /><br />You can implement step definitions for missing steps with these snippets:<br /><br />When /^I add a new post$/ do<br />pending<br />end<br /><br />Then /^I should see a page for the new post$/ do<br />pending<br />end</pre><br />So now the it's obviously finding the 'the post creation page' but the remaining When/Then are still pending.<br /><br />So now I add the following code:<pre class="ruby"><span class="comment"># RAILS_ROOT/features/step_definitions/publish_steps</span><br /><span class="constant">When</span> <span class="punct">/^</span><span class="constant">I</span> <span class="ident">add</span> <span class="ident">a</span> <span class="ident">new</span> <span class="ident">post</span><span class="global">$/</span> <span class="keyword">do</span><br /> <span class="ident">fill_in</span> <span class="punct">"</span><span class="string">Title</span><span class="punct">"</span> <span class="punct">,</span> <span class="symbol">:with</span> <span class="punct">=&gt;</span> <span class="punct">"</span><span class="string">Some Title</span><span class="punct">"</span><br /> <span class="ident">click_button</span> <span class="punct">"</span><span class="string">Create</span><span class="punct">"</span><br /><span class="keyword">end</span></pre><br /><br />Then I run cucumber:<pre>./script/cucumber features/author_publish.feature</pre><br />And get the following:<pre>Feature: author publish<br />As an author<br />I want to publish a post<br />So that it can be read<br /><br /> Scenario: publish a post # features/author_publish.feature:6<br /> Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6<br /> When I add a new post # features/step_definitions/posts_steps.rb:1<br /> Then I should see a page for the new post # features/author_publish.feature:9<br /><br />1 scenario<br />1 undefined step<br />2 passed steps<br /><br />You can implement step definitions for missing steps with these snippets:<br /><br />Then /^I should see a page for the new post$/ do<br /> pending<br />end</pre><br />Now I just need to satisfy the 'Then'<pre class="ruby"><span class="comment"># RAILS_ROOT/features/step_definitions/publish_steps</span><br /><span class="constant">Then</span> <span class="punct">/^</span><span class="constant">I</span> <span class="ident">should</span> <span class="ident">see</span> <span class="ident">a</span> <span class="ident">page</span> <span class="keyword">for</span> <span class="ident">the</span> <span class="ident">new</span> <span class="ident">post</span><span class="global">$/</span> <span class="keyword">do</span><br /> <span class="ident">response</span><span class="punct">.</span><span class="ident">should</span> <span class="ident">contain</span><span class="punct">("</span><span class="string">Post was successfully created</span><span class="punct">")</span><br /><span class="keyword">end</span></pre><br /><br />Then I run cucumber and get the following:<pre>Feature: author publish<br />As an author<br />I want to publish a post<br />So that it can be read<br /><br />Scenario: publish a post # features/author_publish.feature:6<br /> Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6<br /> When I add a new post # features/step_definitions/publish_steps.rb:1<br /> Then I should see a page for the new post # features/step_definitions/publish_steps.rb:6<br /><br />1 scenario<br />3 passed steps<br /></pre><br /><br />Everything works, except the feature I broke, but I'll pick up there in the next post.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-1352938684461231863?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-91020116760497973562009-03-24T13:18:00.000-07:002009-03-24T14:46:44.318-07:00The Importance of Roles in BDDAs I continue to try wrap my head around <a href="http://wiki.github.com/aslakhellesoy/cucumber">Cucumber</a>, <a href="http://rspec.info/"><span class="blsp-spelling-error" id="SPELLING_ERROR_0">RSpec</span></a> and <a href="http://dannorth.net/introducing-bdd"><span class="blsp-spelling-error" id="SPELLING_ERROR_1">BDD</span></a> I found myself struggling while trying to write stories. Then I found this <a href="http://blog.josephwilk.net/ruby/telling-a-good-story-rspec-stories-from-the-trenches.html#comment-570">comment</a> from <a href="http://www.benmabey.com/">Ben <span class="blsp-spelling-error" id="SPELLING_ERROR_2">Mabey</span></a> on one of <a href="http://blog.josephwilk.net/">Joseph <span class="blsp-spelling-error" id="SPELLING_ERROR_3">Wilk</span>'</a>s blog articles; <a href="http://blog.josephwilk.net/ruby/telling-a-good-story-rspec-stories-from-the-trenches.html">"Telling a good story - <span class="blsp-spelling-error" id="SPELLING_ERROR_4">respec</span> stories from the trenches"</a>. It really helped to provide something I was missing, an undertstanding of how important the story tellers perspective is.<br /><br />After reading that I defined a very specific set of roles (Admin, Author, Editor, Hacker and Reader) that I'll be using as I write my stories.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-9102011676049797356?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-68338170351427008852009-03-23T14:13:00.000-07:002009-05-02T10:07:24.963-07:00Baby Step #1 with Rails, RSpec and CucumberThere are lots of resources on the web to help get started with RSpec and Cucumber but there's still a learning curve that I think only experience will overcome. So I decided just to dive in. I don't claim what I've done here is right, or even a good idea, but it's where I started.<br /><br />My initial goal is just to walk through the 'outside in' <a href="http://jamesshore.com/Blog/Red-Green-Refactor.html">red/green/refactor</a> process of writing a feature then examples without spending to much time worrying if I selected the right story or if it's implemented it correctly.<br /><br />This initial cycle is mostly about learning what the tools can do for me.<br /><br />The simplest story that I can imagine goes like this:<br /><br /><pre>as a reader I want to browse the site so that I can read it's content.</pre><br /><br />So with that story in hand It's time to write my first feature<br /><pre> # ./features/reader_browses.feature<br />Feature: browsing<br /> As a reader<br /> I want to browse the site<br /> So that I can view it's content<br /><br />Scenario: visit the homepage<br /> When I go to the homepage<br /> Then I should see "Hello world!"</pre><br /><br />Next I run Cucumber, 'rake features', and get the following....<br /><br /><pre> Feature: browsing<br /> As a reader<br /> I want to browse the site<br /> So that I can view it's content<br /><br />Scenario: visit the homepage # features/reader_browses.feature:6<br /> When I go to the homepage # features/step_definitions/webrat_steps.rb:10<br /> undefined method `root_path' for #<actioncontroller::integration::session:0xb6e35da8> (NoMethodError)<br /> /home/mgreenly/Projects/blog/features/support/paths.rb:6:in `/^I go to (.+)$/'<br /> features/reader_browses.feature:7:in `When I go to the homepage'<br /> Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93<br /><br />1 scenario<br />1 failed step<br />1 skipped step<br />rake aborted!</actioncontroller::integration::session:0xb6e35da8></pre><br /><br />So the first error I get is because we don't have a default route, so lets fix it by adding the following line in "./config/routes.rb"<br /><br /><pre>map.root :controller => 'posts'</pre><br /><br />Then I run Cucumber again, "rake features", and get the following....<br /><br /><pre> Feature: browsing<br />As a reader<br />I want to browse the site<br />So that I can read it's content<br /><br />Scenario: visit the homepage # features/reader_browse.feature:6<br /> When I go to the homepage # features/step_definitions/webrat_steps.rb:10<br /> uninitialized constant PostsController (NameError)<br /> (eval):2:in `/^I go to (.+)$/'<br /> features/reader_browse.feature:7:in `When I go to the homepage'<br /> Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93<br /><br />1 scenario<br />1 failed step<br />1 skipped step<br />rake aborted!</pre><br /><br />So now it's telling me that the default route points to a PostsController and that controller doesn't exist. So lets create both the controller and it's rspec framework with the built in generator.<br /><br /><pre>./script/generate rspec_controller posts</pre><br /><br />Now I'll run the spec, "rake spec", to see what I get<br /><pre> ..<br /><br />Finished in 0.045209 seconds<br /><br />2 examples, 0 failures</pre><br /><br />It passes of course because the controller doesn't do anything and the spec doesn't make any requirements on it.<br /><br />So lets see what Cucumber says now, "rake features"<br /><pre> Feature: browsing<br /> As a reader<br /> I want to browse the site<br /> So that I can view it's content<br /><br />Scenario: visit the homepage # features/reader_browses.feature:6<br /> When I go to the homepage # features/step_definitions/webrat_steps.rb:10<br /> No action responded to index. Actions: (ActionController::UnknownAction)<br /> /usr/local/stow/ruby-1.8.7-p72/lib/ruby/1.8/benchmark.rb:308:in `realtime'<br /> (eval):2:in `/^I go to (.+)$/'<br /> features/reader_browses.feature:7:in `When I go to the homepage'<br /> Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93<br /><br />1 scenario<br />1 failed step<br />1 skipped step<br />rake aborted!</pre><br /><br />There's still an error but it's changed. Now it's complaining because the there's no 'index' action on the Posts controller. So lets drop back to the rspec example for the Posts controller and create a requirement for the index action. Remember first I want it to fail, then pass.<br /><pre><br /># in ./spec/controllers/posts_controller_spec.rb<br />describe "GET 'index'" do<br />it "should be successful" do<br />get 'index'<br /> response.should be_success<br />end<br />end</pre><br /><br />So now when I run "rake spec" I get....<br /><pre><br />.F.<br /><br />1)<br />ActionController::UnknownAction in 'PostsController GET 'index' should be successful'<br />No action responded to index. Actions:<br />./spec/controllers/posts_controller_spec.rb:12:<br /><br />Finished in 0.052123 seconds<br /><br />3 examples, 1 failure<br />rake aborted!</pre><br /><br />Now that the spec is failing we can try to satisfy it. To do that I just create an empty file at "app/views/posts/index.erb" and then rerun "rake spec"<br /><pre> ...<br /><br />Finished in 0.049891 seconds<br /><br />3 examples, 0 failures</pre><br /><br />So now that the spec passes I run Cucumber again, "rake features", and get ....<br /><pre> Feature: browsing<br /> As a reader<br /> I want to browse the site<br /> So that I can view it's content<br /><br />Scenario: visit the homepage # features/reader_browses.feature:6<br /> When I go to the homepage # features/step_definitions/webrat_steps.rb:10<br /> Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93<br /> Could not parse document (RuntimeError)<br /> features/reader_browses.feature:8:in `Then I should see "Hello world!"'<br /><br />1 scenario<br />1 failed step<br />1 passed step<br />rake aborted!</pre><br /><br /><br />It's still failing but now it's upset because it can't find the "Hello world!" text on the index page. This is easy to fix, I just need to add the text to "app/views/posts/index.erb". Now I run "rake features".....<br /><pre><br />Feature: browsing<br /> As a reader<br /> I want to browse the site<br /> So that I can view it's content<br /><br />Scenario: visit the homepage # features/reader_browses.feature:6<br /> When I go to the homepage # features/step_definitions/webrat_steps.rb:10<br /> Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93<br /><br />1 scenario<br />2 passed steps</pre><br />It passes. Just to make sure I didn't screw anything up I run "rake spec" on more time.<br /><pre>...<br />Finished in 0.049713 seconds<br />3 examples, 0 failures</pre><br /><br />That makes one successfull pass through an outside-in red/green/refactor cycle using Cucumber and Rspec with Rails.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-6833817035142700885?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-24792791337902383912009-03-23T11:36:00.000-07:002009-03-23T20:01:15.567-07:00Start a BDD Rails ProjectI was starting a new rails project today that I plan on developing with Cucumber and RSpec. I quickly realized that even though there are tons of tutorials out there none seemed to really describe the process of initializing a new project. So here it is in a nutshell.<pre>$ rails blog<br />$ cd blog<br />$ ./script/generate rspec<br />$ ./script/generate cucumber<br />$ rake db:migrate</pre><br />At this point you should be able to do a 'rake', 'rake spec' or 'rake features' command and not get any errors.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-2479279133790238391?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-1981326435542239902009-03-23T07:33:00.000-07:002009-04-08T09:56:13.339-07:00Rails Development On UbuntuThere's no shortage of posts on the web describing how to setup Ubuntu for Rails development but I'm going to add one more anyway. I choose to write this because none of the posts I ran across were geared towards a current Rails, RSpec, Cucumber, Git setup.<br /><br />I'm using Ubuntu 9.04 (JauntyJackalope) during this installation.<br /><br />First install Ruby and it's associated packages.<pre>sudo apt-get install ruby-full</pre><br />Next install the native SQlite3 libraries and command line tools.<pre>sudo apt-get install sqlite3 libsqlite3-dev</pre><br />Next install some native xml processing libraries used by webrat.<pre>sudo apt-get install libxml2-dev libxslt-dev</pre><br />Next install Git<pre>sudo apt-get install git-core</pre><br />Next install the tools necessary to build native rubygem extensions.<pre>sudo apt-get install ruby-dev build-essential</pre><br />Next install RubyGems from source because the Ubuntu packages are usually not current. I also prefer to install RubyGems into my $HOME directory because mixing source packages in with distribution managed packages is generally a bad idea.<pre>wget -c http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz<br />tar xvzf rubygems-1.3.1<br />cd rubygems-1.3.1<br />ruby setup.rb --prefix=$HOME/.rubygems --no-format-executable</pre><br />Once that's done you need to set some environment variables so that; Ruby can find the gems library, RubyGems can find the gem repository and executable gems will be in the $PATH. Add the following to the file $HOME/.bashrc<br /><pre>export RUBYLIB=$HOME/.rubygems/lib<br />export GEM_HOME=$HOME/.rubygems/gems<br />export PATH=$HOME/.rubygems/bin:$GEM_HOME/bin:$PATH</pre><br />Then make sure the RubyGems library is up-to-date and install all the necessary gems.<pre>gem update --system<br />gem install rake rails rspec-rails cucumber webrat sqlite3-ruby</pre><br />If you want to double check your installation you can try the following...<pre>ruby --version<br />gem --version<br />rails --version<br />spec --version<br />cucumber --version<br /></pre><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-198132643554223990?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com2tag:blogger.com,1999:blog-1539046083268926117.post-69293152738900014702009-03-23T07:04:00.000-07:002009-03-23T12:45:13.336-07:00RubyGems Documentation IndexIn case there's anyone out there that isn't aware; running RubyGems server provides browseable documentation at http://localhost:8808/ for all the installed gems that have rdocs. This morning I decided to automate the launching of the gem server at startup by adding it to my bash profile.<br /><pre># in $HOME/.profile<br />gem server --daemon</pre><br />If you've previously installed some gems without documentation you can regenerate them this way.<br /><pre>gem rdoc --all</pre><div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-6929315273890001470?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0tag:blogger.com,1999:blog-1539046083268926117.post-79912201544173754912009-02-24T03:09:00.000-08:002009-02-24T03:13:53.951-08:00Twitter SearchFor the first time ever I actually used Twitter for something productive today. I was having trouble with GMail this morning and was trying to decided how to verify if it was something on my end or Googles. So I decided to try <a href="http://search.twitter.com/search?q=gmail">http://search.twitter.com</a> which gave me my answer as I watched tweets pile in announcing GMail was down.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1539046083268926117-7991220154417375491?l=blog.michaelgreenly.com'/></div>Mike Greenlyhttp://www.blogger.com/profile/15964361079720234778mgreenly@gmail.com0