Notes posted to Ruby on Rails
RSS feedTo verify if the element exists before replacing.
Just add this code into a initializer file.
Atention: The code starts at the “module ActionView” and the last “end” has to be copied too.
module ActionView
module Helpers module PrototypeHelper class JavaScriptGenerator #:nodoc: module GeneratorMethods def replace_html_if_exists(id, *options_for_render) call "if($('#{id}')) Element.update", id, render(*options_for_render) end end end end end
end
Be careful with name of attribute writer
If restricting access to attributes you normally get code like
attr_accessible :foo,
When using these nested attributes you end up with code like
attr_accessible :foo, :bar_attributes
Its very easy to leave of the _attributes suffix e.g
attr_accessible :foo, :bar
which will cause you all sorts of problems
:selected
If you want some object to be selected by default, be sure to use its id, not the whole object.
collection_select(:post, :author_id, Author.all, :id, :name_with_initial, {:selected => current_book.authors.map(&:id)}) #=> :selected => [1,2,3,4]
and not
collection_select(:post, :author_id, Author.all, :id, :name_with_initial, {:selected => current_book.authors})
Returns a copy of the attribute contents
As szeryf notes, this is a really expensive method, but another important remark is that the contents returned are a copy of the actual values.
model.attributes['name'] # => 'Joe' model.attributes['name'] = 'Jim' model.attributes['name'] # => 'Joe' still model.name # => 'Joe'
This has the potential to be confusing as you’re given the impression you have direct access to the attributes.
Validate() is run always before one of the more specific validation methods
I did not see this mentioned explicitly anywhere.
The method validate is run always before a call to validate_on_create or validate_on_update is made.
Example:
class Foo < ActiveRecord::Base def validate puts 'In validate' end def validate_on_create puts 'In validate_on_create' end def validate_on_update puts 'In validate_on_update' end end
Now, when creating a new Foo using script/console, the output is as follows:
In validate In validate_on_create
and when updating a Foo, the output looks like:
In validate In validate_on_update
Optional local assigns
When you have a partial with optional local assigns, for instance:
<%= render :partial => 'articles/preview' %> <%= render :partial => 'articles/preview', :locals => { :show_call_out => true } %>
And you don’t want the partial to break when the local isn’t assigned, you can reference it through the local_assigns local variable instead of through the template binding:
<% if local_assigns[:show_call_out] %> <em><%= format @article.call_out %></em> <% end %>
Usage example
class Aa
class_inheritable_accessor :test end => [:test] Aa.test = 10 => 10 Aa.test => 10 Bb = Class.new(Aa) => Bb Bb.test => 10 Bb.test = 5 => 5 Bb.test => 5 Aa.test => 10
Re: How to test different responses
In addition to using:
@request.accept = "text/javascript" #=> request JS
as rubymaverick and nachocab suggested, you can also pass :format when calling your action, eg:
it "GET #most_viewed renders #most_viewed.js.rjs template if js requested" do get :most_viewed, :format => 'js' response.should render_template('most_viewed') end
Not really deprecated
This isn’t really deprecated, it’s just relocated to ActiveRecord::AttributeMethods#read_attribute
Important note
It has been said that “it can be compared to, but isn’t the same thing as”:
class Bar class << self attr_accessor :greeting end end
Which is true. However, they are “inherited” isn’t exactly the case. Rather, cattr_accessor uses class variables.
The problem with class variables in Ruby, is that a class variable is the same object across all subclasses of a class. Consider the following example of what happens with cattr_accessor:
class A @@foo = 'foo' def self.foo @@foo end end p A.foo # => "foo" class B < A end p B.foo # => "foo" class B @@foo = 'bar' end p B.foo # => "bar"
So far so good you might think. However, something you might not have expected is that the variable has now also changed in class A:
p A.foo # => "bar"
This is in my opinion almost never what you’d want. More probable is that you’d want the individual class instance to have an accessor. (Remember classes are objects in Ruby). I do the following in regular Ruby:
class A class << self attr_accessor :foo end self.foo = 'foo' end p A.foo # => "foo" class B < A end p B.foo # => nil class B self.foo = 'bar' end p B.foo # => "bar" p A.foo # => "foo"
As you can see, this returns nil when a value hasn’t explicitly been set yet on the new class instance. If you’d like to have inheritance without messing with the superclasses variables, have a look at ActiveSupport’s class_inheritable_accessor, which does the same as I just explained, but creates a clone of the object and assigns it to the subclass whenever a class is inherited.
What I’d normally do in Ruby to fix the issue of it returning nil is to create the accessor manually and have it set the instance variable to the default if it’s nil:
class A class << self def foo @foo ||= 'foo' end end end class B < A end p B.foo # => nil
So to recap:
-
cattr_accessor uses class variables (@@foo), in which case the object is shared across all subclasses of a class. Use it mainly for static data, in which case you’d probably best use a constant.
-
class_inheritable_accessor (or what I showed) uses instance variables (@foo) at the Class instance level. These variables are not shared across all subclasses.
Expensive method!
This method builds the a new hash every time it’s called, so be cautious not to use it in loops etc.
Skipping validation
Unlike the save method, you can’t pass false to update_attributes to tell it to skip validation. Should you wish to do this (consider carefully if this is wise) update the attributes explicitly then call save and pass false:
@model_name.attributes = params[:model_name] @model_name.save false
:discard_month implicitly sets :discard_day to true
This may not be the behaviour that you want, and setting :discard_day => false doesn’t change this. One way of getting around this is to hide the month field using CSS e.g.
#some_date_field_2i { display:none; }
If you use the :default option for the date_select, the correct default month will be passed through to the controller. Using this with :discard_year will give you a dropdown with only the day, but preserve the month and year as provided by :default.
Requirements for extra parameters not working
nm. just delete this entry
cattr_accessor_with_default
Class attribute assessors are neat if you want to set up modifiable constant-like varibles. This is how you’d normally set it up:
module MyPlugin class Conf @@awesome_level = 'huge' cattr_accessor :awesome_level end end
Then you can call and modify it like this:
>> MyPlugin::Conf.awesome_level => 'huge' >> MyPlugin::Conf.awesome_level = 'massive' >> MyPlugin::Conf.awesome_level => 'massive'
If you have a pile of those accessors I’d do something like this (there might be a better way, but it works):
module MyPlugin class Conf def self.cattr_accessor_with_default(name, value = nil) cattr_accessor name self.send("#{name}=", value) if value end cattr_accessor_with_default :awesome_level, 'huge' cattr_accessor_with_default :speed_level, 'insane' cattr_accessor_with_default :indifferent_level cattr_accessor_with_default :craziness_level, 'nuts' end end
This way you declare accessor and it’s optional default value on the same line
Conditions work for lower-level validate methods too
I don’t think this is mentioned in the docs anywhere, or else I couldn’t find it: Because validate, validate_on_create, and validate_on_update are ActiveSupport::Callbacks, their symbol forms support conditions just like validates_presence_of and company:
validate :permaname_must_be_unique, :if => :normal_entry? validate_on_create :posted_at_must_be_valid_timestamp, :unless => Proc.new {|e| e.posted_at.nil? } validate_on_update :title_must_not_contain_apostrophes, :if => :title_starts_with_a_b?
Not really helpful
When you’re trying to construct a specialized path name for a partial based on a record type you’re probably better off writing your own helper.
def topic_partial_path(topic) ['admin', topic.class.table_name, "#{topic.class.table_name.singularize}_as_topic"].join('/') end
Keeping the flash object on multiple redirects
If your controllers are redirecting more than once, the flash contents will be lost. To avoid it, execute flash.keep before each redirection.
Check ActionController::Flash::FlashHash for more handy methods (discard, now, …)
How to use with exclusive scope
Code example
Article.with_exclusive_scope { find(:all) } #=> "SELECT * FROM 'articles'
from http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping
Have the check_box checked by default
To have the check box checked by default, pass either :checked => true or :checked => 'checked' in the options. See ActionView::Helpers::InstanceTag#to_check_box_tag for details.
This is ON by default in :has_many
When defining a has_many relationship this behaviour is on by default. See has_many documentation, look for the :validate flag.
add index with :quiet=>true option for indices that are possibly already added
# Allows you to specify indices to add in a migration that will only be created if they do not # already exist, or to remove indices only if they already exist with :quiet=>true module ActiveRecord::ConnectionAdapters::SchemaStatements
def add_index_with_quiet(table_name, column_names, options = {}) quiet = options.delete(:quiet) add_index_without_quiet table_name, column_names, options rescue raise unless quiet and $!.message =~ /^Mysql::Error: Duplicate key name/i puts "Failed to create index #{table_name} #{column_names.inspect} #{options.inspect}" end alias_method_chain :add_index, :quiet def remove_index_with_quiet(table_name, column_names, options = {}) quiet = options.delete(:quiet) raise "no options allowed for remove_index, except quiet with this hack #{__FILE__}:#{__LINE__}" unless options.empty? remove_index_without_quiet table_name, column_names rescue raise unless quiet and $!.message =~ /^Mysql::Error: Can't DROP/i puts "Failed to drop index #{table_name} #{column_names.inspect}" end alias_method_chain :remove_index, :quiet
end
A catch-all format
If you’d like to specify a respond_to only for 1 or a few formats and render something else for all other formats, eg: (action.rss returns a feed but action.html or action.js should just render 404), use format.all:
respond_to do |format| format.rss { render_rss } format.all { render_404 } end
Rails will render an empty string for all formats that don’t specify a response explicitly.
Security hole in 2.3.2
This method has a security hole in Rails 2.3.2. See http://weblog.rubyonrails.org/2009/6/3/security-problem-with-authenticate_with_http_digest for explanation.
Rails 2.3.3 should fix the problem.
ActiveRecord::RecordNotSaved can be triggered by accidental false return values in callbacks
You may have this exception raised if any of the defined callbacks such as ActiveRecord::Base#before_save or ActiveRecord::Base#before_create return false.
This can happen accidentally. For example:
class MyModel < ActiveRecord::Base before_save :assign_default_foo protected def assign_default_foo self.foo = false end end
Since assign_default_foo leaves a false value on the stack, the model will not be saved. A way around this is to simply leave nil or an empty return instead:
class MyModel < ActiveRecord::Base before_save :assign_default_foo protected def assign_default_foo self.foo = false nil end end
Writing and reading a cookie in the same request.
As of 0349278f3da9f7f532330cf295eed35ede3bae66 cookie updates will persist in the current request.