Flowdock

Notes posted by stevo

RSS feed
May 5, 2020
0 thanks

Make your custom enumerable "flattenable"

Just alias to_a to to_ary

class A
  include Enumerable

  def each
    yield "a"
    yield "b"
  end
end

[A.new, A.new].flatten => [#<A:0x00007fbddf1b5d88>, #<A:0x00007fbddf1b5d60>]

class A
  def to_ary
    to_a
  end
end

[A.new, A.new].flatten => ["a", "b", "a", "b"]
October 24, 2012
0 thanks

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"
August 30, 2012
0 thanks

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
June 13, 2012
0 thanks

Example of usage

e.g.

str = ActiveSupport::StringInquirer.new('test')

str.test? # => true
str.foobar? # => false
May 15, 2012
0 thanks

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

March 27, 2012
7 thanks

Reorder

If you want to override previously set order (even through default_scope), use reorder() instead.

E.g.

User.order('id ASC').reorder('name DESC')

would ignore ordering by id completely

March 22, 2012 - (>= v3.1.0)
2 thanks

Deprecation in 3.1+

In Rails 3.1 and higher, just use ruby’s SecureRandom, e.g.

Before

ActiveSupport::SecureRandom.hex

After

SecureRandom.hex
March 15, 2012 - (>= v3.2.1)
1 thank

Deprecated proxy_owner

Just change your

proxy_owner

calls to

@association.owner

Found it here: http://mileszs.com/deprecation-warnings-for-proxyowner-in-rails

January 20, 2012
1 thank

Example

Check if id column exists in users table

ActiveRecord::Base.connection.column_exists?(:users, :id)
November 21, 2011
0 thanks

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
November 21, 2011 - (>= v3.1.0)
0 thanks

Rails 3.1 - Use request.url instead

As request.request_uri has been deprecated, use

request.url

instead.

October 10, 2011 - (>= v3.1.0)
1 thank

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
October 7, 2011
3 thanks

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 %>
October 4, 2011
1 thank

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

October 4, 2011
2 thanks

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 :)

September 13, 2011
1 thank

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]
July 29, 2011
0 thanks

Basic usage

Basic usage example:

class User < ActiveRecord::Base
   #...
   skip_callback :create, :after, :send_welcome_email
   #...
end
February 22, 2011
7 thanks

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 %>
November 9, 2010
0 thanks

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.

October 28, 2010
0 thanks

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"]
October 26, 2010
3 thanks

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
October 7, 2010
1 thank

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 %>  
August 13, 2010
10 thanks

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

August 13, 2010
0 thanks

Depracated add_to_base

use

model_instance.errors[:base] << "Msg" 

instead of depracated

model_instance.errors.add_to_base("Msg")

for Rails 3

January 22, 2010
1 thank

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
June 19, 2009
1 thank

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