Notes posted to Ruby on Rails
RSS feedDoesn't output into STDOUT
Oddly enough it runs a rake task without any sort of output. To get around it you can simple substitute it with:
puts run('rake your_task')
Unless somebody has a better idea?
Looking for documentation of helper methods like: template, directory, file, etc?
Well, it’s not here. For Rails 3 they are part of Thor::Actions
If you need to pass a value
In the above example ‘value’ happens to be either true or false depending if the option was passed in or not. If you wish to capture an actual value you’ll want something like this:
def add_options!(opt) opt.on('-option=value') { |value| options[:option] = value } end
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
Method moved in 2.1 and again in 3.0 (beta)
It was moved from and back to Module#alias_method_chain
Re: IE GOTCHA
@insane-dreamer
That has nothing to do with IE. When you specify :cache => true you are saying that the files referenced should be saved to a file called all.js. When the script encounters the next line, it will overwrite the same file with the new contents.
Caching is not compressing, it doesn’t make sense to do with individual files, but it can make sense some times. I someone wants to do it, just specify a name for the cached file:
javascript_include_tag 'layout', 'typography', :cache => 'base' javascript_include_tag 'admin/layout', 'admin/extras', :cache => 'admin'
Separator default is not always "." but depends on locale
Locale en:
number_with_precision(111.2345) # => 111.235
Locale fr-FR:
number_with_precision(111.2345) # => 111,235
Same with delimiter.
Destroying Data
As far as I can tell, at least on a migration of a column from an integer to a decimal, this does not get rid of existing data.
Rails v2.1.0 has built-in time-zone support
Rails versions as of 2.1.0 have basic timezone support built-in to the Time object. However, to get a list of all the timezones you need to install the tzinfo gem.
TZInfo::Country.all.sort_by { |c| c.name }.each do |c| puts c.name # E.g. Norway c.zones.each do |z| puts "\t#{z.friendly_identifier(true)} (#{z.identifier})" # E.g. Oslo (Europe/Oslo) end end
TZInfo::TimeZone.get(identifier) returns a TimeZone -object by the identifier.
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.
Takes array
Like assert_difference this method can take an array of expressions to evaluate all of them. For example:
assert_no_difference ['Publisher.count', 'User.count', 'Membership.count'] do post :create end
It creates an assertion for each item in the array. So this will add three assertions to your test.
authenticity_token
<div style=“margin:0;padding:0”>
<input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
</div>
Helper generates a div element with a hidden input inside. This is a security feature of Rails called cross-site request forgery protection and form helpers generate it for every form whose action is not “get”.
Accessing controller data
You can access controller attributes from views via the @controller variable.
It has some important attributes:
-
@controller.controller_name -> the name of the controller
-
@controller.request -> returns the ActionController::Request
-
@controller.request.method -> the request method ( get, post, put, delete )
-
@controller.request.host -> the request host ( ip address or hostname ) where your server runs
-
@controller.request.ip -> the ip where your browser runs
-
how I use it
def rescue_action_in_public(exception)
case exception when ActiveRecord::RecordNotFound, ActionController::UnknownAction, ActionController::RoutingError redirect_to errors_path(404), :status=>301 else redirect_to errors_path(500) end
end
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
Careful when updating foreign key directly
Seems when you change key directly it doesn’t update association automatically.
>> chicken = Chicken.first >> chicken.head => old_head >> chicken.head_id = new_head.id >> chicken.head => old_head
Easy (stupid?) way to fix it:
class Chicken def head_id=(value) self.head = Head.find_by_id(value) end end
Positioning the column. MySQL only
Add support for MySQL column positioning via #add_column and #change_column
add_column and change_column in the MySQL adapter now accept some additional options:
:first => true # Put the column in front of all the columns
:after => column_name # Put the column after ‘column_name’
class AddLastNameToUsers < ActiveRecord::Migration def self.up add_column :users, :last_name, :after => :first_name end def self.down remove_column :users, :last_name end end
or
class AddIdToUsers < ActiveRecord::Migration def self.up add_column :urers, :id, :first => true end def self.down remove_column :users, :id end end
No security
One important thing to remember is that this is NOT hidden in the source code and can be modified by an evil user so all input in a hidden field should be considered as untrustworthy and checked just like a visible field.
database exceptions will still be raised
Note that save() only returns false on validation errors (when valid? returns false). If other errors occur at the database level, like a database deadlock or trying to insert null into a column that doesn’t allow it, that will still raise an exception.
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 -%>
Use :path_prefix for the namespace
Resources are added after the :path_prefix. However if you use a :path_prefix on a resource, it overrides the namespace path instead of appending to it (as I think it should).
Here is what I wrote to create a versioned API access path.
map.namespace :api3, :path_prefix=>"/api/v3" do |api| api.resources :posts api.resources :comments, :path_prefix=>"/api/v3/post/:post_id" end
This will create routes like
path: /api/v3/posts/1 named_route: api3_post() controller=>"api3/posts"
Prevent transactional fixtures for a specific test class
If you want to prevent a specific group of tests from being run inside a transaction, just define inside your test class the methods teardown_fixtures and setup_fixtures with empty bodies.
Prevent transactional fixtures for a specific suite
If you want to prevent a specific group of tests from being run inside a transaction, just define inside your test class the methods teardown_fixtures and setup_fixtures with empty bodies.
How using Array methods
It’s not possible to use Array methods with a scope because it’s not an Array but an ActiveRecord::NamedScope::Scope :
this
Article.promotion.sum(&:price)
doesn’t run.
But you can use the to_a method to transform the ActiveRecord::NamedScope::Scope to an Array :
Article.promotion.to_a.sum(&:price)
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
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.
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.