Flowdock

Recent notes

RSS feed
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
July 28, 2008
15 thanks

Friendlier error message example

The default error messages can be a bit stale and off putting. Try somethings like this:

error_messages_for(
  :user, 
  :header_message => "Oops - We couldn't save your user!", 
  :message => "The following fields were a bit of a problem:", 
  :header_tag => :h1
)

You can also use error_messages_for as follows

<%  form_for User.new do |f| %>
  <%=  f.error_messages :header_message => "..." %>
<%  end  %>
July 28, 2008
2 thanks

":prompt" doesn't work

This does not work:

select :object, :method, options, :prompt =>-select-’

This does work:

select :object, :method, {:include_blank =>-select-’}
July 25, 2008 - (v1.0.0 - v2.1.0)
4 thanks

select_options_tag - no more worries...

no more explicit options_for_select calls..

def select_options_tag(name='',select_options={},options={})
  #set selected from value
  selected = ''
  unless options[:value].blank?
    selected = options[:value]
    options.delete(:value)
  end
  select_tag(name,options_for_select(select_options,selected),options)
end

select_options_tag(‘name’,[[‘oh’,‘no’]],:value=>‘no’)

July 25, 2008 - (v1.0.0 - v2.1.0)
4 thanks

haml, an alternative to ERb

Want something nicer looking (and currently, faster!) than using ERb for your views? Have a look at haml (and it’s companion, sass, for stylesheets). It will make you feel all fuzzy on the inside, I promise :P.

ERb example

<div id="profile">
  <div class="left column">
    <div id="date"><%= print_date %></div>
    <div id="address"><%= current_user.address %></div>
  </div>
</div>

haml equivalent

#profile
  .left.column
    #date= print_date
    #address= current_user.address

Shifting to haml from ERb feels strange at first, but after about 20 minutes it starts to feel nice. A little longer and you’ll really start to notice your productivity (and of course, happiness) increase! :). I’ve starting shifting all new projects developed at our work office over to using haml (and sass), it’s been fantastic!

At first I came across a few things that I couldn’t do in haml, though every time a quick read of the overview doc page would show me a simple syntax for overcoming that issue! :) (which out of interest, is located here: http://haml.hamptoncatlin.com/docs/rdoc/classes/Haml.html)

Give the tutorial a shot if you’re interested: http://haml.hamptoncatlin.com/tutorial

July 25, 2008 - (v1.0.0 - v2.1.0)
0 thanks

render template file different from your action (method) name - alternative

Alternative ways to render templates for actions

# Renders the template for foo (foo.html.erb, foo.haml.erb, foo.text.html.haml, whatever :P)
def foo
end

# Renders the template that would be rendered in foo
# (but without the foo controller action being invoked)
def bar
  render :action => 'foo'
end

# Similar to what bar does, but render's a specifically named template
def roar
  render :template => 'foo'
end

# Similar to what roar does, but render's a template
# from outside of the current controller's views directory
def boo
  render :template => 'global/something'
end
July 25, 2008
2 thanks

If you need something more

Use the brilliant chronic gem: http://chronic.rubyforge.org/

require 'chronic'
Time.now # => Fri Jul 25 00:00:25 0200 2008
Chronic.parse 'tomorrow 8 in the evening'  # => Sat Jul 26 20:00:00 0200 2008
Chronic.parse 'next Monday noon'           # => Mon Jul 28 12:00:00 0200 2008
Chronic.parse 'first Wednesday of Aug'     # => Wed Aug 06 12:00:00 0200 2008
Chronic.parse 'first Wednesday of Aug 7pm' # => Wed Aug 06 19:00:00 0200 2008
July 24, 2008
3 thanks

render_collection

You can wrap render in helpers. For example, render_collection. In app/helpers/application.rb:

module ApplicationHelper
  def render_collection(name, collection)
    render :partial => "shared/#{name}", :collection => collection
  end
end

In views:

<h2>Comments</h2>
<%= render_collection :comments, @photo.comments %>
July 24, 2008
8 thanks

render template file different from your action (method) name

In some cases you have to avoid rails magic that uses template names named as your ActionMailer method.

rails magic

def daily_notification
  # ...
end
# will look for daily_notification.erb

def weekly_notification
  # ...
end
# will look for weekly_notification.erb

your case

Just give necessary value to @template instance variable.

def setup
  # ...
  @template = 'notification'
end

def daily_notification
  # ...
end
# will look for notification.erb

def weekly_notification
  # ...
end
# will look for notification.erb
July 24, 2008 - (v1.0.0 - v2.1.0)
0 thanks

Extract plain text body from TMail parsed email

Here’s a monkey patch for TMail::Mail I wrote to recurse through a message and extract all plain text body components of that message, returning an Array. For most use cases, the resulting Array will contain one String element.

Currently I put this code in a file called lib/tmail_extensions.rb and require ‘tmail_extensions’ in environment.rb

module TMail
  class Mail
    def plain_text_body
      gather_plain_text_parts(self).flatten
    end

  private
    def gather_plain_text_parts(part)    
      returning [] do |message|
        message << part.body.strip if part.content_type == 'text/plain'        
        part.parts.each { |p| message << gather_plain_text_parts(p) }
      end
    end
  end
end
July 24, 2008 - (v2.1.0)
20 thanks

automatically generate scopes for model states

or better known as “throw on some more tasty meta-programming” :). Given an example of a model which has a state (String) which must from a set of defined values, e.g. pending, approved, denied.

class User < ActiveRecord::Base
  STATES = [ 'pending', 'approved', 'denied' ]

  validates_inclusion_of :state, :in => STATES

  # Define a named scope for each state in STATES
  STATES.each { |s| named_scope s, :conditions => { :state => s } }
end

This automatically defines a named_scope for each of the model states without having to define a named_scope manually for each state (nice and DRY).

July 24, 2008 - (v1.0.0 - v2.1.0)
1 thank

Custom environment constants

Custom environment level constants can be passed in to your rails application (server, console, whatever) like this):

# bash, tcsh, whatever shell
GAMEMODE=pregame script/server

Within rails this constant can be accessed by

ENV['GAMEMODE']
=> "pregame"

ENV['DOES-NOT-EXIST']
=> nil
July 23, 2008
3 thanks

options_for_select further example (using a collection and with a default value)

In this example, we are editing a collection of region records, each with its own select list of countries. (Region belongs_to :country.) If the region doesn’t have a country associated, then we want a default message of “unassigned”. Of course, if the region does have a country associated then we want that country displayed:

<% name = "region[" + region.id.to_s + "][country_id]" %>
<% id = "region_" + region.id.to_s %>

<%= select_tag(id, options_for_select([["unassigned" , "0" ]] +
                     Country.to_dropdown, region.country_id),

{:name => name} ) %> This give us:

<select id="region_3" name="region[3][country_id]">
  <option value="0">unassigned</option>
  <option selected="selected" value="12">England</option>
</select>

NB: we’re using the handy acts_as_dropdown plugin (http://delynnberry.com/projects/acts-as-dropdown/) but we could just as easily prepare the select list with map / collect as above.