<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="http://feeds.feedburner.com/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>DevChix » Rails</title>
	
	<link>http://www.devchix.com</link>
	<description>Boys can't have all the fun</description>
	<pubDate>Sat, 14 Jun 2008 06:48:23 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/devchix/zKMD" type="application/rss+xml" /><feedburner:browserFriendly></feedburner:browserFriendly><item>
		<title>Multiple object forms, delegation, and has_one…</title>
		<link>http://www.devchix.com/2008/06/03/multiple-object-forms-delegation-and-has_one/</link>
		<comments>http://www.devchix.com/2008/06/03/multiple-object-forms-delegation-and-has_one/#comments</comments>
		<pubDate>Wed, 04 Jun 2008 05:08:29 +0000</pubDate>
		<dc:creator>desi</dc:creator>
		
		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[Thoughts]]></category>

		<category><![CDATA[Tips and Tricks]]></category>

		<guid isPermaLink="false">http://www.devchix.com/?p=162</guid>
		<description><![CDATA[I had an ah ha moment that maybe shouldn&#8217;t have been such an ah ha moment but it was so I figured I would share it.  Yeah so I am sure most of us have  had a situation where we needed to have multiple model forms. Most of the time now days I [...]]]></description>
			<content:encoded><![CDATA[<p>I had an ah ha moment that maybe shouldn&#8217;t have been such an ah ha moment but it was so I figured I would share it.  Yeah so I am sure most of us have  had a situation where we needed to have multiple model forms. Most of the time now days I use attribute_fu to solve this issue but attribute_fu doesn&#8217;t work with has_one associations. Today I had a situation where I had two fields that were required for a has_one association object. Long story short it came to me that if we just used the delegate method provided by rails that we could essentially act like the attributes we were setting were on the parent model. This meant we only needed to create one form with multiple fields even though some of those fields were actually on a different model. I then remembered that back when I was working with the guys over at ThoughtWorks that we used a Ruby Extension called Forwardable to be able to delegate multiple attributes on one object.</p>
<p>So instead of this:<br />
<code><br />
delegate :first_name, :to => :profile<br />
delegate :last_name, :to => :profile<br />
delegate :some_other_attribute, :to => :profile<br />
</code></p>
<p>side note: I&#8217;m not sure but I don&#8217;t believe delegate can take multiple attributes (I tried to look this up but for some reason couldn&#8217;t find the documentation for this method and didn&#8217;t have time to dig in the code) </p>
<p>You could do the following:</p>
<p>include the Ruby Extension Forwardable in the parent model class</p>
<p><code><br />
include Forwardable<br />
</code></p>
<p>and then add this line:<br />
<code><br />
def_delegators  :profile, :first_name, :last_name, :some_other_attribute<br />
</code></p>
<p>So yeah that was my little ah ha moment. I am sure there are even better ways than this but this was better than what we were looking at doing to begin with.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devchix.com/2008/06/03/multiple-object-forms-delegation-and-has_one/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Rails Conf 2008</title>
		<link>http://www.devchix.com/2008/05/30/rails-conf-2008/</link>
		<comments>http://www.devchix.com/2008/05/30/rails-conf-2008/#comments</comments>
		<pubDate>Fri, 30 May 2008 18:55:18 +0000</pubDate>
		<dc:creator>desi</dc:creator>
		
		<category><![CDATA[Events]]></category>

		<category><![CDATA[News]]></category>

		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.devchix.com/?p=161</guid>
		<description><![CDATA[Hello Ladies,
I am reporting from RailsConf 2008. The main focus of this post is logistics for the conference. I&#8217;ll be posting about the talks as soon as I get to attend one. I have been running around trying to take care of DevChix related stuff.
We were unable to get an official room for a BoF [...]]]></description>
			<content:encoded><![CDATA[<p>Hello Ladies,<br />
I am reporting from RailsConf 2008. The main focus of this post is logistics for the conference. I&#8217;ll be posting about the talks as soon as I get to attend one. I have been running around trying to take care of DevChix related stuff.</p>
<p>We were unable to get an official room for a BoF but I have decided we will just take over some area of the convention center. Lets meet outside Exhibit Hall E at 7:30 on Saturday night. We can discuss whatever we want to. We are also planning appetizers and cocktails after the BoF. <a href="http://www.hashrocket.com">Hashrocket Inc</a>, the company I work for is sponsoring the evening. Thanks Hashrocket! </p>
<p>Please either come to the BoF for more information or find one of the ladies with a DevChix logo on their badges for more information.  I will also have stickers to give out (until they run out).</p>
<p>We would love to meet ALL the women developers at the conference so please come out and get to know us. </p>
<p>Cheers<br />
Desi</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devchix.com/2008/05/30/rails-conf-2008/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Call Me! A quick how-to for getting dialable phone numbers in your Rails app.</title>
		<link>http://www.devchix.com/2008/01/22/call-me-a-quick-how-to-for-getting-dialable-phone-numbers-in-your-rails-app/</link>
		<comments>http://www.devchix.com/2008/01/22/call-me-a-quick-how-to-for-getting-dialable-phone-numbers-in-your-rails-app/#comments</comments>
		<pubDate>Tue, 22 Jan 2008 17:08:11 +0000</pubDate>
		<dc:creator>desi</dc:creator>
		
		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[Thoughts]]></category>

		<category><![CDATA[Tips and Tricks]]></category>

		<guid isPermaLink="false">http://www.devchix.com/2008/01/22/call-me-a-quick-how-to-for-getting-dialable-phone-numbers-in-your-rails-app/</guid>
		<description><![CDATA[This might be something everyone knows but just in case I figured I would post a quick how-to on getting a clickable phone number in your Rails app. This example is only for the iPhone user_agent but you can make it work for other types as well as long as you know the user_agent.
Place the [...]]]></description>
			<content:encoded><![CDATA[<p>This might be something everyone knows but just in case I figured I would post a quick how-to on getting a clickable phone number in your Rails app. This example is only for the iPhone user_agent but you can make it work for other types as well as long as you know the user_agent.</p>
<p>Place the following code snippet in your application.rb file </p>
<pre><code> session :mobile => true, :if => proc { |request| Utility.mobile?(request.user_agent) }

  class Utility

    def self.mobile?(user_agent)
      user_agent =~/(iPhone)/i
    end
  end
</code></pre>
<p>Then in your view or your presenter code put a check for the session variable and if it is set then display the clickable phone number with the tel protocol in the href like so</p>
<pre><code>
"tel:#{contact.phone}"
</code></pre>
<p>and if its not set then just do things normally. Make sure you have the check there because if you don&#8217;t then when someone clicks the link in the browser it will complain about not understanding the tel protocol.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devchix.com/2008/01/22/call-me-a-quick-how-to-for-getting-dialable-phone-numbers-in-your-rails-app/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Programming from the (under)ground up</title>
		<link>http://www.devchix.com/2008/01/05/programming-from-the-underground-up/</link>
		<comments>http://www.devchix.com/2008/01/05/programming-from-the-underground-up/#comments</comments>
		<pubDate>Sun, 06 Jan 2008 01:25:04 +0000</pubDate>
		<dc:creator>lisa</dc:creator>
		
		<category><![CDATA[Apprenticeship]]></category>

		<category><![CDATA[Introductions]]></category>

		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[Thoughts]]></category>

		<guid isPermaLink="false">http://www.devchix.com/2008/01/05/programming-from-the-underground-up/</guid>
		<description><![CDATA[     Hello. Welcome to my first article.
     And my brand spankin new, made-from-scratch stab at programming. It&#8217;s going to be a bumpy ride: bumpy like fun-old-rollercoaster-bumpy not trainwreck-bumpy (universe willing).
     Please allow me to rattle off some quick background facts so you know [...]]]></description>
			<content:encoded><![CDATA[<p>     Hello. Welcome to my first article.</p>
<p>     And my brand spankin new, made-from-scratch stab at programming. It&#8217;s going to be a bumpy ride: bumpy like fun-old-rollercoaster-bumpy not trainwreck-bumpy (universe willing).</p>
<p>     Please allow me to rattle off some quick background facts so you know what planet I&#8217;m coming from. I&#8217;m a 26 year old retired bartender. I did that for more years than I care to say (ok fine, 8). I fancy myself an amateur artist; basically, I paint for therapy and fun. I&#8217;ve always liked things of a nerdy nature (i.e. writing very basic html in a webshell on angelfire when I was 13, Magic the Gathering, guys who majored in Astrophysics, etc). I consider myself very confident and intelligent, and it&#8217;s a shame that went to waste for so many years. That being said, years of bartending with no substantial plans for the future wore me out and made me feel quite desperate for awhile.</p>
<p>     Then something changed. I got beat down so much by the universe&#8217;s way of telling me to stop f&#8217;ing around, that I got fed up with being fed up. Well, Desi McAdam happens to be one of my favorite people on the planet and a very close friend, and she had always offered to teach me programming&#8230;intensively. She and my other longtime friend/ROR evangelist Obie Fernandez had always told me they thought I&#8217;d be a great programmer. I didn&#8217;t know what they were talking about. So I called up my dear Desi and said &#8220;I&#8217;ll do whatever it takes. Let&#8217;s do this thing.&#8221;</p>
<p>     I thought I was going to be learning in my off time while still bartending and getting tutored whenever Desi was in town. I knew this would take a very, very, very long time, but I felt ready for the challenge.</p>
<p>     As it turned out, she and Obie were down here in Florida on the beach working with this fabulous guy Mark Smith. I had met him some weeks before, and we all had a great time together. They wanted to bring an apprentice on to the small team, so voila! Here I am. I am now in full on training starting with nothing but my instinctual and intellectual abilities and no experience. I am extremely grateful for the opportunity I have, and I intend to give back to Desi and Obie by trying hard to be a bad ass programmer.</p>
<p>     Desi is putting alot of effort into being my personal, full-time tutor, and I think she rocks socks for it.</p>
<p>     So I&#8217;m offering up myself, my victories, and my many future foibles here for your musing and amusement.</p>
<p>     Cheers and enjoy</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devchix.com/2008/01/05/programming-from-the-underground-up/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Migrating from Test::Unit to RSpec</title>
		<link>http://www.devchix.com/2008/01/04/migrating-from-testunit-to-rspec/</link>
		<comments>http://www.devchix.com/2008/01/04/migrating-from-testunit-to-rspec/#comments</comments>
		<pubDate>Fri, 04 Jan 2008 17:08:06 +0000</pubDate>
		<dc:creator>jacqui</dc:creator>
		
		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[Testing]]></category>

		<category><![CDATA[Thoughts]]></category>

		<guid isPermaLink="false">http://www.devchix.com/2008/01/04/migrating-from-testunit-to-rspec/</guid>
		<description><![CDATA[At Streeteasy, nearly half of our tests are still written in Test::Unit, so it&#8217;s hard to see what our actual test coverage is using Rcov.
I read recently that RSpec got support for Test::Unit interoperability. Obviously now is the time to make the switch from Test::Unit to RSpec. You can do it without a mass exodus [...]]]></description>
			<content:encoded><![CDATA[<p>At <a href="http://www.streeteasy.com/">Streeteasy</a>, nearly half of our tests are still written in <a href="http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/index.html">Test::Unit</a>, so it&#8217;s hard to see what our actual test coverage is using <a href="http://eigenclass.org/hiki.rb?rcov">Rcov</a>.</p>
<p>I read recently that <a href="http://www.rubyinside.com/rspec-11-released-now-supports-rails-20-674.html">RSpec got support for Test::Unit interoperability</a>. Obviously now is the time to make the switch from Test::Unit to RSpec. You can do it without a mass exodus from Test::Unit. Use your existing tests inside the RSpec test harness.</p>
<p>So here&#8217;s how I converted all out legacy tests to rspec.</p>
<h1>Step One: update RSpec</h1>
<p>This is fairly self-explanatory and written up <a href="http://rspec.info/upgrade.html">elsewhere</a>. However, in short, I updated both our rspec and rspec_on_rails plugins, remembering to rerun </p>
<p><code>$ ruby script/generate rspec</code></p>
<p>Make a back-up copy of the spec_helper if you&#8217;ve customized it. Compare it to the one generated for the new rspec version to see if there&#8217;s been any significant changes, and if so, merge them into your helper.</p>
<h1>Step Two: move your files from test/ to spec/</h1>
<p>This is what I did: I copied test/unit/* to spec/models/, renaming them appropriately:</p>
<p><code>
<pre>
  ## current_path is a hash where :tu =&gt; test::unit path and :s =&gt; spec path
  def make_tests_specs(current_path)
    current_path[:tu].each do |file|
      unless file == &#8220;.&#8221; or file == &#8220;..&#8221;
        full_file = File.join(current_path[:tu].path, file)
        if File.directory? full_file
          spec_file = full_file.gsub(/test/, &#8220;spec&#8221;)
          spec_file.gsub!(/unit/, &#8220;models&#8221;)
          spec_file.gsub!(/functional/, &#8220;controllers&#8221;)
          FileUtils.mkdir_p(spec_file) unless File.exists? spec_file
          make_tests_specs({:tu =&gt; Dir.new(full_file), :s =&gt; Dir.new(spec_file)})
        else
          new_spec = File.join(current_path[:s].path, file.gsub(/_test/, &#8220;_spec&#8221;))
          puts &#8220;converting TestCase #{full_file} to ExampleGroup #{new_spec}\n&#8221;
          File.copy(full_file, new_spec)
        end
      end
    end
  end
</pre>
<p></code></p>
<h1>Step Three: change your assertions</h1>
<p>While that would move all my tests over to the specs directory and rename them, I figured, why should I stop there? I realized I could probably do a lot of the Test::Unit to RSpec syntax conversions programmatically. Here are the regular expressions I used to do the substitutions - while these worked great for our codebase at StreetEasy, your mileage may vary, so be sure to use caution before running this against your code base (and hey, you do use source control, don&#8217;t you?):</p>
<p><code>
<pre>
if line =~ %r@require.*?test_helper@
  new_file.puts "require '#{RAILS_ROOT}/test/test_helper'"
  new_file.puts "require '#{RAILS_ROOT}/spec/spec_helper'"
elsif line =~ %r@class.*?TestCase@ and line !~ %r@Controller@
  new_file.puts "describe 'transitioning from TestCase to ExampleGroup' do"
elsif line =~ %r@class\s*(.*?)Test@
  new_file.puts "describe #{$1}, 'transitioning from TestCase to ExampleGroup' do"
  new_file.puts "integrate_views"
elsif line =~ %r@def setup@
  new_file.puts "before do"
elsif line =~ %r@def test_(.*?)\n@
  new_file.puts "it \"should #{$1.humanize.downcase}\" do"
elsif line =~ %r@assert_response :success@
  new_file.puts "response.should be_success"
elsif line =~ %r@assert_response :redirect@
  new_file.puts "response.should be_redirect"
elsif line =~ %r@assert_redirected_to (.*?)\n@
  new_file.puts "response.should redirect_to(#{$1})"
elsif line =~ %r@assert_equal (.*?),\s(.*?)\n@
  m1 = $1
  m2 = $2
  unless m1.nil? or m2.nil?
    unless m1.match(/nil/) or m2.match(/nil/)
      new_file.puts "#{m1}.should == #{m2}"
    end
  end
elsif line =~ %r@assert assigns\(:(.*?)\)$@
  new_file.puts "assigns[:#{$1}].should be_true&#8221;
else
  unless line =~ /^class.*?Controller.*?rescue_action.*?end$/i or line =~ /require &#8216;.*?_controller&#8217;/i or line =~ /^#/
    new_file.puts line
  end
end
</pre>
<p></code></p>
<p>The above block of code will convert any response (:success, :redirect, redirect_to), assigns, and equality assertions. It will change your method declarations to &#8220;it &#8217;should&#8230;&#8217; do/end&#8221; block syntax. It will require both the test_helper and spec_helper. It will integrate_views by default on your functional (controller) tests; you might want to go through those by hand later and separate out the front-end stuff into a set of <a href="http://errtheblog.com/posts/66-view-testing-20">view tests</a>, depending on <a href="http://tuples.us/2007/08/17/my-life-with-bdd-and-rspec/">how you</a> <a href="http://rubyforge.org/pipermail/rspec-users/2007-August/002769.html">feel about</a> <a href="http://rspec.info/documentation/rails/writing/views.html">them</a>. </p>
<p>You could definitely write more substitutions - there are more assertions in Test::Unit, of course. If your code base makes extensive use of other assertions you&#8217;ll probably want to add to the regexes above. In our case, however, I found the above satisfied converting the most straight-forward Test::Unit assertions, leaving me with the slightly more complicated examples to convert by hand, which brings me to the next step. </p>
<p>FYI, you can find a table of Test::Unit assertions and their RSpec equivalents <a href="http://rspec.info/documentation/test_unit.html">here on the RSpec documentation site</a>.</p>
<h1>Step Four: run rake:spec and see what happens</h1>
<p>You might as well see what situation any automated conversion process left you in. I had a few errors during this phase - mostly due to the fact that I forgot to move my fixtures, woops - but it didn&#8217;t take me very long to resolve them.</p>
<h1>Step Five: go through each sparkling new spec and convert any remaining Test::Unit-based assertions into RSpec syntax.</h1>
<p>The good news: you can take your time on this step since RSpec now plays nice with Test::Unit. I was in a particularly motivated and productive mood after finding myself with a big directory full of specs (and an amazingly empty test/ directory) so I just went through them all right then and there. It took me most of the afternoon, but when I left work that day I was able to see this, which is just awesome:</p>
<p><code><br />
$ rake spec<br />
........................................................................................................................................................................................................................................................................................................................................................................................................................................................</p>
<p>Finished in 70.023361 seconds</p>
<p>440 examples, 0 failures<br />
</code></p>
<p>As you can see from the amount of time it took all 440 examples to run, we could definitely benefit from a greater use of mocks and stubs in our controller tests. 70 seconds is a tad bit long. That&#8217;s next on the to do list - mock and stub wherever I can in our controller tests, followed by integration/big picture testing using Story Runner, and then, if there&#8217;s time*, refactor our specs to be better organized and efficient - I can think of several places where I had wished for nested examples, or could have used shared examples, had I been aware they existed at the time.</p>
<p>I&#8217;ll be writing up my experience with Story Runner in the next couple of weeks. Til then, though, best of luck in your specs!</p>
<p>* always a compromise when you&#8217;re trying to do things the right way and add business value simultaneously.</p>
<p>For more information Behavior Driven Development, check out <a href="http://dannorth.net/">Dan North</a>&#8217;s article, &#8220;<a href="http://dannorth.net/introducing-bdd">Introducing BDD</a>.&#8221;</p>
<p>You can find the documentation for RSpec at <a href="http://rspec.info/index.html">http://rspec.info</a>.</p>
<p>Many thanks to Joshua Sierles, Paul Marsh, and Josh Knowles for their time in reviewing this!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devchix.com/2008/01/04/migrating-from-testunit-to-rspec/feed/</wfw:commentRss>
		</item>
		<item>
		<title>RubyEast Recap, Slides, and Other Thoughts</title>
		<link>http://www.devchix.com/2007/09/30/rubyeast-recap-slides-and-other-thoughts/</link>
		<comments>http://www.devchix.com/2007/09/30/rubyeast-recap-slides-and-other-thoughts/#comments</comments>
		<pubDate>Sun, 30 Sep 2007 22:25:25 +0000</pubDate>
		<dc:creator>desi</dc:creator>
		
		<category><![CDATA[Apprenticeship]]></category>

		<category><![CDATA[Presentation]]></category>

		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[Testing]]></category>

		<category><![CDATA[Thoughts]]></category>

		<guid isPermaLink="false">http://www.devchix.com/2007/09/30/rubyeast-recap-slides-and-other-thoughts/</guid>
		<description><![CDATA[I spoke at RubyEast this past Friday and I think the presentation went pretty well. It was my first presentation in a speaker/audience type setting so I was very nervous. I have presented at Agile 2006 but it was a game (interactive) and was co-presented by several other people. This presentation was the first time [...]]]></description>
			<content:encoded><![CDATA[<p>I spoke at RubyEast this past Friday and I think the presentation went pretty well. It was my first presentation in a speaker/audience type setting so I was very nervous. I have presented at Agile 2006 but it was a game (interactive) and was co-presented by several other people. This presentation was the first time I stood in front of a room full of people and spoke and everything went very well. Like I said I was really nervous but as soon as I got started the nervousness went away. I think I am very lucky because I was able to present to a room full of very nice/cool people and that made the experience a great one. I want to actually thank the people who came to hear me present and who gave me great feedback and encouragement afterwards it really made my day.  If you are interested here are the slides for the presentation. <a href="http://www.devchix.com/wp-content/uploads/2007/09/atourofrailstesting-rspec.pdf">A Tour Of Rails Testing using RSpec</a></p>
<p>I didn&#8217;t get to see many of the sessions because I was busy preparing for my talk but I was able to catch <a href="http://www.jroller.com/obie/entry/obie_s_rubyeast_2007_presentations">Obie&#8217;s presentation - Advanced ActiveRecord</a> which was really good (and I am not just saying that because he is my boyfriend). I also caught the ending Keynote where Nap (I actually don&#8217;t know his real name) announced the Rails Rumble winners. There were several screencasts and it made me wish that Obie, Clay, Nick and I would have had time to get the video that was shot of us over the weekend edited and ready for prime time.  We had a blast doing the competition and while we didn&#8217;t win (we got honorable mention) we learned a lot and I think we all grew closer in those 48 hours. The teams that did win did a tremendous job on their apps and well deserved the loot. Take a look at the winners there really are some great apps. <a href="http://railsrumble.com/2007/9/28/and-the-envelope-please">Rails Rumble Winners</a></p>
<p>Friday evening a bunch of people got together after the conference and played several games of Werewolf which is a really fun game to play. I got to know a lot of people during that game and it was a great way to wind down. </p>
<p>Couple of other thoughts before I end the post. <a href="http://shesgeeky.org/">ShesGeeky (un)Conference</a> sounds like it is going to kick major ass so any of you ladies out there who can attend make sure you get registered. Additionally, ladies if you want to talk during the conference please contact the organizers. </p>
<p>GrrrlCamp seems to be getting a good footing. I was lucky enough to meet THE Gloria this past Friday and I look forward to being a part of GrrlCamp.</p>
<p>I have taken on an apprentice and she will soon be posting to the blog about her experiences. I am in the process of trying to see if creating an apprenticeship type program run by DevChix is possible because after speaking with Sonia (one of the women on DevChix) she helped me figure out that I would really like to have a program that fits the apprenticeship model rather than a mentoring program. Look for more to come on this in the future.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devchix.com/2007/09/30/rubyeast-recap-slides-and-other-thoughts/feed/</wfw:commentRss>
		</item>
		<item>
		<title>An Attachment Walked Into A Bar. Was That U, Fu?</title>
		<link>http://www.devchix.com/2007/08/22/an-attachment-walked-into-a-bar-was-that-u-fu/</link>
		<comments>http://www.devchix.com/2007/08/22/an-attachment-walked-into-a-bar-was-that-u-fu/#comments</comments>
		<pubDate>Thu, 23 Aug 2007 03:38:49 +0000</pubDate>
		<dc:creator>sarah g</dc:creator>
		
		<category><![CDATA[Database]]></category>

		<category><![CDATA[Design]]></category>

		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://www.devchix.com/2007/08/22/an-attachment-walked-into-a-bar-was-that-u-fu/</guid>
		<description><![CDATA[I recently spent some time working on a Rails application that needed to have various kinds of attachments, such as PDFs and images, for various types of resources, such as Programs and Questions.  The app in question lets you create programs and questionnaires for the purpose of granting continuing medical education (CME) credits to [...]]]></description>
			<content:encoded><![CDATA[<p>I recently spent some time working on a Rails application that needed to have various kinds of attachments, such as PDFs and images, for various types of resources, such as Programs and Questions.  The app in question lets you create programs and questionnaires for the purpose of granting continuing medical education (CME) credits to doctors &#8212; so they&#8217;re apprised of the bleeding edge techniques for sawing off your arm or tying your tooth to a doorknob. Not that this matters for the piece, but context &#8212; like sugar or salt &#8212; makes things go down easier. </p>
<p>If you can picture an office of harried administrators dealing with  ever-changing AMA requirements &#8212; &#8220;This brochure needs to go with this program!&#8221;, &#8220;This xray picture needs to go with Question 7&#8243;, &#8220;This program needs to sing &#8216;Missing You&#8217; while showing you diagrams of a root canal&#8221;.. (OK, maybe not the second item), you&#8217;ll see why I wanted to build a system that could expand easily. I did not want to find myself in the position of trying to look tough while shamefacedly muttering, &#8220;I&#8217;m sorry, PDFs can only go with *Programs*, <em>as you initially specified</em>&#8221; and watching these not-so-gentle administrative souls stare at me with fully warranted incredulity.  Blaming the client is like [fill-in-the-blank]:  weirdly fun for about 10 minutes before the hangover sets in.</p>
<p>This project turned into a tour of attachment_fu, single table inheritance, polymorphism and functional testing of uploads. Thanks to input from friends, blogs and the usual Internet suspects, I got it up and running in its first form and decided to write it up. Hopefully what I&#8217;ve learned can help someone else.  <a href="#linkouts">&#8220;Link-outs&#8221;</a>, the reclusive programmer&#8217;s form of the &#8220;Shout Out&#8221; (aka a list of great resources), can be found at the bottom of the article. </p>
<h2>Goal</h2>
<p>To implement a system that would allow for many types of files to be uploaded and attached to many types of resources. </p>
<h2>Requirements</h2>
<ol>
<li>Many  types of assets (image, pdf, etc.)</li>
<li>Assets belong to many types of resources (program, question, etc)</li>
<li>Assets upload to the file system</li>
<li>Assets validate in the context of their parent resource [you upload the pdf in the program form;  the image in the question form, etc]</li>
<li>All functional tests pass for uploads</li>
</ol>
<h2>Design</h2>
<p>Rather than have database tables for each attachment type (PDFs and images, then eventually MP3s) I made one table called Assets. Listening to my requirements, a friend suggested setting up my Assets model using polymorphism and single table inheritance and that I look into attachment_fu for the uploads. For this exercise I will only be focusing on the PDF upload.  Readers can extrapolate from there for image or other media types. This article does not address image resizing, or anything related to uploading images. There are some great links at the bottom that deal with these topics in depth.</p>
<h2>Modeling the domain: </h2>
<p>Assets (the table) has a field called <strong>type </strong>that is be [PDF, Image (etc)]; a field called<strong> resource_type</strong> which is the name of the class it belongs to, [Program, Question, etc.] and <strong>resource_id</strong> which is the id of the resource it belongs to.  </p>
<p>The Asset class will inherit from ActiveRecord.  PDF and Image (and any future file types we add to the system) inherit from Asset. Line items from Assets (the table) will look like this (leaving out the other fields for now)</p>
</p>
<pre>
<strong>type   | resource_type | resource_id</strong>

Pdf      | Program         | 82
Image  | Question         | 79
</pre>
<p>For my assets, I created the following models:<br />
asset.rb, image.rb and pdf.rb. The latter two inherit from Asset.</p>
<pre>
# [Asset.rb]
class Asset < ActiveRecord::Base
  belongs_to :resource, :polymorphic=>true
end

# [Pdf.rb]
class Pdf < Asset
  belongs_to :program
  has_attachment  :content_type=>&#8216;application/pdf&#8217;,
                         :storage=>:file_system,
                         :path_prefix=>&#8217;public/uploads/pdfs/&#8217;,
                         :size => 1.megabyte..3.megabytes
  validates_presence_of   :content_type, :filename

   def validate
     if filename &#038;&#038;  /pdf$/.match(filename).nil?
         errors.add(:filename, &#8220;must be a PDF &#8220;)
     end
  end

end
</pre>
<p>Because I am using attachment_fu, I need to have a set of basic fields in my table that will handle image and file uploads.  The migration I created looks like this:</p>
</p>
<pre>

t.column :parent_id,  :integer     # for the thumbnails of resized images, required for plugin
t.column :type, :string               # for STI on this table (curent types are image, PDF)
t.column :resource_type, :string  # for polymorphism on this table (current types are Program, Question)
t.column :resource_id, :integer    # for polymorphism, (program_id, question_id, etc)
t.column :content_type, :string    # Fields here [content_type] and below all attachment_fu requirements
t.column :filename, :string
t.column :thumbnail, :string
t.column :size, :integer
t.column :width, :integer
t.column :height, :integer</pre>
</pre>
<p>To create the relationship for my parent models (question and program) I set up the polymorphic relationship like so:</p>
<pre>
# [program.rb]
class Program < ActiveRecord::Base
   has_many    :pdfs, :as=>:resource,  :dependent => :destroy
end

# [question.rb]
class Question < ActiveRecord::Base
  has_many      :images, :as=>:resource, :dependent => :destroy
end
</pre>
<p>Note the difference between delete and destroy. <strong>Destroy</strong> will remove the entry from the database and the file from the filesystem.  <strong>Delete </strong>will only delete the database entry.</p>
<p>Once this is set up, you can test it in the console and see the magic of Rails at work. Through the column names and relationships we&#8217;ve built, entries in Assets are automatically created.  It&#8217;s one of the things that&#8217;s great about Rails. It doesn&#8217;t simply describe different modeling patterns: it implements them if you set up your code correctly.  It&#8217;s a beautiful thing.</p>
<h2>Uploading</h2>
<p>Without moving files to the server, the system is not complete. So, onto attachment_fu. The attachment_fu tutorials I found online all made the assumption that the attachment you are uploading is an entity of its own, in its own form: a mugshot, for instance.  My case was different: I needed to embed the attachment in the forms for their parent resources (pdf in the program form; image in the question form, etc) rather than independently.  So I needed to validate a form for more than one model. In my _form.rhtml partial for program I have the pdf field like so:</p>
<pre>
# [views/program/_form.rhtml]

< label for="program_notes" >Notes</ label ><br/>
<%= f.text_area :notes  %>

# &#8230; more fields &#8230;
< label for="program_status" >Add Brochure? [PDF Files only]< /label >
<%= file_field("pdf", "uploaded_data") %>
</pre>
<p>This means that both a pdf object and a program object are being passed back to the program controller. Since we know that  a program has_many pdfs, we can use program.pdfs.build in order to create &#8212; then validate and ultimately save &#8212; pdf objects.</p>
<h2>Validating in context of parent resource:</h2>
<p>I needed to check for errors with my program fields and my pdf field. This snippet is what I ended up with. The second line, validate_and_build_pdf is just a result of refactoring: it allows the validation methods to be run if you select a file through the upload file field (so if you try to upload a gif, the validation in Pdf.rb will squak) &#8212; but not if you just leave that field empty. </p>
<pre>
#[programs_controller.rb]
def create
  @program = Program.new(params[:program])
   validate_and_build_pdf(params[:pdf],params[:pdf][:uploaded_data])
   if @program.save
      flash[:notice] = &#8216;Program was successfully created.&#8217;
      redirect_to :action => &#8216;list&#8217; and return
   else
      render :action => &#8216;new&#8217;
   end
end

# this method stops program from saving the asset if the field is blank,
# and returns type-errors if relevant (files not PDF)
def validate_and_build_pdf(fileParams,file)
  if Asset.is_valid_file?(file)
     @pdf = @program.pdfs.build(fileParams)
  end
end
</pre>
</pre>
<p>The reason for the Asset.is_valid_file? method was the same: if no file was uploaded an entry was still being made in the assets table due to the polymorphic relationship. So  I had to make sure I was dealing with an actual fileobject, not an empty field. Since this would be used across the application for all types of file uploads, I made this a class method of Asset. </p>
<pre>
[Asset.rb]
# Checks that the object is one of the following types before running validation on it
def self.is_valid_file?(fileObj)
  if fileObj.is_a?(Tempfile)|| fileObj.is_a?(StringIO)
     || (defined?(ActionController::TestUploadedFile) &#038;&#038; fileObj.instance_of?(ActionController::TestUploadedFile))
     true
  else
     false
  end
end
</pre>
<p>You may be wondering what&#8217;s up with the ActionController::TestUploadedFile condition &#8212; it&#8217;s because when you run functional tests of uploaded assets, that is what they are &#8212; they are not instances of fileObj.  </p>
<p>Once it&#8217;s determined that you&#8217;re dealing with a valid file and you hit custom validation methods (such as informing the user that the file needs to be a pdf), you can include them with your program error messages so you only get one messages box at the top of your form, not two:</p>
<pre>
<%= error_messages_for :program, :pdf %>
</pre>
<h2>Pass Functional Tests</h2>
<p>This part is where you test all your uploads from your functional tests.  To test uploaded files, you need to put files in your fixtures/files directory then build your tests.</p>
<pre>
# [programs_controller_test.rb]
def test_create_with_pdf
  fdata = fixture_file_upload(&#8217;/files/semi.pdf&#8217;, &#8216;application/pdf&#8217;)
  num_programs = Program.count
  post :create,   :multipart => true,
  :pdf=>{&#8221;uploaded_data&#8221;=>fdata},
  :program => {:name=>&#8221;Newest Created Program&#8221;,
                    :is_ongoing=>1,
                    :status=>&#8221;Edit&#8221;}
  assert_response :redirect
  assert_redirected_to :action => &#8216;list&#8217;
  assert_equal num_programs + 1, Program.count
end
</pre>
<p>I had a gotcha here &#8212; I realized that I has to have the pdf array separate from the program array (since in the form itÃ¢â‚¬â„¢s field_for pdf).  I fixed it by submitting two objects, program and pdf, to the controller, seen above. </p>
<p>Finally, running the functional testing leaves files on the file system and they should be deleted at the end of all tests. So I added a destroy method in the teardown, which removes them.</p>
<pre>
def teardown
  Pdf.find(:all).each { |p| p.destroy }
end
</pre>
<p><a name="failing_tests"></a><strong>Failing Upload Tests</strong><br />
Just to check that only Pdfs can be uploaded for programs, I added a handful of other file types to my directory and created an array of invalid file types in my setup:</p>
<pre>
# [programs_controller_test.rb]
def setup
 # other stuff&#8230;.
 @invalid_files = [['lipsum.doc',"application/doc"],['lipsum.rtf',"application/rtf"],
             ['lipsum.txt',"text/plain"],['squiggle.gif','image/gif' ],
             ['squiggle.jpg','image/jpg' ], ['squiggle.png','image/png' ],
             ['squiggle.psd',"image/x-photoshop"],['squiggle.zip', 'application/zip']
             ]
end
</pre>
<p>and created a test to check these uploads will fail:</p>
<pre>
  def test_create_with_invalid_file_types
    @invalid_files.each do |name, type|
      fdata = fixture_file_upload('/files/' + name, type)
      post :create,   :multipart => true,
                      :pdf=>{"uploaded_data"=>fdata},
                      :program => {:name=>"Newerest Created Program",
                             :is_ongoing=>1,
                             :status=>"Edit",
                             :start_date=>Time.now.tomorrow,
                             :end_date=>''}
      assert_response 200
    end
  end
</pre>
<p>This gave me peace of mind that my files are being uploaded, only the right types are accepted, and all is good in the land of PDFs and Programs. And voila, we are done and on to the next task.</p>
<p><a name="linkouts"></a><br />
<h2>Link Outs</h2>
<p>Here&#8217;s a listing of various online resources on the topics covered in this post.  This should be enough for a good start. Happy Uploading! </p>
<ul>
<li><a href="http://svn.techno-weenie.net/projects/plugins/attachment_fu/" target="_new">Attachment Fu itself</a></li>
<li><a href="http://significantbits.wordpress.com/2007/04/06/using-attachment_fu-by-techno-weenie-to-add-image-attachment-support-to-your-rails-application/" target="_new">Significant Bits fu tutorial: </a></li>
<li><a href="http://www.almosteffortless.com/2007/03/25/working-with-attachment_fu/" target="_new">Almost Effortless&#8217; Fu-tutorial</a> </li>
<li><a href="http://clarkware.com/cgi/blosxom/2007/02/24#FileUploadFu" target="_new">Mike Clark&#8217;s tutorial</a></li>
<li><a href="http://www.imarichardson.com/2007/05/06/tweaking-upload-location-with-attachment_fu/" target="_new">Changing upload location tutorial</a> </li>
<li><a href="http://manuals.rubyonrails.com/read/chapter/28#page237 " target="_new">Testing file uploads: A broad overview (not using fu)</a> </li>
<li><a href="http://jonathanleighton.com/blog/testing-file-uploads-in-ruby-on-rails" target="_new">Jonathan Leighton (who created the fixture_file_upload patch)</a> </li>
<li><a href="http://www.subelsky.com/2007/05/functional-testing-for-attachment-fu.html#links" target="_new">More functional testing of attachment-fu</a></li>
<li><a href="http://wiki.rubyonrails.org/rails/pages/SingleTableInheritance" target="_new">An overview of Single Table Inheritance </a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.devchix.com/2007/08/22/an-attachment-walked-into-a-bar-was-that-u-fu/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Ruby East Registration Open</title>
		<link>http://www.devchix.com/2007/08/16/ruby-east-registration-open/</link>
		<comments>http://www.devchix.com/2007/08/16/ruby-east-registration-open/#comments</comments>
		<pubDate>Thu, 16 Aug 2007 16:53:45 +0000</pubDate>
		<dc:creator>desi</dc:creator>
		
		<category><![CDATA[Events]]></category>

		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[Thoughts]]></category>

		<guid isPermaLink="false">http://www.devchix.com/2007/08/16/ruby-east-registration-open/</guid>
		<description><![CDATA[Ruby East registration is open. It is a one day Philadelphia based Ruby on Rails Conference that is being held 9/28. They have limited space so if you are planning to go you should probably go ahead and register. I am pretty happy to say that there will be 3 DevChix members presenting at the [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.ruby-east.com">Ruby East</a> registration is open. It is a one day Philadelphia based Ruby on Rails Conference that is being held 9/28. They have limited space so if you are planning to go you should probably go ahead and register. I am pretty happy to say that there will be 3 DevChix members presenting at the conference. I think it will be a great day.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.devchix.com/2007/08/16/ruby-east-registration-open/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Twitter4R on Rails</title>
		<link>http://www.devchix.com/2007/07/26/twitter4r-on-rails/</link>
		<comments>http://www.devchix.com/2007/07/26/twitter4r-on-rails/#comments</comments>
		<pubDate>Thu, 26 Jul 2007 20:22:21 +0000</pubDate>
		<dc:creator>mbbx6spp</dc:creator>
		
		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[Tips and Tricks]]></category>

		<guid isPermaLink="false">http://www.devchix.com/2007/07/26/twitter4r-on-rails/</guid>
		<description><![CDATA[Last night Twitter4R version 0.2.4 was released with a fix that makes using Twitter4R in Rails much easier.
So let&#8217;s quickly kick the tires to see how this all works:

Install the Twitter4R v0.2.4 (or above) Ruby Gem: sudo gem install twitter4r
Create a new rails application: rails twitter4rails
After setting up your config/database.yml to your personal tastes and [...]]]></description>
			<content:encoded><![CDATA[<p>Last night Twitter4R version 0.2.4 was released with a fix that makes using Twitter4R in Rails much easier.</p>
<p>So let&#8217;s quickly kick the tires to see how this all works:</p>
<ol>
<li>Install the Twitter4R v0.2.4 (or above) Ruby Gem: sudo gem install twitter4r</li>
<li>Create a new rails application: rails twitter4rails</li>
<li>After setting up your config/database.yml to your personal tastes and tweaking the Rails configuration settings in environment.rb, scroll to the bottom of environment.rb and add the following:<br />
<blockquote><p>gem(&#8217;twitter4r&#8217;, &#8216;&gt;=0.2.4&#8242;)<br />
require(&#8217;twitter&#8217;) # loads core library<br />
require(&#8217;twitter/console&#8217;) # loads a helper method we will use<br />
require(&#8217;twitter/rails&#8217;) # added Rails extensions for Twitter4R</p>
<p>module YourAppNamespace<br />
ENV["RAILS_ENV"] ||= &#8220;test&#8221; # assume test environment if no RAILS_ENV set.<br />
ClientContext = Twitter::Client.from_config(&#8221;#{RAILS_ROOT}/config/twitter.yml&#8221;, ENV["RAILS_ENV"])<br />
end</p></blockquote>
</li>
<li>Now in your controllers you can access YourAppNamespace::ClientContext object as you need to or any other part of the Twitter4R API.</li>
</ol>
<p>If you still want more, feel free to check out the following links:</p>
<ul><a title="Twitter4R on Rails reference application" href="http://rubyforge.org/frs/download.php/23274/twitter4rails.post-0_2_4.zip">	</a></p>
<li><a title="Twitter4R RDoc" href="http://twitter4r.rubyforge.org/rdoc/">TweetApp: Twitter4R on Rails sample application (&gt;=0.2.4)</a></li>
<li><a title="Twitter4R RDoc" href="http://twitter4r.rubyforge.org/rdoc/">Twitter4R RDoc</a></li>
<li><a title="Twitter4R Screencast" href="http://www.youtube.com/watch?v=je_lMsdGYNw">Twitter4R on Rails Screencast (low res. currently, higher res coming)</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.devchix.com/2007/07/26/twitter4r-on-rails/feed/</wfw:commentRss>
		</item>
		<item>
		<title>will_paginate array?</title>
		<link>http://www.devchix.com/2007/07/23/will_paginate-array/</link>
		<comments>http://www.devchix.com/2007/07/23/will_paginate-array/#comments</comments>
		<pubDate>Tue, 24 Jul 2007 05:07:07 +0000</pubDate>
		<dc:creator>desi</dc:creator>
		
		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[Tips and Tricks]]></category>

		<guid isPermaLink="false">http://www.devchix.com/2007/07/23/will_paginate-array/</guid>
		<description><![CDATA[Today I started putting pagination in the app that I have been working on. Based on recommendations from Obie I decided to use &#8220;will_paginate&#8221;, a rails plugin for pagination put out by the err the blog guys. It worked amazingly and the view helper was great! I really like the fact that I can apply [...]]]></description>
			<content:encoded><![CDATA[<p>Today I started putting pagination in the app that I have been working on. Based on recommendations from <a href="http://jroller.com/obie">Obie</a> I decided to use <a href="http://errtheblog.com/post/4791">&#8220;will_paginate&#8221;, a rails plugin for pagination put out by the err the blog guys.</a> It worked amazingly and the view helper was great! I really like the fact that I can apply the same look and feel to all page pagination throughout the app&#8230; well umm.. until I wanted to add pagination to a collection not generated from a finder or association. Since I really wanted everything to look the same and behave the same I did the following little trick so that you can call paginate on a plain old array.</p>
<pre>
<strong>class Array
  def paginate(page=1, per_page=15)
    pagination_array = WillPaginate::Collection.new(page, per_page, self.size)
    start_index = pagination_array.offset
    end_index = start_index + (per_page - 1)
    array_to_concat = self[start_index..end_index]
    array_to_concat.nil? ? [] : pagination_array.concat(array_to_concat)
  end
end</strong></pre>
<p>Before folks say anything about the above code.. yes I know it could be more concise if I didn&#8217;t use all the local variables but I wanted it to be really clear what I was doing here so.. leave it alone.</p>
<p>Now basically you can say </p>
<pre>
<strong>myarray.paginate(params[:page], per_page)</strong>
</pre>
<p>If you want to see it work yourself feel free to run this spec.</p>
<pre>

<strong>require File.dirname(__FILE__) + &#8216;/../spec_helper&#8217;

describe &#8216;Given we call paginate on an array&#8217; do
  it &#8217;should return an array containing the first 3 elements of the org array when page = 1 and per_page_count = 3&#8242; do
    array = ["a","b","c","d","e"]
    current_page = 1
    show_per_page = 3
    expected_array = ["a", "b", "c"]
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it &#8217;should return an array containing the last 2 elements of the org array when page = 2 and per_page_count = 3&#8242; do
    array = ["a","b","c","d","e"]
    current_page = 2
    show_per_page = 3
    expected_array = ["d", "e"]
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it &#8217;should return an array containing all the elements of the org array when page = 1 and per_page_count = 5&#8242; do
    array = ["a","b","c","d","e"]
    current_page = 1
    show_per_page = 5
    expected_array = ["a","b","c","d","e"]
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it &#8217;should return an array containing all the elements of the org array when page = 1 and per_page_count greater than org number of elements i.e = 6&#8242; do
    array = ["a","b","c","d","e"]
    current_page = 1
    show_per_page = 6
    expected_array = ["a","b","c","d","e"]
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it &#8217;should return an empty array if you ask for a page that does not exist&#8217; do
    array = ["a","b","c","d","e"]
    current_page = 3
    show_per_page = 5
    expected_array = []
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it &#8217;should return an empty array if you ask for a negative page number&#8217; do
    array = ["a","b","c","d","e"]
    current_page = -1
    show_per_page = 5
    expected_array = []
    (array.paginate(current_page, show_per_page)).should == expected_array
  end

  it &#8217;should return an empty array if you ask for a negative per_page number&#8217; do
    array = ["a","b","c","d","e"]
    current_page = 1
    show_per_page = -5
    expected_array = []
    (array.paginate(current_page, show_per_page)).should == expected_array
  end
end</strong>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.devchix.com/2007/07/23/will_paginate-array/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
