Notes posted to Ruby on Rails
RSS feedDon't cache it!
Don’t store a connection in a variable, because another thread might try to use it when it’s already checked back in into the connection pool. See: ActiveRecord::ConnectionAdapters::ConnectionPool
connection = ActiveRecord::Base.connection threads = (1..100).map do Thread.new do begin 10.times do connection.execute("SELECT SLEEP(1)") # WRONG ActiveRecord::Base.connection.execute("SELECT SLEEP(1)") # CORRECT end puts "success" rescue => e puts e.message end end end threads.each(&:join)
Looking for method "l" and "t"
translate # Lookup text translations localize # Localize Date and Time objects to local formats
These have the aliases #t and #l
Dont use _delete
Most blog articles about accepts_nested_attributes_for, including the one from @mattsa and @annaswims, tell you to add a
'_delete' => 1
when you want a deletion checkbox, hidden attribute, etc.
But this stopped being true a while ago. This is just a “Watch Out!” Make sure you use
'_destroy' => 1
instead.
:method => :delete, etc.
If you’re upgrading to Rails 3 you’ll need to make sure you include rails.js (which is in public/javascripts when you rails new someproject) You’ll need to include it after prototype. And you’ll need to have a 1.7 version of prototype.
When you do a
link_to "Delete", @some_obj, :method => "delete", :confirm => "Are you sure?"
Rails 3 will generate
<a href="some_obj/id" data-confirm="Are you sure?" data-method="delete">Delete</a>
rails.js will creates observers that convert that into a form.
Be aware that this probably won’t work as a link from inside a form (since forms in forms isn’t valid).
TimeZone in ActiveSupport
This seems to return ActiveSupport::TimeZone and not ::TimeZone as of v.3.0.0 and later.
Using namespaces
If you are using a namespace in your routes.rb, for example:
namespace :admin do resources :products end
then you can:
url_for([:admin, @product])
and:
url_for([:edit, :admin, @product])
How to deal with Missing host to link to!
You just need to define default_url_options[:host] in your class. The easiest way to do it:
class SomeClass include ActionController::UrlWriter default_url_options[:host] = YourApp::Application.config.action_mailer.default_url_options[:host] def some_method some_superb_url(maybe_even_some_variable_here) end end
Using namespaces
If you are using a namespace in your routes.rb, for example:
namespace :admin do resources :products end
then you can:
url_for([:admin, @product])
and:
url_for([:edit, :admin, @product])
Works with scoped too
It’s also available to use after scope chain too, like in any other AR action, for example:
User.where('age > 69').delete_all
Passing arguments to block
To pass arguments to block being captured, just list them as capture method params. I.e.
def export(exportable, export_klass, options={}, &block) result = "" #... if block_given? result += capture(my_custom_var_i_want_to_pass_to_block, &block) end result end
Then simply…
<%= export(@a, @b) do |my_custom_var| %> <% if my_custom_var.nil? %> My custom var is nil!!! <% end %> <% end %>
case-insensitive uniqueness
For case-insensitive uniqueness:
validate :username, :uniqueness => {:case_sensitive => false}
You can call it with an { :on => ~~~ } as the last argument
For example:
validate :must_be_friends, :on => :create
No type cast to Float
In Rails 3 the returned value will be type cast to the column’s type and not Float. So when calculating average on a column the column’s type need to be float, the result will be truncated otherwise.
Undocumented callbacks
Not sure why this isn’t documented… there are callbacks for before/after_add and before/after_remove. Example
has_many :things, :after_add => :set_things, :after_remove => :remove_things def set_things(thing) ... end def remove_things(thing) ... end
some gotchas
Works
named_scope :public, :conditions => "public = true"
Works
PUBLIC_CONDITIONS = "public = true" named_scope :public, :conditions => SomeModel::PUBLIC_CONDITIONS
Works
named_scope :public, lamba { {:conditions => SomeModel.public_conditions} } def self.public_conditions "public = true" end
Doesn’t work
named_scope :public, :conditions => SomeModel.public_conditions def self.public_conditions "public = true" end
If on Rails 3
If you’re on Rails 3, you should look into
http://apidock.com/rails/ActiveRecord/Relation/update_all
update_all (and delete_all) don't play nicely with default_scope
If you have
class Topic < ActiveRecord::Base default_scope :conditions => "forums.preferences > 1", :include => [:forum] end
and you do a
Topic.update_all(...)
it’ll fail with
Mysql::Error: Unknown column 'forums.preferences' in 'where clause'
The work around for this is:
Topic.send(:with_exclusive_scope) { Topic.update_all(...) }
You can monkey patch this using this code (and requiring it in environment.rb or else where)
module ActiveRecordMixins class ActiveRecord::Base def self.update_all!(*args) self.send(:with_exclusive_scope) { self.update_all(*args) } end def self.delete_all!(*args) self.send(:with_exclusive_scope) { self.delete_all(*args) } end end end end
Then just you update_all! or delete_all! when it has a default scope.
Dont reject! on the yielded batch
If you remove any values from the batch, the while loop in find_in_batches breaks even if there are additional batches:
People.count # => 3000 People.find_in_batches do |peeps| peeps.reject!(&:bad?) # ... more operations on peeps puts 'Tick' end
Running the above code, you’ll only see Tick once. Rather use:
People.find_in_batches do |peeps| peeps = peeps.reject(&:bad?) # ... more operations on peeps puts 'Tick' end
You should see Tick outputted 3 times
Use this for validatating nested forms
When you create a nested form, and want the main object to validate all nested models, you should make all the nested models dirty or validations will not run upon them.
class Order < ActiveRecord::Base has_many :order_lines accepts_nested_attributes_for :order_lines before_validation :mark_order_lines_as_changed private def mark_order_lines_as_changed order_lines.each(&:updated_at_will_change!) end end
Does not respond to ajax call
I inherited some code that used form_remote_tag. send_file and send_data did not work.
Changing from from_remote_tag to form_tag and all worked as expected.
you probably want:
or possibly:
ActionController::TestRequest.new (which has a demo of how to set env vars on a controller test)
Doesn't work for associations.
This method relies on #blank? to determine if the attribute is valid.
When you call #blank? on an ActiveRecord object, it returns true as long as there are no changes to the object.
So you can validate the base attribute (i.e.: product_id), but you’ll have no guarantee that it points to a valid record without your own validator.
Common AJAX options
See the documentation for link_to_remote to see the common AJAX options, like :before and :completed.