覆盖has_many关联getter

时间:2016-04-22 00:48:22

标签: ruby-on-rails activerecord

用户可以拥有多辆车 -

  • User: has_many :cars
  • Car: belongs_to :user

每次拨打@user.cars时,都会返回默认搜索顺序中的cars列表。

如果我希望关联在某个任意字段上排序,我可以

class User < ActiveRecord::Base
  has_many :cars, -> { order :num_wheels }
end

但是,让我们说我的排序逻辑很复杂,我想重写关联getter来实现我自己的逻辑

我尝试了类似的东西 -

class User < ActiveRecord::Base
  has_many :cars

  def cars
    # Pretend this is complex logic
    cars.order(:num_wheels)
  end
end

然而,这显然会失败,因为您无法在被覆盖的cars方法中引用原始cars而不会无限循环。

有没有办法引用&#34;原作&#34;从被覆盖的吸气剂里面吸气?

谢谢!

3 个答案:

答案 0 :(得分:10)

使用super:

class User < ActiveRecord::Base
  has_many :cars

  def cars
    # Pretend this is complex logic
    super.order(:num_wheels)
  end
end

当您使用像has_many这样的宏时,Rails会动态创建一个模块(可以通过User.generated_association_methods访问)。在您的情况下,定义访问者和读者(例如&#34; cars&#34;在您的情况下) ,可以通过User.generated_association_methods.instance_methods访问。此模块成为User类的祖先,因此您可以通过&#34; super&#34;访问读取器方法(汽车)。在你自己的汽车方法。

答案 1 :(得分:2)

根据我的理解,我相信has_many本质上正在做的是:

Class User < ActiveRecord::Base
  has_many :cars

  # is essentially
  def cars
    Car.where(user_id: self.id)
  end
end

因此,当用户想要列出所有汽车时,它仍然是User.cars。当使用ActiveRecord时,has_many假设汽车的方法名称和相关的外键。

答案 2 :(得分:0)

试试这个:

template <typename C, typename E> std::false_type has_event_handler_impl(...);
template <typename C, typename E>
auto has_event_handler_impl(int)
-> decltype(static_cast<void>(std::declval<C>().receive(std::declval<const E>())),
            std::true_type{});

template <typename C, typename E>
using has_event_handler = decltype(has_event_handler_impl<C, E>(0));

template <class... Es>
class Emitter {
public:

    template<class C>
    void reg(C& callback) {
        const int dummy[] = { 0, (regT<Es>(callback), 0)...};
        static_cast<void>(dummy); // Avoid unused variable warning
    }

    template <typename E>
    void emit(const E& event)
    {
        for (auto const& handler : get_vector<E>()) {
            handler(event);
        }
    }

private:
    template <typename E, typename C>
    std::enable_if_t<has_event_handler<C, E>::value>
    regT(C& callback)
    {
        auto lambda = [&callback](const E& event) { return callback.receive(event); };
        get_vector<E>().push_back(lambda);
    }

    template <typename E, typename C>
    std::enable_if_t<!has_event_handler<C, E>::value>
    regT(C&)
    {
        /* Empty */
    }

    template <typename E>
    std::vector<std::function<void(const E&)>>& get_vector()
    {
        return std::get<std::vector<std::function<void(const E&)>>>(handlers_);
    }

private:
    std::tuple<std::vector<std::function<void(const Es&)>>...> handlers_;
};