Flowdock

Notes posted to Ruby on Rails

RSS feed
February 14, 2009
0 thanks

Simple rescue

Take it easy:

def create
  @model = Model.new(params[:model)
  @model.save!
rescue
  logger.error(!$.to_s)
end

Global variable !$ refers to the Exception object.

February 13, 2009
1 thank

throws exception

when use use Model.find([1,2,3,4])

throws exception if no record exists with any of this ID

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 12, 2009
0 thanks

using collection=objects

It will fire one insert query per new record

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 10, 2009
2 thanks
February 9, 2009
5 thanks
February 9, 2009
1 thank

:foreign_type option

I’m not sure if this has always been around but in 2.3, belongs_to takes a :foreign_type option on polymorphic associations. This behaves the same way as :foreign_key but for the type field.

February 7, 2009 - (>= v2.2.1)
1 thank

Documentation

Good docs can be found here: http://www.artweb-design.de/2008/7/18/the-ruby-on-rails-i18n-core-api

See also: http://rails-i18n.org/wiki for an extensive list of resources.

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

Deprecated

This method is deprecated. You should use:

I18n.translate('activerecord.errors.messages')
February 6, 2009 - (>= v2.1.0)
1 thank

Whacky edge case

The above works great as long as you select the primary key of the owning assocations.

preload_associations calls a group_by on that object so if there is no primary key attributed filled out it will reduce the records to 1 object. ex: rows = foo.find(:all,:select=>“boo.id, foo.name, foo.age, count(DISTINCT foo.id)”, :group=>“foo.name,foo.age having count( DISTINCT foo.id) > 1”,:joins=>“INNER JOIN bar.foo on bar.foo_id = foo.id”)

preload_assications(rows,:bar)

rows.first.bar.name #=> sql call already made in preload rows.last.bar.name #=> just made another sql call to get bar

fix: :select=>“foo.id, boo.id, foo.name, foo.age, count(DISTINCT foo.id)”

now preload_associations will include all the ids found instead of just the 1st one.

February 4, 2009
1 thank

How I use Optimistic Locking

I have used Optimistic locking often, but usually I only need it in one or two places in the codebase, not everywhere an object is saved whose model has a lock_version column. So what I usually end up doing is using a little module I wrote called OptimisticallyLockable (awesome name right?). Here it is:

module OptimisticallyLockable

  def self.included(receiver)
    receiver.lock_optimistically = false
    receiver.class_eval do

      def self.with_optimistic_locking
        original_lock = self.lock_optimistically
        self.lock_optimistically = true

        begin
          yield
        ensure
          self.lock_optimistically = original_lock
        end
      end

    end
  end

end

When included in a model that has a lock_version column it will turn off optimistic locking. Then when you want to actually use optimistic locking you can just use the with_optimistic_locking method like this:

class Blog
   include OptimisticallyLockable

   def do_something_destructive!
     self.class.with_optimistic_locking do
        #  do something important here
     end
   end                                
end
February 4, 2009
0 thanks

usage example

code

class MyModel < ActiveRecord::Base
  def created_today?
    self.created_on.to_date == Date.today
  end
end
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
0 thanks

Getting functionality from instances

If you want to get this functionality from instances of the models too, you can easily monkey patch rails to allow this. This is only semi-bad practice, AFAIK.

In config/initializers/overrides.rb:

#
# Wrapper for @model.human_attribute_name -> Model.human_attribute_name
#
class ActiveRecord::Base
  def human_attribute_name(*args)
    self.class.human_attribute_name(*args)
  end
end

Now, why is this bad? Because we change a core class, and that makes maintainability a little bit harder since new developers will not know about this.

The good part is that is such a small thing and as long as human_attribute_name exists in the class, this extension will work.

Example usage:

# index.html.haml
%th= User.human_attribute_name('login')
%th= User.human_attribute_name('email')
# ...

# show.html.haml
%p
  %strong= "#{@user.human_attribute_name('login')}: "
  = @user.login

In this example, about nothing was gained at all, really. A better example is when you are building helpers to do stuff like displaying model attributes for you and whatnot. Examples for this would be far too huge to show off here, but basically, you avoid having to do stuff like this:

if options.include? :model
  model = options[:model]
  model = model.class unless model.is_a? Class
  model.human_attribute_name(field)
else
  # ...
end

and you want to keep your views clean (@user vs. @user.class)

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
1 thank

Add Rspec files to the annotations

By default the annotations search the ‘test’ folder, but not the ‘spec’ folder if you are using Rspec. To get those specs involved do this:

require 'source_annotation_extractor'

class SourceAnnotationExtractor
  def find(dirs=%w(app lib spec))
    dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
  end
end

If you have other folders you want to check, just add them to the dirs list.

January 27, 2009 - (v2.1.0 - v2.2.1)
1 thank

Description

Similar to Ruby’s built-in attr_writer, only it creates a class attribute writer method (as opposed to an instance attribute writer method).

January 27, 2009 - (v2.1.0 - v2.2.1)
2 thanks

Description

Similar to Ruby’s built-in attr_reader, only it creates a class attribute reader method (as opposed to an instance attribute reader method).

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 24, 2009
0 thanks

Adding "alt" text to the image tag

<%= image_submit_tag (“create.gif”, :alt => “Create new entity”) %>

This way when the images are disabled or don’t work you get the nice custom text instead of the standard one. Pretty useful.