Globalizing the Current URL in a Rails View

Ruby on Rails No Comments »

Rails I18n, globalize2 and Sven Fuchs’ routing_filter provide a nigh complete globalization solution for Rails, especially after the splendid new localized view support in Rails 2.3.

So I was looking for a way to display small flags found at famfamfam and link them to the “localized” versions of the current uri in all pages of the application, so that for example

/widget/8/edit

would become

/de/widget/8/edit

and

/

would become

/de/

Having said routing_filter installed, I created this little helper method in application_helper.rb:

#application_helper.rb

def localize_current_path(locale)
    current_uri = request.env['PATH_INFO']
    my_path = ActionController::Routing::Routes.recognize_path current_uri, :method => :get
    #above works only with :get, you could pass in the current method with :method => request.method.to_sym
    url_for(my_path.merge!(:locale => locale))
  end

This code demonstrates:

  • getting the elements that make up the current route with ActionController::Routing::Routes.recognize_path, this method returns a hash
  • merging the locale into said hash
  • generating the new url with url_for

The helper can now be easily used in the view like so (code is adjusted for the famfamfam British flag whose name is gb.gif):

#app/views/shared/_little_flags.html.erb

<%  {:de => “Deutsch”, :en => “English”, :th =>”Thai”}.each_pair do |key, value| %>
        <%= link_to image_tag(“famfamfam/flags/gif/#{key == :en ? “gb” : key}.gif”, :alt => value, :title => value),
                                 localize_current_path(key) %>
        <%end%>

Hope it helps.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Multi-Level Nested Object Forms in Rails (with jQuery)

Ruby on Rails 2 Comments »

Complex forms were the focus of Railscasts 73, 74 and 75 and the latter episode outlined the rather involved way to get a single nested model to work correctly with form elements generated on the client via javascript.

Nested model forms built into Rails became the most requested feature and release 2.3 finally delivered with the accepts_nested_attributes_for function. Still, challenges remained, especially when trying to generate form elements on the client.

A suggested solution was an altered form builder pre-populated with the nested models, which was published in the complex forms sample application. This solution was ported to support jQuery, but an extension to multi-level nesting (> 2 levels) was still lacking.

Now a fork of the complex forms examples show off a solution with any level of nesting that is extremely easy to implement.

As ryanb states in his answer to this question on stackoverflow, proceed like so:

git clone git://github.com/ryanb/complex-form-examples.git
cd complex-form-examples
git checkout -b deep origin/deep
rake db:migrate
script/server

What about jQuery? Very easy. Simply replace the two functions in application.js with

function insert_fields(link, method, content) {
    var new_id = new Date().getTime();
    var regexp = new RegExp(”new_” + method, “g”)
    $(link).before(content.replace(regexp, new_id));
}

function remove_fields(link) {
    var hidden_field = $(link).prev(”input[type=hidden]“);
    if (hidden_field) {
        hidden_field.val(’1′);
    }
    $(link).closest(”.fields”).hide();
    } 

Complex forms with multiple levels of nesting is finally upon us. And super easy to implement.

Update: As jQuery won’t be able to see the newly added elements (because binding took place at $(document).ready()), check out jQuery live and the livequery plugin. The latter supports more events, including the onchange of a select and also works with jQuery < 1.3.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Rails Auto Complete with jQuery/jRails

Ruby on Rails, jQuery 1 Comment »

Having long used jRails in place of of scriptaculous/prototype with my Rails projects, I found that the latter won’t work with the original auto_complete plugin for Rails.

So I tried a few solutions such as this which uses the auto_complete_jquery plugin,  as well as more involved solutions such as this and this one which uses HAML. However, I didn’t want to write javascript and also have all the helpers of the original plugin, to implement the solution presented in Railscast 102, with a RESTful call to the correct controller (the original plug-in doesn’t work RESTfully in its default configuration).

I ended up using the jrails_auto_complete plugin and it works fine so far. Be sure though to include all js files included with project. Aside from some minor styling issues it seems to work just like the original auto_complete plugin with the same helpers and the code used in the Railscast.

Here’s a sample text_field_with_auto_complete tag using the auto_update_element/afterUpdateElement callback which fires when the selection is made:

    <%= text_field_with_auto_complete :hotel_search,
      :destination_code, {:size => 30 },
      {:url => (destinations_path(:format => :js)),
      :method => :get,
      :param_name => ’search’,
    :after_update_element => “function(element,value){alert(’You have chosen ‘ +
      $(\”#hotel_search_destination_code\”).val());}”
    } %>

 Of course this cries out for being extracted into a helper but it can be hard to get the syntax right, especially for the callback.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Custom Primary Key and Mass Assignment

Ruby on Rails No Comments »

Say you have an ActiveRecord model with a custom primary key and no field named “id”, a common occurrence when working with legacy data:

hotel.rb

class Hotel < ActiveRecord::Base
  set_primary_key :hotel_id

 (…)

Now you have the pertinent form

<% form_for(@hotel) do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label ‘Hotel ID’ %>:
    <%= f.text_field :hotel_id %>
  </p>
  <p>
    <%= f.label ‘Name’ %>:
    <%= f.text_field :name %>
  </p>
<p>
    <%= f.label ‘Destination ID’ %>:
    <%= f.text_field :destination_id %>
  </p>

  <p>
    <%= f.submit ‘Save’ %>
  </p>
<% end %>

and default controller code:

hotels_controller.rb 

def create
    @hotel = Hotel.new(params[:hotel])

    respond_to do |format|

if @hotel.save

(…)

However, this won’t work. The mass assignment at

@hotel = Hotel.new(params[:hotel])

can’t be used with a “custom” primary key as this code called by the initializer excludes the primary key:

active_record/base.rb

def attributes_from_column_definition
        self.class.columns.inject({}) do |attributes, column|
          attributes[column.name] = column.default unless column.name == self.class.primary_key
          attributes
        end
      end

It is therefore necessary to call the initializer (Hotel.new) without the params hash and set the attributes manually:

hotel_controller.rb 

def create
    @hotel = Hotel.new
    my_params = params[:hotel]
    @hotel.hotel_id = my_params[:hotel_id]
    @hotel.name_english = my_params[:name]
    @hotel.destination_id = my_params[:destination_id]

 respond_to do |format|
      if @hotel.save

(…)

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

no such file to load — readline (LoadError) when running script/console

Ruby on Rails, Ubuntu/Linux 5 Comments »

If you get

Loading development environment (Rails 2.3.2)
/usr/local/lib/ruby/1.8/irb/completion.rb:10:in `require’: no such file to load — readline (LoadError)
        from /usr/local/lib/ruby/1.8/irb/completion.rb:10
        from /usr/local/lib/ruby/1.8/irb/init.rb:252:in `require’
        from /usr/local/lib/ruby/1.8/irb/init.rb:252:in `load_modules’
        from /usr/local/lib/ruby/1.8/irb/init.rb:250:in `each’
        from /usr/local/lib/ruby/1.8/irb/init.rb:250:in `load_modules’
        from /usr/local/lib/ruby/1.8/irb/init.rb:21:in `setup’
        from /usr/local/lib/ruby/1.8/irb.rb:54:in `start’
        from /usr/local/bin/irb:13

when running script/console, you might be missing some libraries after you’ve installed Ruby from source. So you might try (Ubuntu, Debian):

sudo apt-get install libncurses5-dev

sudo apt-get install libreadline5-dev

Then cd to the folder with your unpacked Ruby sources, subfolder ext/readline:

cd /usr/src/ruby-1.8.7-p72/ext/readline

ruby extconf.rb

make

sudo make install

There is no need to recompile Ruby. If you get any of these:

checking for tgetnum() in -lncurses… no
checking for tgetnum() in -ltermcap… no
checking for tgetnum() in -lcurses… no
checking for readline/readline.h… no
checking for editline/readline.h… no

you’re still missing libraries so re-check if the apt-get commands above completed without errors. Hope it helps.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]
WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in