Ruby delegating class/relation-level methods

我只是一个虾纸丫 提交于 2019-12-11 04:49:02

问题


EDIT -- I changed the object-oriented modeling to better reflect the not-particularly-intuitive relationship in my app, (thanks, Anthony) so that this question makes more sense. Sorry about that.

In my Rails app, I want certain models to be able to delegate to other models on not just an instance level but also a class/relation level. That is to say, assuming a House model, which has_many Users and defines a class-level method "addresses" that's called on relations of houses, I could do this:

users.addresses

Behind the scenes, this would actually do two things: 1) run users.houses (which would grab all houses with an ID among those plucked from the house_id column of the relation of users), and 2) call addresses on that relation of houses.

My attempt currently looks something like this:

class House
  has_many :users

  def self.addresses
    map(&:address)
  end

  def address
    "#{street_address}, {state}, #{country}"
  end
end

class User

  belongs_to :house

  delegate :address, to: :house

  class << self
    delegate :addresses, to: :houses
  end

  def self.houses
   Houses.where(id: pluck(:house_id))
  end

  ...

end

This fundamentally seems to work -- almost. If I have a group of users, I can do users.houses and grab the associated relation of houses. If I call addresses on an unrelated relation of houses, the method works great. If I call, users.addresses, it calls the class-level method of that name in houses.rb.

But, the method errors when I try to chain these things together.

If I call users.addresses (or users.houses.addresses, so delegation isn't the issue here per se), the House.addresses method is called, but within that method, self seems to be not the relation of houses that users.houses should (and generally does) return, but just the House class itself. Consequently, if I call any array methods on self within addresses (which would work on a relation), the method throws an error such as undefined method 'map' for <Class:0x1230101239123>.

This problem persists even if I take away my fancy class-level delegation logic and replace it with the explicit version:

class User
  ...
  def self.addresses
    houses.addresses
  end
end

Same error. Also when I just try users.houses.addresses.

The only version that does work is the following:

class User
  ...
  def self.addresses
    houses.map(&:address)
  end
end

In other words, the logic chain seems to be identical, but moving the map into the User method and out of the House method fixes things.

I'm super confused by this, because to my eyes, that last (successful) version should fundamentally be identical to the two failing versions above. The only difference is that in the successful version, I'm repeating logic in a way I'd like to avoid.

So I guess the questions are

  • 1) Why is users.houses returning a relation, but users.addresses (and users.houses.addresses) suddenly calling addresses on the class rather than the relation?

  • 2) Given these issues, is there a better, more Railsy way to set up the relationship between House and User such that I can run these class-level query methods (ie users.houses)?

Any opinions on the best way to go about fixing this?

来源:https://stackoverflow.com/questions/27828821/ruby-delegating-class-relation-level-methods

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!