Notes posted by stevo
RSS feed

If you happen to face some weird rounding issue...
i.e.
helper.number_to_currency(187) => "190 kr"
check out your… translations! Especially ‘significant’ key… In my case it was
number: currency: format: significant: 'false'
that broke rounding. It should have been
number: currency: format: significant: ! 'false'
And now it works perfectly
helper.number_to_currency(187) => "187 kr"

How to handle dynamic controller class evaluation based on params
Possible with following snippet of code (for instance if each branch has some different controller logic, but if the controller is not present, it should fallback to default controller).
Advantages are so we do not have to make blank inherited controllers and routes for them, to do it with plain inheritance.
class ActionDispatch::Routing::RouteSet::CustomDispatcher < ActionDispatch::Routing::RouteSet::Dispatcher # These are the controllers that we should attempt fallbacks on FALLBACK_CONTROLLERS = /customer\/branch\/(projects|events)$/ def controller(params, default_controller=true) # This defines when we want to attempt fallbacks pattern super unless params[:branch_id] && params[:controller].try(:match, FALLBACK_CONTROLLERS) controller_param = params[:controller] # Having these supplied, we handle controller evaluation by our own method... controller_reference_with_fallbacks(params[:branch_id], controller_param) rescue NameError => e raise ActionController::RoutingError, e.message, e.backtrace if default_controller end private def controller_reference_with_fallbacks(branch_id, controller_param) # This is how fallbacks are evaluated controller_name = "#{controller_param.sub('/branch', "/branch/#{branch_id}").camelize}Controller" controller = ActiveSupport::Dependencies.reference(controller_name) begin controller.get(controller_name) rescue NameError => e # If there is no specific class for given branch, fallback to original class controller_reference(controller_param) end end end ActionDispatch::Routing::Mapper::Mapping.class_eval do private # We do overwrite dispatcher class, that is used to evaluate controller classes from params def app ActionDispatch::Routing::Mapper::Constraints.new( to.respond_to?(:call) ? to : ::ActionDispatch::Routing::RouteSet::CustomDispatcher.new(:defaults => defaults), blocks, @set.request_class ) end end

Example of usage
e.g.
str = ActiveSupport::StringInquirer.new('test') str.test? # => true str.foobar? # => false

STI - Making callbacks trigger in inherited classes
Assuming we have
class ParentClass < ActiveRecord::Base attr_accessible :type end class ChildClass < ParentClass after_save :perform_something end
Executing
ParentClass.create({:type => "ChildClass"})
will not trigger ChildClass callbacks. What is more, it will return instance of ParentClass instead of ChildClass.
To resolve this issue, you need to define following module
module ActiveRecord module CallbacksAwareSti extend ActiveSupport::Concern module ClassMethods def new(*args, &block) return super(*args, &block) unless args.first.respond_to?(:with_indifferent_access) type = args.first.with_indifferent_access[:type] if type.blank? or (type = type.constantize) == self super(*args, &block) else super(*args, &block).becomes(type) end end end end end
and include it in parent class
class ParentClass < ActiveRecord::Base include ActiveRecord::CallbacksAwareSti attr_accessible :type end
Inspired by http://stackoverflow.com/questions/4518935/activerecord-problems-using-callbacks-and-sti


Deprecation in 3.1+
In Rails 3.1 and higher, just use ruby’s SecureRandom, e.g.
Before
ActiveSupport::SecureRandom.hex
After
SecureRandom.hex

Deprecated proxy_owner
Just change your
proxy_owner
calls to
@association.owner
Found it here: http://mileszs.com/deprecation-warnings-for-proxyowner-in-rails

Example
Check if id column exists in users table
ActiveRecord::Base.connection.column_exists?(:users, :id)

Custom configuration keys
It is nice to know, that you can store any custom configuration key in configure block… E.g.
YourApp::Application.configure do # ... config.my_custom_setting = "QWERTY1234" end
Then you can just access it by calling
YourApp::Application.config.my_custom_setting

Rails 3.1 - Use request.url instead
As request.request_uri has been deprecated, use
request.url
instead.

Use Ruby instead!
E.g.
class TestClass < SomeSuperClass attr_accessor :sample_acc def initialize @sample_acc = [] super end end
If nil is not a valid value for this accessor, then you can just define reader for it.
class TestClass attr_accessor :sample_acc def sample_acc @sample_acc ||= 98 end end

How to submit current url
For example to change some kind of param on select change…
<%= form_tag({}, {:method => :get}) do %> <%= select_tag :new_locale, options_for_select(I18n.available_locales, I18n.locale), :onchange => "this.form.submit();" %> <% end %>

Unexpected rounding behavior
Both 2.5 and 1.5 are rounded to 2…
ree-1.8.7-2010.02 > sprintf("%.f", 0.5) => "0" ree-1.8.7-2010.02 > sprintf("%.f", 1.5) => "2" ree-1.8.7-2010.02 > sprintf("%.f", 2.5) => "2" ree-1.8.7-2010.02 > sprintf("%.f", 3.5) => "4" ree-1.8.7-2010.02 > sprintf("%.f", 4.5) => "4"
use round instead to get proper behavior

How to change format automatically depending on locale...
… without passing locale option.
In your application_helper.rb (or in other helper) place following code:
def number_to_currency(number, options = {}) options[:locale] ||= I18n.locale super(number, options) end
Then, in your locale files:
en-GB: number: currency: format: format: "%n %u" unit: "USD"
And that is it :)

Some unexpected behaviour
When using Array as default value, it behaves more like cattr_accessor…
class A attr_accessor_with_default :b, [] end x = A.new x.b << 1 #puts x.b.inspect => [1] y = A.new y.b << 2 #puts y.b.inspect => [1, 2]

Basic usage
Basic usage example:
class User < ActiveRecord::Base #... skip_callback :create, :after, :send_welcome_email #... end

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

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 %>

How to perform :onchange ajax call using jQuery (Rails 3)
As remote_function no longer exists in Rails 3, here is some handy substitution
<%= select_tag :assignee_id, options_for_select([["A",1],["B",2]]), :onchange => "$.post('#{model_singular_path(id)}', {'_method':'put', 'model_name[model_field]':this.value} );" %>
You can obviously pass more attributes, change method etc.

map_with_index
Of course such a method does not exist, however we can simulate it easily
%w(a b c).to_enum(:each_with_index).map{|a,i| "#{a}, #{i}"} => ["a, 0", "b, 1", "c, 2"]

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

Preserve order of elements within fields_for
I had the @colleagues collection prepared that I wanted to be rendered within fields_for block. However it was searchlogic object with filtering/sorting applied, and the sort order was not preserved in resultant view.
<%= f.fields_for :evaluators, @colleagues do |builder| %> <%= render "colleague", :f => builder %> <% end %>
Solution to this problem was typecasting this collection to array
<%= f.fields_for :evaluators, @colleagues.to_a do |builder| %> <%= render "colleague", :f => builder %> <% 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

Depracated add_to_base
use
model_instance.errors[:base] << "Msg"
instead of depracated
model_instance.errors.add_to_base("Msg")
for Rails 3

Interesting usage for polymorphic asset model :)
…to automatically define default scopes of inherited classes.
class Asset < ActiveRecord::Base belongs_to :resource, :polymorphic => true before_save :set_asset_type def set_asset_type self.asset_type = self.class.name end def self.inherited(subclass) super subclass.send(:default_scope, :conditions => "asset_type='#{subclass.name}'") end 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