Flowdock

Notes posted to Ruby on Rails

RSS feed
July 18, 2013 - (v3.2.1 - v3.2.13)
0 thanks

Nested i18n attributes

If you want to use nested attributes in a i18n file (like person :has_many => :addresses), write:

en:
  activerecord:
    attributes:
      person:
        name:
      "person/addresses":
        street: "Street name"
      "person/phones":
        area: "Area code"
        number: Number
July 16, 2013 - (>= v3.2.1)
0 thanks

Difference between fullpath

From what I’ve seen, it looks like the difference between this and #fullpath is that this method doesn’t include parameters that weren’t in the original url (i.e. parameters that were sent via POST instead of GET).

July 16, 2013
0 thanks

isolate_namespace description with example

Normally when you create controllers, helpers and models inside an engine, they are treated as if they were created inside the application itself. This means that all helpers and named routes from the application will be available to your engine’s controllers as well.

However, sometimes you want to isolate your engine from the application, especially if your engine has its own router. To do that, you simply need to call isolate_namespace. This method requires you to pass a module where all your controllers, helpers and models should be nested to:

module MyEngine
 class Engine < Rails::Engine
   isolate_namespace MyEngine
 end

end

With such an engine, everything that is inside the MyEngine module will be isolated from the application.

Detail reference: http://edgeapi.rubyonrails.org/classes/Rails/Engine.html

July 12, 2013 - (v3.0.0 - v3.2.13)
0 thanks

Accept header ignored

Rails ignores the accept header when it contains “,/” or “/,” and returns HTML (or JS if it’s a xhr request).

This is by design to always return HTML when being accessed from a browser.

This doesn’t follow the mime type negotiation specification but it was the only way to circumvent old browsers with bugged accept header. They had he accept header with the first mime type as image/png or text/xml.

July 11, 2013
0 thanks

Using fields_for with collection (no association)

accepts_nested_attributes_for has some detractors: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models

But fields_for is defined both on FormBuilder and FormHelper and is still useful even when accepts_nested_attributes_for is not being used. Consider a “to do list” application where we wish to edit and update a list of tasks (Task model). I like to use a TaskCollectionController to manage the actions on the collection, it feels more RESTful than overloading a TaskController:

class TaskCollectionController < ApplicationController
# task_collection_edit GET /task_collection/edit 
  def edit
    @tasks = Task.all
  end

# task_collection_update PUT /task_collection/update
  def update
    # it would be better to use a TaskCollection form object here
    # to make this controller skinny,
    # for the sake of brevity I skipped the form object
    params[:tasks].values.each do |attrs|
      if attrs[:_destroy] == '1'
        Task.find(attrs[:id]).destroy
      elsif attrs[:id].blank?
        Task.create(attrs.slice!(:id,:_destroy))
      else
        Task.find(attrs[:id]).update_attributes(attrs.slice!(:id,:_destroy))
      end
    end
    redirect_to task_collection_edit_path
  end
end

In the edit view, I use fields_for to index added tasks, and it also will insert the id as a hidden field for existing tasks:

%h1 Edit Task Collection
= form_tag task_collection_update_path, :method => :put do
  %table#tasks
    %tr
      %th Name
      %th Priority
    - @tasks.each do |task|
      = fields_for 'tasks[]', task, :hidden_field_id => true do |task_form|
        = render :partial => 'task_form', :locals => {:task_form => task_form}

  = button_tag 'Save'

= button_tag "Add task", :type => :button, :id => :add_task

%script{:type => 'html/template', :id => 'task_form_template'}
  = fields_for 'tasks[]', Task.new, :index => 'NEW_RECORD', :hidden_field_id => true do |task_form| render(:partial => 'task_form', :locals => {:task_form => task_form}); end

:javascript
  $(function(){
    task_form = function(){ return $('#task_form_template').text().replace(/NEW_RECORD/g, new Date().getTime())}
    var add_task = function(){ $('#tasks').append(task_form()) }
    $('#add_task').on('click', add_task)
  })

:hidden_field_id => true triggers the insertion of the id field, and a placeholder index “NEW_RECORD” is replaced by javascript when a task is added, as others have described here. When there’s no association, the index key is :index, vs. :child_index in the case of an association.

Here’s the partial used in the edit view:

%tr
  %td= task_form.text_field :name
  %td= task_form.select :priority, [1,2,3,4,5]
  %td= remove_item(task_form)

The remove_item helper triggers the deletion of persisted tasks:

module TaskCollectionHelper
  def remove_item(form_builder)
    if form_builder.object.new_record?
      # If the task is a new record, remove the div from the dom
      link_to_function( 'Remove', "$(this).closest('tr').remove()")
    else
      # However if it's a "real" record it has to be deleted from the database,
      # hide the form and mark for destruction
      form_builder.hidden_field(:_destroy) +
      link_to_function( 'Remove', "$(this).closest('tr').hide(); $(this).siblings().attr('value',1)")
    end
  end
end
June 26, 2013 - (v3.0.0 - v3.2.13)
0 thanks

IS NOT NULL or !=

where.not()

# SELECT `users`.* FROM `users` WHERE (`users`.`id` != 1) AND (`users`.`name` IS NOT NULL)
 User.where.not(id: 1).where.not(name: nil)
June 26, 2013 - (v3.2.13)
0 thanks

Typing mismatch

This block

if size = options.delete(:size)
  options[:width], options[:height] = size.split("x") if size =~ %{^\d+x\d+$}
end

has type mismatch

%r{^\d+x\d+$}
June 19, 2013 - (>= v3.2.13)
0 thanks

Must use :class, not 'class'

Note that

<%= content_tag_for(:li, @person, :class => “bar”) %>

does the right thing.

<%= content_tag_for(:li, @person, ‘class’ => “bar”) %>

will not!

June 17, 2013
1 thank

Use dom_id( resource_instance ) to create the HTML id

In the notes on this page people use:

car_ids_#{c.id}

But you can use this function in stead:

dom_id(c) 
June 5, 2013 - (v2.0.3 - v3.2.13)
0 thanks

Html inside Lable tag

I need this

<label>
   Show
   <select size="1" name="dyntable_length" aria-controls="dyntable">
     <option value="10" selected="selected">10</option>
     <option value="25">25</option>
     <option value="50">50</option>
     <option value="100">100</option>
   </select>

   entries
 </label>

I made a helper method:

def entries_lablel()
   label_tag '' do
     concat 'Show '
     concat content_tag(:select, options_for_select([10, 25, 50, 100]),
       {name: 'dyntable_length', size: 1}
     )
     concat ' entries'
   end
 end

and In my html.erb file I called it

<%= entries_lablel %>

You can pass paramateres to make it more generic also You can add multiple select elements or any other element using the same

May 31, 2013
1 thank

Do not mistakenly use serialize like other similar directives - attr_accessible, attr_accessor

serialize seems very similar to other directives that work on attributes such as attr_accessible. One may mistakenly assume that serialize can take a list of attributes. For eg:

class Tuk < ActiveRecord::Base
  attr_accessible :foo, :bar
  serialize :foo, :bar
end

This may lead to a cryptic error. Eg.

puts !Tuk.first.foo.nil?

causes:

NoMethodError at /file:location undefined method `new' for :bar:Symbol

This is because it tries to parse the YAML string stored in foo as an instance of :bar.

May 30, 2013
0 thanks

Avoiding to_param method when using URL helper methods

I recently found myself in the situation where I needed to generate URLs which included the ID instead of the value returned from the model’s to_param method (since someone had overridden the to_param method). It turned out to be easier than I thought. You can simply pass an ID to the helper method and it will construct the URL correctly:

edit_admin_foobar_path(@foobar.id)
# /admin/foobars/123/edit
May 23, 2013 - (v3.0.0 - v3.2.13)
0 thanks

Use @output_buffer to set the context.

You can use assert_select to test helpers, just have to set the @output_buffer before you do.

Code example

class CurrencyHelperTest < ActionView::TestCase

  setup do
    # can use helper methods here
    @output_buffer = currency 54.78
  end

  test 'currency use a div' do
    asert_select 'div'
  end

end
May 23, 2013
0 thanks

A simple usage example

See http://apidock.com/rails/String/inquiry

env = "production".inquiry
env.production?  # => true
env.development? # => false
May 18, 2013
0 thanks

:include is also valid option

my_company.serializable_hash(:include => [:people])

May 15, 2013
0 thanks
April 27, 2013 - (v3.1.0 - v3.2.13)
2 thanks
April 10, 2013 - (v3.2.1 - v3.2.13)
2 thanks

be aware that this writes to tmp/cache

Its supposed to be http caching, but Rails will actually cache the response to whatever you specified as the cache store, *as well*, but only if you specify :public => true. The default is filestore so it will try to write to tmp/cache.

Only a problem if you don’t have the proper permissions set, in that scenario your apache/nginx logs could fill up very quickly with “permission denied errors”

Full explanation is here http://blog.tonycode.com/archives/418

April 4, 2013 - (>= v1.0.0)
0 thanks

increment_by_sql for PG

Note, if you’re using the code below for incrementing by SQL with a Postgres database, it’s not going to like the backticks. Just remove them:

def increment_with_sql!(attribute, by = 1)
  raise ArgumentError("Invalid attribute: #{attribute}") unless attribute_names.include?(attribute.to_s)
  original_value_sql = "CASE WHEN #{attribute} IS NULL THEN 0 ELSE #{attribute} END"
  self.class.update_all("#{attribute} = #{original_value_sql} + #{by.to_i}", "id = #{id}")
  reload
end
April 2, 2013 - (v3.0.0 - v3.2.13)
0 thanks

Use concat insted of joining collection explicitely

concat method will be useful to join the collection object from looping conditions.

arr = ["a", "b", "c"]
content_tag(:ul, :class => 'a class') do
  arr.each do |item|
  concat content_tag(:li, item)
end

And this will generate the html as shown below

<ul class="a class">
  <li>a</li>
  <li>b</li>
  <li>c</li>
</ul>
March 29, 2013
0 thanks
March 29, 2013
0 thanks

Edge case

Have look how #between? handle adge case. It’s different from DateTime’s.

Date.yesterday.between?(Date.yesterday, Date.tomorrow)
=> true

Date.tomorrow.between?(Date.yesterday, Date.tomorrow)
=> true
March 27, 2013 - (>= v3.0.0)
0 thanks

When dealing with has_many through

The non-repeating primary key id must be used with find_in_batches.

  • User has many things

  • User has many socks through things

  • Sock has many things

  • Sock has many users through things

For the sake of argument, assume the first user has two socks and all other users have one sock. There are 1000 users in total and 1001 socks in total.

User.joins(:socks).count
=> 1001
agg = []
# Incorrect
User.joins(:socks).find_in_batches{|g| agg += g}
agg.count
=> 1000

Sock.joins(:users).count
=> 1001
agg = []
# Correct
Sock.joins(:users).find_in_batches{|g| agg += g}
agg.count
=> 1001
March 27, 2013 - (v3.0.0 - v3.2.13)
0 thanks
March 18, 2013
3 thanks

Beware - May cause performance issues

A serialized attribute will always be updated during save, even if it was not changed. (A rails 3 commit explains why: http://github.com/rails/rails/issues/8328#issuecomment-10756812)

Guard save calls with a changed? check to prevent issues.

class Product < ActiveRecord::Base
  serialize :product_data
end

bad

product = Product.first
product.save

good

product = Product.first
product.save if product.changed?
March 5, 2013 - (v3.0.0 - v3.2.8)
0 thanks

Exceptions raised within are ignored.

From http://guides.rubyonrails.org/active_record_validations_callbacks.html

The after_commit and after_rollback callbacks are guaranteed to be called for all models created, updated, or destroyed within a transaction block. If any exceptions are raised within one of these callbacks, they will be ignored so that they don’t interfere with the other callbacks. As such, if your callback code could raise an exception, you’ll need to rescue it and handle it appropriately within the callback.

March 1, 2013
0 thanks

Default field order

If you want to set an app wide default order for the fields (rather than passing :order each time), use the locale file.

eg. edit config/locale/en.yml to include:

en:
  date:
    order: 
      - :day
      - :month
      - :year
February 22, 2013
0 thanks

Checking content_for

There’s a much simpler way to check if content exists or not, and it’s been provided as example in docs since 05.2010:

module StorageHelper
  def stored_content
    content_for(:storage) || "Your storage is empty"
  end
end

But this behavior was broken with SafeBuffer. You’ll be able to use it again when this issue (github.com/rails/rails/issues/9360) will be fixed and merged with master.