Handy Rails Tips Ruby on Rails tips and tutorials https://handyrailstips.com/ Tue, 28 Jul 2020 00:11:30 +0000 Tue, 28 Jul 2020 00:11:30 +0000 Jekyll v4.1.0 A naming convention for ActionMailer emails <p>One of the great things about Ruby on Rails is its philosophy of <em>Convention over Configuration</em>. Simply put, this means that developers are encouraged to name and organise their code in specific ways, and to stick to these naming principles wherever possible. The benefit of <em>CoC</em> is that developers spend less mental energy trying to make decisions about things like “where should I store my database schema?”, so they can spend more mental energy on building a great product.</p> <p>In Rails apps, emails are typically sent by a library called ActionMailer. While ActionMailer is an excellent tool for the job, there’s no common convention in the Rails community on how to name mailer classes and methods. In this post, I’ll share with you the convention I’ve been using on projects I’ve worked on recently.</p> <h2 id="the-essence-of-a-mailer">The essence of a mailer</h2> <p>To develop a naming convention, we must first reflect on how our code is being used, and look for some common patterns that apply to most use cases. In doing so, I’ve realised that most mailers can all be boiled down to these common features:</p> <dl> <dt>Recipient</dt> <dd>The recipient is the entity that will receive the email. This will normally be a person, but it could also be another app or remote service that receives email inputs. Recipients might be your service users, customers, internal admins, or unauthenticated visitors to your website.</dd> <dt>Resource</dt> <dd>Emails are usually sent out to provide information about a specific resource or <em>domain concept</em> within the system. In other words, emails are usually sent about something relevant to some <em>thing</em>. An <em>article</em> has been published, a <em>subscription</em> has lapsed, a <em>purchase</em> has been completed. These will often be the models that you’ve defined in your code, but they might also be more abstract concepts (like a <em>password reminder</em>).</dd> <dt>Change</dt> <dd>Transactional emails don’t just spontaneously fire off. They’re usually sent in response to an event or some <em>change</em> that’s happened within the system. An example could be when some resource is created, updated, cancelled, or renewed.</dd> <dt>Intention</dt> <dd>Services email people for different reasons, which I’ve called the <em>intention</em>. In practice, using this convention, I’ve only ever come across four different intentions when sending emails: <em>notification</em>, <em>confirmation</em>, <em>warning</em>, and <em>provision</em>. You might discover more in your own use-cases, but I haven’t needed more than these in the past few months.</dd> </dl> <p>By identifying these common patterns within our mailers, we can now try to establish a naming convention that communicates what an email is being sent for within our code.</p> <h2 id="what-should-you-name-your-mailer-class">What should you name your Mailer class?</h2> <p>Web applications can sometimes become very large, and the number of different emails that are sent out can easily grow into the dozens, and this sometimes means large ActionMailer classes with dozens of methods defined in them. Most sensible developers will avoid having just one mailer class, and try to split the emails up into separate mailer classes for different purposes.</p> <p>But this raises the question: what should I name my mailer class? Should I name it based on the recipient? Or the subject matter? Or the namespace within my app? A mixture of these?</p> <p>My preference is to <em>name ActionMailer classes based on the recipient</em>. This should always be the term you use within your business domain. If you call your customers customers, call your mailer <code class="language-plaintext highlighter-rouge">CustomerMailer</code>. If you call your customers “guests”, call your mailer <code class="language-plaintext highlighter-rouge">GuestMailer</code>. This makes it very clear who is the intended recipient of a given email.</p> <h2 id="what-should-you-name-your-mailer-method">What should you name your Mailer method?</h2> <p>Just like our class name convention makes it easy to see, at a glance, who the recipient of an email is, <em>your mailer method name should make it clear what the email is about</em>, and why it’s being sent. With this in mind, I use the following naming convention for mailer methods names: <code class="language-plaintext highlighter-rouge">#&lt;resource&gt;_&lt;change&gt;_&lt;intention&gt;</code>. To get a feel for this convention, let’s look at a couple of examples:</p> <h3 id="example-1">Example 1:</h3> <p>A visitor to your blog receives an email to notify them that their comment has been approved. In this case, the recipient is a <em>visitor</em> (Recipient), and we’re <em>notifying</em> them (Intention) that their <em>comment</em> (Resource) has been <em>approved</em> (Change).</p> <p>In this case, we’d define <code class="language-plaintext highlighter-rouge">VisitorMailer#comment_approved_notification</code></p> <h3 id="example-2">Example 2:</h3> <p>An admin of your business receives an email to notify them that a customer payment has failed. In this case, the recipient is an <em>admin</em> (Recipient), and we’re <em>notifying</em> them (Intention) that their <em>payment</em> (Resource) has <em>failed</em> (Change).</p> <p>In this case, we’d define <code class="language-plaintext highlighter-rouge">AdminMailer#payment_failed_notification</code></p> <h3 id="example-3">Example 3:</h3> <p>A member of your organisation requests a link to reset their password. In this case, the recipient is a <em>member</em> (Recipient), and we’re <em>providing</em> them (Intention) with a <em>password</em> (Resource) <em>reset</em> (Change).</p> <p>In this case, we’d define <code class="language-plaintext highlighter-rouge">MemberMailer#password_reset_provision</code></p> <h2 id="conclusion">Conclusion</h2> <p>So there’s my proposed naming convention for ActionMailer methods. I’ve been using this with great success, and it’s one less thing I have to think about when I’m coding apps.</p> <p>If you have any thoughts, please feel free to share them with me on Twitter <a href="https://twitter.com/morricegavin" title="Gavin Morrice on Twitter">(@morricegavin)</a>.</p> Fri, 10 Jul 2020 00:00:00 +0000 https://handyrailstips.com/2020/07/10/naming-convention-for-action-mailer-emails-in-rails.html https://handyrailstips.com/2020/07/10/naming-convention-for-action-mailer-emails-in-rails.html actionmailer Rails conventions Grab data from your production database using ActionMailer <p>This is a nasty and naughty hack, and one I’m abashedly apologetic for.</p> <p>I was doing some work on a personal project recently, and wanted to pull some data out of the live database. I only needed a few columns from a couple of tables, so it didn’t seem to warrant a SQL dump.</p> <p>I decided to format the records as JSON data, and copy/paste them from the console directly. But it turned out the length of the JSON data was longer than the backtrace in my terminal window, so I wasn’t able to see it all at once to select, copy, and paste.</p> <p>Instead, I decided to save the data in a temporary file, and then email it to myself—using a temporary, memory-only mailer object.</p> <p>Since this code was only held in memory during my rails console session, it disappeared as soon as I exited the session.</p> <p>Here’s the code I used to do that:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">array</span> <span class="o">=</span> <span class="c1"># ... get all of the data that I need from the DB</span> <span class="c1"># Create the Tempfile object</span> <span class="n">tempfile</span> <span class="o">=</span> <span class="no">Tempfile</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"my-data"</span><span class="p">)</span> <span class="c1"># Write the data to the tempfile as JSON</span> <span class="n">tempfile</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">array</span><span class="p">.</span><span class="nf">to_json</span><span class="p">)</span> <span class="c1"># Subclass ApplicationMailer in memory</span> <span class="k">class</span> <span class="nc">DynamicMailer</span> <span class="o">&lt;</span> <span class="no">ApplicationMailer</span> <span class="c1"># Define a mailer method to send myself the file we just created</span> <span class="k">def</span> <span class="nf">send_attachment</span><span class="p">(</span><span class="n">tempfile</span><span class="p">)</span> <span class="n">attachments</span><span class="p">[</span><span class="s1">'attached.json'</span><span class="p">]</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="n">tempfile</span><span class="p">.</span><span class="nf">path</span><span class="p">)</span> <span class="n">mail</span><span class="p">(</span><span class="ss">to: </span><span class="s1">'me@handyrailstips.com'</span><span class="p">,</span> <span class="ss">body: </span><span class="s2">""</span><span class="p">,</span> <span class="ss">subject: </span><span class="s2">"Incoming..."</span><span class="p">)</span> <span class="k">end</span> <span class="k">end</span> <span class="c1"># Send the email</span> <span class="no">DynamicMailer</span><span class="p">.</span><span class="nf">send_attachment</span><span class="p">(</span><span class="n">tempfile</span><span class="p">).</span><span class="nf">deliver_now</span> <span class="c1"># Clean up the Tempfile afterwards</span> <span class="n">tempfile</span><span class="p">.</span><span class="nf">close</span> <span class="n">tempfile</span><span class="p">.</span><span class="nf">unlink</span> </code></pre></div></div> <p>Hopefully the annotations in the code example make sense.</p> <ol> <li>I pulled the data that I wanted from the database in an Array of Hashes</li> <li>I created a Tempfile to store the data in</li> <li>I wrote the data to the Tempfile in the JSON format</li> <li>I created a new Mailer class in memory with one method for sending attached files</li> <li>I sent myself the Tempfile full of JSON</li> <li>I cleaned up the Tempfile afterwards.</li> </ol> <p>I don’t advise that you ever use this horrible little hack for anything, on any project, ever.</p> Wed, 27 May 2020 00:00:00 +0000 https://handyrailstips.com/hack/2020/05/27/grab-data-from-your-production-db-using-action-mailer.html https://handyrailstips.com/hack/2020/05/27/grab-data-from-your-production-db-using-action-mailer.html ActionMailer Tempfiles database hack DRYer, neater filters with class-based filters <p>Recently I was working on a project which required various permissions for each controller. A user could have permission to access one feature of the site but not another.</p> <p>Here’s a great tip for keeping your rails controllers simple and DRY when working with multiple before filter conditions.</p> <p>The before_filter method takes not only a method name as an argument, it can also take custom class objects as an argument. Here’s an example:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">OrdersController</span> <span class="c1"># run this before filter on :index, :show and :destroy actions</span> <span class="n">before_filter</span> <span class="no">PermissionCheck</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span> <span class="ss">:orders</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="ss">:index</span><span class="p">,</span> <span class="ss">:show</span><span class="p">,</span> <span class="ss">:destroy</span><span class="p">]</span> <span class="p">)</span> <span class="k">end</span> </code></pre></div></div> <p>When requests are made to those actions that require this before filter, Rails will create a new instance of PermissionCheck and call it’s <code class="language-plaintext highlighter-rouge">before()</code> method (you have to define this method yourself). Here’s an example of the PermissionCheck class and how it works:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/permission_check.rb</span> <span class="c1"># Here we create a new subclass of Struct, an easy way to create a class with an attribute.</span> <span class="k">class</span> <span class="nc">PermissionCheck</span> <span class="o">&lt;</span> <span class="no">Struct</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span> <span class="ss">:permission_name</span> <span class="p">)</span> <span class="c1"># NOTE - here it is assumed that you have</span> <span class="c1"># a) A method to find the current_user from the controller: ApplicationController#current_user</span> <span class="c1"># b) A method to check if the current_user has a specific permission: User#has_permission?</span> <span class="c1"># c) A method to redirect and notify the user if they do not have adequate permissions: ApplicationController#unauthorized_action</span> <span class="c1"># this is called from the controller automatically when we use before_filter</span> <span class="k">def</span> <span class="nf">before</span><span class="p">(</span> <span class="n">controller</span> <span class="p">)</span> <span class="c1"># unless the current_user has permission...</span> <span class="k">unless</span> <span class="n">controller</span><span class="p">.</span><span class="nf">current_user</span><span class="p">.</span><span class="nf">has_permission?</span><span class="p">(</span> <span class="n">permission_name</span> <span class="p">)</span> <span class="c1"># redirect and notify user</span> <span class="n">controller</span><span class="p">.</span><span class="nf">unauthorized_action</span> <span class="k">end</span> <span class="k">end</span> <span class="c1"># after_filters can be defined in the same way</span> <span class="k">def</span> <span class="nf">after</span><span class="p">(</span> <span class="n">controller</span> <span class="p">)</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>To make things neater still, we can create a class method for this in ApplicationController:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span> <span class="c1"># Helper method to add this before filter to the controller</span> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">check_permission</span><span class="p">(</span> <span class="n">permission</span><span class="p">,</span> <span class="n">options</span> <span class="o">=</span> <span class="p">{}</span> <span class="p">)</span> <span class="n">before_filter</span> <span class="no">PermissionCheck</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span> <span class="n">permission</span> <span class="p">),</span> <span class="n">options</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>And our controllers now look like:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">OrdersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span> <span class="n">check_permission</span> <span class="ss">:orders</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="ss">:index</span><span class="p">,</span> <span class="ss">:show</span><span class="p">,</span> <span class="ss">:destroy</span><span class="p">]</span> <span class="c1"># etc...</span> <span class="k">end</span> <span class="k">class</span> <span class="nc">CustomersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span> <span class="n">check_permission</span> <span class="ss">:customers</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="ss">:index</span><span class="p">,</span> <span class="ss">:destroy</span><span class="p">]</span> <span class="c1"># etc...</span> <span class="k">end</span> </code></pre></div></div> <p>Defining classes for before filters and after filters offers far more flexibility than simply using methods. So, if your before filters are starting to mount up and they contain a lot of similar code, then defining a class may be a better solution for you.</p> Tue, 18 May 2010 00:00:00 +0000 https://handyrailstips.com/2010/05/18/dryer-neater-filters-with-class-based-filters.html https://handyrailstips.com/2010/05/18/dryer-neater-filters-with-class-based-filters.html DRY legacy Improve Performance With Record/Resource Caching <p>For my <a href="https://gavinmorrice.com">personal homepage</a> I wanted to load the latest tips from Handy Rails Tips as well as my most recent Tweets on twitter. As these resources are stored on different sites from my homepage, there was a little lag when loading the landing page.</p> <p>I don’t tweet or post tips every day so it seemed a little unnecessary to have these loaded fresh with every hit to my site. Instead, updating them once a day seemed fine.</p> <p>Here’s a quick tip for caching <code class="language-plaintext highlighter-rouge">ActiveResource</code> resources or any other data that doesn’t necessarily need to be ‘hot off the press’.</p> <p>Below is the Tip model for my homepage:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Tip</span> <span class="o">&lt;</span> <span class="no">ActiveResource</span><span class="o">::</span><span class="no">Base</span> <span class="nb">self</span><span class="p">.</span><span class="nf">site</span> <span class="o">=</span> <span class="s2">"http://handyrailstips.com/"</span> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">cached</span> <span class="k">return</span> <span class="vc">@@cached</span> <span class="k">if</span> <span class="vc">@@last_cached</span> <span class="o">==</span> <span class="no">Date</span><span class="p">.</span><span class="nf">today</span> <span class="vc">@@last_cached</span> <span class="o">=</span> <span class="no">Date</span><span class="p">.</span><span class="nf">today</span> <span class="vc">@@cached</span> <span class="o">=</span> <span class="nb">self</span><span class="p">.</span><span class="nf">all</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>I wont cover the ins and outs of <code class="language-plaintext highlighter-rouge">ActiveResource</code> here, but let’s have a look at the cached method:</p> <p>the first line will return the value of the class variable <code class="language-plaintext highlighter-rouge">@@cached</code> only if the <code class="language-plaintext highlighter-rouge">@@last_cached</code> date is today. If not, we update <code class="language-plaintext highlighter-rouge">@@last_cached</code> to equal today’s date and then re-set <code class="language-plaintext highlighter-rouge">@@cached</code> to equal all tips (only 5 are returned from Handy Rails Tips). All subsequent calls to the cached method on this date will return the contents of <code class="language-plaintext highlighter-rouge">@@cached</code>.</p> <p>Then, in the controller all I had to add was:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">HomepagesController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span><span class="o">::</span><span class="no">Base</span> <span class="c1"># GET /</span> <span class="k">def</span> <span class="nf">index</span> <span class="vi">@tips</span> <span class="o">=</span> <span class="no">Tip</span><span class="p">.</span><span class="nf">cached</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>Simple!</p> <p>For the tweets I took pretty much the same approach but did not use <code class="language-plaintext highlighter-rouge">ActiveResource</code>.</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Tweet</span> <span class="nb">require</span> <span class="s2">"net/http"</span> <span class="c1"># a Net::HTTP object to be used for each request</span> <span class="no">HTTP_OBJ</span> <span class="o">=</span> <span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"twitter.com"</span><span class="p">)</span> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">cached</span> <span class="c1"># return the cached tweets if they were cached today</span> <span class="k">return</span> <span class="vi">@cached_tweets</span> <span class="k">if</span> <span class="vi">@last_cached</span> <span class="o">==</span> <span class="no">Date</span><span class="p">.</span><span class="nf">today</span> <span class="c1"># else, find the latest tweets and changed the cached date to today</span> <span class="vi">@last_cached</span> <span class="o">=</span> <span class="no">Date</span><span class="p">.</span><span class="nf">today</span> <span class="vi">@cached_tweets</span> <span class="o">=</span> <span class="n">find_latest</span> <span class="k">end</span> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">find_latest</span><span class="p">(</span><span class="n">no</span> <span class="o">=</span> <span class="mi">5</span><span class="p">)</span> <span class="n">response</span><span class="p">,</span> <span class="n">xml</span> <span class="o">=</span> <span class="no">HTTP_OBJ</span><span class="p">.</span><span class="nf">request_get</span> <span class="s2">"/statuses/user_timeline/gavin_morrice.xml?callback=twitterCallback2&amp;count=</span><span class="si">#{</span><span class="n">no</span><span class="si">}</span><span class="s2">"</span> <span class="c1"># returns an array of new Tweets with the content of each &lt;text&gt; tag</span> <span class="no">Nokogiri</span><span class="p">(</span> <span class="n">xml</span> <span class="p">).</span><span class="nf">css</span><span class="p">(</span><span class="s2">"status"</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">status</span><span class="o">|</span> <span class="n">new</span> <span class="n">status</span><span class="p">.</span><span class="nf">css</span><span class="p">(</span><span class="s2">"text"</span><span class="p">).</span><span class="nf">text</span> <span class="p">}</span> <span class="k">end</span> <span class="nb">attr_reader</span> <span class="ss">:text</span> <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span> <span class="n">text</span> <span class="p">)</span> <span class="vi">@text</span> <span class="o">=</span> <span class="n">text</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>The twitter API returns quite a lot of XML so I used Nokogiri to pick out what I need, the status tags. Each of these are then parsed and their content loaded into a new Tweet object. Calling <code class="language-plaintext highlighter-rouge">@tweet.text</code> returns the text content from this tweet.</p> <p>Remember – by default, classes are not cached from one request to another in the development or test environments so to check this is working properly you’ll have to set <code class="language-plaintext highlighter-rouge">cache_classes</code> to true in development.rb.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the webserver when you make code changes. config.cache_classes = true </code></pre></div></div> Thu, 25 Feb 2010 00:00:00 +0000 https://handyrailstips.com/2010/02/25/improve-performance-with-record-slash-resource-caching.html https://handyrailstips.com/2010/02/25/improve-performance-with-record-slash-resource-caching.html legacy Faster Tests With factory_grabber <p>I use factories instead of fixtures when testing. If you haven’t already discovered factories, check out this Railscast: Factories not Fixtures.</p> <p>Now that you’re up to speed: I often find when writing tests that I simply want any record. Here’s a quick example scenario from a controller spec:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">describe</span> <span class="no">CommentsController</span> <span class="k">do</span> <span class="n">describe</span> <span class="s2">"POST /posts/1/comments"</span> <span class="k">do</span> <span class="n">before</span> <span class="k">do</span> <span class="vi">@post</span> <span class="o">=</span> <span class="no">Factory</span> <span class="ss">:post</span> <span class="vi">@comment_attributes</span> <span class="o">=</span> <span class="no">Factory</span><span class="p">.</span><span class="nf">attributes_for</span><span class="p">(</span><span class="ss">:comment</span><span class="p">,</span> <span class="ss">:post</span> <span class="o">=&gt;</span> <span class="vi">@post</span><span class="p">)</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">do_post</span> <span class="n">post</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:comment</span> <span class="o">=&gt;</span> <span class="vi">@comment_attributes</span><span class="p">,</span> <span class="ss">:post_id</span> <span class="o">=&gt;</span> <span class="vi">@post</span><span class="p">.</span><span class="nf">id</span> <span class="k">end</span> <span class="n">it</span> <span class="s2">"should create a new comment for @post "</span> <span class="k">do</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="n">do_post</span> <span class="p">}.</span><span class="nf">should</span> <span class="n">change</span> <span class="p">{</span><span class="vi">@post</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">count</span><span class="p">}.</span><span class="nf">by</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="k">end</span> <span class="n">it</span> <span class="s2">"should redirect to @post"</span> <span class="k">do</span> <span class="n">do_post</span> <span class="n">response</span><span class="p">.</span><span class="nf">should</span> <span class="n">redirect_to</span><span class="p">(</span><span class="n">post_path</span><span class="p">(</span><span class="vi">@post</span><span class="p">))</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>In this example, we’re creating a new post record even although there may already be several posts in the database. Since this test is only asserting that a new comment has been created for <code class="language-plaintext highlighter-rouge">@post</code> it doesn’t really matter what the post’s specific attributes are. All we care about is that it’s a valid post we can create comments for.</p> <p>Inserting new records to the database is usually slower than simply retrieving an existing record. As a result, constantly creating factories when we already have appropriate records means our tests are slower and more inefficient than they should be.</p> <h2 id="factory-grabber">Factory Grabber</h2> <p>I recently published a gem which addresses this issue. To check it out, simply run:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo gem install git://github.com/bodacious/factory_grabber.git </code></pre></div></div> <p>Factory Grabber is intended to be used with Factory Girl by ThoughtBot.</p> <p>To use factory grabber in your tests/specs simply call the number of records and the model name as a method on Grab like so:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># return 47 individual comment records</span> <span class="vi">@comments</span> <span class="o">=</span> <span class="no">Grab</span><span class="p">.</span><span class="nf">forty_seven_comments</span> <span class="c1"># will return 9 user records each with last_name "Smith"</span> <span class="vi">@smiths</span> <span class="o">=</span> <span class="no">Grab</span><span class="p">.</span><span class="nf">nine_users</span><span class="p">(</span><span class="ss">:last_name</span> <span class="o">=&gt;</span> <span class="s2">"Smith"</span><span class="p">)</span> <span class="c1"># return one record</span> <span class="vi">@user</span> <span class="o">=</span> <span class="no">Grab</span><span class="p">.</span><span class="nf">a_user</span> <span class="vi">@article</span> <span class="o">=</span> <span class="no">Grab</span><span class="p">.</span><span class="nf">an_article</span> <span class="vi">@post</span> <span class="o">=</span> <span class="no">Grab</span><span class="p">.</span><span class="nf">one_post</span> </code></pre></div></div> <p>If there are appropriate records already in the database, Grab finds them. If there are not appropriate records, Grab will create them using the factories you’ve already defined.</p> <p>For a practical example:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">describe</span> <span class="s2">"GET /posts/1"</span> <span class="k">do</span> <span class="n">integrate_views</span> <span class="n">before</span> <span class="k">do</span> <span class="vi">@post</span> <span class="o">=</span> <span class="no">Grab</span><span class="p">.</span><span class="nf">one_post</span> <span class="ss">:title</span> <span class="o">=&gt;</span> <span class="s2">"This is the post title"</span><span class="p">,</span> <span class="ss">:body</span> <span class="o">=&gt;</span> <span class="s2">"This is the post's body"</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">do_get</span> <span class="n">get</span> <span class="ss">:show</span><span class="p">,</span> <span class="ss">:id</span> <span class="o">=&gt;</span> <span class="vi">@post</span> <span class="k">end</span> <span class="n">it</span> <span class="s2">"should show the post title"</span> <span class="k">do</span> <span class="n">do_get</span> <span class="n">response</span><span class="p">.</span><span class="nf">should</span> <span class="n">include_text</span><span class="p">(</span><span class="sr">/This is the post title/</span><span class="p">)</span> <span class="k">end</span> <span class="n">it</span> <span class="s2">"should show the post body"</span> <span class="k">do</span> <span class="n">do_get</span> <span class="n">response</span><span class="p">.</span><span class="nf">should</span> <span class="n">include_text</span><span class="p">(</span><span class="sr">/This the post's body/</span><span class="p">)</span> <span class="k">end</span> <span class="k">end</span> <span class="c1"># test pagination</span> <span class="n">describe</span> <span class="s2">"GET /posts?page=1"</span> <span class="k">do</span> <span class="n">integrate_views</span> <span class="n">before</span> <span class="k">do</span> <span class="c1"># ensures there are at least eleven Post records</span> <span class="c1"># if there are less than eleven, new posts are created</span> <span class="c1"># if there are eleven or more no posts are created</span> <span class="no">Grab</span><span class="p">.</span><span class="nf">eleven_posts</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">do_get</span> <span class="n">get</span> <span class="ss">:index</span><span class="p">,</span> <span class="ss">:page</span> <span class="o">=&gt;</span> <span class="mi">1</span> <span class="k">end</span> <span class="n">it</span> <span class="err">“</span><span class="n">should</span> <span class="n">find</span> <span class="n">the</span> <span class="n">latest</span> <span class="mi">10</span> <span class="n">posts</span><span class="err">”</span> <span class="k">do</span> <span class="n">do_get</span> <span class="n">assigns</span><span class="p">[</span><span class="ss">:posts</span><span class="p">].</span><span class="nf">should</span> <span class="o">==</span> <span class="no">Post</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="ss">:all</span><span class="p">,</span> <span class="ss">:order</span> <span class="o">=&gt;</span> <span class="err">“</span><span class="n">created_at</span> <span class="no">DESC</span><span class="err">”</span><span class="p">,</span> <span class="ss">:limit</span> <span class="o">=&gt;</span> <span class="mi">10</span><span class="p">)</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>Here’s example of the performance boots you can achieve:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> user system total real(secs) Create 50 new factories 0.100000 0.200000 0.300000 ( 6.354282) Grab 50 separate factories 0.300000 0.000000 0.300000 ( 0.310373) Grab 50 factories at once 0.020000 0.000000 0.020000 ( 0.011400) </code></pre></div></div> <p>In this case, grabbing 50 existing records is almost 20 times faster than creating 50 new factories!</p> <p>Just make sure you turn off transactional fixtures in your test_helper.rb or spec_helper.rb files:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Spec</span><span class="o">::</span><span class="no">Runner</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span> <span class="n">config</span><span class="p">.</span><span class="nf">use_transactional_fixtures</span> <span class="o">=</span> <span class="kp">false</span> <span class="n">config</span><span class="p">.</span><span class="nf">use_instantiated_fixtures</span> <span class="o">=</span> <span class="kp">false</span> <span class="n">config</span><span class="p">.</span><span class="nf">fixture_path</span> <span class="o">=</span> <span class="no">RAILS_ROOT</span> <span class="o">+</span> <span class="s1">'/spec/fixtures/'</span> <span class="k">end</span> </code></pre></div></div> <p>This gem is still in it’s infancy, I’d welcome any feedback/suggestions.</p> Wed, 09 Sep 2009 00:00:00 +0000 https://handyrailstips.com/2009/09/09/faster-tests-with-factory-grabber.html https://handyrailstips.com/2009/09/09/faster-tests-with-factory-grabber.html legacy Make Your Life Easier—Write Less CSS <p>As far as programming languages go, CSS is the least fun to write. Without variables, mixins or mathematical operations, it can be very time-consuming to write CSS and a nightmare to make changes.</p> <p>Introducing… Less: Leaner CSS.</p> <p>Less is a gem that helps alleviate the pain of writing CSS by making it DRYer and far more intuitive.</p> <p>To install the gem, simply run:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo gem install less </code></pre></div></div> <p>You’ll also want to use the less-for-rails plugin by August Lilleaas:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>script/plugin install git://github.com/augustl/less-for-rails.git </code></pre></div></div> <h2 id="usage">Usage</h2> <p>To create a stylesheet using Less, simply add a new file to your <strong>/stylesheets</strong> directory with the format <strong>“.less”</strong>. Eg, <strong>application.less</strong>. Write your CSS in this file instead of <strong>application.css</strong> – the less-for-rails plugin will ensure application.css is updated as you make changes.</p> <p>Previously, a snippet of CSS may have looked something like this:</p> <div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">div</span><span class="nf">#content</span><span class="p">{</span> <span class="nl">width</span><span class="p">:</span> <span class="m">80%</span><span class="p">;</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">0</span> <span class="nb">auto</span><span class="p">;</span> <span class="p">}</span> <span class="nt">div</span><span class="nc">.comment</span><span class="p">{</span> <span class="nl">border</span><span class="p">:</span> <span class="nb">thin</span> <span class="nb">solid</span> <span class="no">silver</span><span class="p">;</span> <span class="nl">border-bottom</span><span class="p">:</span> <span class="nb">thick</span> <span class="nb">solid</span> <span class="m">#BEBEBE</span><span class="p">;</span> <span class="nl">background</span><span class="p">:</span> <span class="m">#E5E5E5</span><span class="p">;</span> <span class="nl">width</span><span class="p">:</span> <span class="m">80%</span><span class="p">;</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">0</span> <span class="nb">auto</span><span class="p">;</span> <span class="nl">height</span><span class="p">:</span> <span class="m">100px</span><span class="p">;</span> <span class="p">}</span> <span class="nc">.comment</span> <span class="nt">p</span><span class="p">{</span> <span class="nl">margin-top</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span> <span class="nl">font-family</span><span class="p">:</span> <span class="nb">sans-serif</span><span class="p">;</span> <span class="p">}</span> <span class="nc">.comment</span> <span class="nt">p</span> <span class="nt">a</span><span class="p">{</span><span class="nl">text-decoration</span><span class="p">:</span> <span class="nb">none</span><span class="p">;}</span> <span class="nt">form</span><span class="nf">#new_comment</span><span class="p">{</span> <span class="nl">border</span><span class="p">:</span> <span class="nb">thin</span> <span class="nb">solid</span> <span class="no">silver</span><span class="p">;</span> <span class="nl">border-bottom</span><span class="p">:</span> <span class="nb">thick</span> <span class="nb">solid</span> <span class="m">#BEBEBE</span><span class="p">;</span> <span class="nl">background</span><span class="p">:</span> <span class="m">#E5E5E5</span><span class="p">;</span> <span class="nl">width</span><span class="p">:</span> <span class="m">80%</span><span class="p">;</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">0</span> <span class="nb">auto</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>With Less, this can be simplified to:</p> <div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@comment_height</span><span class="p">:</span> <span class="m">100px</span><span class="p">;</span> <span class="k">@inner_width</span><span class="p">:</span> <span class="m">80%</span><span class="p">;</span> <span class="nc">.centered</span><span class="p">{</span> <span class="nl">width</span><span class="p">:</span> <span class="err">@</span><span class="n">inner_width</span><span class="p">;</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">0</span> <span class="nb">auto</span><span class="p">;</span> <span class="p">}</span> <span class="nc">.comment_borders</span><span class="p">{</span> <span class="err">@</span><span class="py">comment_background</span><span class="p">:</span> <span class="m">#E5E5E5</span><span class="p">;</span> <span class="nl">border</span><span class="p">:</span> <span class="nb">thin</span> <span class="nb">solid</span> <span class="no">silver</span><span class="p">;</span> <span class="nl">border-bottom</span><span class="p">:</span> <span class="nb">thick</span> <span class="nb">solid</span> <span class="p">(</span><span class="err">@</span><span class="n">comment_background</span><span class="p">/</span><span class="m">1.2</span><span class="p">);</span> <span class="nl">background</span><span class="p">:</span> <span class="err">@</span><span class="n">comment_background</span><span class="p">;</span> <span class="err">.centered;</span> <span class="p">}</span> <span class="nt">div</span><span class="nf">#content</span><span class="p">{</span><span class="err">.centered;</span><span class="p">}</span> <span class="nt">div</span><span class="nc">.comment</span><span class="p">{</span> <span class="err">.comment_borders;</span> <span class="nl">height</span><span class="p">:</span> <span class="err">@</span><span class="n">comment_height</span><span class="p">;</span> <span class="err">p</span> <span class="err">{</span> <span class="nl">margin-top</span><span class="p">:</span> <span class="err">@</span><span class="n">comment_height</span><span class="p">/</span><span class="m">20</span><span class="p">;</span> <span class="nl">font-family</span><span class="p">:</span> <span class="nb">sans-serif</span><span class="p">;</span> <span class="err">a</span> <span class="err">{</span><span class="nl">text-decoration</span><span class="p">:</span> <span class="nb">none</span><span class="p">;}</span> <span class="err">}</span> <span class="err">}</span> <span class="nt">form</span><span class="nf">#new_comment</span><span class="p">{</span><span class="err">.comment_borders;</span><span class="p">}</span> </code></pre></div></div> <p>In this example, the variable <code class="language-plaintext highlighter-rouge">@comment_height</code> is set to <code class="language-plaintext highlighter-rouge">100px</code> and the <code class="language-plaintext highlighter-rouge">margin-top</code> value for <code class="language-plaintext highlighter-rouge">p</code> tags within a <code class="language-plaintext highlighter-rouge">.comment</code> div is set to <code class="language-plaintext highlighter-rouge">@comment_height/20</code>. If we change the height of <code class="language-plaintext highlighter-rouge">.comment</code> this margin will also change so that it’s always a <code class="language-plaintext highlighter-rouge">5%</code> of the total height.</p> <p>Another really cool feature is the ability to apply mathematical operations to colour values. So if we have a div with <code class="language-plaintext highlighter-rouge">background-color #7f7f7f</code>, we can multiply this value by two for the <code class="language-plaintext highlighter-rouge">text-color</code> property, meaning the text will always be twice as bright as the <code class="language-plaintext highlighter-rouge">background-color</code> (up to the #FFFFFF limit).</p> <p>The class <code class="language-plaintext highlighter-rouge">.comment_borders</code> is a mixin. Mixins allow you to store commonly shared values in one place. These can then be included into any other css instruction by simply writing the class name followed by a semicolon.</p> <p>For more information, check out the <a href="https://web.archive.org/web/20110322122747/http://lesscss.org/">Less homepage</a>.</p> Mon, 07 Sep 2009 00:00:00 +0000 https://handyrailstips.com/2009/09/07/make-your-life-easier-write-less-css.html https://handyrailstips.com/2009/09/07/make-your-life-easier-write-less-css.html legacy DRYing Up Your Code With A Little Metaprogramming <p>Metaprogramming in a nutshell is writing code that writes code. Here’s a really simple example:</p> <div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%</span> <span class="mi">100</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span> <span class="o">|</span><span class="n">number</span><span class="o">|</span> <span class="cp">%&gt;</span> <span class="cp">&lt;%=</span> <span class="n">content_tag</span> <span class="ss">:strong</span><span class="p">,</span> <span class="s2">"hello"</span> <span class="cp">%&gt;</span><span class="nt">&lt;br</span> <span class="nt">/&gt;</span> <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> </code></pre></div></div> <p>Will output:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;strong&gt;</span>hello<span class="nt">&lt;/strong&gt;</span> </code></pre></div></div> <p>a hundred times.</p> <p>This really simple example illustrates the power of metaprogramming; in theory, infinite lines of code can be <strong>written by</strong> just a few lines of your code.</p> <p>The Rails source code is full of examples of metaprogramming, ever wondered how Active Record is able to provide methods like <code class="language-plaintext highlighter-rouge">find_by_username()</code>?</p> <p>Here are a couple of examples of how you can clean up your code using metaprogramming techniques.</p> <h2 id="some-metaprogramming-tips">Some metaprogramming tips</h2> <p>Suppose you have an model, say <code class="language-plaintext highlighter-rouge">Comment</code>, that can exist in several states: “unflagged”, “flagged”, “approved” and “removed”. The comments table has an integer column called “state” that represents each of these states:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Comment</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span> <span class="c1"># comment state is 1 by default and then changes as it is flagged, approved etc.</span> <span class="no">STATES</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:unflagged</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="ss">:flagged</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">,</span> <span class="ss">:approved</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">,</span> <span class="ss">:removed</span> <span class="o">=&gt;</span> <span class="mi">4</span> <span class="p">}</span> <span class="k">end</span> </code></pre></div></div> <p>You want methods to check if the <code class="language-plaintext highlighter-rouge">Comment</code> is or isn’t in one of these states so you add the following:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Comment</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span> <span class="c1"># comment state is 1 by default and then changes as it is flagged, approved etc.</span> <span class="no">STATES</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:unflagged</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="ss">:flagged</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">,</span> <span class="ss">:approved</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">,</span> <span class="ss">:removed</span> <span class="o">=&gt;</span> <span class="mi">4</span> <span class="p">}</span> <span class="k">def</span> <span class="nf">unflagged?</span> <span class="n">state</span> <span class="o">==</span> <span class="mi">1</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">flagged?</span> <span class="n">state</span> <span class="o">==</span> <span class="mi">2</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">approved?</span> <span class="n">state</span> <span class="o">==</span> <span class="mi">3</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">removed?</span> <span class="n">state</span> <span class="o">==</span> <span class="mi">4</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>Great! Now you can call <code class="language-plaintext highlighter-rouge">@comment.flagged?</code> or <code class="language-plaintext highlighter-rouge">@comment.approved?</code> etc.<br /> This is not ideal though, those four methods are really similar!</p> <h2 id="defining-methods-on-the-fly">Defining methods on the fly.</h2> <p>By using <code class="language-plaintext highlighter-rouge">define_method</code> we can achieve the exact same thing as we have above but with much less fuss! Check this out:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Comment</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span> <span class="no">STATES</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:unflagged</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="ss">:flagged</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">,</span> <span class="ss">:approved</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">,</span> <span class="ss">:removed</span> <span class="o">=&gt;</span> <span class="mi">4</span> <span class="p">}</span> <span class="no">STATES</span><span class="p">.</span><span class="nf">each_pair</span> <span class="k">do</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span> <span class="n">define_method</span> <span class="s2">"</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">?"</span> <span class="k">do</span> <span class="n">state</span> <span class="o">==</span> <span class="n">value</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>For each <strong>key</strong> in the hash (the names of our states) a new method is created that checks if the object’s state is equal to <strong>value</strong>. Twelve lines of code condensed into 4 – Magic!</p> <p>Lets also add methods to change the state of each comment:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Comment</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span> <span class="no">STATES</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:unflagged</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="ss">:flagged</span> <span class="o">=&gt;</span> <span class="mi">2</span><span class="p">,</span> <span class="ss">:approved</span> <span class="o">=&gt;</span> <span class="mi">3</span><span class="p">,</span> <span class="ss">:removed</span> <span class="o">=&gt;</span> <span class="mi">4</span> <span class="p">}.</span><span class="nf">each_pair</span> <span class="k">do</span> <span class="o">|</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="o">|</span> <span class="n">define_method</span> <span class="s2">"</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">?"</span> <span class="k">do</span> <span class="n">state</span> <span class="o">==</span> <span class="n">value</span> <span class="k">end</span> <span class="n">define_method</span> <span class="s2">"to_</span><span class="si">#{</span><span class="n">key</span><span class="si">}</span><span class="s2">"</span> <span class="k">do</span> <span class="c1"># no point in troubling the database if the state is already == value</span> <span class="n">update_attribute</span> <span class="ss">:state</span><span class="p">,</span> <span class="n">value</span> <span class="k">unless</span> <span class="n">state</span> <span class="o">==</span> <span class="n">value</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>When we create a new hash, the hash itself is returned so we can call <code class="language-plaintext highlighter-rouge">each_pair</code> directly on the hash making this code even neater. We now also have methods that will change the state: <code class="language-plaintext highlighter-rouge">to_unflagged</code>, <code class="language-plaintext highlighter-rouge">to_flagged</code>, <code class="language-plaintext highlighter-rouge">to_approved</code>, <code class="language-plaintext highlighter-rouge">to_removed</code>.</p> <p>If you have models in your applications that exist in one of several states then this technique could really come in handy to clean up your code. You may also be interested in the <a href="http://enum-column.rubyforge.org/">enum-colum plugin</a>. Enum columns are like string columns but can only have limited amount of permitted values. This is a lot easier to keep track of than using integers like I have in this example.</p> Sat, 01 Aug 2009 00:00:00 +0000 https://handyrailstips.com/2009/08/01/drying-up-your-code-with-a-little-metaprogramming.html https://handyrailstips.com/2009/08/01/drying-up-your-code-with-a-little-metaprogramming.html DRY metaprogramming legacy Dynamic Page Caching With Prototype <p>Ryan Bates recently released a <a href="http://railscasts.com/episodes/169-dynamic-page-caching">Railscast on Dynamic Page Caching</a>. This technique caches each page and then updates the page’s dynamic content using Javascript.</p> <p>Although I like this technique, it requires the jQuery javascript library whereas Rails ships with the Prototype library and so there’s a high chance your application already relies on Prototype.</p> <p>Here is a variation on Ryan Bate’s technique you can implement with Prototype:</p> <p>Assuming you already have a controller named UsersController and a show action, add the following view to <strong>views/users/show.js.erb</strong>:</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">document</span><span class="p">.</span><span class="nx">observe</span><span class="p">(</span><span class="dl">"</span><span class="s2">dom:loaded</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="o">&lt;%</span> <span class="k">if</span> <span class="nx">logged_in</span><span class="p">?</span> <span class="o">%&gt;</span> <span class="nx">$$</span><span class="p">(</span><span class="dl">'</span><span class="s1">.login_required</span><span class="dl">'</span><span class="p">).</span><span class="nx">invoke</span><span class="p">(</span><span class="dl">'</span><span class="s1">show</span><span class="dl">'</span><span class="p">);</span> <span class="c1">//reveals all of the hidden links</span> <span class="o">&lt;%</span> <span class="nx">end</span> <span class="o">%&gt;</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">content_div</span><span class="dl">'</span><span class="p">).</span><span class="nx">insert</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;%= escape_javascript render(:partial =&gt; "layouts/flash_notices") %&gt;</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// inserts the flash partial</span> <span class="p">});</span> </code></pre></div></div> <p>and then this line in the head of your layouts:</p> <div class="language-rhtml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">javascript_include_tag</span> <span class="s2">"prototype"</span><span class="p">,</span> <span class="s2">"/users/show/current"</span> <span class="cp">%&gt;</span> </code></pre></div></div> <p>I’ve made a few assumptions here but you can change the javascript to suit your needs. The first line here ensures that the javascript is run once the page has loaded.</p> <p>The second line assumes you have a helper method called <code class="language-plaintext highlighter-rouge">logged_in?</code>. The code within this block will only be run if there is a logged in user.</p> <p>Line three uses the prototype <code class="language-plaintext highlighter-rouge">$$</code> function to find all tags with class name “login_required” and sets the CSS display value to “show”. This is the part that actually displays or hides the dynamic content depending on whether there’s a logged in user.</p> <p>Finally, line five assumes you have a div on page with id “content_div” and inserts the partial “layouts/flash_notices” inside it. Without this, the flash messages will not be displayed when loading cached pages. Using the <code class="language-plaintext highlighter-rouge">insert()</code> function you can insert html content into various objects within the DOM. This method also includes a second argument to specify whether the content should be added as the first or last child. Read the <a href="https://web.archive.org/web/20090803192007/http://www.prototypejs.org/api/element/insert">Prototype API</a> for more info.</p> <p>“flash_notices” is a partial in the views/layouts folder that looks <a href="http://handyrailstips.com/tips/3-hiding-the-flash-message-after-a-time-delay">something like this</a>. Next, add this helper method to <code class="language-plaintext highlighter-rouge">ApplicationHelper</code>:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nn">ApplicationHelper</span> <span class="k">def</span> <span class="nf">admin_links</span><span class="p">(</span> <span class="nb">id</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">block</span> <span class="p">)</span> <span class="n">concat</span> <span class="s2">"&lt;div class='login_required' style='display:none;' </span><span class="si">#{</span> <span class="s2">"id='</span><span class="si">#{</span><span class="nb">id</span><span class="si">}</span><span class="s2">'"</span> <span class="k">if</span> <span class="nb">id</span> <span class="si">}</span><span class="s2">&gt;"</span> <span class="k">yield</span> <span class="n">concat</span> <span class="s2">"&lt;/div&gt;"</span> <span class="kp">nil</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>Now, if you want to hide links from non-authorised users you simply have to stick it inside an <code class="language-plaintext highlighter-rouge">admin_link</code> block like so:</p> <div class="language-rhtml highlighter-rouge"><div class="highlight"><pre class="highlight"><code># content here will be visible to everyone <span class="cp">&lt;%</span> <span class="n">admin_link</span> <span class="k">do</span> <span class="cp">%&gt;</span> # content here won't be visible to non-authorised users <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> # content here will be visible to everyone </code></pre></div></div> <p>This will output:</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code># content here will be visible to everyone <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">'login_required'</span> <span class="na">style=</span><span class="s">'display:none;'</span><span class="nt">&gt;</span> # content here won't be visible to non-authorised users <span class="nt">&lt;/div&gt;</span> # content here will be visible to everyone </code></pre></div></div> <p>You can also specify an id for the div by passing this as an argument to <code class="language-plaintext highlighter-rouge">admin_links</code>:</p> <div class="language-rhtml highlighter-rouge"><div class="highlight"><pre class="highlight"><code># content here will be visible to everyone <span class="cp">&lt;%</span> <span class="n">admin_link</span> <span class="s2">"id_for_my_div"</span> <span class="k">do</span> <span class="cp">%&gt;</span> # content here won't be visible to non-authorised users <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> # content here will be visible to everyone </code></pre></div></div> <p>Just make sure you have set perform_caching to true in <strong>config/environments/development.rb</strong>.</p> Fri, 24 Jul 2009 00:00:00 +0000 https://handyrailstips.com/2009/07/24/dynamic-page-caching-with-prototype.html https://handyrailstips.com/2009/07/24/dynamic-page-caching-with-prototype.html legacy Writing A Rails Application With TextMate <p>For Mac users, TextMate is powerful tool for writing code. But having a great tool is pretty pointless unless you know how to use it. By spending a few hours learning about TextMate’s features you can almost half the time it takes you to write code. This tutorial will walk you through creating a simple Rails app using some of TextMate’s more advanced features.</p> <p>Before you start, I’d recommend you get the latest version of the <a href="https://web.archive.org/web/20101030161332/http://railsbundle.com/dist/Ruby%20on%20Rails.tmbundle.tar.gz">Ruby on Rails TextMate bundle</a> and install it. Also—I realise the application from this tutorial is far from perfect, but I figured this would be a good way to introduce the features and show examples of how they can be used. If you have any suggestions please leave a comment below.</p> <p>Lets create a new rails app, in your terminal type the following:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails text_mate_demo </code></pre></div></div> <p>Once the app has been created, type <code class="language-plaintext highlighter-rouge">cd text_mate_demo</code> followed by <code class="language-plaintext highlighter-rouge">mate .</code> to open the full application in TextMate.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd text_mate_demo mate . </code></pre></div></div> <p>In true Rails fashion, this demo is going to create a simple blog site. To create our first model type the following in your terminal window:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>script/generate model Post title:string body:text script/generate model Comment name:string body:text post:references rake db:migrate </code></pre></div></div> <p><em>Note–TextMate does come with commands for calling generator scripts but I’ve never been able to get them to work. To try them out hit: <strong>ctrl</strong> + <strong>|</strong> (or <strong>ctrl</strong> + <strong>shift</strong> + <strong>\</strong>)</em>.</p> <p>Next, jump back to TextMate and type <strong>ctrl</strong> + <strong>alt</strong> + <strong>cmd</strong> + <strong>D</strong> to hide the project drawer–we won’t be using it in this tutorial.</p> <p>Instead we’ll navigate by pressing <strong>cmd</strong> + <strong>T</strong> and typing (part of) the name of the file you want to open. Lets go to <code class="language-plaintext highlighter-rouge">post_test.rb</code> and write some unit tests for our Post model. Type <strong>cmd</strong> + <strong>T</strong> followed by “poste” and hit enter.</p> <p>For our unit tests we’ll create a setup method. Here’s a good chance to introduce tab triggers. Type <code class="language-plaintext highlighter-rouge">def</code> followed by the <strong>tab</strong> key. This should create a new empty method with the name highlighted. Name this method “setup” and hit <strong>tab</strong> again to jump down a line.</p> <p>To setup our unit tests lets create a new Post instance and check if it’s valid.</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'test_helper'</span> <span class="k">class</span> <span class="nc">PostTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span> <span class="k">def</span> <span class="nf">setup</span> <span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">new</span> <span class="vi">@post</span><span class="p">.</span><span class="nf">valid?</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>On a new line type “test” and hit tab to create a new test. Lets call it “should not be valid without title”. As a shortcut to writing <code class="language-plaintext highlighter-rouge">assert_equal</code>, you can write <code class="language-plaintext highlighter-rouge">ase</code> and hit <strong>tab</strong>, then <strong>tab</strong> for the expected value and <strong>tab</strong> again for the actual value.<br /> I’ve also added another two tests to meet our requirements:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'test_helper'</span> <span class="k">class</span> <span class="nc">PostTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span> <span class="k">def</span> <span class="nf">setup</span> <span class="vi">@post</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">new</span> <span class="vi">@post</span><span class="p">.</span><span class="nf">valid?</span> <span class="k">end</span> <span class="nb">test</span> <span class="s2">"should not be valid without title"</span> <span class="k">do</span> <span class="n">assert_equal</span> <span class="vi">@post</span><span class="p">.</span><span class="nf">errors</span><span class="p">.</span><span class="nf">on</span><span class="p">(</span><span class="ss">:title</span><span class="p">),</span> <span class="s2">"can't be blank"</span> <span class="k">end</span> <span class="nb">test</span> <span class="s2">"should not be valid without a body"</span> <span class="k">do</span> <span class="n">assert_equal</span> <span class="vi">@post</span><span class="p">.</span><span class="nf">errors</span><span class="p">.</span><span class="nf">on</span><span class="p">(</span><span class="ss">:body</span><span class="p">),</span> <span class="s2">"can't be blank"</span> <span class="k">end</span> <span class="nb">test</span> <span class="s2">"should have many comments"</span> <span class="k">do</span> <span class="vi">@post</span> <span class="o">=</span> <span class="n">posts</span><span class="p">(</span><span class="ss">:one</span><span class="p">)</span> <span class="mi">2</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="vi">@post</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">create!</span> <span class="ss">:name</span> <span class="o">=&gt;</span> <span class="s2">"John Smith"</span><span class="p">,</span> <span class="ss">:body</span> <span class="o">=&gt;</span> <span class="s2">"This is a comment"</span> <span class="p">}</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>Save this page and then press <strong>ctrl</strong> + <strong>\</strong> followed by <strong>7</strong> to test units. The three tests should fail. Now press <strong>alt</strong> + <strong>cmd</strong> + <strong>down</strong> to jump to <strong>post.rb</strong>.</p> <p>Our unit tests specify that our posts should not be valid without a title or a body. To quickly add a <code class="language-plaintext highlighter-rouge">validates_presence_of</code> validation, type <code class="language-plaintext highlighter-rouge">vp</code> and then press <strong>tab</strong>. Now type <code class="language-plaintext highlighter-rouge">“title”</code> and then press <strong>tab</strong> and <strong>delete</strong> to clear the extra options (which we don’t require here).</p> <p>For our second validation we’re going to try something different. On a new line, type <code class="language-plaintext highlighter-rouge">va</code> and then press <strong>esc</strong>. The escape key can be used to auto-complete any method, constant or variable name that’s already present on that document. This is a great way to save time retyping long method/variable names.</p> <p>To add our <code class="language-plaintext highlighter-rouge">has_many</code> association, type <code class="language-plaintext highlighter-rouge">hm</code> and hit <strong>tab</strong>. Type <code class="language-plaintext highlighter-rouge">comment</code> and then <strong>tab</strong>, <strong>delete</strong> again.</p> <p>These validations could do with being DRYed up but we’ll leave that for now. In the meantime, press <strong>cmd</strong> + <strong>/</strong> to start a comment and leave yourself a <a href="/2009/06/05/leave-yourself-notes-with-annotations.html">TODO note for later</a> <code class="language-plaintext highlighter-rouge"># TODO – DRY this up</code>.</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Post</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span> <span class="c1"># TODO - DRY this up</span> <span class="n">validates_presence_of</span> <span class="ss">:title</span> <span class="n">validates_presence_of</span> <span class="ss">:body</span> <span class="n">has_many</span> <span class="ss">:comments</span> <span class="k">end</span> </code></pre></div></div> <p>Now we need to create a Comment model so our third test passes.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>script/generate model Comment name:string body:text post:references rake db:migrate </code></pre></div></div> <p>Quick note – jump to <strong>comments.yml</strong> and populate the <code class="language-plaintext highlighter-rouge">“post”</code> attribute like so:</p> <div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">one</span><span class="pi">:</span> <span class="na">name</span><span class="pi">:</span> <span class="s">MyString</span> <span class="na">email</span><span class="pi">:</span> <span class="s">MyString</span> <span class="na">post</span><span class="pi">:</span> <span class="s">one</span> <span class="na">body</span><span class="pi">:</span> <span class="s">MyText</span> <span class="na">two</span><span class="pi">:</span> <span class="na">name</span><span class="pi">:</span> <span class="s">MyString</span> <span class="na">email</span><span class="pi">:</span> <span class="s">MyString</span> <span class="na">post</span><span class="pi">:</span> <span class="s">two</span> <span class="na">body</span><span class="pi">:</span> <span class="s">MyText</span> </code></pre></div></div> <p>Now jump to <code class="language-plaintext highlighter-rouge">comment_test.rb</code>, again using <strong>cmd</strong> + <strong>T</strong> and add some tests like so:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'test_helper'</span> <span class="k">class</span> <span class="nc">CommentTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span> <span class="k">def</span> <span class="nf">setup</span> <span class="vi">@comment</span> <span class="o">=</span> <span class="no">Comment</span><span class="p">.</span><span class="nf">new</span> <span class="vi">@comment</span><span class="p">.</span><span class="nf">valid?</span> <span class="k">end</span> <span class="nb">test</span> <span class="s2">"should belong to post"</span> <span class="k">do</span> <span class="n">assert_equal</span><span class="p">(</span> <span class="n">posts</span><span class="p">(</span><span class="ss">:one</span><span class="p">),</span> <span class="n">comments</span><span class="p">(</span><span class="ss">:one</span><span class="p">).</span><span class="nf">post</span> <span class="p">)</span> <span class="k">end</span> <span class="nb">test</span> <span class="s2">"should not be valid without name"</span> <span class="k">do</span> <span class="n">assert_equal</span><span class="p">(</span><span class="vi">@comment</span><span class="p">.</span><span class="nf">errors</span><span class="p">.</span><span class="nf">on</span><span class="p">(</span><span class="ss">:name</span><span class="p">),</span> <span class="s2">"can't be blank"</span><span class="p">)</span> <span class="k">end</span> <span class="nb">test</span> <span class="s2">"should not be valid without body"</span> <span class="k">do</span> <span class="n">assert_equal</span><span class="p">(</span><span class="vi">@comment</span><span class="p">.</span><span class="nf">errors</span><span class="p">.</span><span class="nf">on</span><span class="p">(</span><span class="ss">:body</span><span class="p">),</span> <span class="s2">"can't be blank"</span><span class="p">)</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>Again, run the tests, see the failures and then write the model:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Comment</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span> <span class="n">belongs_to</span> <span class="ss">:post</span> <span class="n">validates_presence_of</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">:body</span> <span class="k">end</span> </code></pre></div></div> <p>The tests should now pass.</p> <p>There’re a whole bunch of shortcuts for commonly used methods. Most of them are used by writing a few letters and then hitting <strong>tab</strong>. To browse through them go to <strong>Bundles &gt; Ruby on Rails</strong>.</p> <p>This may be a good time to DRY up the Post model. Press <strong>ctrl</strong> + <strong>shift</strong> + <strong>T</strong> to have a look at the <a href="/2009/06/05/leave-yourself-notes-with-annotations.html">annotations</a> you’ve left in your code. You should see 1 TODO in post.rb. Click on the link to jump to this comment and remove the duplication:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Post</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span> <span class="n">validates_presence_of</span> <span class="ss">:title</span><span class="p">,</span> <span class="ss">:body</span> <span class="n">has_many</span> <span class="ss">:comments</span> <span class="k">end</span> </code></pre></div></div> <p>Now we need a controller for our posts. By pressing <strong>shift</strong> + <strong>alt</strong> + <strong>cmd</strong> + <strong>down</strong> you should see the goto menu. Press 1 for controller. Since there isn’t already a posts controller TextMate will ask if you’d like to create one. Click “create”. (From this point let’s just <em>imagine</em> we’ve written tests first).</p> <p>Now we need an index action to display our posts. Again, using our “def” then <strong>tab</strong> shortcut, create an index action and try to add some code here to find all of the posts and order them by “created_at DESC”. But crap! We’ve forgotten the different options available for the <strong>find</strong> method. <strong>Here comes the coolest trick TextMate has to offer…</strong></p> <p>Highlight the <strong>find</strong> method and press <strong>ctrl</strong> + <strong>H</strong>. A window will pop up with either 2 or 3 options. If you only see 2, then select “Documentation for Selection” and select “ActiveRecord::Base::find”. If you do have the 3rd option “Documentation for Word” then select it. This gives you a chance to browse the Rails documentation for any method and see examples of how to use it. This is a really powerful tool! By pressing <strong>cmd</strong> + <strong>H</strong> you can see the documentation for practically any gem you’ve installed. It also works for other languages like HTML and CSS so it’s a great way to learn as you go.</p> <p>You quickly read up on <strong>find</strong> and see the options available. Now you can add the required code to the index action:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">PostsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span> <span class="k">def</span> <span class="nf">index</span> <span class="vi">@posts</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">find</span> <span class="ss">:all</span><span class="p">,</span> <span class="ss">:conditions</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1">'created_at &gt; ?'</span><span class="p">,</span> <span class="mi">3</span><span class="p">.</span><span class="nf">months</span><span class="p">.</span><span class="nf">ago</span><span class="p">],</span> <span class="ss">:order</span> <span class="o">=&gt;</span> <span class="s2">"created_at DESC"</span><span class="p">,</span> <span class="ss">:limit</span> <span class="o">=&gt;</span> <span class="mi">5</span><span class="p">,</span> <span class="ss">:include</span> <span class="o">=&gt;</span> <span class="ss">:comments</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>If you want to clean up this hash a little, select the options by holding <strong>alt</strong> and clicking and dragging from before “:conditions” to the bottom right – just after “:comments”. With the hash selected, press <strong>alt</strong> + <strong>cmd</strong> + <strong>]</strong> to align assignments. This should help make your code a little easier for others to read.</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">index</span> <span class="vi">@posts</span> <span class="o">=</span> <span class="no">Post</span><span class="p">.</span><span class="nf">find</span> <span class="ss">:all</span><span class="p">,</span> <span class="ss">:conditions</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1">'created_at &gt; ?'</span><span class="p">,</span> <span class="mi">3</span><span class="p">.</span><span class="nf">months</span><span class="p">.</span><span class="nf">ago</span><span class="p">],</span> <span class="ss">:order</span> <span class="o">=&gt;</span> <span class="s2">"created_at DESC"</span><span class="p">,</span> <span class="ss">:limit</span> <span class="o">=&gt;</span> <span class="mi">5</span><span class="p">,</span> <span class="ss">:include</span> <span class="o">=&gt;</span> <span class="ss">:comments</span> <span class="k">end</span> </code></pre></div></div> <p>To collapse this method and neaten your controller, press <strong>alt</strong> + <strong>cmd</strong> + <strong>2</strong>. You can collapse foldings at different levels by using <strong>alt</strong> + <strong>cmd</strong> + <strong>1</strong>,<strong>2</strong>,<strong>3</strong> etc.</p> <p>With the cursor still in the index action, press <strong>alt</strong> + <strong>cmd</strong> + <strong>down</strong> to create the view template for the index action.</p> <p>To create rhtml tags press <strong>ctrl</strong> + <strong>&gt;</strong>. By pressing this again you can cycle through the various erb tags available. To create new html tags press <strong>ctrl</strong> + <strong>&lt;</strong>. Using this method, add the post title to our new heading tags like so:</p> <div class="language-rhtml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%</span> <span class="vi">@posts</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">post</span><span class="o">|</span> <span class="cp">%&gt;</span> <span class="nt">&lt;h3&gt;</span><span class="cp">&lt;%=</span> <span class="n">post</span><span class="p">.</span><span class="nf">title</span> <span class="cp">%&gt;</span><span class="nt">&lt;/h3&gt;</span> <span class="cp">&lt;%=</span> <span class="n">post</span><span class="p">.</span><span class="nf">body</span> <span class="cp">%&gt;</span> <span class="cp">&lt;%</span> <span class="n">post</span><span class="p">.</span><span class="nf">comments</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">comment</span><span class="o">|</span> <span class="cp">%&gt;</span> <span class="cp">&lt;%</span> <span class="n">div_for</span> <span class="n">comment</span> <span class="k">do</span> <span class="cp">%&gt;</span> <span class="nt">&lt;p&gt;</span><span class="cp">&lt;%=</span> <span class="n">comment</span><span class="p">.</span><span class="nf">name</span> <span class="cp">%&gt;</span><span class="nt">&lt;/p&gt;</span> <span class="nt">&lt;p&gt;</span><span class="cp">&lt;%=</span> <span class="n">comment</span><span class="p">.</span><span class="nf">body</span> <span class="cp">%&gt;</span><span class="nt">&lt;/p&gt;</span> <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> <span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span> </code></pre></div></div> <p>This tutorial could go on for quite some time but those are the commands that I feel are the most useful. You may have already noticed too that TextMate already has its own Rails tutorial which covers loads of the snippets etc. You can access this tutorial by pressing <strong>cmd</strong> + <strong>H</strong> and selecting “View Demo Help”.</p> <p>If there’s anything you’d like to see here that I haven’t covered, feel free to leave a comment.</p> Wed, 22 Jul 2009 00:00:00 +0000 https://handyrailstips.com/2009/07/22/writing-a-rails-application-with-textmate.html https://handyrailstips.com/2009/07/22/writing-a-rails-application-with-textmate.html editor TextMate productivity legacy Knowing when to use flash.now <p>The flash hash is designed to carry a message from one action to the next. Here’s a simple example:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">create</span> <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">create</span> <span class="n">params</span><span class="p">[</span><span class="ss">:user</span><span class="p">]</span> <span class="k">if</span> <span class="vi">@user</span><span class="p">.</span><span class="nf">save</span> <span class="n">flash</span><span class="p">[</span><span class="ss">:notice</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"User account created - Sweet!"</span> <span class="n">redirect_to</span> <span class="vi">@user</span> <span class="k">else</span> <span class="n">render</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s2">"new"</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>You’ve probably seen examples like this a hundred times. A new user is created, the <code class="language-plaintext highlighter-rouge">flash[:notice]</code> is set to <code class="language-plaintext highlighter-rouge">“User account created – Sweet!”</code> and then the user is re-directed to the show action where the notice is displayed. However, if the <code class="language-plaintext highlighter-rouge">@user</code> object is not valid, there’s no redirect. Instead, the new template is rendered but we’re still in the <code class="language-plaintext highlighter-rouge">create</code> action.</p> <p>Suppose you wanted to display a message on screen if there’s an error with the user record; a message that will be displayed when the new page is re-rendered. We could use the flash hash here too:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">create</span> <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">create</span> <span class="n">params</span><span class="p">[</span><span class="ss">:user</span><span class="p">]</span> <span class="k">if</span> <span class="vi">@user</span><span class="p">.</span><span class="nf">save</span> <span class="n">flash</span><span class="p">[</span><span class="ss">:notice</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"User account created - Sweet!"</span> <span class="n">redirect_to</span> <span class="vi">@user</span> <span class="k">else</span> <span class="n">flash</span><span class="p">[</span><span class="ss">:error</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"Couldn't create user - Damn!"</span> <span class="n">render</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s2">"new"</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>However, because the flash hash is designed to be available to the next action, if the user then moves on to another page they’ll still see the error message displayed on the screen. Not only does this look pretty bad, it could confuse your users if they see an error message when there is no longer an error.</p> <p>To display a flash message on the current action, a message that will not be carried on to the next action, use <code class="language-plaintext highlighter-rouge">flash.now</code>:</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">create</span> <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">create</span> <span class="n">params</span><span class="p">[</span><span class="ss">:user</span><span class="p">]</span> <span class="k">if</span> <span class="vi">@user</span><span class="p">.</span><span class="nf">save</span> <span class="n">flash</span><span class="p">[</span><span class="ss">:notice</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"User account created - Sweet!"</span> <span class="n">redirect_to</span> <span class="vi">@user</span> <span class="k">else</span> <span class="n">flash</span><span class="p">.</span><span class="nf">now</span><span class="p">[</span><span class="ss">:error</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"Couldn't create user - Damn!"</span> <span class="n">render</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s2">"new"</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>In this case, the error message will be displayed on screen when the new page is re-rendered but will not be carried on to the next.</p> Sun, 28 Jun 2009 00:00:00 +0000 https://handyrailstips.com/2009/06/28/knowing-when-to-use-flash-dot-now.html https://handyrailstips.com/2009/06/28/knowing-when-to-use-flash-dot-now.html legacy