Rails TipsSection: Ruby on Rails
These are my rails tips for the railscast contest:)
Contest link: Railscasts.com/contest

So here we go:

Tip 1: Automatic assets ninimization and packaging with rails 2.0.x

This one is actually just a link: link
In rails 2.0, it is possible to cache multiple javascript and css files into 1 file (one for js, and one for css). Thats a really nice feature as it speeds up the page loading a lot when you have a log css and javascript files. But they fotgot one interesting feature: minimization (removing unnecessary whitespaces and comments...). In Dave Troy's article you can find out how to automate that with rails 2.0.x

Tip 2: Counter in looped partials

This is something I came across when reading the rails api. Its a small one, but I think there are still some people who don't know about it.

Did you ever did something like this:
	<% counter = 0 %>
	<% for post in @posts do%>
		<%= render :partial => 'post', :locals => {:counter => counter} %>
		<% counter++ %>
	<% end %>
and in the post-partial:
	Post number : <%= counter %>
	<%= h post.text %>
	...
All the counter stuff is quite ugly, so there must be a better way, and there is :) Apparantly there is a special counter variable when you use partials: just append _counter to the name of the partial, in this case post_counter
so in this case we would write:
	<% for post in @posts do%>
		<%= render :partial => 'post' %>
	<% end %>
and in the post-partial:
	Post number : <%= post_counter %>
	<%= h post.text %>
	...
Much nicer

Tip 3: Trim whitespace

Ryan Bates from railscasts.com already pointed out how to trim whitespace generated by erb code by using <%- -%>. There is actually a config setting who does the same thing without having to put the minus sign all over the place. Put this in you environment.rb:
	config.action_view.erb_trim_mode = ">"
Now you can use your erb blocks without the minus sign. This actually only takes care of the whitespace after an erb block, so newlines will disappear, but tabs and other whitespace before the block will stay. I didn't find a configuration setting to handle the whitespace before the block.

Tip 4: Custom helpers accepting a block

If you have some stuff in your views you have to wrap in always the same divs, this is a good way to do it. It uses a helper who accepts a block, and renders a partial, so you can easily modify your container layout.

Say you have a site with a sidebar and inside of the sidebar multiple blocks with the same layout, someting like this:
	<div id="sidebar">
		<div class="block">
			<div class="block_header">First Title</div>
			<div class="block_body">
				<b>this is the content of block 1</b>
				Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
				sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
			</div>
		</div>
		
		<div class="block">
			<div class="block_header">Second Title</div>
			<div class="block_body">
				<b>this is the content of block 2</b>
				Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
				sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
			</div>
		</div>
	</div>
It would be nice if you could do something like this:
	<% container "First Title" do %>
		<b>this is the content of block 1</b>
		Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
		sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
	<% end %>
	<% container "Second Title" do %>
		<b>this is the content of block 2</b>
		Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
		sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
	<% end %>
This is how you do it:
views/other/_container.html.erb: make a partial with the container:
	<div class="block">
		<div class="block_header"><%= title %></div>
		<div class="block_body">
			<%= block %>
		</div>
	</div>
You can put the partial in whatever view-folder you like, but make sure you target it the right way in your helper function! helpers/application_helper.rb: make a helper function:
  def container(title, &block)
    @t = capture do
      render :partial => 'other/container', :locals => { :title => title, :block => capture(&block) }
    end

    concat @t, block.binding
  end

Tip 5: Custom form-fields

Wouldn't it be nice if you could write your own form-fields?
for example an address field would build a textfield for the street, number, zip-code, city... so you dont have to write that same code everytime you need the address fields.

You can do it by using a custom form-builder. The actual form builder is taken from the "Advanced Rails Recipes"-eBook (Keep Forms DRY and Flexible by Mike Mangino), but we will extend it with an address field.

In your view you will be able to do this:
	<% form_for @address, :builder => CustomFormBuilder do |f| %>
		<%= f.address_field :address %>
	<% end %>
so lets get started...
First, lets create our field partials.
Make a directory 'forms' in app/views with 3 partials in it:
_field.html.erb: the container for our normal fields
	<div class="form_row">
		<div class="form_label"><%= label %>:</div>
		<div class="form_content"><%= element %></div>
	</div>
_field_with_errors.html.erb: the container for fields with errors
	<div class="form_row field_with_errors">
		<span class="form_error"><%= error %></span>
		<div class="form_label"><%= label %>:</div>
		<div class="form_content"><%= element %></div>
	</div>
_address_field.html.erb: the partial for the address field
	<div class="form_row">
		<div class="form_label">Street:</div>
		<div class="form_content">
			<%= text_field object, :street %>
			Nr.:<%= text_field object,  :number, :size => 4 %>
			Bus:<%= text_field object, :bus, :size => 4 %>
		</div>
	
		<div class="form_label">Zip:</div>
		<div class="form_content">
			<%= text_field object, :zip, :size => 10 %>
			City: <%= text_field object, :city %>
		</div>
	
		<div class="form_label">Country:</div>
		<div class="form_content">
			<%= country_select object, :country_id %>
		</div>
	</div>
Now add a file in your lib-directory that will hold the formbuilder
custom_form_builder.rb
	module ActionView
	  module Helpers
	    module FormHelper
	      def address_field(object_name, method, options = {})
	        #stub function
	      end
	    end
	  end
	end

	class CustomFormBuilder < ActionView::Helpers::FormBuilder

	  # override default error div for form elemens with errors
	  ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| html_tag }

	  helpers = field_helpers +
	    %w(date_select datetime_select time_select collection_select) +
	    %w(collection_select select country_select time_zone_select) -
	    %w(label fields_for)

	  helpers.each do |name|
	    define_method name do |field, *args|
	      options = args.detect {|argument| argument.is_a?(Hash)} || {}
	      build_shell(field, options) do
	        super
	      end
	    end
	  end

	  def address_field(method, options = {})
	   build_shell('address', options){ @template.address_field(@object_name, method, options.merge(:object => @object)) }
	  end

	  def build_shell(field, options)
	    @template.capture do
	      locals = {
	        :element => yield,
	        :label   => label(field, options[:label]),
	        :help    => options[:help]
	      }

	      if has_errors_on?(field)
	        locals.merge!(:error => error_message(field, options))
	        @template.render :partial => 'forms/field_with_errors',
	                         :locals  => locals
	      elsif (field == 'address')
	        @template.render :partial => 'forms/address_field',
	                         :locals => {:object => @object_name}
	      else
	        @template.render :partial => 'forms/field',
	                         :locals  => locals
	      end
	    end
	  end

	  def error_message(field, options)
	    if has_errors_on?(field)
	      errors = object.errors.on(field)
	      errors.is_a?(Array) ? errors.to_sentence : errors
	    else
	      ''
	    end
	  end

	  def has_errors_on?(field)
	    !(object.nil? || object.errors.on(field).blank?)
	  end
	end

Extra Tip: own base controller for profile page

If you have a website where all users can have a profile page with a guestbook section, photo-album section... and you want stuff to happen on al profile-controllers, but not on the main-website pages. You cant put them in application.rb, so, lets make a custom base-controller for our profile controllers, so we can put before_filters in that base-controller (like we would put before_filters in application.rb for stuff that has to happen on every page).


in app/controllers, make a profile_controller:
profile_controller.rb
	class ProfileController < ApplicationController
	  before_filter :get_profile_user
	
	  def get_profile_user
	      @profile_user = User.find_by_name(params[:profile_user]) #you will have to set up your routes to have such a parameter
	  end
	end
now you can inherit for example you GuestbookController from ProfileController
app/guestbook_controller.rb:
	class GuestbookController < ProfileController #ProfileController instead of ApplicationController
	  def index
		@profile_user #here we can access @profile_user, as we fetched it in the profile_controller with a before_filter
	  end
	end
Sections 
- PHP (1)
- General (2)
- Ruby on Rails Snippet (3)
- Ruby on Rails (13)
- HTML (1)
- Css (1)
- regex (1)
- Photoshop (1)
- Leopard (1)
A little note to Internet Explorer users:
This site is not made for Internet Explorer. Some pages may look like shit. Yes, i could fix it for IE, but no, I'm not gonna do it. Try Opera or Firefox instead!