Good notes posted to Ruby on Rails
RSS feedPassing 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}
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
Disable STI
I had to add “self.inheritance_column” as opposed to simply “inheritance_column” to get this to work.
Code example
class MyModel < ActiveRecord::Base # disable STI self.inheritance_column = :_type_disabled end
complex conditions
If you need add complex conditions you can use this:
Model.where(:foo => 'bar').where(:attr => 1).update_all("author = 'David'")
helpers using options for select
You can now add html options to select options by creating a container with items like the following:
Code Example
['display','value',:class => 'option_class']
This will produce:
Code Example
<option value="value" class="option_class">display</option>
link to nothing if link_to_if condition is false
When the link_to_if condition is false you can get this helper to display nothing by doing something like this
<%= link_to_if(message.user, 'Poster', message.user){} %>
^ That will display nothing if message.user does not exist.
Polymorphic has_many within inherited class gotcha
Given I have following classes
class User < ActiveRecord::Base end class ::User::Agent < ::User has_many :leads, :as => :creator end
I would expect, that running
User::Agent.first.leads
will result in following query
SELECT "leads".* FROM "leads" WHERE ("leads".creator_id = 6 AND "leads".creator_type = 'User::Agent')
however it results in
SELECT "leads".* FROM "leads" WHERE ("leads".creator_id = 6 AND "leads".creator_type = 'User')
Possible solutions:
-
Make User class use STI - polymorphic relations will then retrieve correct class from :type field (however in my situation it was not an option)
-
If You do never instantiate User class itself, mark it as abstract
class User < ActiveRecord::Base self.abstract_class = true end
-
If You do instantiate User class, as last resort You can overwrite base_class for User::Agent
class ::User::Agent < ::User has_many :leads, :as => :creator def self.base_class self end end
-
If none of above is an option and You do not care that You will lose some of relation’s features, You can always
class User::Agent < ::User has_many :leads, :as => :creator, :finder_sql => %q(SELECT "leads".* FROM "leads" WHERE ("leads".creator_id = #{id} AND "leads".creator_type = 'User::Agent')) end
null to no effects
change_column will not query to replace the null values when you change null to false, even if you have a default set. This may cause the query to fail (may depend on the database used).
change_column_null will optionally take a value to replace nulls if you are setting null to false. If you want to set a default and disallow nulls you likely can’t do both in one change_column call.
use raw() instead
Don’t use this method unless you’re sure your string isn’t nil. Instead use the raw() method, which wont raise an exception on nil.
needs to be paired with respond_to
Needs to be paired with respond_to at the top of your class.
class MyController < ApplicationController respond_to :js, :html
Sending array parameters
Another technique to use when you need to send an array parameter is pass in the :multiple option.
check_box("puppy", "commands", {:multiple => true}, "sit", nil) check_box("puppy", "commands", {:multiple => true}, "fetch", nil) check_box("puppy", "commands", {:multiple => true}, "roll_over", nil)
If all checkboxes are checked, the paramters will be:
"puppy" => {"commands" => ["sit", "fetch", "roll_over"]}
NOTE: because of the gotcha, the hidden fields will be inserted and any unchecked boxes will be sent as “” (empty string). You will need to filter those values out in your model:
class Dog < ActiveRecord::Base def commands=(commands) commands.reject(&:blank?) end end
Undocumented :inverse_of option
Support for the :inverse_of option was backported to 2.3.6+.
Here’s the description from the original commit: http://github.com/rails/rails/commit/ccea98389abbf150b886c9f964b1def47f00f237
You can now add an :inverse_of option to has_one, has_many and belongs_to associations. This is best described with an example:
class Man < ActiveRecord::Base has_one :face, :inverse_of => :man end class Face < ActiveRecord::Base belongs_to :man, :inverse_of => :face end m = Man.first f = m.face
Without :inverse_of m and f.man would be different instances of the same object (f.man being pulled from the database again). With these new :inverse_of options m and f.man are the same in memory instance.
Currently :inverse_of supports has_one and has_many (but not the :through variants) associations. It also supplies inverse support for belongs_to associations where the inverse is a has_one and it’s not a polymorphic.
Undocumented :inverse_of option
Support for the :inverse_of option was backported to 2.3.6+.
Here’s the description from the original commit: http://github.com/rails/rails/commit/ccea98389abbf150b886c9f964b1def47f00f237
You can now add an :inverse_of option to has_one, has_many and belongs_to associations. This is best described with an example:
class Man < ActiveRecord::Base has_one :face, :inverse_of => :man end class Face < ActiveRecord::Base belongs_to :man, :inverse_of => :face end m = Man.first f = m.face
Without :inverse_of m and f.man would be different instances of the same object (f.man being pulled from the database again). With these new :inverse_of options m and f.man are the same in memory instance.
Currently :inverse_of supports has_one and has_many (but not the :through variants) associations. It also supplies inverse support for belongs_to associations where the inverse is a has_one and it’s not a polymorphic.
Undocumented :inverse_of option
Support for the :inverse_of option was backported to 2.3.6+.
Here’s the description from the original commit: http://github.com/rails/rails/commit/ccea98389abbf150b886c9f964b1def47f00f237
You can now add an :inverse_of option to has_one, has_many and belongs_to associations. This is best described with an example:
class Man < ActiveRecord::Base has_one :face, :inverse_of => :man end class Face < ActiveRecord::Base belongs_to :man, :inverse_of => :face end m = Man.first f = m.face
Without :inverse_of m and f.man would be different instances of the same object (f.man being pulled from the database again). With these new :inverse_of options m and f.man are the same in memory instance.
Currently :inverse_of supports has_one and has_many (but not the :through variants) associations. It also supplies inverse support for belongs_to associations where the inverse is a has_one and it’s not a polymorphic.
Validating presence of parent in child
When creating a parent and its children using nested attributes, you can use the :inverse_of option on the association to correctly set the parent back references:
class Parent < ActiveRecord::Base has_many :children, :inverse_of => :parent accepts_nested_attributes_for :children end class Child < ActiveRecord::Base belongs_to :parent validates_presence_of :parent end
add_to_base in Rails 3
use
model_instance.errors[:base] << "Msg"
instead of depracated
model_instance.errors.add_to_base("Msg")
for Rails 3
In Rails3 use "unscoped" instead
The with_exclusive_scope examples no longer work in Rails3 because with_exclusive_scope is now a protected method which can and should not be used in a controller. Use the new unscoped method instead:
Article.unscoped
For mor details and examples have a look at: http://github.com/rails/rails/commit/bd1666ad1de88598ed6f04ceffb8488a77be4385.
Moved in Rails 3
In Rails 3, this has moved to ActionDispatch::TestProcess
(Which means, if you want to use it in a test, you need to add the following to test_helper.rb:)
include ActionDispatch::TestProcess
When scripts don't end in .js
For example, Google Custom Search’s URL is http://www.google.com/jsapi
It’s an ugly hack, but works:
= javascript_include_tag('http://www.google.com/jsapi').sub('.js', '')
to set NULL => NO
use :null => false
change_column :my_table, :my_column, :integer, :default => 0, :null => false
uniqueness
You can scope uniqueness as well
validates :user_name, :presence => true, :uniqueness => {:scope => :account_id} # the old way validates_uniqueness_of :user_name, :scope => :account_id
Common use
I typically use require_dependency when developing a class or module that resides in my rails app, perhaps in the lib/ dir. A normal require statement does not reload my changes, so I use require_dependency in files that reference my newly developed class or module.
multiple attributes with the same validations
You can list multiple attributes if they share the same validations
validates :title, :body, :presence => true
sending the attributes as an array will return an error
validates [:title, :body], :presence => true #=> ArgumentError: Attribute names must be symbols
Naming fragment cache
One of the common ways of using fragment caching is to cache content that’s shared across the site (eg. left navigation, menus, widgets etc.) that looks and works the same regardless of the name of the action or controller calling it. In such cases it’s very easy to just use named fragment caching eg.:
<% cache('left_nav') do -%> <%= display_left_nav -%> <% end -%>
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.
Can be used with has_many associations
You can also use this to validate that a has_many association has a specified number of records on the other end:
has_many :members validates_length_of :members, :minimum => 1
Available statuses
All the available statuses (extracted from SYMBOL_TO_STATUS_CODE hash) in a slightly more readable form:
:continue => 100 :switching_protocols => 101 :processing => 102 :ok => 200 :created => 201 :accepted => 202 :non_authoritative_information => 203 :no_content => 204 :reset_content => 205 :partial_content => 206 :multi_status => 207 :im_used => 226 :multiple_choices => 300 :moved_permanently => 301 :found => 302 :see_other => 303 :not_modified => 304 :use_proxy => 305 :temporary_redirect => 307 :bad_request => 400 :unauthorized => 401 :payment_required => 402 :forbidden => 403 :not_found => 404 :method_not_allowed => 405 :not_acceptable => 406 :proxy_authentication_required => 407 :request_timeout => 408 :conflict => 409 :gone => 410 :length_required => 411 :precondition_failed => 412 :request_entity_too_large => 413 :request_uri_too_long => 414 :unsupported_media_type => 415 :requested_range_not_satisfiable => 416 :expectation_failed => 417 :unprocessable_entity => 422 :locked => 423 :failed_dependency => 424 :upgrade_required => 426 :internal_server_error => 500 :not_implemented => 501 :bad_gateway => 502 :service_unavailable => 503 :gateway_timeout => 504 :http_version_not_supported => 505 :insufficient_storage => 507 :not_extended => 510
Re: Caveat when using dynamic layouts
Since there’s no way to edit posts on here, I need to correct myself and say that what I posted before doesn’t work, since you can’t specify layout multiple times:
class OrdersController < BaseController layout :determine_layout, :only => :new layout "public", :except => :new # ... end
So don’t do that. The only way to ensure that the other actions get the default theme is to drop :only/:except and do the conditions yourself:
class OrdersController < BaseController layout :determine_layout private def determine_layout %w(new).include?(action_name) ? "some_layout" : "public" end end
All this to say, beware of :only/:except – they aren’t as useful as you think they are.
Caveat when using dynamic layouts
Worth noting that if you have a controller which inherits from another controller which has a layout, and in this child controller you’re determining the layout at runtime using a method for specific actions, the other actions you are excluding will not inherit the layout from the parent controller.
For example, if you’ve got this
class BaseController < ApplicationController layout "public" end class OrdersController < BaseController layout :determine_layout, :only => :new # index, show, new, create, edit, update, destroy ... end
then OrdersController#index, #show, and #edit won’t get the “public” layout – in fact they won’t get a layout at all. So you’ll need to do this instead:
class OrdersController < BaseController layout :determine_layout, :only => :new layout "public", :except => :new # ... end