Archive for the ‘Ruby on Rails’ Category

Onchange Event Fired from Select Field in Rails Form

Posted 04 Mar 2010 — by admin
Category Ruby on Rails, javascript

In the view there is a regular Rails form and a javascript function that will be triggered when the country select field is changed. The javascript function will make an ajax request to the country_select url with the country code passed as the id variable, e.g., /country_code/us for the United States. I’m also using the Carmen plugin for this example which will provide a list of countries and their respective states/provinces. Not all countries are full supported. More information on Carmen can be found at http://autonomousmachine.com/2009/4/1/carmen-a-rails-plugin-for-geographic-names-and-abbreviations and http://github.com/jim/carmen

<%form_for(@model) do |f| %>
<script type="text/javascript" charset="utf-8">
  function change_state_select(state_code
  {
    new Ajax.Request('/country_select/'+state_code
    {
      method: 'get',
      onSuccess: function(transport) {
        $('state_select').replace(transport.responseText);
      }
    });
  }
 </script>
 <%= f.select :country,
   Carmen::COUNTRIES,
   {},
   { : onchange => "change_state_select(this.options[this.selectedIndex].value);" }
%>
<div id='state_select'></div>

***Note the : onchange should really be one word but an emoticon shows up otherwise :o nchange :( ***
Since not all countries are supported I need to execute some conditional logic in the action country_select. If the country is supported I’ll return a snippet of html containing a select field that my form will use. If the country is not supported I’ll return a text field so that the user can write in their state/province.

class CountrySelectController < ApplicationController
  def country_selecet
      begin
         @states = Carmen::states(params[:id])
      rescue
         @states = nil
      end
      render :partial => "country_select/states"
  end
end

In the final partial that is rendered there is either a select field or a text field

<div id="state_select">
  <% if @states.nil? %>
    <%= text_field_tag :model, :state %>
  <% else %>
    <%= select :model, :state, Carmen::states(@states)%>
  <% end %>
</div>

Rails Plugin Acts as Taggable on Steriods

Posted 02 Mar 2010 — by admin
Category Ruby on Rails

You can download it here http://github.com/suitmymind/acts-as-taggable-on-steroids as well as read usage info (which is for the most part reprinted here).

./script/plugin install http://svn.viney.net.nz/things/rails/plugins/acts_as_taggable_on_steroids
./script/generate acts_as_taggable_migration
rake db:migrate

Then in your model

class Post < ActiveRecord::Base
    acts_as_taggable
 end

And usage is as follows

p = Post.find(:first)
p.tag_list # []
p.tag_list = "Funny, Silly"
p.save
p.tag_list # ["Funny", "Silly"]
p.tag_list.add("Great", "Awful")
p.tag_list.remove("Funny")

#to find...
Post.find_tagged_with('Funny, Silly')
Post.find_tagged_with('Funny, Silly', :match_all => true)

To use this in a form and let users enter a comma separated list of tag names...

form_for @post do |f|
f.text_field :tag_list

And to get a tag cloud

  #controller
  class PostController < ApplicationController
    def tag_cloud
      @tags = Post.tag_counts
    end
  end

  # and in view...
  <style>
  .css1 { font-size: 1.0em; }
  .css2 { font-size: 1.2em; }
  .css3 { font-size: 1.4em; }
  .css4 { font-size: 1.6em; }
  </style>

  <% tag_cloud @tags, %w(css1 css2 css3 css4) do |tag, css_class| %>
    <%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %>
  <% end %>

*Note. If you have a controller "tags_controller.rb" and the auto generated (if you used ./script/generate) helper file "tags_helper.rb" you'll need to make sure to copy the contents of the plugin lib module of the same name, into the helper file. You'll get an error otherwise.

Rails Helper to Remove Leading Zero in 12 Hour Time Format

Posted 01 Mar 2010 — by admin
Category Ruby on Rails

I can’t find a strftime() format that will output the hour without the leading zero. For instance 6:20 will be instead 06:20. This just looks a little sloppy. I created a datetime.rb intializer which will contain custom datetime formats for my application.

# RAILS_ROOT/config/initializers/datetime.rb
Time::DATE_FORMATS[:date] = "%a %b %d, %Y"
Time::DATE_FORMATS[:time] = "%I:%M%p"
# ...

Next I set up a helper function which will string together the :date and :time formats and remove the leading zero in the hour with the .gsub method on the string.

  # RAILS_ROOT/app/helpers/application_helper.rb
  def fulltime(created_at)
    created_at.to_s(:date)+" "+created_at.to_s(:time).gsub(/^0/,'').downcase
  end

In a view I’ll just pass the timestamp to the fulltime function

fulltime(@post.created_at)

Extending Rails Form Builders

Posted 01 Mar 2010 — by admin
Category Ruby on Rails

Extending forms in Rails is simple and will greatly reduce the amount of code in your views. This example is taken right from the Agile Web Development book on Rails(2.1.*) with one minor tweak. I want to pass a label argument along with the field name so that I can display a more human friendly string to represent the form field.

# RAILS_ROOT/app/helpers/custom_tag_builder.rb
class CustomTagBuilder < ActionView::Helpers::FormBuilder

  def self.create_tagged_field(method_name)
    define_method(method_name) do |label, *args|
      label_name = args.first.blank? ? label : args.first[:label] # my change
      @template.content_tag("p",
        @template.content_tag("label",
          label_name.to_s.humanize, :for => "#{@object_name}_#{label}") +" 
"+ super) end end field_helpers.each do |name| create_tagged_field(name) end end

You can then use this in your views

form_for @your_model, :builder => CustomTagBuilder do |f|
  f.text_field :fullname
  f.text_field :email, :label => "Email (will not be published)"

My change tests for the presence of a label argument otherwise using the name of the form field/model attribute. In this case the fullname attribute will be outputted as “Fullname” while “Email (will not be published)” as the label for the email text field.

Install and Serve a Rails Application from PHP Subdirectory Using Apache, Phussion Passenger and Ruby Enterprise Edition

Posted 25 Feb 2010 — by admin
Category Ruby on Rails

Here is how to install a Rails application out of a subdirectory (rather than as a subdomain) with the Apache web server(Apache2). In this example I’m going to use my own blog which is a Wordpress installation and serve a Rails application from the subdirectory “reader”. Note, I’m not going to keep my Rails application in the document root of my Wordpress Blog, which is a PHP application and therefore anyone could browse the ruby source code :( . I’ll keep it elsewhere on the filesystem and tell Apache about the location in the VirtualHost file.

You can visit the application by going to http://seanbehan.com/reader. The application just parses a bunch of RSS feeds and displays them.
It uses the Feedzirra library, which I’ve also written about http://seanbehan.com/ruby-on-rails/installing-feedzirra-rss-parser-on-ubuntu-8/.

I’m using Phussion Passenger and Ruby Enterprise Edition from the folks at Mod Rails. Installing both Phussion Passenger and Ruby Enterprise Edition is simple and a very well documented process. However, you’ll need to download and compile them from source. If you install Ruby Enterprise Edition (REE) it comes w/ Passenger so you won’t need to download it separately. I recommend going with REE. http://www.rubyenterpriseedition.com/download.html

Installing Ruby Enterprise Edition

Here are the commands to download and install (change the X.X.X to the package you’ve actually downloaded).

wget http://rubyforge.org/frs/download.php/68719/ruby-enterprise-1.8.7-2010.01.tar.gz
tar xzvf ruby-enterprise-X.X.X.tar.gz
./ruby-enterprise-X.X.X/installer

When you run the installer you’ll be prompted for an installation location. Just hit enter to install in the default location. Follow the instructions from there and remember to copy/paste any code that they give you.

Official Instructions on installation for Passenger on its own are available here http://www.modrails.com/install.html I’ve written about setting up an entire box w/ Passenger here http://seanbehan.com/ruby-on-rails/new-ubuntu-slice-apache-mysql-php-ruby-on-rails-git-and/ If you already have Passenger installed and want to use REE just download and install REE and it’ll recompile Passenger with REE support if you follow the instructions.

*** If you install REE you’ll need to either link or reinstall all your gems. I linked the REE gem with the one in /usr/bin so that I can run gem install and REE will be aware of it.

ln -s /opt/ruby-enterprise-X.X.X/bin/gem /usr/bin/gem

The VirtualHost

If you have Passenger and REE successfully installed you’ll need to modify your VirtualHost file and add Rails application information to it.

<VirtualHost *>
  # Normal virtual host info
  ServerName seanbehan.com
  ServerAlias *.seanbehan.com
  DocumentRoot /var/www/seanbehan.com/wordpress

  # Rails info goes here
  Alias /reader /var/www/seanbehan.com/reader/public
  <Location /reader>
    PassengerAppRoot /var/www/seanbehan.com/reader
    RailsEnv production
  </Location>
</VirtualHost>

The “Location” directive tells apache to forward requests starting with /reader to the directory
/var/www/seanbehan.com/reader/public which is the location of our Rails app. However, we need to add the PassengerAppRoot assignment so that it knows where the actual application lives.

Routing in Rails with Relative Path

And finally, your Rails application will need to be aware of the relative url prefix assigned to each route. Normally, you could do this w/ the :path_prefix at the individual route level like so

map.resources :feeds, :path_prefix => "reader"

This will work but you can add a line to your RAILS_ROOT/config/environments/production.rb file which will handle all your routes for you. This way you don’t need to setup a relative path on your development environment work.

# in RAILS_ROOT/config/environments/production.rb
config.action_controller.relative_url_root = '/reader'

Final Thoughts

Remember Ruby Enterprise Edition is the new Ruby Interpreter that your Rails web applications are using. If you
have any gem or rake issues make sure that you’re using the same interpreter that REE is using. Look in the location
installation of REE “/opt/ruby-ent…” bin/gem or bin/rake and see if that helps. I just linked those to the standard /usr/bin/gem and /usr/bin/rake and everything worked fine.

Also I’ve read some people have trouble using the alias with passenger. This may be an older issue but works for me without a problem on Ubuntu (latest).

Here are some useful resources I found along the way…

http://robots.thoughtbot.com/post/159806388/phusion-passenger-with-a-prefix

http://www.modrails.com/documentation/Users%20guide.html#deploying_rails_to_sub_uri

http://www.modrails.com/documentation/Users%20guide.html#RailsBaseURI

http://stackoverflow.com/questions/848258/server-prefix-and-rails-routes

Placing an Authenticity Token in a Rails Form

Posted 14 Dec 2009 — by admin
Category Ruby on Rails
 <%= hidden_field_tag :authenticity_token, form_authenticity_token %>

Descending Sort By in Model For Active Record Hash on Created_at attribute

Posted 10 Nov 2009 — by admin
Category Ruby on Rails

If you have a couple collections from the database and you want to sort it without the help of Active Record, take a look at the sort_by method on Array type. I’ve used this before when I have a couple of collections which are slightly different but I need them in a chronological order.


  @posts_group_a = Post.find :all, :conditions => ["user_id = ?", current_user.id]
  @posts_group_b = Post.find :all, :conditions => ["user_id = ?", friend_user.id]

  #merge the two arrays here
  @posts = @posts_group_a + @posts_group_b

  # notice the "-" is for descending order and the "to_i" casts the date time to an integer (required)
  @posts.sort_by {|post| - post.created_at.to_i}

Output Logger and SQL to the Rails Console in Development Mode

Posted 10 Nov 2009 — by admin
Category Ruby on Rails

If you want to take a look at the SQL being generated by active record while your using the console, you can either type this into the console when it loads

ActiveRecord::Base.logger = Logger.new(STDOUT)

Or you can add it to your environment so that it’ll be the default behavior
rails_root/config/environments/development.rb

#...
ActiveRecord::Base.logger = Logger.new(STDOUT)

It’s a nice way to keep you away of any expensive queries you may unknowingly be writing!

A Through Z

Posted 30 Oct 2009 — by admin
Category Ruby on Rails

How to print the alphabet in Rails very easily.

("A".."Z").each {|letter| link_to letter, "/#{letter"}
"A".upto("Z") {|letter| link_to letter, "/#letter"}

Rails Paperclip Plugin Options for Attaching Files

Posted 15 Oct 2009 — by admin
Category Ruby on Rails

I usually change some of the default settings when I use the Paperclip plugin. For most of my projects I don’t like having separate directories for each image that is uploaded. I prefer, in this instance, to put avatars of different sizes together under one directory and differentiated based on the style size of the image. To do this just set the path and url options like so…

  has_attached_file :avatar,
    :styles => {:thumb => "75x75", :medium => "150x150", :large => "500x500"},
    :default_url => "/images/default_avatar.png",
    :url => "/system/avatars/:id/:style_:basename.:extension",
    :path => ":rails_root/public/system/avatars/:id/:style_:basename.:extension"

Also, when you set up the database your model will need to have the following columns for Paperclip to work properly

      t.column :avatar_file_name, :string
      t.column :avatar_content_type, :string
      t.column :avatar_file_size, :integer

Don’t forget that to handle file uploads in Rails you need to set the form with

form_for current_user, :html =>{:multipart => true} do |f|

Otherwise, your upload won’t work :(