Flowdock

Notes posted to Ruby on Rails

RSS feed
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 & instead of the correct &-sign.

January 9, 2009 - (v2.2.1)
0 thanks

Dynamic labels

Code example

<%=label_tag 'category_'+cat.title, cat.title%>
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
0 thanks

Alternative force initial value

@Bounga - Thanks for the suggestion. Didn’t know about that method. An alternative method I have used is just to assign the default value to the object in your controller. For example your “new” action might now look like:

def new
  @ecard = Ecard.new params[:ecard]
  @ecard.sender ||= 'contact@host.com'
end
January 6, 2009
1 thank

Replace allowed tags/attributes

The docs above state how to add and remove tags from the default list. But what if you just want to replace the entire list with a list of your own? You can easily do that with the following code:

ActionView::Base.sanitized_allowed_tags.replace %w(strong em b i hr br ul ol li blockquote)
ActionView::Base.sanitized_allowed_attributes.replace %w(href)

Note that if you put this in your initialization block you must use the config.after_initialize hack (to override the default that will be set) but if you put it in an initializer (i.e. a file in the initializers directory) that code is executed after Rails initialization so no need to use any hack. Just use the code above.

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.2.1)
3 thanks

What to use instead

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

January 2, 2009
3 thanks

current_action? and current_controller?

I use them in link_unless_current_controller helper.

def current_action?(options)
  url_string = CGI.escapeHTML(url_for(options))
  params = ActionController::Routing::Routes.recognize_path(url_string, :method => :get)
  params[:controller] == @controller.controller_name && params[:action] == @controller.action_name
end

def current_controller?(options)
  url_string = CGI.escapeHTML(url_for(options))
  params = ActionController::Routing::Routes.recognize_path(url_string, :method => :get)
  params[:controller] == @controller.controller_name
end
December 29, 2008 - (v2.2.1)
1 thank

Deprecation warning for using options without hash

As of Rails 2.2.0, truncate gives a Deprecation warning if you don’t use a hash for the options. Ex:

The old way

truncate(project.description, 100, "... Read More")

The warning

DEPRECATION WARNING: truncate takes an option hash instead of separate length and omission arguments.

The new way

truncate(project.description, :ommision => "... Read More", :length => 100)
December 28, 2008
1 thank

i think that's a bit better resourceful approach...

Code example

auto_discovery_link_tag :atom, formatted_movies_url(:atom), :title=>'New movies'
December 24, 2008 - (>= v2.2.1)
5 thanks

uninitialized constant ActionView::Base::CompiledTemplates::TimeZone

If you get this error, you need to use ActiveSupport::TimeZone.us_zones instead of TimeZone.us_zones.

Example:

<%= form.time_zone_select(:time_zone, ActiveSupport::TimeZone.us_zones) %>
December 23, 2008
0 thanks

Incorrectly named option

The :add_month_number option should be :add_month_numbers

December 18, 2008 - (<= v2.2.1)
0 thanks

Belongs_to, Has_many association

I have a belongs_to, has_many association between manufacturer and modelname. (manufacturer has many modelnames).

In the new modelname page, I have a drop down menu that lists all the manufacturers, so I choose eg Dell, and then in modelname.name field I enter inspiron or what ever. To create that drop down I used:

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

The reason that works was explained by fcheung and rsl on #rubyonrails. Thanks:

the form builder basically calls the method whose docs you have read, inserting the appropriate first argument

The object is referenced internally when you call f.whatever. in any of those tags, the object is omitted when used on a form block variable

December 16, 2008 - (v1.0.0 - v2.2.1)
14 thanks

Force initial value

If you want to force an initial value for your text_field which is normally based on your object attribute value, you can use :

text_field :ecard, :sender, :value => 'contact@host.com'
December 12, 2008
7 thanks

acts_as_state_machine named scopes

If you are using the acts_as_state_machine plugin, this will generate all named scopes for your various states.

Place it after the acts_as_state_machine and state declarations.

class Task < ActiveRecord::Base
  acts_as_state_machine :initial => :waiting
  state :waiting
  state :running
  state :finished    

  states.each { |s| named_scope s, :conditions => { :state => s.to_s } }
end

Then doing a Task.waiting will return the corresponding tasks.

December 11, 2008
3 thanks

with resources

/products

current_page?(products_path)

# => true
December 11, 2008 - (>= v1.0.0)
5 thanks

Calling migrations within migrations

It’s very occasionally a wise strategy to call migrations from within other migrations. This is typically done when you are adding a migration that deletes a now-obsolete table.

Let’s say one night when you were drunk or otherwise not thinking straight you did something like this:

class CreateExGirlfriendTexts < ActiveRecord::Migration
  def self(dot)up
    create_table :ex_girlfriend_texts { |t| ... }
  end

  def self(dot)down
    drop_table :ex_girlfriend_texts
  end
end

Oops! You could add this for your “undo” migration the next morning:

class FixDrunkMistake < ActiveRecord::Migration
  def self(dot)up
    CreateExGirlfriendTexts.down
  end

  def self(dot)down
    CreateExGirlfriendTexts.up
  end
end

Now, in the event you decide you really did like that table, you can always get it back easily. Keep in mind this will be made more complicated if your table is modified over multiple transactions.

December 11, 2008
6 thanks

Accessing aggregate methods with :group

You can access aggregate methods (such as SUM, COUNT, etc.) when using a JOIN and GROUP BY query by simply naming the aggregate columns and calling them as methods on the returned objects:

hits_by_page = WebpageHit.all({
  :select => "webpages.*, COUNT(webpage_hit.id) AS view_count",
  :joins => :webpage,
  :group => "webpages.id"
})
homepage_hits = hits_by_page[homepage.id].view_count

The view_count method is added to the Webpage model by this call. Note, however, that this method returns a string, and is not typecasted by Rails.

December 10, 2008
0 thanks

Example

my_instance.connection.clear_query_cache

December 6, 2008
0 thanks

:cache_path strangeness

I’ve noticed that using an example like this one shown in the docs has some issues with URI escaping:

caches_action :feed, :cache_path => Proc.new { |controller|
     controller.params[:user_id] ?
       controller.send(:user_list_url, controller.params[:user_id], controller.params[:id]) :
       controller.send(:list_url, controller.params[:id]) }
 end

When I do this, the :myroute_url methods return URLs with escaped parameters, as one would expect, but for some reason Rails unescapes these strings by the time it uses them to store the cache blob. This is messing up my cache expiration routines, because they keep track of the escaped versions of the urls that are returned by the routing methods, not the unescaped versions. It would be easier if Rails didn’t do any magic to the string and simply used exactly what you pass to cache_path.

December 3, 2008 - (v2.2.1)
5 thanks

Method doesn't exists

Don’t confuse it with new_record? in ActiveRecord

December 1, 2008 - (>= v2.2.1)
1 thank

Upgrading from Rails v2.1 to v2.2

Don’t forget to run

rake gems:refresh_specs

if you vendored any gem plugins under Rails v2.1 to update them with the proper specs.

November 28, 2008 - (>= v2.2.1)
2 thanks

Removed form Rails core

country_select and country_options_for_select were removed from Rails core since 2.2 release, but extracted to plugin. http://github.com/rails/country_select/tree/master/README

Also you could be interested in localized_country_select plugin which uses Rails internationalization framework I18n. http://github.com/karmi/localized_country_select/tree/master/README.rdoc

November 26, 2008
10 thanks

Types array shorthand

You can have respond_to blocks that look like this:

respond_to do |format|
  format.html
  format.xml
end

Here each individual format doesn’t receive a block and so Rails automatically tries to render the appropriate view for the mime type (e.g. action.html.erb, action.xml.erb or as a last resort action.erb)

You can do exactly the same thing by passing an array of Mime types to respond_to like this:

respond_to(:html, :xml)
November 24, 2008 - (<= v2.2.1)
7 thanks

Full List of Supported Formats

With a sample date of December 25th, 2008, at 14:35:05:

:db           # => 2008-12-25 14:35:05
:number       # => 20081225143505
:time         # => 14:35
:short        # => 25 Dec 14:35
:long         # => December 25, 2008 14:35
:long_ordinal # => December 25th, 2008 14:35
:rfc822       # => Thu, 25 Dec 2008 14:35:05 +0000
November 21, 2008
0 thanks

Installation

Install the plugin by typing

script/plugin install auto_complete

Remember to restart webservice :)

November 20, 2008 - (<= v2.1.0)
0 thanks

Problematic :scroll option

If you use the :scroll => true option, note that at http://github.com/madrobby/scriptaculous/wikis/sortable-create it says:

“If you want your sortable list to be scrollable, wrap the list in a div and set the div to scrollable as apposed to making the ul element scrollable. Also, in IE you must set “position:relative” on the scrollable div.”