Flowdock

Recent notes

RSS feed
August 12, 2008
9 thanks

Overriding the default div class="fieldWithErrors"

By default fields that are invalid are wrapped in:

<div class="fieldWithErrors">
  <input type="text" name="blah">
</div>

To override and wrap in spans instead of divs place the following in your environment.rb:

ActionView::Base.field_error_proc = Proc.new { |html_tag, instance| "<span class=\"fieldWithErrors\">#{html_tag}</span>" }

or to not use wrapping at all:

ActionView::Base.field_error_proc = Proc.new { |html_tag, instance| "#{html_tag}" }
August 11, 2008
10 thanks

RE: Using validates_format_of to validate URIs

Further to Olly’s note below, you can also specify the protocol to further limit the valid uri’s, else things like ‘ftp ://someurl.com’ (there’s only a space in there to get it to display on here) would be valid.

validates_format_of :uri, :with => URI.regexp(['http'])
August 11, 2008
8 thanks

Validations

update_attribute will not perform validations checks when the Validation module is included.

If you want to perform validations when updating, use update_attributes instead.

August 11, 2008 - (>= v2.1.0)
2 thanks

Examples

@articles = cache(‘articles’) do

Articles.latest 

end

Or:

@articles = cache([‘articles’, user.id], :expires_in => 15.minutes)

# Advanced Rails Recipies says: "expires_in option works only with memcached store"
Articles.latest

end

Also if you’d like to have short ‘cache’ call in your model you can have it - add the following method to the model:

def cache(key, options = {})

ActionController::Base.cache_store.fetch(key, options) { yield }

end

August 10, 2008
4 thanks

converts a Time object to a string in rfc822 / rfc2822 format

>> time = p.updated_at

> Fri Jun 20 14:05:30 +0100 2008

>> time.class

> Time

>> time.httpdate

> “Fri, 20 Jun 2008 13:05:30 GMT”

.

August 10, 2008
10 thanks

New way of calling partials on collections

You can directly call a partial on a collection of objects like this:

<%= render :partial => @users %>

This will call the partial _user.html.erb and populate a local variable ‘user’ within the partial. Then you can display the user partial in this way:

_user.html.erb:

Name: <%= user.name %> <br />
Email: <%= user.email %> <br />

The above render statement is equivalent to this code:

<% for user in @users %>
  <%= render :partial => 'user', :locals => { :user => user } %>
<% end %>
August 8, 2008
0 thanks

finder_sql

If you are using the finder_sql option, it is important to use single quotes if need to interpolate variables, such as the id of the record. Otherwise you will get the object_id of the class.

August 8, 2008
18 thanks

Using validates_format_of to validate URIs

You can use the validates_format_of with the regular expression in the URI library ensure valid URIs.

Example

require 'uri'

class Feed < ActiveRecord::Base
  validates_format_of :uri, :with => URI.regexp
end
August 8, 2008
2 thanks

X-Sendfile equivalent for Nginx

Nginx supports a similar http header to X-Sendfile called X-Accel-Redirect.

Set the X_SENDFILE_HEADER constant somewhere (eg in your environment.rb) file:

ActionController::Streaming::X_SENDFILE_HEADER = 'X-Accel-Redirect'  

Then you can use x_sendfile => true as usual.

More here: http://wiki.codemongers.com/NginxXSendfile

August 8, 2008
7 thanks

Avoiding duplicate results when you do a join

When you use the :joins option you can get multiple instances of the same item. For example, say you want every User who owns one or more Lists. With the code below, if a user owns 5 lists, the User will show up five times in the results:

users = User.find(:all, 
  :conditions => ['users.id = lists.user_id'], 
  :joins => [:lists], 
  :order => 'users.username')

You can cause each found user to appear only once by using the :select option with “DISTINCT”:

users = User.find(:all, 
  :conditions => ['users.id = lists.user_id'], 
  :joins => [:lists], 
  :select => 'DISTINCT users.*'
  :order => 'users.username')
August 7, 2008
12 thanks

Function to Determine Layout

Sometimes its nice to have different layouts choosen automagicly:

class ApplicationController < ActionController::Base
 layout :determine_layout
 def determine_layout
   if is_admin?
     "admin"
   else
     "application"
   end
...
end
August 7, 2008 - (>= v2.1.0)
6 thanks

around_filter - working example

More detailed, working example of usage:

class HomeController < ApplicationController   
  around_filter :action1, :action2

  around_filter do |controller, action|
    logger.info "code block before action"
    action.call
    logger.info "code block after action"
  end

  def index
    logger.info "ACTION"
  end       

  private # filters should not be available for external URL

  def action1            
    logger.info "ACTION1 before yield"
    yield "ACTION1" 
    logger.info "ACTION1 after yield"
  end

  def action2            
    logger.info "ACTION2 before yield"
    yield "ACTION2" 
    logger.info "ACTION2 after yield"
  end       
end

Results (in log file):

ACTION1 before yield
ACTION2 before yield
code block before action
ACTION
Rendering home/index
code block after action
ACTION2 after yield
ACTION1 after yield 
August 6, 2008
1 thank

not loaded if any gems missing at startup

similar to http://rails-doc.org/rails/Rails/Initializer/load_application_initializers#95-initializers

observers and custom initializers aren’t loaded if any gems are missing -

current behaviour isn’t great - as it’s not intuitive - and you get no warning.

here’s a lighthouse ticket i created a while ago about this.

http://rails.lighthouseapp.com/projects/8994/tickets/618-initializer-doesnt-load-observers-initializers-if-unable-to-load-a-gem#ticket-618-1

August 6, 2008 - (v2.1.0)
1 thank

RE: Using counters with collections

Note that as of Rails 2.1, it’s currently not possible to override the internal counter variable you get when using collections via passing it in through :locals.

This is a useful feature when you have a collection of items rendered but then wish to add another one - most likely via an AJAX request.

I’ve been informed it’s back in Edge, so hopefully it’ll re-appear again in Rails 2.2.

August 6, 2008 - (<= v2.1.0)
1 thank

RE: Cross browser issues

In response to subblue’s note below, you should bear in mind that there may be circumstances where you want an AJAX request to enter the format.html block and not format.js.

When you’re returning HTML content, for example.

By using jQuery’s .ajaxSetup method in such an indiscriminate way (applying it by default to all ajax requests), you make it harder to keep track of what’s returning what.

A better alternative is to use it as a stored function;

function set_content_type_to_javascript(xhr) {
 xhr.setRequestHeader("Accept", "text/javascript");
}

and call this from within your .ajax requests when required;

$.ajax({
 type: "GET",
 url: "/some/url",
 dataType: "script",
 beforeSend: function(xhr) { set_content_type_to_javascript(xhr) },etc…
});
August 5, 2008
17 thanks

Multipart form

Don’t forget to add :multipart => true if you have file upload in your form.

<% form_for "user", :html => { :multipart => true } do |f| %>
August 4, 2008
7 thanks

Example: find by associated table

Say you have tables “authors” and “books” and they have a one-to-many association.

You want authors who have written books with “cooking” in the title…

cookbook_authors = Author.find(:all, 
  :conditions => ['books.title LIKE ?', '%cooking%'], 
  :joins => [:books], 
  :order => 'authors.last_name' )

For many-to-many associations, it’s a similar pattern. Say you have tables “people” and “organizations” with a many-to-many association through the join table “organization_memberships”.

Ski Club members whose first name starts with “a”…

ski_club_members = Person.find(:all, 
  :conditions => ['first_name LIKE ? AND organizations.name = ?', 
    'a%', 'Ski Club'], 
  :joins => [:organizations], 
  :order => 'people.last_name' )
August 4, 2008
0 thanks

How to handle the MultiparameterAssignmentErrors exception

If you expect to get this error, do this in your controller:

def create
  @event = Event.new
  @event.attributes = params[:event]
  @event.save!
rescue ActiveRecord::MultiparameterAssignmentErrors => e
  e.errors.each do |error|
    @event.errors.add(error.attribute, ActiveRecord::Errors.default_error_messages[:invalid])
  end
rescue ActiveRecord::RecordInvalid
  # event is invalid
ensure
  if @event.errors.empty? and not @event.new_record?
    redirect_to event_path(@event)
  else
    render :action => :new
  end
end

If attribute assignment gives the MultiparameterAssignmentErrors exception we handle it by adding ‘invalid’ errors to the attributes involved. We also rescue the RecordInvalid exception and handle all the redirecting/rendering in the ensure block.

August 4, 2008
3 thanks

Detailed messages for a nested model

Detailed messages for a nested model

<%@address = @order.address%>
<%=error_messages_for :address%>
August 4, 2008 - (>= v2.1.0)
1 thank

You can turn off dirty objects

If you expierence problems with dirty objects you can turn it off:

ActiveRecord::Base.partial_updates = false
August 4, 2008
1 thank
August 4, 2008 - (>= v2.1.0)
1 thank

"created_at" instead "created_on"

In examples( at least for version 2.1) should be Person.find(:last, :order => “created_at DESC”, :offset => 5)

instead of: Person.find(:last, :order => “created_on DESC”, :offset => 5)

the same with 2nd auto-generated table: in my rails 2.1 it is updated_at not _on

August 3, 2008 - (<= v2.1.0)
6 thanks

Cross browser issues

We use jQuery as our Javascript library of choice, but have to use a work around for full cross-browser support.

In jQuery you need to set the AJAX request headers as:

$.ajaxSetup({

beforeSend: function(xhr) {xhr.setRequestHeader(“Accept”, “text/javascript”);}

});

But we found that IE and Safari sends headers like: HTTP_ACCEPT=>“text/html, /, text/javascript”, with the javascript header last so this mucks up the respond_to block as it will always enter the first block (usually format.html) and never reach your format.js block.

We have a before filter called on required actions that forces the request format to be javascript if it is an xml_http_request?

def fix_xml_http_request
  if request.xml_http_request?
    request.format = :js
  end
end
August 1, 2008 - (v2.1.0)
2 thanks

initializers

initializers/* are not loaded if one of the required gems fails to load. So your inflections, mines, etc, will not be loaded.

The required gems are defined on environment.rb with config.gem

August 1, 2008
2 thanks

email_to('xxx@xxx','xxx@xxx',:encode=>'javascript') does NOT work

as i always want the email in the link text, email_to does not help me…

so here comes the rescue!

#http://unixmonkey.net/?p=20
# Takes in an email address and (optionally) anchor text,
# its purpose is to obfuscate email addresses so spiders and
# spammers can't harvest them.
def js_antispam_email_link(email, linktext=email)
  user, domain = email.split('@')
  # if linktext wasn't specified, throw email address builder into js document.write statement
  linktext = "'+'#{user}'+'@'+'#{domain}'+'" if linktext == email 
  out =  "<noscript>#{linktext} #{user}(ät)#{domain}</noscript>\n"
  out += "<script language='javascript'>\n"
  out += "  <!--\n"
  out += "    string = '#{user}'+'@'+''+'#{domain}';\n"
  out += "    document.write('<a href='+'m'+'a'+'il'+'to:'+ string +'>#{linktext}</a>'); \n"
  out += "  //-->\n"
  out += "</script>\n"
  return out
end
July 31, 2008
8 thanks

Multiple filter methods with :only, :except

Notice that this methods accepts *filters param, so you can pass array of methods with :only or :except too

Example

before_filter [:authorize, :set_locale], :except => :login

July 30, 2008
19 thanks

Value parameter

You can add a value to your hidden field by using the :value parameter.

Example
hidden_field(:object, :field, :value => params[:requestval])
July 30, 2008
0 thanks

Set time zone in before filter

To set your time zone you could create a before_filter in your application.rb controller

class ApplicationController < ActionController::Base

 before_filter :set_timezone

 def set_timezone
   Time.zone = 'GMT'
 end

end
July 30, 2008 - (v2.1.0)
1 thank

2.1 sets UTC time by default

Rails 2.1 sets hour select to UTC time value, not local server time by default. So if you’re not in UTC time zone don’t forget to specify timezone in your config/environment.rb: config.time_zone = 'Vilnius'

July 30, 2008
7 thanks

Using gmail SMTP server to send mail

First you would need to sign up with Google Apps, which is a very painless process:

http://www.google.com/a/cpanel/domain/new

Next you need to install a plugin that will allow ActionMailer to make a secure connection to google:

script/plugin install git://github.com/caritos/action_mailer_tls.git

We need this due to transport layer security used by google.

Lastly all you need to do is place this in your environment.rb file and modify it to your settings:

ActionMailer::Base.smtp_settings = {
 :address => "smtp.gmail.com",
 :port => 587,
 :domain => "your.domain_at_google.com",
 :authentication => :plain,
 :user_name => "google_username",
 :password => "password"
}