Flowdock

Recent notes

RSS feed
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.0.0 - 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.0.0 - 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
18 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"
}
July 30, 2008
6 thanks

Different Method for Subdomains

@james

You can also access the subdomain via the subdomains array.

request.subdomains.first
July 29, 2008 - (v2.1.0)
15 thanks

Scoped using - more simple way

Regarding to the example from james, there is a more simple way to do this:

user.messages.update_all(:read => true)
July 28, 2008 - (v2.0.0 - v2.1.0)
0 thanks

Bug that causes escape buildup

There is a bug in this meethod that causes an escape build up when you have links or image urls for example with ampersands in them. Over time, it goes something like this:

& -> &amp; -> &amp;amp; -> &amp;amp;amp; -> &amp;amp;amp;amp; -> etc

This breaks the url so links and images are not clickable/viewable. To fix, simply unescape before you reescape. Works like a charm. We have the following in an initializer, “html_sanitizer_patch.rb”, that fixes this behaviour.

module HTML
  class WhiteListSanitizer < Sanitizer
    protected
    def process_attributes_for(node, options)
      return unless node.attributes
      node.attributes.keys.each do |attr_name|
      value = node.attributes[attr_name].to_s
      if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value)
        node.attributes.delete(attr_name)
        else
          node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(CGI::unescapeHTML(value))
        end
      end
    end
  end
end
July 28, 2008 - (v2.0.0 - v2.1.0)
0 thanks

Bug that looks for "500 .html" rather than "500.html"

There is a very small bug in this method of Rails that causes error pages you change in public/ not to be shown, because Rails looks for “404 .html” and “500 .html” (note the space). The fix is simple.

#{status.to_s[0..3]}

needs to become

#{status.to_s[0...3]}

If you’re like me and don’t want to edit Rails itself, at the bottom of environment.rb, stick some code that overwrites this method to fix the bug. We have the following:

module ActionController
  class Dispatcher
    class << self
      private
        def failsafe_response_body(status)
          error_path = "#{error_file_path}/#{status.to_s[0...3]}.html"
          if File.exist?(error_path)
            File.read(error_path)
          else
            "<html><body><h1>#{status}</h1></body></html>"
          end
        end
    end
  end
end
July 28, 2008 - (v2.1.0)
0 thanks

Bug? does not encode options

polymorphic_path(item,options) =polymorphic_path(item)+hash_to_url_query(options)

def hash_to_url_query(hash)
  url = []
  hash.each{|k,v| url << "#{k}=#{v}"}
  "=" + (url * '&')
end