WideFix tech post

Rails's url_for weaknesses

Rails helper link_to is the most famous and used any Rails developer. Usually the first argument is a link name and the second is a parameters which are used to build url. You are able to pass 4 types of these parameters: helper which is generated by routes, string, hash and since rails 3 - record. But when and which one to use for your task? This is an interesting question.

url_for

Passed to link_to parameters go to the url_for method. This method builds known url which is eventually passed to the href attribute of generated link. So to answer on this question we should answer on question what and when to pass to the url_for.

As I’ve already said url_for accepts 4 types of parameters and see how it’s possible to pass them there:

url_for("/events/#{@event.id}") # string
url_for(event_path(@event)) # generated helper
url_for(controller: 'events', action: 'show', id: @event.id) # hash
url_for(@event) # polymorphic

All there calls of url_for generate the same result /events/:id, for example /events/1.

The most convenient seems the last call - you just pass @event record and that’s it. There is no any brainstorming or surfing generated routes with rake routes task (this task shows all available routes for your Rails application, run it if you still hadn’t done this yet). It really convenient not only that’s why but it will be useful when you change controller or route which serves the event. You won’t have to change all your entire code in Rails application where you used old route helpers or string urls, hashes and so on.

The most complicated seems the hash variant. It looks the biggest and there is no doubt that why it is silently deprecated by Rails community. Rails developers avoid to using it at all. The reason is not only in this reason but in one more which I will tell you in a few seconds.

There is no any rule which method of proposed to use and of course you are not restricted to use any of these call types.

But let’s see what is their speed. I wrote the benchmarking test especially for this case:

class EventsHelperTest < ActionView::TestCase
  def setup
    country = Country.create(name: 'Belarus')
    state = State.create(name: 'Minsk')
    @event = Event.create(country: country, state: state, name: 'Drinking bear')
  end

  def test_url_for
    n = 10000
    Benchmark.bmbm do |x|
      x.report('string') { n.times { url_for("/events/#{@event.id}") } }
      x.report('helper') { n.times { url_for(event_path(@event)) } }
      x.report('hash') { n.times { url_for(controller: 'events', action: 'show', id: @event.id) } }
      x.report('polymorphic') { n.times { url_for(@event) } }
    end
  end
end

And these are results of their running:

rake test test/helpers/events_helper_test.rb
Run options: --seed 23668

# Running tests:

Rehearsal -----------------------------------------------
string        0.010000   0.000000   0.010000 (  0.019028)
helper        0.480000   0.010000   0.490000 (  0.498942)
hash          1.250000   0.000000   1.250000 (  1.253836)
polymorphic   0.920000   0.010000   0.930000 (  0.924670)
-------------------------------------- total: 2.680000sec

                  user     system      total        real
string        0.010000   0.000000   0.010000 (  0.013747)
helper        0.480000   0.000000   0.480000 (  0.478646)
hash          1.200000   0.000000   1.200000 (  1.200571)
polymorphic   0.890000   0.000000   0.890000 (  0.892464)
.

Finished tests in 5.542858s, 0.1804 tests/s, 0.0000 assertions/s.

1 tests, 0 assertions, 0 failures, 0 errors, 0 skips

As you see the slowest here is a hash style. And then polymorphic generating goes. Helper style on the 3rd place and it’s not surprise that string generating wins.

Conclusion

For my opinion the most convenient style here is polymorphic. But it’s 2x slower than helper style. String style is the fastest but we can’t use it because if you change your routes your tests may not show you that there is an error in generating routes - string will be generated as before but helper method won’t be presented and page will fail that’s why.

So my conclusion is to use helper methods everywhere where it’s possible. If you have some place in your application where you have to define which helper to use by record type or any other sign the polymorphic way is more preferable.

And the last conclusion - avoid using hash types of generating url at all.

Are you seeking assistance with Ruby on Rails development?

Read also