Recent notes
RSS feedDifferent Method for Subdomains
@james
You can also access the subdomain via the subdomains array.
request.subdomains.first
Writing and reading a cookie in the same request.
Note that when setting the value of a cookie, the new value won’t be accessible until the next page request. In this way, the cookies hash cannot be used in the same manner as the session hash.
For example:
# given that cookies[:temporary_data] was set to 'foo' in the previous request cookies[:temporary_data] = 'bar' p cookies[:temporary_data] # => 'foo'
Scoped using - more simple way
Regarding to the example from james, there is a more simple way to do this:
user.messages.update_all(:read => true)
Bug that causes escape buildup
There is a bug in this meethod that causes an escape build up when you have links or image urls for example with ampersands in them. Over time, it goes something like this:
& -> & -> & -> & -> & -> etc
This breaks the url so links and images are not clickable/viewable. To fix, simply unescape before you reescape. Works like a charm. We have the following in an initializer, “html_sanitizer_patch.rb”, that fixes this behaviour.
module HTML class WhiteListSanitizer < Sanitizer protected def process_attributes_for(node, options) return unless node.attributes node.attributes.keys.each do |attr_name| value = node.attributes[attr_name].to_s if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value) node.attributes.delete(attr_name) else node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(CGI::unescapeHTML(value)) end end end end end
Bug that looks for "500 .html" rather than "500.html"
There is a very small bug in this method of Rails that causes error pages you change in public/ not to be shown, because Rails looks for “404 .html” and “500 .html” (note the space). The fix is simple.
#{status.to_s[0..3]}
needs to become
#{status.to_s[0...3]}
If you’re like me and don’t want to edit Rails itself, at the bottom of environment.rb, stick some code that overwrites this method to fix the bug. We have the following:
module ActionController class Dispatcher class << self private def failsafe_response_body(status) error_path = "#{error_file_path}/#{status.to_s[0...3]}.html" if File.exist?(error_path) File.read(error_path) else "<html><body><h1>#{status}</h1></body></html>" end end end end end
Bug? does not encode options
polymorphic_path(item,options) =polymorphic_path(item)+hash_to_url_query(options)
def hash_to_url_query(hash) url = [] hash.each{|k,v| url << "#{k}=#{v}"} "=" + (url * '&') end
Friendlier error message example
The default error messages can be a bit stale and off putting. Try somethings like this:
error_messages_for( :user, :header_message => "Oops - We couldn't save your user!", :message => "The following fields were a bit of a problem:", :header_tag => :h1 )
You can also use error_messages_for as follows
<% form_for User.new do |f| %> <%= f.error_messages :header_message => "..." %> <% end %>
":prompt" doesn't work
This does not work:
select :object, :method, options, :prompt => ’-select-’
This does work:
select :object, :method, {:include_blank => ’-select-’}
select_options_tag - no more worries...
no more explicit options_for_select calls..
def select_options_tag(name='',select_options={},options={}) #set selected from value selected = '' unless options[:value].blank? selected = options[:value] options.delete(:value) end select_tag(name,options_for_select(select_options,selected),options) end
select_options_tag(‘name’,[[‘oh’,‘no’]],:value=>‘no’)
haml, an alternative to ERb
Want something nicer looking (and currently, faster!) than using ERb for your views? Have a look at haml (and it’s companion, sass, for stylesheets). It will make you feel all fuzzy on the inside, I promise :P.
ERb example
<div id="profile"> <div class="left column"> <div id="date"><%= print_date %></div> <div id="address"><%= current_user.address %></div> </div> </div>
haml equivalent
#profile .left.column #date= print_date #address= current_user.address
Shifting to haml from ERb feels strange at first, but after about 20 minutes it starts to feel nice. A little longer and you’ll really start to notice your productivity (and of course, happiness) increase! :). I’ve starting shifting all new projects developed at our work office over to using haml (and sass), it’s been fantastic!
At first I came across a few things that I couldn’t do in haml, though every time a quick read of the overview doc page would show me a simple syntax for overcoming that issue! :) (which out of interest, is located here: http://haml.hamptoncatlin.com/docs/rdoc/classes/Haml.html)
Give the tutorial a shot if you’re interested: http://haml.hamptoncatlin.com/tutorial
render template file different from your action (method) name - alternative
Alternative ways to render templates for actions
# Renders the template for foo (foo.html.erb, foo.haml.erb, foo.text.html.haml, whatever :P) def foo end # Renders the template that would be rendered in foo # (but without the foo controller action being invoked) def bar render :action => 'foo' end # Similar to what bar does, but render's a specifically named template def roar render :template => 'foo' end # Similar to what roar does, but render's a template # from outside of the current controller's views directory def boo render :template => 'global/something' end
link_to with block (Edge Rails)
New ability in Edge Rails to use link_to called with a block, e.g. The following is a view example:
<% link_to some_path do %> <strong>Awesome link</strong> -- it's pretty awesome <% end %>
This change was made in: http://github.com/rails/rails/commit/8190bce8bc7249b7b9f3680195336eb3ca9508ee
Patch in yourself, or likewise you can use the following snippet (which is the new link_to method with modifications [there are also Array extensions on edge to provide .second, .third etc which aren’t present]).
url_helper_extensions.rb
module ActionView module Helpers module UrlHelper def link_to(*args, &block) if block_given? options = args.first || {} html_options = args[1] concat(link_to(capture(&block), options, html_options)) else name = args.first options = args[1] || {} html_options = args[2] url = case options when String options when :back @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()' else self.url_for(options) end if html_options html_options = html_options.stringify_keys href = html_options['href'] convert_options_to_javascript!(html_options, url) tag_options = tag_options(html_options) else tag_options = nil end href_attr = "href=\"#{url}\"" unless href "<a #{href_attr}#{tag_options}>#{name || url}</a>" end end end end end
If you need something more
Use the brilliant chronic gem: http://chronic.rubyforge.org/
require 'chronic' Time.now # => Fri Jul 25 00:00:25 0200 2008 Chronic.parse 'tomorrow 8 in the evening' # => Sat Jul 26 20:00:00 0200 2008 Chronic.parse 'next Monday noon' # => Mon Jul 28 12:00:00 0200 2008 Chronic.parse 'first Wednesday of Aug' # => Wed Aug 06 12:00:00 0200 2008 Chronic.parse 'first Wednesday of Aug 7pm' # => Wed Aug 06 19:00:00 0200 2008
render_collection
You can wrap render in helpers. For example, render_collection. In app/helpers/application.rb:
module ApplicationHelper def render_collection(name, collection) render :partial => "shared/#{name}", :collection => collection end end
In views:
<h2>Comments</h2> <%= render_collection :comments, @photo.comments %>
render template file different from your action (method) name
In some cases you have to avoid rails magic that uses template names named as your ActionMailer method.
rails magic
def daily_notification # ... end # will look for daily_notification.erb def weekly_notification # ... end # will look for weekly_notification.erb
your case
Just give necessary value to @template instance variable.
def setup # ... @template = 'notification' end def daily_notification # ... end # will look for notification.erb def weekly_notification # ... end # will look for notification.erb
Extract plain text body from TMail parsed email
Here’s a monkey patch for TMail::Mail I wrote to recurse through a message and extract all plain text body components of that message, returning an Array. For most use cases, the resulting Array will contain one String element.
Currently I put this code in a file called lib/tmail_extensions.rb and require ‘tmail_extensions’ in environment.rb
module TMail class Mail def plain_text_body gather_plain_text_parts(self).flatten end private def gather_plain_text_parts(part) returning [] do |message| message << part.body.strip if part.content_type == 'text/plain' part.parts.each { |p| message << gather_plain_text_parts(p) } end end end end
automatically generate scopes for model states
or better known as “throw on some more tasty meta-programming” :). Given an example of a model which has a state (String) which must from a set of defined values, e.g. pending, approved, denied.
class User < ActiveRecord::Base STATES = [ 'pending', 'approved', 'denied' ] validates_inclusion_of :state, :in => STATES # Define a named scope for each state in STATES STATES.each { |s| named_scope s, :conditions => { :state => s } } end
This automatically defines a named_scope for each of the model states without having to define a named_scope manually for each state (nice and DRY).
Custom environment constants
Custom environment level constants can be passed in to your rails application (server, console, whatever) like this):
# bash, tcsh, whatever shell GAMEMODE=pregame script/server
Within rails this constant can be accessed by
ENV['GAMEMODE'] => "pregame" ENV['DOES-NOT-EXIST'] => nil
options_for_select further example (using a collection and with a default value)
In this example, we are editing a collection of region records, each with its own select list of countries. (Region belongs_to :country.) If the region doesn’t have a country associated, then we want a default message of “unassigned”. Of course, if the region does have a country associated then we want that country displayed:
<% name = "region[" + region.id.to_s + "][country_id]" %> <% id = "region_" + region.id.to_s %> <%= select_tag(id, options_for_select([["unassigned" , "0" ]] + Country.to_dropdown, region.country_id),
{:name => name} ) %> This give us:
<select id="region_3" name="region[3][country_id]"> <option value="0">unassigned</option> <option selected="selected" value="12">England</option> </select>
NB: we’re using the handy acts_as_dropdown plugin (http://delynnberry.com/projects/acts-as-dropdown/) but we could just as easily prepare the select list with map / collect as above.
:only, :except and passing in multiple parameters
To specify that the filter should be applied to or excluded from given controller actions, use the :only and :except parameters. To pass in multiple controller actions use an array:
before_filter :authorize, :except => [:index, :show] before_filter :authorize, :only => :delete
Using .map(&:item)
You can only use .map(&:item) with find(:all, not find(:first. For example; the first works, but the second does not.
@person = Person.find(:all, :conditions => { :id => @person.id}, :select => "name").map(&:name) @person = Person.find(:first, :conditions => { :id => @person.id}, :select => "name").map(&:name)
Keep your controllers clear
When you use redirect_to or render with flash[:notice] or flash[:error], you can define some helper methods in your ApplicationController (or somewhere you want):
class ApplicationController < ActionController::Base protected %w(notice error).each do |message| class_eval <<-END_EVAL def redirect_#{message}(url, message) flash[:#{message}] = message redirect_to url end def render_#{message}(action, message) flash[:#{message}] = message render :action => action end END_EVAL end end
Now you have four methods - redirect_notice, redirect_error, render_notice and render_error.
Another solution for visual_effect in code block example in link_to_remote
Update: Code block example in link_to_function not link_to_remote (can’t edit subject annymore :( )
This doesn’t work:
page[:details].visual_effect :toggle_blind
My solution:
page.visual_effect :toggle_blind, ‘details’
Custom annotation types
For group work you may need something more than FIXME, OPTIMIZE and TODO. Just create new rake file and place it to lib/tasks:
require 'source_annotation_extractor' task :notes do SourceAnnotationExtractor.enumerate "WTF|OMG", :tag => true end namespace :notes do desc "Enumerate all WTF annotations" task :wtf do SourceAnnotationExtractor.enumerate "WTF" end desc "Enumerate all OMG annotations" task :omg do SourceAnnotationExtractor.enumerate "OMG" end end
or create an array of new types and generate tasks dynamicaly.
Pass id collections with check box tags
It can be useful to pass a collection of ids to a controller, especially in the case of a has_many relationship, for example:
User has_many Roles
In your view you could have something like:
<ul> <% @roles.each do |role| %> <li> <%= check_box_tag 'role_ids[]', role.id -%> <%= h role.name -%> </li> <% end %> </ul>
Note the square brackets after role_ids - this is important for passing a collection through to the controller.
If you place this in a form and submit it, you can expect to see a param passed into the controller that looks like:
"role_ids"=>["1", "2", "3"]
subdomains from request
Useful for discovering what domain/subdomain(s) the current request came from (Rails application may operate differently depending on which subdomain is passed in, this is a great way to segment functionality using the route).
request.host.split('.')
Doesn’t get much simpler than that :).
Likewise if you want to see only the subdomain component(s). Given a domain, example.com
request.host.gsub('example.com', '').split('.')
Easy and effective admin authentication
Great for use within an AdminController (in which all other administrative controllers inherit from AdminController).
class AdminController < ApplicationController before_filter :authenticate def authenticate authenticate_or_request_with_http_basic('Administration') do |username, password| username == 'admin' && password == 'password' end end end
perform update_all scoped within a has_many collection
For example: having two models, User and Message (user has_many messages, each message has a boolean flag called ‘read’). You want to mark all messages as read for a particular user.
Mark all messages as read for a particular user
Message.update_all({:read => true}, {:id => user.messages})
Loading fixtures in migrations
This helper is wrapper around Fixtures#create_fixtures and just load fixtures from specified directory (db/migrate/data by default):
class ActiveRecord::Migration def self.load_data(filename, dir = 'db/migrate/data') Fixtures.create_fixtures(File.join(RAILS_ROOT, dir), filename) end end
It is usefull for tables with data like country list:
class CreateCountries < ActiveRecord::Migration def self.up create_table :countries do |t| t.string :name, :code, :null => false t.timestamps end load_data :countries end def self.down drop_table :countries end end
Full Select
The full select using this would be something like:
Code Example
<%= select_tag(“job[state_id]”, options_from_collection_for_select(State.find(:all), “id”, “name”)) %>