14

Rails has two nice ways to avoid Law of Demeter violations in models.

The first is this:

class Restaurant < ActiveRecord::Base
    belongs_to :franchise
    delegate :owner, to: :franchise
end

The second is this:

class Restaurant < ActiveRecord::Base
    belongs_to :franchise
    has_one :owner, through: :franchise
end

What is the difference? Is there anything to recommend one option over the other in some or all instances?

The only difference I can detect is that the delegate option seems to generate two SQL queries to get at the latter record, whereas belongs_to :through seems to do it in one query.

henrebotha
  • 1,244
  • 2
  • 14
  • 36
  • 2
    Interesting question, can you see what happens with the sql that's generated when you run it? – Tim Jan 14 '16 at 10:17
  • Ah, sorry! Forgot to add this. Hang on while I edit... – henrebotha Jan 14 '16 at 10:18
  • Interesting, I always presumed `delegate` to be a ruby method which dealt with in-memory classes. – Richard Peck Jan 14 '16 at 10:20
  • 1
    With the right indexes, I'd assume one query is better as it's less round trips, I mean apart from marshalling the result, it's mostly about getting the right result from the DB. It also implies, potentially, that chaining delegates is going to get progressively more expensive (not something I've done, but I'd considered..) – Tim Jan 14 '16 at 10:23

1 Answers1

17

has_one through: is rails relation and is optimized for some cases - for example it will use joins automatically to fetch record, also can be eager-loaded to avoid the N+1 problem when dealing with multiple Restaurants:

Restaurant.all.includes(:owner).each{|r| some code accessing r.owner }

If owner was a delegate code like the above (with .includes removed) would result in two queries per each Restaurant, but with eager loading they will be all fetched in one

Vasfed
  • 18,013
  • 10
  • 47
  • 53