Entries in ruby on rails (5)

Dealing with codes - phonetic_alphabet plugin (Ruby on Rails)

Tuesday, November 13, 2007 at 06:23PM

Most fonts are designed for readability of words, not complicated codes and recently, we've had to provide referreral codes to our partners so that we can track the applications that come from them. Instead of sufferring through referral codes being incorrectly provided because someone has confused an O for an 0 or a 1 for a l or even an I (see what I mean!), I created a plugin that turns any string into it's spoken version using the NATO phonetic alphabet.

To install this plugin in Ruby on Rails, just type the following from your application's root directory:

script/plugin install _
https://svn01.allmyfunds.com.au/svn/public/plugins/phonetic_alphabet

To use, see this except from the README file:

You can get the phonetic equivalent of a single character with PhoneticAlphabet[single_character]

or

You can convert a string to it's phonetic equivalent using the String#to_phonetic overridden method.

  Example:

    puts "hello world".to_phonetic

  Outputs:

    hotel echo lima lima oscar SPACE whiskey oscar romeo lima delta

You can also pass in a different separator:

  Example:

    puts "hello world".to_phonetic("-")

  Outputs:

    hotel-echo-lima-lima-oscar-SPACE-whiskey-oscar-romeo-lima-delta

ActiveRecord and programmatically working with attributes.

Saturday, February 17, 2007 at 06:30PM

Ruby on Rails is great. Partially because of all the ORM stuff and partially because Ruby is infitely extendable and overrideable (yes, I just made that word up).

On my current project, I have a lot of extended attributes in models that modify other attributes or even other models entirely. So, when I create new objects and assign their attributes, I would prefer the mass assignments to go through these custom mutators.

Now in Ruby, this is relatively simple. When you want to call a method within an object programmatically, you can use the #send method:

    str = "Hello world!"
    puts str.send(:size)

    Displays: 12

However, when you are performing property setting, you have to do some manipulation to the attribute name first before you can assign a value to it. For example, the code for sending the values of a hash to the corresponding mutator methods in a Person object would be the following:

    hash = {
        :first_name => "Wayne",
        :last_name => "Robinson",
        :date_of_birth => "1982-02-15"
    }

    hash.each do | key, value |
      person.send("#{key}=", value)
    end

As you can see, there is a little bit of repetition there if you have to use that in more than one place. So, I've DRYed this code into the following ActiveRecord extension. Just pop this active_record_getters_and_setters.rb file in your lib/ directory and require it in your environment.rb file with:

    require 'active_record_getters_and_setters.rb' 

You will now have three extra methods accessible to you in all your ActiveRecord objects.

ActiveRecord::Base#set(attribute, value)
    Assigns value to the mutator (setter) for attribute.

ActiveRecord::Base#get(attribute)
    Gets the value from the accessor (getter) for attribute.

ActiveRecord::Base#set_attributes(attribute_hash)
   Does an ActiveRecord::Base#set for each key/value pair in attribute_hash.

Multiple Values from Scriptaculous' Autocomplete

Saturday, November 11, 2006 at 04:48PM

Ever wanted to extract multiple values from a Script.aculo.us Google Suggest-like autocomplete text field? I recently did and here's how.

If you aren't aware of how to use autocomplete text fields, please read over the simple and customised demos available at Script.aculo.us. Also, a warning, this demo utilises Ruby on Rails however, with a little bit of modification, this should work using the Script.aculo.us library without Ruby on Rails.

This example will auto-populate a state and postcode based on the user's selected suburb (yes, I'm Australian).

The first step is to create a view for the page containing the autocomplete field and for the autocomplete field itself.

Controller:

def new
# This is the main controller that will
# contain the autocomplete field
@contact = Contact.new
end

def auto_complete_for_suburb
suburb = params[:suburb]
@surburbs = Suburb.find_by_name(suburb,
:order => "name", :limit => 20)
render :partial => "auto_complete_suburb"
end

View for the new action (assume an application.rhtml template has been created, this template must include the Prototype and Script.aculo.us javascript libraries):

<%= form_tag({:action => :create}, {:method => :post}) %>
<table>
<tr>
<th>Name:</th>
<td><%= text_field(:contact, :name) %></td>
</tr>
<tr>
<th>Suburb:</th>
<td><%= text_field_with_autcomplete(:contact, :suburb,
:select => "value",
:after_update_element =>
"function (ele, value) {
$("contact_state").value =
Ajax.Autocompleter.extract_value(value,
'STATE');
$("contact_postcode").value =
Ajax.Autocompleter.extract_value(value,
'POSTCODE'); }
") %>
</td>
</tr>
<tr>
<th>State:</th>
<td><%= text_field(:contact, :state, :size => 10) %></td>
</tr>
<tr>
<th>Postcode:</th>
<td><%= text_field(:contact, :postcode,
:size => 10) %></td>
</tr>
</table>
<%= end_form_tag %>

Before we continue any further, it is worth-while defining the _auto_complete_suburb.rhtml partial.

<ul class="suburbs">
<% unless @suburbs.nil? -%>
<% @suburbs.each do | suburb | -%>
<li>
<%= h("#{suburb[:name]}, #{suburb[:state]}" +
"#{suburb[:postcode]}") %>
<div class="value" style="display: none;">
<%= h(suburb[:name]) %>
</div>
<div class="STATE" style="display: none;">
<%= h(suburb[:state]) %>
</div>
<div class="POSTCODE" style="display: none;">
<%= h(suburb[:postcode]) %>
</div>
</li>
<% end -%>
<% end -%>
</ul>

The autocompleter view has three (3) hidden <div> tags which contain the extra data used by the base Autocompleter Javascript methods as well as the new one (extract_value) that will be defined below. 

You may also want to cast your eye over the suburb field definition in the new contact view as this is where most of the action is. There are two options to note:

  • the :value option which specifies the class name of the element which contains the value to place in attribute (the default would be whatever is within the rendered <li> field which, as we will find out below, will contain more than just the selected suburb)
  • the :after_update_element option which specifies a piece of Javascript to execute when the item is selected. You will see that this Javascript executes the Ajax.Autocompleter.extract_value function twice. This function does not exist in Script.aculo.us but is provides an easy way to extract extra values from an autocomplete list.

The Script.aculo.us Autocompleter methods conviently pass the complete contents of the <li> field to the method specified in the :after_update_element option. This allows us to extract any additional values from this data. I have created a simple addition to the Ajax.Autocompleter class below that speeds this extraction:

Additional method for Ajax.Autocompleter class. This can be declared anywhere after the inital Script.aculo.us script inclusion. For my purposes I put this at the top of my application.js file. 

Ajax.Autocompleter.extract_value = 
function (value, className) {
var result;

var elements =
document.getElementsByClassName(className, value);
if (elements && elements.length == 1) {
result = elements[0].innerHTML.unescapeHTML();
}

return result;
};

 So that's all there is to it. If anyone would like me to create a demo of the above code, ask me and, if I get enough requests, I'll put something together.

Ruby on Rails - ActiveRecord#build_from_xml function

Monday, May 1, 2006 at 10:20PM

I was playing with the new to_xml feature of Ruby on Rails and I found myself wondering... if you can create XML from ActiveRecord objects, why can't you create ActiveRecord objects from XML?

After searching for a while in the RoR Documentation I wasn't able to find the inverse functionality of to_xml. So now, it seems, I have an opportunity to contribute back to the Rails community with an a functional improvement of my own. I announce to you the build_from_xml method to ActiveRecord.

Just place the below code in your config/environment.rb file.


  require "rexml/document"
  module ActiveRecord
    class Base
      def self.build_from_xml(xml)
        xml = REXML::Document.new(xml) if xml.class == String

        ar = self.new
        xml.elements[1].elements.each do | ele |
          sym = ele.name.underscore.to_sym

          # An association
          if ele.has_elements?
            klass = self.reflect_on_association(sym).klass
            ar.__send__(sym) << klass.build_from_xml(ele)

          # An attribute
          else
            ar[sym] = ele.text
          end
        end

        return ar
      end
    end
  end

You can call this from the main class of any ActiveRecord object. Here is an example.

This ruby code:


  firm_xml = File.new("firm_data.xml").read
  firm = Firm.build_from_xml(firm_xml)

Will convert this XML file into a fully functional ActiveRecord object, including the associations.


  <firm>
    <rating type="integer">1</rating>
    <name>37signals</name>
    <clients>
      <client>
        <rating type="integer">1</rating>
        <name>Summit</name>
        <id type="integer">1</id>
        <firm-id type="integer">1</firm-id>
      </client>
      <client>
        <rating type="integer">1</rating>
        <name>Microsoft</name>
        <id type="integer">2</id>
        <firm-id type="integer">1</firm-id>
      </client>
    </clients>
    <accounts>
      <account>
        <id type="integer">1</id>
        <firm-id type="integer">1</firm-id>
        <credit-limit type="integer">50</credit-limit>
      </account>
    </accounts>
    <id type="integer">1</id>
  </firm>

You may have noticed one caveat. This function accepts well formed XML code only that conforms to your model. If it doesn't, it may produce unpredictable results but will probably raise the usual ActiveRecord exceptions in most non-trivial error cases. Oh, and it requires REXML, but you knew that already right.

I will probably convert this to a plugin in the not-to-distant future. That is if the code isn't included in Rails' release branch (hint, hint).

Rails is truly amazing.

Thursday, April 27, 2006 at 10:16AM

I can never get over how truly amazing Ruby on Rails is. I was recently asked to create a quick and nasty demo application to showcase some of my skills. So, following the provided specs, I whipped up the following application in less than a day (of course, I spent a larger portion of that than I'd like to admit on user interface tweaks - I wish I had design talent).

Now, I'm not a graphic designer/illustrator by any means, but my coding is pretty good. You can check out the application here. It's a pretty basic file uploader with the ability to make files public. Also, the upload supports RTF and HTML in addition to plain text.