<?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 action</title>
    <link>http://christopher.wojno.com/articles/tag/action</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>
  </channel>
</rss>
