使用单例类动态覆盖/添加ActiveRecord关联 [英] Overwriting/Adding an ActiveRecord association dynamically using a singleton class

查看:55
本文介绍了使用单例类动态覆盖/添加ActiveRecord关联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

业务逻辑是这样的:用户通过联接表坐在船上,我想称它为故障单模型。但是,当某个User实例想要检查船上还有谁时,就会出现一个条件,询问该用户是否具有权限才能看到船上的每个人,或者只看到船上的某些人。如果某个用户可以看到所有人,则可以正常进行交易: some_user.boats.first.users 返回所有拥有该船票的用户。但是对于某些用户来说,船上唯一的人(就他们而言)就是餐厅里的人。因此,如果用户的票证被标记(使用 acts_as_taggable 样式系统)和餐厅,则仅有唯一的用户从 some_user.boats.first返回.users 应该是带有标签为 Dining Room的用户。

The business logic is this: Users are in a Boat through a join table, I guess let's call that model a Ticket. But when a User instance wants to check who else is on the boat, there's a condition that asks if that user has permission see everyone on the Boat, or just certain people on the Boat. If a User can see everyone, the normal deal is fine: some_user.boats.first.users returns all users with a ticket for that boat. But for some users, the only people that are on the boat (as far as they're concerned) are people in, let's say the dining room. So if User's ticket is "tagged" (using an acts_as_taggable style system) with "Dining Room", the only Users returned from some_user.boats.first.users should be Users with tickets tagged "Dining Room".

仅作记录,我并不是在尝试从getgo中设计出一些疯狂的东西-我是在尝试将这种任意分组楔入(主要是)存在的分组中系统。
因此,我们得到了:

Just for the record, I'm not trying to design something to be insane from the getgo - I'm trying to wedge this arbitrary grouping into a (mostly) existent system. So we've got:

class User
  has_many :tickets
  has_many :boats, :through => :tickets
end

class Ticket
  belongs_to :user
  belongs_to :boat
end

class Boat
  has_many :tickets
  has_many :users, :through => :tickets
end

最初,我认为我可以有条件地修改虚拟类,例如:

Initially, I thought that I could conditionally modify the virtual class like:

singleton = class << a_user_instance ; self ; end
singleton.class_eval(<<-code
  has_many :tickets, :include => :tags, :conditions => ['tags.id in (?)', [#{tag_ids.to_s(:db)}]]
code
)

一路深入到生成SQL的过程,但是生成后,它生成的SQL结尾为:

That gets all the way down to generating the SQL, but when generated, it generates SQL ending in:

LEFT OUTER JOIN tags ON( tags。 id = taggings。 tag_id)在哪里(门票 ._id = 1069416589 AND((5001,4502)中的tagsid))

我尝试在ActiveRecord代码中进行挖掘,但是在上面的SQL中找不到带下划线的前缀 id的任何地方。我知道在加载ActiveRecord类时会加载关联,对于单例类,我会假设相同。 耸肩

I've tried digging around the ActiveRecord code, but I can't find anywhere that would prefix that 'id' in the SQL above with an underscore. I know that associations are loaded when an ActiveRecord class is loaded, and I'd assume the same with a singleton class. shrug.

我还使用了 alias_method_chain ,例如:

singleton = class << a_user_instance ; self ; end
singleton.class_eval(<<-code
  def tickets_with_tag_filtering
    tags = Tag.find(etc, etc)
    tickets_without_tag_filtering.scoped(:include => :tags, :conditions => {:'tags.id' => tags})
  end
  alias_method_chain :tickets, :tag_filtering
code
)

但是,尽管这种方法产生了所需的票证,但这些票证上的所有联接都使用类中的条件,而不是虚拟类。 some_user.boats.first.users 返回所有用户。

But while that approach produces the desired Tickets, any joins on those tickets use the conditions in the class, not the virtual class. some_user.boats.first.users returns all users.

任何类型的评论都会受到赞赏,尤其是如果我用这种方法把错误的树叫起来。谢谢!

Any type of comment will be appreciated, especially if I'm barking up the wrong tree with this approach. Thanks!

推荐答案

对下划线问题的一个疯狂猜测是,Rails当时基于上下文生成关联代码评价。在单例课程中可能会使事情变得混乱,例如:

So a wild guess about your underscore issue is that Rails is generating the assocation code based on the context at the time of evaluation. Being in a singleton class could mess this up, like so:

"#{owner.table_name}.#{association.class.name}_id = #{association.id}"

可以进入那里,在您的singleton类上定义一个类名属性,看看是否可以解决问题。

You could get in there and define a class name property on your singleton class and see if that fixes the issue.

总的来说,我不建议这样做。它会产生难以追踪的行为,并且无法有效扩展。

On the whole I don't recommend this. It creates behavior that is agonizing to track down and impossible to extend effectively. It creates a landmine in the codebase that will wound you or someone you love at a later time.

相反,请考虑使用 named_scope 声明:

Instead, consider using a named_scope declaration:

class User
   has_many :taggings, :through => :tickets

   named_scope :visible_to, lambda { |looking_user|
      { :include => [ :tickets, :taggings ], 
        :conditions => [ "tickets.boat_id in (?) and taggings.ticket_id = tickets.id and taggings.tag_id in (?)", looking_user.boat_ids, looking_user.tag_ids ] 
      }
    }
end

虽然您可能必须返回并编辑一些代码,但这在方法上更加灵活可以使用:

While you may have to go back and edit some code, this is much more flexible in the ways it can be used:

Boat.last.users.visible_to( current_user )

很显然,对查找对象施加了限制,该限制的目的是什么。由于条件是在运行时动态计算的,因此您可以处理客户遇到的下一个怪异修改。假设他们的某些用户具有X射线视力和透视能力:

It's clear that a restriction is being placed on the find, and what the purpose of that restriction is. Because the conditions are dynamically calculated at runtime, you can deal with the next weird modification your client hits you with. Say some of their users have xray vision and clairvoyance:

class User
   named_scope :visible_to, lambda { |looking_user|
      if looking_user.superhuman?
        {}
      else
        { :include => [ :tickets, :taggings ], 
          :conditions => [ "tickets.boat_id in (?) and taggings.ticket_id = tickets.id and taggings.tag_id in (?)", looking_user.boat_ids, looking_user.tag_ids ] 
        }
      end
    }
end

通过返回空哈希,可以有效地使范围的作用无效。

By returning an empty hash, you can effectively nullify the effect of the scope.

这篇关于使用单例类动态覆盖/添加ActiveRecord关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆