Recent notes
RSS feedAdd has_keys? method to Hash class
class Hash
def has_keys?(*_keys) (_keys - self.keys).empty? end
end
h = {1=>‘a’,2=>‘b’}
h.has_keys?(1,2) #-> true
h.has_keys?(1,3) #-> false
You must use the yielded object
A warning note (at least for v2.3.4): if you don’t use the yielded format object, you will get a cryptic error like this:
NoMethodError (You have a nil object when you didn't expect it! The error occurred while evaluating nil.call): app/controllers/comments_controller.rb:11:in `create'
So make sure you use it!
No Layout, other options
While it renders to the same rules as render, you need to specify params.
You’d think this would work:
render_to_string "users/profile", :layout => false
You need to do this instead
render_to_string(:layout => "users/profile", :layout => false)
has_and_belongs_to_many_with_deferred_save
Be aware that has_and_belongs_to_many saves association to join table immediately after assign. It does NOT wait for my_object.save. Hence if save does not get through validations (or fail for any other reason), associated records will still be in the database.
Here is a nice workaround: http://github.com/TylerRick/has_and_belongs_to_many_with_deferred_save
Avoiding the "multiple values for a block parameter" warning
As pointed out below, you can also have optional parameters. But you will get something like “warning: multiple values for a block parameter (0 for 1)” if you omit them.
You can avoid those warnings by passing *args and picking the parameters yourself:
define_method :that_method do |*args|
foo = args[0] || 'my default' # ... end
Now the warning will be gone. Just make sure you fetch your parameters from *args and assign a default value (unless you want them to default to nil).
Be careful about ActiveRecord::Base.include_root_in_json
If you have set ActiveRecord::Base.include_root_in_json = true, then when you do .to_json on an object, the output will begin with the class name rather than just a hash of attributes.
from the rails docs
konata = User.find(1) ActiveRecord::Base.include_root_in_json = true konata.to_json => { "user": {"id": 1, "name": "Konata Izumi", "age": 16, "created_at": "2006/08/01", "awesome": true} }
if you try to pass this output as the argument to from_json, things will blow up.
User.new.from_json(konata.to_json[“user”]) will just pass in the attribute hash and will work.
Also, note that if you’ve use attr_accessible to limit mass assignment, you’ll have problems if you try pass in attributes that are not allowed to be mass assigned.
Changes self
This method changes the object/array the method is called on. For example:
a = ["a", "b", "c"] b = ["x", "y", "z"] a.concat(b) #=> [a", "b", "c", "z", "y", "z"] a #=> [a", "b", "c", "z", "y", "z"]
In this example the object A is modified, the method modifies the object, then returns the new object.
smtp syntax error 555 5.5.2
If You’re seeing a Net::SMTPFatalError (555 5.5.2 Syntax error ...) than You should check the email’s from header ! You probably have brackets while calling the from attribute setter :
Works in Rails < 2.3.3
def signup_notification(recipient) recipients recipient.email_address_with_name subject "New account information" from %("My App" <no-reply@myapp.com>) end
Works in Rails 2.3.5
def signup_notification(recipient) recipients recipient.email_address_with_name subject "New account information" from 'no-reply@myapp.com' # no <> brackets ! end
in Rails 2.3.3 the from email address will get wrapped with angle brackets, thus it must not have them within the address.
Avoid DoubleRenderError
One can not invoke render twice during an action. Thus if You have a complicated rendering logic but at the end would like to render some default content, or just would like to find out whether render has been called during the current action, use performed?. This also works with “empty” renderings such as head.
Rendering After Exception In respond_to() Block
Remember, format blocks set the response’s content type. This can present problems when handling errors.
class MediaController rescue_from ActionController::MissingFile do |e| # User's browser probably wont display this # Content-Type is application/x-shockwave-flash render :file => File.join(Rails.public_path, '404.html'), :status => 404 end # show details or stream video def show @media = Media.find params[:id] respond_to do |format| format.html format.flv { send_file @media.path, :disposition => 'inline' } end end end
For these situations you must set :content_type when calling render:
render :file => File.join(Rails.public_path, '404.html'), :status => 404, :content_type => 'text/html'
Dynamic exists? methods
There are no dynamic exists? methods analogous to dynamic finders, which means that while you can do this:
Person.find_by_name('David')
you can’t do this:
Person.exists_by_name('David') # DOES NOT WORK
nor this:
Person.exists_by_name?('David') # DOES NOT WORK
However, you can simulate this with dynamic scope:
Person.scoped_by_name('David').exists?
You’ll have to admit that this is so much better than the plain old method:
Person.exists?(:name => "David")
nil Argument Raises An I18n::ArgumentError
You might want to do this:
module ActionView module Helpers module TranslationHelper def localize(*args) #Avoid I18n::ArgumentError for nil values I18n.localize(*args) unless args.first.nil? end # l() still points at old definition alias l localize end end
end
Mode Flags
RDONLY, TRUNC, etc… are defined in the File::Constants module which is include'd by IO and File.
IO.open fd, IO::RDONLY File.open path, File::RDONLY
Though as pointed out above, they are interchangeable.
Errors Raised
Non IO errors (IOError) are contained in the Errno module. They are the same as those given in open(2), see:
http://www.kernel.org/doc/man-pages/online/pages/man2/open.2.html#ERRORS
Common Errors
-
Errno::ENOENT: No such file or directory
-
Errno::EACCES: Permission denied
-
Errno::EEXIST: File exists (i.e. IO::EXCL | IO::CREAT)
Does not work with polymorphic relations
If you have polymorphic relations, e.g.:
class Bookmark < ActiveRecord::Base belongs_to :thing, :polymorphic => true belongs_to :owner, :polymorphic => true end
and you want to ensure that a thing can bookmarked by an owner at most once, you can’t do this:
validates_uniqueness_of :thing, :scope => :owner
Instead, you must use the real column names, e.g.:
validates_uniqueness_of :thing_id, :scope => [:thing_type, :owner_id, :owner_type]
Also behaves like File#expand_path
You can also use URI.join to resolve relative and absolute links:
URI.join('http://example.com/', '/example').to_s # => "http://example.com/example" URI.join('http://example.com/example', 'test').to_s # => "http://example.com/test" URI.join('http://example.com/example/', 'test').to_s # => "http://example.com/example/test" URI.join('http://example.com/example/foo', '../css').to_s # => "http://example.com/css"
Require file from the same folder
If you want to require file from the same folder, the simplest way is
require File.expand_path('../file-to-require', __FILE__)
If your file is /lib/book.rb
File.expand_path('../page', '/lib/book.rb') => '/lib/page.rb'
The :method goes in the :html option
When using a restful form helper and you want to use a method other than POST, remember to put the :method in the :html option.
e.g. To send a DELETE request instead of the usual POST (with a nested resource thrown in for good measure) use:
<% form_for [@post, @comment], :html => { :method => :delete } do |f| -%>
Bad example
Note that it would be better to avoid the alias_method line in the example and just call super.
:autosave => false vs. :autosave => nil
The documentation above mentions that :autosave => true always saves the association and that it’s “off” by default. What it doesn’t mention what they mean by “off”.
-
:autosave => nil (the default “off” behavior) will still autosave the association if it has changed or is a new record.
-
:autosave => false seems to prevent autosaving of the association, even if it has changed.
I’ve found :autosave => false to be useful behavior when trying to prevent cyclical dependency loops; there are likely other useful use cases out there.
Careful with this method.
Despite the name and description, it will actually update any changed fields on the model rather than just the desired attribute.
def update_attribute(name, value) send(name.to_s + '=', value) save(false) end
See? Use update_all and pass in the model ID as a condition, instead.
Fetching records when column is set to nil or false
If you want to fetch all records when one column (boolean) is set to nil or false, try this:
Project.all(:conditions => "archived IS NULL OR archived = 'f'")
Doesn't return nil on empty array when param is given
This does not return nil if the array is empty and n is given.
[].shift(2) # => [] a = [] a.shift(2) # => [] a # => []
Always gracefully degrade if JS isn't available
If you always want to degrade when JS isn’t available you can add something like to environment.rb
module ActionView module Helpers module PrototypeHelper def link_to_remote(name, options = {}, html_options = nil) html_options ||= {} html_options[:href] ||= options[:url] link_to_function(name, remote_function(options), html_options || options.delete(:html)) end end end end
Interpolating
Note that to interpolate, the sequences must be inside single quotes:
# replace /ll/ with itself 'hello'.gsub(/ll/, '\0') # returns 'hello' 'hello'.gsub(/ll/, "\0") # returns 'he\000o'
Wrong example
In the authentication filter example above, the time condition should be reversed: we only want to find the user if time is still in the future (because it’s the valid-until time).
So the example should look like this:
id, time = @verifier.verify(cookies[:remember_me]) if time > Time.now self.current_user = User.find(id) end
at_exit in sintra
in main.rb of sinatra:
at_exit { Application.run! if $!.nil? && Application.run? }
How to test callback methods
When testing callback methods, try to test the callback chain separate from its implementation.
Say this is your model:
class Project belongs_to :owner has_many :milestones after_save :create_milestones after_save :notify_owner private def notify_owner owner.project_created! end def create_milestones milestones.create(:name => 'Milestone 1') end end
You should write your spec like this:
describe Project do describe 'create_milestones' do it 'should create an initial milestone' do project = Project.new project.milestones.should_receive(:create) project.send(:create_milestones) end end describe 'notify_owner' do it 'should notify its owner' do project = Project.new(:owner => mock_model(User)) project.owner.should_receive(:project_created!) project.send(:notify_owner) end end describe 'after_save' do it 'should run the proper callbacks' do project = Project.new project.should_receive(:create_milestones) project.should_receive(:notify_owner) project.run_callbacks(:after_save) end end end
Here is some more advice on how to test callback methods in Rails:
http://gem-session.com/2010/03/how-to-test-callback-methods-in-rails
Using models in your migration
Here is some advice how to call your models in a migration without shooting yourself in the foot:
http://gem-session.com/2010/03/how-to-use-models-in-your-migrations-without-killing-kittens
Basically you can inline models into your migrations to decouple them from changes in your model:
class AddCurrentToVendor < ActiveRecord::Migration class Vendor < ActiveRecord::Base end class Article < ActiveRecord::Base has_many :vendors, :class_name => 'AddCurrentToVendor::Vendor', :order => 'created_at' end def self.up add_column :vendors, :current, :boolean Article.all.each do |article| article.vendors.first.andand.update_attribute(:current, true) end end def self.down remove_column :vendors, :current end end