<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Wojno: Tag rails</title>
    <link>http://christopher.wojno.com/articles/tag/rails</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Exploration through Code</description>
    <item>
      <title>Array.valN: Working smarter with Rails, with Ruby</title>
      <description>&lt;p&gt;Rails has a powerful form builder mechanism. However, if you want to make a list of things and have the data flow back to you without involving ActiveRecord, you&amp;#8217;re in for some hurt; well, you were, unless you decide to use this module. Warning: this article is moderately advanced. I&amp;#8217;m assuming you&amp;#8217;re familiar with ActionControllers and the ActionView in my examples. If you&amp;#8217;re familiar with how to use the basics of Rails, you should probably only read the first 1/2. Read the first code block to see what my code can do. But to see why this code is useful, read on to the examples.&lt;/p&gt;


	&lt;p&gt;Say I have a set of fox phrases. Now, if a certain fox is to have a list of catch phrases, and you &lt;em&gt;don&amp;#8217;t&lt;/em&gt; want to store them in a database, you can&amp;#8217;t use Rails&amp;#8217; form helpers (text_field, hidden_field, etc.) and the params[] calls without a good deal of ugly, hack code (creating spoof instance variables to display data and creating a fake active record objects to receive that data from a form). To help beautify code, I created an additional way of accessing (setting and getting) elements in an array (valN). Behold the ArrayIterAccessors:&lt;/p&gt;


&lt;pre style="overflow:auto; clear:right;"&gt;
&amp;gt;&amp;gt; @catch_phrases = ['Addiction is like Pokemon!','Hey! There goes my pickup!','Chucky Bacon!']
=&amp;gt; ["Addiction is like Pokemon!", "Hey! There goes my pickup!", "Chucky Bacon!"]
&amp;gt;&amp;gt; @catch_phrases.val2
=&amp;gt; "Chucky Bacon!" 
&amp;gt;&amp;gt; @catch_phrases.val2 = 'Chunky Bacon! Chunky Bacon! Chunky Bacon!'
=&amp;gt; "Chunky Bacon! Chunky Bacon! Chunky Bacon!" 
&amp;gt;&amp;gt; @catch_phrases
=&amp;gt; ["Addiction is like Pokemon!", "Hey! There goes my pickup!", "Chunky Bacon! Chunky Bacon! Chunky Bacon!"]
&lt;/pre&gt;

	&lt;p&gt;&amp;#8220;Now,&amp;#8221; you say, &amp;#8220;why the heck is he not just using the []!?&amp;#8221; Well, I say, Try doing that within ActionView (rhtml template) for a text field:&lt;/p&gt;


&lt;pre&gt;
&amp;lt;%= text_field 'catch_phrases', '[0]' %&amp;gt;
&lt;/pre&gt;

	&lt;p&gt;That will fail horribly. Depending on what you set @catch_phrases to, you&amp;#8217;ll get:&lt;/p&gt;


&lt;pre&gt;
undefined method `[0]' for ['Addiction is like Pokemon!','Hey! There goes my pickup!','Chucky Bacon!']:Array
&lt;/pre&gt;

	&lt;p&gt;And that doesn&amp;#8217;t get you anywhere. So, supposin&amp;#8217; you say, oh, I dunno:&lt;/p&gt;


&lt;pre&gt;
# My special module (should be a plugin but for demonstration
# purposes, I'm defining it inline
# Skip this code for now
class CatchPhrasesController &amp;lt; ApplicationController
  module ArrayIterAccessors
  alias :old_method_missing :method_missing
  def method_missing( sym, *args, &amp;#38;block )
  sym_s = sym.to_s; t = sym_s[/\d+/]
    if t and sym_s[/\Aval\d+=?\Z/]
      if sym_s[/=\Z/]
        self[t.to_i] = args.first; return self[t.to_i]
      else
        return self[t.to_i]
      end
    else
      return old_method_missing( sym, *args, &amp;#38;block )
    end
  end
end
  # this is an action for your CatchPhrases Controller URL:
  # http://site.example.com/catch_phrases/new
  def new
    Array.send( :include, ArrayIterAccessors )
    @catch_phrases = ['Addiction is like Pokemon!','Hey! There goes my pickup!','Chucky Bacon!']
  end
end
### in your view: new.rhtml ###
&amp;lt;% @catch_phrases.each_with_index do |item,index| %&amp;gt;
&amp;lt;%= text_field 'catch_phrases', "val#{index}" %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/pre&gt;

	&lt;p&gt;So when your view renders, you&amp;#8217;ll see 3 text fields, each with the catch phrase pre-filled (so you&amp;#8217;ll have an addiction catch phrase, a pickup catch phrase, and a chunky bacon catch phrase). You can edit them and when you submit the form, you can reconstitute your array by:&lt;/p&gt;


&lt;pre&gt;
@catch_phrases = params[:catch_phrases].keys.collect{|k| k.to_s[/\d+/] }.sort.collect{|k| params[:catch_phrases][('val'+k.to_s).to_sym] }
&lt;/pre&gt;

	&lt;p&gt;If you didn&amp;#8217;t change the catch phrases, you&amp;#8217;ll get the exact array: @catch_phrases, back. And if you &lt;em&gt;&lt;span class="caps"&gt;DID&lt;/span&gt;&lt;/em&gt; change the phrases, you&amp;#8217;ll get an array, in order, of the altered catch phrases!&lt;/p&gt;


	&lt;h2&gt;Pitfalls&lt;/h2&gt;


	&lt;ol&gt;
	&lt;li&gt;No ActiveRecord (well, I claimed that as a plus above) means: no validation. That&amp;#8217;s up to you, unless you include a validatable mixin (there are a few of those). This also means there is no way of reporting errors back automatically. I suppose I could write something later to do this.&lt;/li&gt;
		&lt;li&gt;That last line is ugly and slow.&lt;/li&gt;
		&lt;li&gt;The use example is for demonstration purposes and is awful: &lt;b&gt;&lt;span class="caps"&gt;DO NOT USE IT&lt;/span&gt;.&lt;/b&gt; That will mix the module in &lt;span class="caps"&gt;EVERY&lt;/span&gt; time you render the &amp;#8220;new&amp;#8221; action. To do this correctly take the module and the &lt;pre&gt;Array.send :include, ArrayIterAccessors&lt;/pre&gt; code out of your controller and:
	&lt;ol&gt;
	&lt;li&gt;Put it in a plugin OR&lt;/li&gt;
		&lt;li&gt;Create a new file in lib/array_iter_accessors.rb (in your Rails project) and put the module into that. Then, in your environment.rb file (at the bottom), put the &lt;pre&gt;Array.send :include, ArrayIterAccessors&lt;/pre&gt; code. That way, the mixin will only be done once.&lt;/li&gt;
	&lt;/ol&gt;&lt;/li&gt;
	&lt;/ol&gt;


	&lt;h1&gt;What I did&lt;/h1&gt;


	&lt;p&gt;I overrided the method_missing method for the Array class (then mixed it in). I scan for any method calls for methods beginning with &amp;#8220;val&amp;#8221; and ending in a number. I also parse for an &amp;#8217;=&amp;#8217; in case you&amp;#8217;d like to use it for assignment too. Then, I call the array&amp;#8217;s [] operator to access the values at the desired position. And because I&amp;#8217;m using the array&amp;#8217;s built-in operator, you&amp;#8217;ll see all the error messages and behavior you&amp;#8217;re used to seeing without my module.&lt;/p&gt;


	&lt;h2&gt;Why val?&lt;/h2&gt;


	&lt;p&gt;Because it&amp;#8217;s two letters shorter than &amp;#8220;value.&amp;#8221; What? You expected a profound reason? Sorry if this precludes your ability to keep track of your girlfriends.&lt;/p&gt;


	&lt;p&gt;You have enough here to write your own plug-in, or just to use it when you need it. Happy riding.&lt;/p&gt;</description>
      <pubDate>Mon, 13 Aug 2007 11:37:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:3c80d5b7-f72e-41e8-a255-9a62b0ca789a</guid>
      <author>Christopher Wojno</author>
      <link>http://christopher.wojno.com/articles/2007/08/13/array-valn-working-smarter-with-rails-with-ruby</link>
      <category>Ruby Snippets</category>
      <category>Rails Snippets</category>
      <category>ruby</category>
      <category>rails</category>
      <category>array</category>
      <category>val</category>
      <category>action</category>
      <category>view</category>
      <category>active</category>
      <category>controller</category>
      <category>module</category>
      <category>plugin</category>
    </item>
    <item>
      <title>Session of Fear</title>
      <description>&lt;p&gt;Now, before you think &amp;#8220;&lt;acronym title="Fear, Uncertainty, Disorder"&gt;FUD&lt;/acronym&gt; Time!&amp;#8221;, I preface this post with the following warning: Yes, this is a problem with not just Rails but all sessions; no, it&amp;#8217;s not unsolvable. So, in light of the bad news, there is much good news.&lt;/p&gt;


	&lt;h1&gt;The Problem&lt;/h1&gt;


	&lt;p&gt;&lt;a href="http://www.rubyonrails.org/"&gt;Rails&lt;/a&gt; applications are very useful. However, most require sessions in order to be useful. Sure, you can provide an &lt;a href="http://api.rubyonrails.org/classes/ActionWebService/Base.html"&gt;ActionWebService&lt;/a&gt; or &lt;span class="caps"&gt;REST&lt;/span&gt;-based service to not use sessions, but for the majority of user services, you&amp;#8217;re stuck with sessions.&lt;/p&gt;


	&lt;h2&gt;A Session About Sessions&lt;/h2&gt;


	&lt;p&gt;(Go ahead and skip this part if you&amp;#8217;re already familiar with how sessions work)&lt;/p&gt;


	&lt;p&gt;A session is a way a server remembers who you are (in case you didn&amp;#8217;t know). The most common way of allowing a server to ID you, without knowing who you are exactly, is by sharing a secret! Well, it&amp;#8217;s supposed to be secret and we&amp;#8217;ll come to that in a bit. When a new session is created, your session ID is generated (it&amp;#8217;s random, hopefully) and saved in the server. The id can be associated with other data, such as your name, or permissions. When you request a page, the server will give you this ID. You almost never see that ID because it&amp;#8217;s sent in the background as a &amp;#8220;cookie.&amp;#8221; You&amp;#8217;ve probably heard of these before. From now on, every time you request a page from the server, your cookie data is automatically included in the request (your web browser takes care of this) so the server can figure out who you are. Done correctly, no personally identifiable information is sent over the Web. Just your (hopefully) random ID.&lt;/p&gt;


	&lt;p&gt;Sounds good so far. But if you&amp;#8217;re not using &lt;span class="caps"&gt;HTTPS&lt;/span&gt; (SSL/TSL encryption), your secret contained in that cookie can be seen when it is first sent, and when you access any page thereafter (because you&amp;#8217;re sending it each time remember). So, if someone has access to your traffic (if you&amp;#8217;re using a shared WiFi or a hub or have a shady &lt;span class="caps"&gt;ISP&lt;/span&gt;), they can pull out your secret. Here&amp;#8217;s an old session I sniffed using a program called &lt;a href="http://www.snort.org/"&gt;snort:&lt;/a&gt;&lt;/p&gt;


&lt;pre style="overflow:auto;"&gt;
GET /javascripts/prototype.js?1186461045 HTTP/1.1..
Host: christopher.wojno.com..
User-Agent: Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.1.6) Gecko/20070810 Firefox/2.0.0.6..
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5..Accept-Language: en-us,en;q=0.5..
Accept-Encoding: gzip,deflate..
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7..
Keep-Alive: 300..
Connection: keep-alive..
Cookie: _session_id=db392fa5b39125fa7c1e581b9c1ec71d; is_admin=yes....
&lt;/pre&gt;

	&lt;p&gt;There it is, the last line. &amp;#8220;Cookie.&amp;#8221; So when my browser was trying to download some javascript libraries (&lt;a href="http://www.prototypejs.org/"&gt;Prototype&lt;/a&gt; is quite good), it sent my session ID. Now, the javascript doesn&amp;#8217;t care about my session ID, but it was sent any way. If someone were to see that sent over the Internet (at any point on it&amp;#8217;s way to the server), he or she could create a fake cookie with that ID and login as me without my password! (don&amp;#8217;t try it, I&amp;#8217;ve already logged out and reset the session) They could then, go into the settings and lock me out of my own system (until I change the password in the ruby console&amp;#8230; that&amp;#8217;ll show &amp;#8216;em?). In the meantime, the damage has been done and it can happen again.&lt;/p&gt;


	&lt;h1&gt;&lt;span class="caps"&gt;SSL&lt;/span&gt;!&lt;/h1&gt;


	&lt;p&gt;I won&amp;#8217;t go into &lt;span class="caps"&gt;SSL&lt;/span&gt;, but that little piece of technology has enabled credit card purchases over the Internet for years and will hopefully continue to do so for years to come. Any way, in short, it&amp;#8217;s encryption and it will encrypt cookies too!&lt;/p&gt;


	&lt;p&gt;So, we need to force rails to encrypt cookies. Not as simple as it sounds. First, you need to setup an &lt;span class="caps"&gt;SSL&lt;/span&gt; certificate on your server. Easier said than done, though, some Apache packages come with the tools to automatically generate one. For a confusing, yet interesting read, see &lt;a href="http://en.wikipedia.org/wiki/X.509"&gt;&lt;span class="caps"&gt;X509&lt;/span&gt;&lt;/a&gt; on Wikipedia.&lt;/p&gt;


	&lt;p&gt;I assume you installed your &lt;span class="caps"&gt;SSL&lt;/span&gt; certificate and have configured &lt;a href="http://www.apache.org/"&gt;Apache&lt;sup&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/a&gt; to run with &lt;span class="caps"&gt;SSL&lt;/span&gt; (or &lt;a href="http://www.lighttpd.net"&gt;lighty,&lt;/a&gt; if you&amp;#8217;re using that instead (&lt;a href="http://mongrel.rubyforge.org/"&gt;mongrel&lt;/a&gt; is not mentioned as it does not have &lt;span class="caps"&gt;SSL&lt;/span&gt;, though there is a way to use &lt;span class="caps"&gt;SSL&lt;/span&gt; and mongrel together)). If not, there are loads of &lt;a href="http://www.google.com/search?hl=en&amp;#38;client=firefox-a&amp;#38;rls=org.mozilla%3Aen-US%3Aofficial&amp;#38;hs=Kie&amp;#38;q=apache+ssl+tutorial&amp;#38;btnG=Search"&gt;tutorials.&lt;/a&gt; I warn you though, it&amp;#8217;s fairly technical.&lt;/p&gt;


	&lt;h1&gt;Hold onto your Cookies!&lt;/h1&gt;


	&lt;p&gt;So, you have &lt;span class="caps"&gt;SSL&lt;/span&gt; and a Rails application running (I&amp;#8217;m assuming). How do you tell Rails to make sure your cookies are only sent via &lt;span class="caps"&gt;SSL&lt;/span&gt;? Rails lets you specify it.&lt;/p&gt;


	&lt;h2&gt;Tell Rails to use &lt;span class="caps"&gt;SSL&lt;/span&gt; Only&lt;/h2&gt;


	&lt;p&gt;By default, Rails does &lt;em&gt;&lt;span class="caps"&gt;NOT&lt;/span&gt;&lt;/em&gt; enforce &lt;span class="caps"&gt;SSL&lt;/span&gt; on cookies or sessions (that would be frustrating for development, wouldn&amp;#8217;t it?). So you have to enable that enforcement yourself. If you want your session cookie to be sent over &lt;span class="caps"&gt;SSL&lt;/span&gt; site-wide (and generally, you do!), head into your rails application directory and open (in your favorite editor) app/controllers/application.rb  Add the following anywhere in the ApplicationController class:&lt;/p&gt;


&lt;pre&gt;
class ApplicationController &amp;lt; ActionController::Base
  session :session_key =&amp;gt; '_session_id', :session_secure =&amp;gt; true
end
&lt;/pre&gt;

	&lt;p&gt;The ApplicationController class definition should already exist, don&amp;#8217;t duplicate it. Also, make sure that session isn&amp;#8217;t already specified. If it is, the important part here is &amp;#8221;:session_secure =&amp;gt; true&amp;#8221;. Rails will now tell the browser to only send the session cookie &lt;em&gt;if&lt;/em&gt; the browser is using the https  (SSL) protocol. This feature is poorly &lt;a href="http://api.rubyonrails.org/classes/ActionController/SessionManagement/ClassMethods.html"&gt;documented&lt;/a&gt; but hopefully this will help keep your applications that you write, safe. &lt;b&gt;&lt;span class="caps"&gt;NOTE&lt;/span&gt;: You &lt;em&gt;&lt;span class="caps"&gt;MUST&lt;/span&gt;&lt;/em&gt; use &lt;span class="caps"&gt;SSL&lt;/span&gt; if you enable this or your application will become extremely forgetful&lt;/b&gt; (Who are you? What are you doing in my kitchen?!).&lt;/p&gt;


	&lt;p&gt;If you&amp;#8217;re interested in storing &lt;span class="caps"&gt;OTHER&lt;/span&gt; data (not overly recommended though due to this and another exploit) in other cookies, Rails offers &lt;a href="http://api.rubyonrails.org/classes/ActionController/Cookies.html"&gt;cookie&amp;#8212;manipulation&lt;/a&gt; (not really management, the &lt;span class="caps"&gt;API&lt;/span&gt; leaves something to be desired). You can tell individual cookies to only be sent over encrypted connections, just like the session cookie. The other exploit: if the computer is shared and the browser doesn&amp;#8217;t clear out the cookies, the next person to sit at the computer can harvest the cookie information, so don&amp;#8217;t store passwords, e-mail addresses&amp;#8230; really anything in cookies, it&amp;#8217;s just a bad idea. DO store worthless information you don&amp;#8217;t want to save in your server, such as squirrel preferences.&lt;/p&gt;


	&lt;p&gt;Admittedly, generally the odds of someone having access to your network traffic (and your cookies, no! &lt;em&gt;MY&lt;/em&gt; cookies!) directly is moderate. For the attacker to successfully pick out your cookie data from the slew of traffic wizzing by, he/she would have to be looking and looking for a specific cookie name. So make sure no one has it out for you.&lt;/p&gt;


	&lt;p&gt;I don&amp;#8217;t mean to sound down on the Rails team. Dang-fine-job I say. Cookies aren&amp;#8217;t really important and the session is cookie-based, so session security falls by the way-side. It&amp;#8217;s up to all developers to keep his or her eyes open for potential pitfalls.&lt;/p&gt;


&lt;pre&gt;
1_000.thank( Rails::DevTeam.members.collect{|m|m.email} )
&lt;/pre&gt;

	&lt;p id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; Note: Apache doesn&amp;#8217;t know how to run Ruby code as of this writing. You need to use an Apache&amp;#8217;s mod_proxy. This will (after it&amp;#8217;s been configured) then pass the requests from Apache, to &lt;a href="http://mongrel.rubyforge.org/"&gt;mongrel&lt;/a&gt;, &lt;a href="http://www.lighttpd.net"&gt;lighty&lt;/a&gt;, or&amp;#8230; WEBrick (if you&amp;#8217;re nuts)&lt;/p&gt;</description>
      <pubDate>Fri, 10 Aug 2007 16:48:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:360a7ead-402b-4c87-be50-8ade85a22381</guid>
      <author>Christopher Wojno</author>
      <link>http://christopher.wojno.com/articles/2007/08/10/session-of-fear</link>
      <category>Rails Snippets</category>
      <category>ruby</category>
      <category>rails</category>
      <category>ssl</category>
      <category>cookie</category>
      <category>session</category>
      <category>secure_session</category>
      <category>security</category>
      <category>hijack</category>
    </item>
    <item>
      <title>One-line Plurals</title>
      <description>&lt;h1&gt;Problems with Plurals&lt;/h1&gt;


	&lt;p&gt;Trevor brought up a good point in my last Rails &lt;a href="/articles/2007/08/06/easy-and-or-in-ruby-on-rails"&gt;post&lt;/a&gt; concerning plurals.&lt;/p&gt;


	&lt;p&gt;So I decided to enlighten him/create something new.&lt;/p&gt;


	&lt;p&gt;Rails offers a mixin for Action View called (oddly enough) &lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#M000618"&gt;pluralize.&lt;/a&gt;  It&amp;#8217;s fairly simple, but unnecessarily limited. Here&amp;#8217;s some syntax:&lt;/p&gt;


&lt;pre&gt;
&amp;gt;&amp;gt; pluralize(1, 'person')
=&amp;gt; 1 person
&amp;gt;&amp;gt; pluralize(3, 'person')
=&amp;gt; 3 people
&lt;/pre&gt;

	&lt;p&gt;It&amp;#8217;s annoying if you don&amp;#8217;t want the number or are trying to construct complicated sentences. The other big problem is that it doesn&amp;#8217;t work outside of an ActionView. Luckily, there&amp;#8217;s &lt;a href="http://api.rubyonrails.org/classes/Inflector.html#M001079"&gt;pluralize&lt;/a&gt; So I complied with Trevor&amp;#8217;s request but used the pluralize function:&lt;/p&gt;


&lt;pre&gt;
&amp;gt;&amp;gt; module SmartPlural
&amp;gt;&amp;gt; def plural( count=0 )
&amp;gt;&amp;gt; return ( count != 1 ? self.pluralize : self.dup )
&amp;gt;&amp;gt; end
&amp;gt;&amp;gt; end
=&amp;gt; nil
&amp;gt;&amp;gt; String.send :include, SmartPlural
=&amp;gt; String
&amp;gt;&amp;gt; "Trevor".plural( 3 )
=&amp;gt; "Trevors" 
&amp;gt;&amp;gt; "Trevor".plural( 1 )
=&amp;gt; "Trevor" 
&lt;/pre&gt;

	&lt;p&gt;Sorry for the lack of a ! (bang) version. That requires writing C-code&amp;#8230; and that means writing the pluralize function again&amp;#8230; and I&amp;#8217;m not doing that. Yes, you can call me lazy.&lt;/p&gt;


	&lt;h1&gt;What happened here?&lt;/h1&gt;


	&lt;p&gt;For those who are curious as to what I did: I created a module called SmartPlural, the name doesn&amp;#8217;t really matter. After that, I created a new function called &amp;#8220;plural&amp;#8221; that will pluralize the string if the number is not 1 (0 apples, 1 apple, 2 apples, etc.)[1]. Finally, I install the new module into the String class as a &lt;a href="http://whytheluckystiff.net/ruby/pickaxe/html/tut_modules.html#S2"&gt;mixin.&lt;/a&gt; Then I tested it on my friend here.&lt;/p&gt;


	&lt;h2&gt;Why didn&amp;#8217;t you do: (x.length == 1 ? &amp;#8216;foo&amp;#8217; : &amp;#8216;foos&amp;#8217;)&lt;/h2&gt;


	&lt;p&gt;Well, what if I don&amp;#8217;t like foos (bad Mr. T joke here, I won&amp;#8217;t suffer it upon you though)? Say I want to talk about octopus:&lt;/p&gt;


&lt;pre&gt;
&amp;gt;&amp;gt; 'octopus'.pluralize
=&amp;gt; "octopi" 
&lt;/pre&gt;

	&lt;p&gt;Rails already knows about quite a few irregular words and you don&amp;#8217;t want to pluralize &lt;ins&gt;every&lt;/ins&gt; word you&amp;#8217;ll be pluralizing yourself; that&amp;#8217;s just stupid and not &lt;acronym title="Don&amp;#8217;t Repeat Yourself"&gt;DRY&lt;/acronym&gt;. Use the pluralize function that comes with Rails. Here&amp;#8217;s yet another reason to do so. What if I like to &lt;a href="http://www.scifi.com/firefly/"&gt;juggle baby geese&lt;/a&gt;, you know, goslings? As in the previous example, octopus is a special word, it&amp;#8217;s plural isn&amp;#8217;t simply the original followed by an s. If I have:&lt;/p&gt;


&lt;pre&gt;
&amp;gt;&amp;gt; 'goose'.pluralize
=&amp;gt; "gooses" 
&lt;/pre&gt;

	&lt;p&gt;Rails is &lt;ins&gt;wrong&lt;/ins&gt;. It doesn&amp;#8217;t know about geese or goose. Now, I don&amp;#8217;t expect the Rails team to think of every word, and &lt;a href="http://api.rubyonrails.org/classes/Inflector/Inflections.html"&gt;neither did they.&lt;/a&gt; You can tell Rails how to plualize those special cases and it will be effective &lt;ins&gt;everywhere&lt;/ins&gt;, now that&amp;#8217;s &lt;acronym title="Don&amp;#8217;t Repeat Yourself"&gt;DRY&lt;/acronym&gt;.&lt;/p&gt;


&lt;pre&gt;
&amp;gt;&amp;gt; Inflector.inflections do |inflection|
&amp;gt;&amp;gt; inflection.irregular( 'goose', 'geese' )
&amp;gt;&amp;gt; end
=&amp;gt; [[/(g)eese$/i, "\\1oose"],... &amp;lt;snipped&amp;gt;
&amp;gt;&amp;gt; "goose".plural( 3 ) # make sure you have SmartPlural included in String
=&amp;gt; "geese" 
&lt;/pre&gt;

	&lt;h2&gt;Not all&lt;/h2&gt;


	&lt;p&gt;One more thing: the default value out of String.plural is, well, the plural form:&lt;/p&gt;


&lt;pre&gt;
&amp;gt;&amp;gt; "Trevor".plural
=&amp;gt; "Trevors" 
&lt;/pre&gt;

	&lt;p&gt;So if you&amp;#8217;re just interested in the plural form, and don&amp;#8217;t care about numbers, you don&amp;#8217;t have to use String.pluralize. Just a little bonus, a very small one.&lt;/p&gt;


	&lt;h1&gt;The One-Liner&lt;/h1&gt;


	&lt;p&gt;To use this code, just type (or preferably, paste):&lt;/p&gt;


&lt;pre style="overflow:auto;"&gt;
module SmartPlural; def plural(count=0); return (count != 1 ? self.pluralize : self.dup ); end; end; String.send :include, SmartPlural
&lt;/pre&gt;

	&lt;p id="fn1"&gt;&lt;sup&gt;1&lt;/sup&gt; I like apples&amp;#8230;&lt;/p&gt;</description>
      <pubDate>Thu, 09 Aug 2007 14:30:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:53e8846b-b818-4fea-b0ec-4c4bc76a8a2f</guid>
      <author>Christopher Wojno</author>
      <link>http://christopher.wojno.com/articles/2007/08/09/one-line-plurals</link>
      <category>Rails Snippets</category>
      <category>rails</category>
      <category>plural</category>
      <category>one</category>
      <category>line</category>
    </item>
    <item>
      <title>Easy And/Or in Ruby on Rails</title>
      <description>&lt;p&gt;This missing feature of rails has really bugged me, but it&amp;#8217;s so useful.&lt;/p&gt;


	&lt;p&gt;If you have a list of words such as: apples, oranges, and bananas as an array:&lt;/p&gt;


&lt;pre&gt;
&amp;gt;&amp;gt; list = ['apples','oranges','bananas']
=&amp;gt; ["apples", "oranges", "bananas"]
&lt;/pre&gt;

	&lt;p&gt;You&amp;#8217;d like to be able to have a variable length list and still have it look correct in the view. So a smaller list:&lt;/p&gt;


&lt;pre&gt;
&amp;gt;&amp;gt; list = ['oranges','bananas']
=&amp;gt; ["oranges", "bananas"]
&lt;/pre&gt;

	&lt;p&gt;Should look like: &amp;#8220;oranges and bananas&amp;#8221;.&lt;/p&gt;


&lt;pre&gt;
&amp;gt;&amp;gt; list = ['apples','oranges','bananas']
=&amp;gt; ["apples", "oranges", "bananas"]
&amp;gt;&amp;gt; and_or_list 'and', list
=&amp;gt; "apples, oranges, and bananas" 
&amp;gt;&amp;gt; list.pop
=&amp;gt; "bananas" 
&amp;gt;&amp;gt; and_or_list 'and', list
=&amp;gt; "apples and oranges" 
&amp;gt;&amp;gt; list.pop
=&amp;gt; "oranges" 
&amp;gt;&amp;gt; and_or_list 'and', list
=&amp;gt; "apples" 
&lt;/pre&gt;

	&lt;p&gt;The following block of code will do just that:&lt;/p&gt;


&lt;pre&gt;
  def and_or_list( andor, list )
    list = list.dup
    comma = (list.size &amp;gt; 2 ? ',' : '')
    list2 = list.pop if list.size &amp;gt; 1
    s = list.join(', ')
    s &amp;lt;&amp;lt; comma+' '+andor+' ' + list2 if list2
    s
  end
&lt;/pre&gt;

	&lt;p lang="fr"&gt;Vioa!&lt;/p&gt;


	&lt;p&gt;Instant and easy listing of various things, in English. Your users will never know it&amp;#8217;s generated.&lt;/p&gt;


	&lt;p&gt;I&amp;#8217;ve wrapped it up in a neat little plug-in for you.  Just install it in your vendor/plugins directory. It will automatically be available in your views.&lt;/p&gt;


	&lt;p&gt;&lt;a href="/files/and_or_list.tar.gz"&gt;AndOrList Module&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Mon, 06 Aug 2007 22:05:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:a510761c-2a6d-4604-9dd1-b0314e9217fe</guid>
      <author>Christopher Wojno</author>
      <link>http://christopher.wojno.com/articles/2007/08/06/easy-and-or-in-ruby-on-rails</link>
      <category>Rails Snippets</category>
      <category>ruby</category>
      <category>rails</category>
      <category>and</category>
      <category>or</category>
      <category>list</category>
      <category>sentence</category>
      <category>english</category>
    </item>
  </channel>
</rss>
