Flowdock

Notes posted to Ruby on Rails

RSS feed
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 - (<= 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
7 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.

January 23, 2009
1 thank

Alternate for Rails 2.0

Obviously these methods are protected so usage in an app is discouraged. But if you need to use it anyway for some reason Rails 2.0 also has sanitize_sql_for_conditions which operates exactly like sanitize_sql used to (i.e. it determines if it needs to be processed as an array or hash). So if you are going to blow by the protected status might as well use the easier version. :)

January 22, 2009
8 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 15, 2009
2 thanks

constantize

@ncancelliere - Instead use constantize which is provided as part of ActiveSupport. It is much easier. So:

mystring.constantize
January 15, 2009 - (v1.2.6 - v2.2.1)
0 thanks

Convert String to Class

this example shows how you can take a string and # make it a class and then send it a method

Kernel.const_get(my_string.capitalize).select_options 
January 14, 2009
2 thanks

See #data_select for available options

Available symbols for “options” hash are described on date_select page

January 13, 2009
2 thanks

Easier Universal Partials

@hosiawak provides a great example of how to use polymorphic_path to produce universal partials. You can actually simply his example even more with the following:

<%= link_to 'Edit', edit_polymorphic_path(obj) %>
<%= link_to 'Delete', obj, :method => :delete %>

So the things to note are that in addition to polymorphic_path, Rails also provides an “edit_” version just like on your resources so you can use that instead of specifying the action specifically. The second thing to remember is if you pass just the raw object as the path then then rails will automatically wrap it in a call to polymorphic_path.

January 13, 2009
7 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 -%>