0

I have models of one of my projects set up to something like this (using Rails 3.2 and Mongoid 3.0):

class Parent
  include Mongoid::Document

  has_many :kids

  def schools
    return kids.map { |kid| kid.school }
  end
end

class School
  include Mongoid::Document

  has_many :kids
end

class Kid
  include Mongoid::Document

  belongs_to :parent
  belongs_to :school
end

My Parent model acts as a standard User model that I have set up with Devise. I want a SchoolController with an index and show method that only allow access to schools that the parent has a kid in. The best way to do this, according to this site: http://www.therailsway.com/2007/3/26/association-proxies-are-your-friend/, is to do something like:

def index
  @schools = current_user.schools
end

def show
  @school = current_user.schools.find(params[:id])
end

However, because Mongoid doesn't allow has_many :through relations, Parent#schools is a custom method which returns an array, not an assocition proxy, and therefore #find is not a method that can be used. Is there a way to create an association proxy from an array of documents? Or is there a smarter way to handle this simple access control issue?

4

1 回答 1

0

The most elegant way I have been able to solve this thus far is to attach a custom method to the array returned by Parents#schools.

I give the returned array a #find method:

class Parent
  include Mongoid::Document

  has_many :kids

  def schools
    schools = self.kids.map { |kid| kid.school }

    # Returns a school with the given id. If the school is not found, 
    # raises Mongoid::Errors::DocumentNotFound which mimics Criteria#find.
    def schools.find(id)
      self.each do |school|
        return school if school.id.to_s == id.to_s
      end
      raise Mongoid::Errors::DocumentNotFound.new(School, {:_id => id})
    end

    return schools
  end
end

That way I can keep my Controller logic simple and consistent:

class ParentsController < ApplicationController

  def show
   @school = current_user.schools.find(params[:id])
  end

end

Not sure if there is any better way at this point.

于 2012-09-26T19:40:27.957 回答