Recent notes
RSS feedalternative
not sure but block_called_from_erb? seems to be the replacement
if you get NameError: uninitialized constant ActionController::Caching::Sweeper
I got hit with this on an upgrade. Had a reference to ApplicationController::Base in development.rb (prod as well) which caused this problem http://rails.lighthouseapp.com/projects/8994/tickets/1977
Fix was to remove the loading of ApplicationController::Base and put it in an initializer (where it should have been).
Content_tag in helpers
Content_tag works great in a helper and is a nice way to clean up your views.
If you’re returning more than one content_tag you’ll need to concat them:
@content = content_tag(:tr, "first item") @content << content_tag(:tr, "second item")
Be mindful that when doing the above, you must use parentheses around the content_tag options. In the above example, content_tag :tr, “second item” will return an error.
Finding all records WITHOUT associations
(Thanks to someone on the rails IRC channel who gave me this tip.)
Where Users and Events have a habtm relationship, to find all Users that have no events:
User.find(:all, :include => :events, :conditions => { "events_users.event_id" => nil})
(Note that when specifying a condition on a joined table, you have to put the field name in a string rather than a symbol. In the above example, :events_users.event_id will not work.)
New and improved version
As this method is now deprecated, check the documentation for the new version (adds some more options), which gets included via a module:
So, how do you enable db sessions?
First, run:
rake db:sessions:create
Then, run your pending migrations. This will create the migration you need to run in order to create the sessions table.
Second, go into config/environment.rb and uncomment or put in:
config.action_controller.session_store = :active_record_store config.action_controller.session = { :session_key => '_your_session_name_here', :secret => 'SOME_CRYPTOGRAPHICALLY_SECURE_KEY' }
Third, get yourself a secure key with:
rake secret
And finally, paste your new key into the :secret above.
Generating empty conditions
In some cases, you might find it useful for your lamba to generate empty conditions based on the passed parameter.
Class Article << ActiveRecord::Base named_scope :category, lambda { |cat| if cat == :all { :conditions => {} } else { :conditions => { :category_id => cat } } end } end
Allows you to call something like this:
categories = user_is_admin ? :all : @current_category Article.category(categories)
Mostly useful when chaining named_scopes together. Avoids more complicated if statements.
Passing optional arguments with defaults to a named_scope
An easy way to do this. (This also shows how you can use joins in a named_scope as well.)
Class User << ActiveRecord::Base belongs_to :semester named_scope :year, lambda { |*year| if year.empty? || year.first.nil? { :joins => :semester, :conditions => ["year = #{CURRENT_SEMESTER}"]} else { :joins => :semester, :conditions => ["year = #{year}"]} end } end
You can then call:
User.year # defaults to CURRENT_SEMESTER constant User.year() # same as above User.year(nil) # same as above; useful if passing a param value that may or may not exist, ie, param[:year] User.year(2010)
Remember, named_scope returns an array
named_scope always returns a named_scope object, that acts like an array, even if you’re using it to only find one record. So if you’re trying to perform an association on the results of a named_scope, use the first method to return the model object and not the named_scope object.
Ie:
user = User.my_name_scope user.articles # assuming User has_many Articles
will return an error. use this instead:
user = User.my_named_scope.first user.articles
(Of course this is a poor example because what you should be doing is performing the named_scope on Article with user as the condition, instead of on User. But if you do need to use the results of a named_scope to perform an association call, you have to do it this way to avoid an error.)
Use helpers in your ActionMailer views
It’s very easy to give your mailer access to helpers:
# Let your mailer user the ApplicationHelper methods class MyMailer < ActionMailer::Base helper :application end
Use the current URL, with changes
You can use the current URL, whatever it is, with changes, as in:
# Create a link to the current page in RSS form url_for(:overwrite_params => {:format => :rss})
This can be super-helpful because it preserves any GET params (like search parameters)
Broadened Flash helper
Building on the below excellent example, you can create something with default options for how long it’s displayed and how long the fade is, and highlight:
def show_flash(options={}) options = {:fade => 3, :display => 3, :highlight => true}.merge(options) html = content_tag(:div, flash.collect{ |key,msg| content_tag(:div, msg, :class => key, :attributes => "style = display: none;") }, :id => 'flash-message') html << content_tag(:script, "new Effect.Highlight('flash-message');") if options[:highlight] html << content_tag(:script, "$('flash-message').appear();") html << content_tag(:script, "setTimeout(\"$('flash-message').fade({duration: #{options[:fade]}});\", #{options[:display]*1000});") end
Nested Model Forms
For a good example of nested model forms check out the rails blog.
Expression
You can put some expression too. For example for I18n (using haml on view):
# some_locale.yml
links: contacts: "My contacts"
# index.html.haml
= link_to "#{t "links.contacts"}", :action => 'contacts'
Better autopad numbers
There is a much better way than to use diwadn’s method if you want to pad numbers with zeros. Here’s my recommended way to do it:
"Number: %010d" % 12345 #=> "Number: 0000012345"
It’s very easy. First we begin our placeholder with “%”, then we specify a zero (0) to signify padding with zeros. If we omitted this zero, the number would be padded with spaces instead. When we have done that, just specify the target length of the string. At last a single “d” is placed to signify that we are inserting a number.
Please see String#% and Kernel#sprintf for more information about how to do this.
Here’s another example of how to do it:
12345.to_s.rjust(10, "0") #=> "0000012345"
See String#rjust for more information.
Any of these methods are a lot better than the method outlined below.
Post =form post, NOT the http POST method
Post =forum post, NOT the http POST method
Note: any “Post” on this page has nothing to do with http methods. When I just looked at the collection_select code I was thrown off.
link_to_remote renders javascript as text
If you want to render an RJS file with link to remote you shouldn’t specify the :update function. Using this:
link_to_remote('add',:update=> 'someId', :url => {:controller => 'my_controller', :action => 'new'} ) # assume having 'my_controller/new.rjs'
will return something like
try { Element.update("someElement\n"); } catch (e) { alert('RJS error:\n\n' + e.toString()); alert('Element.update(\"someId\", \"item\\n\");'); throw e }
You should be specifying the id you are updating in the RJS file, not in link_to_remote. This will render correctly:
link_to_remote('add',:url => {:controller => 'my_controller', :action => 'new'} ) # assume having 'my_controller/new.rjs'
Only use :update when rendering html.
Comparing Date with Numeric in mixed sort
While:
Date#<=>(other)
can accept a Numeric object as other, the reverse is not true:
Numeric#<=>(other)
cannot accept a Date object as other.
So if you are sorting a list containing a mix of dates and numbers, you can get different results depending on the starting order!
a = Date.parse("2008-01-01") b = Date.parse("2009-10-22") c = Date.parse("2005-01-04") d = 0 [a,b,c,d].sort #=> [0, Tue, 04 Jan 2005, Tue, 01 Jan 2008, Thu, 22 Oct 2009] [b,c,d,a].sort #=> ArgumentError: comparison of Fixnum with Date failed
More Information
More information can be found at ActionController::Verification::ClassMethods
User a block to extend your associations
You can use blocks to extend your associations with extra methods.
code sample
has_many :children, :dependent => :destroy do def at(time) proxy_owner.children.find_with_deleted :all, :conditions => [ "created_at <= :time AND (deleted_at > :time OR deleted_at IS NULL)", { :time => time } ] end end Model.children.each # do stuff Model.children.at( 1.week.ago ).each # do old stuff
you must use ‘proxy_owner’ to link back to your model.
Autopad Numbers with Zeros (0s)
Here’s a handy code for padding 0s in a string. This is useful when you need to generate numbers for forms, such as invoices or orders. For example, you want to turn an invoice number 12345 to 0012345:
$ irb >> s = "0000000" => "0000000" >> num = "12345" => "12345" >> s.insert(-(num.to_s.length + 1), num.to_s)[0, s.length - num.to_s.length] if num.to_s.length <= s.length => "0012345"
Another fix
Another way around this problem, with code that already employs String.first, is to change the ActiveSupport definition as follows (in environment.rb)
module ActiveSupport module CoreExtensions module String module Access def first(limit = 1) chars.to_a[0..(limit - 1)].to_s end end end end end
Incompatible with Ruby 1.8.7
If using Rails < 2.2 with Ruby 1.8.7, calling truncate will result in the following error:
undefined method `length' for #<Enumerable::Enumerator:0xb74f952c>
The workaround (other than upgrading to Rails 2.2 or higher), is to overwrite the truncate method, by inserting the following at the end of environment.rb (or where it will be called on startup):
module ActionView module Helpers module TextHelper def truncate(text, length = 30, truncate_string = "...") if text.nil? then return end l = length - truncate_string.chars.to_a.size (text.chars.to_a.size > length ? text.chars.to_a[0...l].join + truncate_string : text).to_s end end end end
Selected parameter
batasrki’s note on “selected” parameter is only true for cases which “value_method” returns an int also.
The strictly correct requirement for it to work is:
object.value_method == selected
(“object” is the current object on the iteration over collection)
Since the params hash returns Strings, using it against a value_method with return type of int will never give a valid match (thus no auto-selection is done), i.e., “13” != 13.
When you’ll be using other types of value_method, like String, there’s no need to append “.to_i”, e.g:
options_from_collection_for_select(@posts, "slug", "title", params[:slug])
where “slug” is a String, it will work as expected (current selected post is auto-selected by default).
Use table_exists? in initializers
When using ActiveRecords in initializers, eg. for creating small constant data on startup, use table_exists? for those statements.
the initalizers are also called for migrations, etc, and if you are installing a new instance of the project these initializers will fail the migration to create those tables in the first place.
Can also be used to conditionally apply filters
For example:
# Skip login filter if the request is for CSS before_filter :require_login, :unless => lambda { |controller| controller.request.format.css? }
Calling request.format on the controller returns a Mime::Type object, which can then be queried for mime types, other examples:
controller.request.format.html? controller.request.format.json?