Flowdock

Good notes posted to Ruby on Rails

RSS feed
February 17, 2009
9 thanks

Empty elements

If you want to output an empty element (self-closed) like “br”, “img” or “input”, use the tag method instead.

February 13, 2009
3 thanks

New test syntax

You can use either one and even mix in the same test case if you want:

class Test < Test::Unit::TestCase
  # old way to define a test method (prefix with test_)
  def test_should_be_valid_without_content
    assert Comment.new.valid?
  end

  # new way to define a test
  test "should be valid without content" do
    assert Comment.new.valid?
  end
end
February 10, 2009
8 thanks

Security issue with non-HTML formats

Please note that using default to_xml or to_json methods can lead to security holes, as these method expose all attributes of your model by default, including salt, crypted_password, permissions, status or whatever you might have.

You might want to override these methods in your models, e.g.:

def to_xml
  super( :only => [ :login, :first_name, :last_name ] )
end

Or consider not using responds_to at all, if you only want to provide HTML.

February 9, 2009
5 thanks
February 7, 2009 - (>= v2.2.1)
3 thanks

Deprecated

This method is deprecated. You should use:

I18n.translate('activerecord.errors.messages')
February 3, 2009
3 thanks

Possible gotcha

Please note that exists? doesn’t hold all the conventions of find, i.e. you can’t do:

Person.exists?(:conditions => ['name LIKE ?', "%#{query}%"]) # DOESN'T WORK!
February 3, 2009
3 thanks

Nothing here

You’re probably looking for I18n::Backend::Simple.

February 3, 2009
4 thanks

This method will still rewrite all the values of the table

Even if you update only a small boolean flag on your record, update_attribute will generate an UPDATE statement that will include all the fields of the record, including huge BLOB and TEXT columns. Take this in account.

February 1, 2009 - (v2.2.1)
3 thanks

You can't use Symbols, but you can use Regexps

You can’t use Symbol (although Symbol is accepted with render :action => :new), like:

assert_template :new # WON'T WORK!

But you can use Regexp, e.g.:

assert_template /new/ # WORKS OK

Note that the String matched with your Regexp is the full path to the template relative to the view/ directory of your app, so this will not work:

assert_template /^new$/ # WON'T WORK!

However this might:

assert_template /^employees\/new.html.haml$/
January 30, 2009 - (v1.2.0 - v2.1.0)
3 thanks

Hash conditions require explicit key and value

When condition passed as hash, the behavior is different from a finder method. Finder methods, such as:

find(:all, :user=>user)

will apply the user_id = user.id convention, provided user is an association (e.g. belongs_to :user). The exists? method will not do the same. You must specify the foreign key name and value explicitly, i.e:

exists?(:user_id=>user.id)
January 27, 2009 - (>= v2.2.1)
5 thanks

Getting the object in a partial

If you need to get the object for the form inside a partial, and can’t use the instance variable, use the #object method… This is particularly useful when you’re dealing with single-table inheritance subclasses (e.g. MyOtherClass inherits from MyClass) or when you are using the same partial across different controllers.

new.html.erb

<% form_for(@my_object) do %>
  <%= render :partial => 'form' %>
  <%= submit_tag 'Create' %>
<% end %>

_form.html.erb

<% if f.object.class.is_a? MyClass %>
 <%# do something... %>
<% elsif f.object.is_a? MyOtherClass %>
  <%# do something else... %>
<% end %>
January 27, 2009
5 thanks

Use collect in nested content_tags

Remember to use #collect instead of #each in nested content_tags

arr = ['a','b','c']
content_tag :div do 
  arr.collect { |letter| content_tag(:scan, letter) 
end
#=> <div>
#      <scan>a</scan>
#      <scan>b</scan>
#      <scan>c</scan>
#   </div>

If you used #each you would get this (which is probably a mistake):

#=> <div>
#      abc
#   </div>
January 26, 2009
5 thanks

two ways to disable single table inheritance

  1. Don’t use the column name ‘type’

  2. Or if the first is no option for you: Tell Rails to look for a not existing column like:

class MyModel < ActiveRecord::Base

  # disable STI
  inheritance_column = :_type_disabled
end  
January 26, 2009 - (>= v2.2.1)
3 thanks

alternative (working with 2.2.X)

ActionController::Base.relative_url_root

January 25, 2009
3 thanks

Routes = RouteSet.new

In config/routes.rb you can see this:

ActionController::Routing::Routes.draw do |map|
  #routes
end

If you want to look at the code in ActionController::Routing you won’t find the definition of Routes. That’s because it’s actually an instance of the class RouteSet, defined in action_controller/routing.rb

Routes = RouteSet.new
January 24, 2009
8 thanks

How to test different responses of respond_to

You can shorten this:

@request.env['HTTP_ACCEPT'] = "application/rss"

To this:

@request.accept = "application/rss"

Also, if you send more than one Mime type, it will render the first one:

@request.accept = "text/javascript, text/html" #=> renders JS
@request.accept = "text/html, text/javascript" #=> renders HTML
January 22, 2009
5 thanks

Using fields_for with has_many associations

If you want to edit each element of an array of objects (such as with a has_many type association), you will need to include “[]” in your field parameter name, like so:

<% fields_for "object[]" do |subfield| -%>
  [...]
 <% end -%>

Because you named the field parameter “object[]”, fields_for will assume you have an instance variable @object to use for the fields’ values. To fake this, you can do something like:

<% objects.each do |@object| -%>
  <% fields_for "object[]" do |subfield| -%>
    [...]
  <% end -%>
<% end -%>

If that looks like sacrilegious Rails code to you, then you could consider:

<% objects.each do |object| -%>
  <% fields_for "object[]", object do |subfield| -%>
    [...]
  <% end -%>
<% end -%>

In either case, params[:object] will be a hash where the ID of each object (determined via ActiveRecord::Base#to_param ) is associated with a hash of its new values:

params = { 'object' => { '123' => { 'field' => 'newval' }, '159' => { 'field' => 'newval' } } }
January 20, 2009 - (>= v2.2.1)
5 thanks

Reloading memoized values

Memoize is used to cache the result of a method. It’s roughly equivalent of having:

def memoized_method(*args)
  @result[args] ||= (
    # do calculation here
  )
end

However, the result is cached so that it’s not calculated for every request.

To recalculate cached value use either

obj.memoized_method(:reload)

or

obj.memoized_method(true)
January 20, 2009
5 thanks

Javascript encoding DOES work!

grosser assertion is false :

mail_to('xxx@xxx.com', nil, :encode => :javascript)
# => "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%78%78%78%40%78%78%78%2e%63%6f%6d%22%3e%78%78%78%40%78%78%78%2e%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>"

Use “nil” as the second parameter to tell mail_to that you want to use the first parameter for both text and email link

January 16, 2009 - (>= v2.2.1)
4 thanks

Remember to mixin the ActiveSupport::Memoizable module

To use memoize in your model you need to extend the model class with the module, like this:

class Person < ActiveRecord::Base
  # Mixin the module
  extend ActiveSupport::Memoizable

  def expensive_method
    # do something that is worth remembering
  end
  memoize :expensive_method
end

If you use memoizable in most of your models you could consider mixing the module into all ActiveRecord models by doing this in an initializer:

ActiveRecord::Base.extend(ActiveSupport::Memoizable) 
January 16, 2009
7 thanks

/products/1

Code example

current_page?(product_path(@product))
# => true
January 13, 2009
6 thanks

Universal partial

polymorphic_url is very useful if you want to create an universal partial that works for more than 1 type of object passed to it.

For example in you sidebar you might have a _sidebar.html.erb partial that’s supposed to display links to “Edit” and “Delete” actions. You can write it in such a way that it can be reused for different types of objects (in the example below we pass either a Post or a Note).

your_template.html.erb

<%= render :partial => 'shared/sidebar', :locals => { :obj => Post.new -%>

other_template.html.erb

<%= render :partial => 'shared/sidebar', :locals => { :obj => Note.new -%>

_sidebar.html.erb

<%= link_to "Edit", polymorhpic_url(obj, :action => 'edit') -%>
<%= link_to "Delete", polymorphic_url(obj), :method => :delete -%>
January 9, 2009
3 thanks

Adding params to generated url

Whenever you want to append custom parameters to a to be generated url it might be necessary to stop url_for from escaping.

url_for(:action => 'some_action', :custom1 => 'some_value', :custom2 => 'some_value', :escape => false)

If the escape => false option is not passed the generated url contains &amp; instead of the correct &-sign.

January 8, 2009
10 thanks

Refactoring excessive code for selects

@garg: It is not recommended to have excessive code in the views. You should refactor your code a bit.

<%= f.select(:manufacturer_id, Manufacturer.find(:all).collect {|u| [u.name, u.id]}, :prompt => 'Select') %>

could be changed to this:

# in app/helpers/manufacturer_helper.rb
def manufacturers_for_select
  Manufacturer.all.collect { |m| [m.name, m.id] }
end

# in the view
<%= f.select(:manufacturer_id, manufacturers_for_select, :prompt => 'Select') %>

I would look into collection_select though:

<%= f.collection_select(:manufacturer_id, Manufacturer.all, :id, :name, :prompt => 'Select') %>

It’s much more clean and you don’t have to define a helper for it to be readable (altough it’s still quite long).

If you have to do this often, you should define a FormBuilder extension, so you get methods like f.manufacturer_select:

<%= f.manufacturer_select(:manufacturer_id, Manufacturer.all) %>

IMO, most projects should have a custom form builder anyway, so the addition would be very small. This is my personal opinion, so you don’t have to listen to it. :-)

January 8, 2009
5 thanks

Watch out for syntax errors

Watch out when you are using returning with hashes. If you would write code like

def foo(bars)
  returning {} do |map|
    bars.each { |bar| map[bar.first] = bar }
  end
end

you will get a syntax error since it looks like you tried to supply two blocks! Instead you should write it with parenthesis around the hash:

def foo(bars)
  returning({}) do |map|
    bars.each { |bar| map[bar.first] = bar }
  end
end
January 6, 2009
5 thanks

Default allowed tags and attributes

I found it a bit hard to find the default tags and attributes in the docs.

As of Rails 2.2.2 they are:

Tags

del, dd, h3, address, big, sub, tt, a, ul, h4, cite, dfn, h5, small, kbd, code,
b, ins, img, h6, sup, pre, strong, blockquote, acronym, dt, br, p, div, samp,
li, ol, var, em, h1, i, abbr, h2, span, hr

Attributes

name, href, cite, class, title, src, xml:lang, height, datetime, alt, abbr, width

Getting the latest list

You can query for this list yourself with the following code on the console:

>> puts helper.sanitized_allowed_tags.to_a * ", "
... will output tag list ...
>> puts helper.sanitized_allowed_attributes.to_a * ", "
... will output attribute list ...

The same principal can probably be applied to sanitize_css.

January 2, 2009 - (v2.0.0 - v2.2.1)
3 thanks

What to use instead

For versions 2.0+, use ActiveRecord::Base::sanitize_sql_array