问题
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.housesreturning a relation, butusers.addresses(andusers.houses.addresses) suddenly callingaddresseson 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