我有一个充当过滤器的范围。例如:
class User
scope :in_good_standing, -> { where(:some_val => 'foo', :some_other_val => 'bar' }
end
由于in_good_standing
取决于多个条件,因此我希望将User
的实例定义为:
def in_good_standing?
some_val == 'foo' && some_other_val == 'bar'
end
但是,我真的希望避免重复实例方法和命名范围之间的逻辑。有没有办法以一种简单引用范围的方式定义#in_good_standing?
?
我意识到这些是非常不同的概念(一个是类方法,一个是实例方法),因此我的问题。正如@MrDanA在评论中提到的那样,我能得到的最接近的是检查我感兴趣的记录是否存在于更大的范围内,这可能是我正在寻找的答案。
关于从我的示例中分离出不同范围的响应是有用的,但我正在寻找一种应用于应用程序的通用模式,其中一些非常复杂的逻辑由作用域驱动。
答案 0 :(得分:11)
Scopes
只是class methods
。您可以像这样定义
def self.in_good_standing?
#your logic goes here
end
答案 1 :(得分:10)
添加原始评论作为答案:
正如@meagar所说,你不能,因为他们做的事情非常不同。您可以做的最多就是让实例方法调用范围并检查它是否是返回结果的一部分。但是,如果实例尚未保存,则无法工作。所以在你的方法中你可以做到:
User.in_good_standing.where(:id => self.id).present?
答案 2 :(得分:3)
不,没有。一个是构建数据库查询,一个是与实例化对象的成员一起工作。
答案 3 :(得分:0)
如果你真的想要干这个,你可能需要使用类方法而不是范围(如pavan暗示的那样)。类方法可以与作用域相同,并允许您使用公共位代码来确定属性和值(例如哈希常量?)。
但我建议不要这样做。为了干燥,这种抽象级别可能有点过头了。在我看来,您的实例方法可以分解为更简单的消息...例如good_some_val?
和good_some_other_val?
。另外,因为比较字符串通常是坏/脆。无论如何,总的来说,我希望您希望以不同的方式改进对象方法和范围。范围是它们的方式,因为您要查询数据库。就这样吧,但让你的对象继续传递最好的消息!
答案 4 :(得分:0)
是的,您可以从实例方法中调用作用域。
我已经可以使用以下模式从实例方法中调用作用域:
# Model
class Obj
# using numeric ids for this example
# simple scope to return an instance of the record with an id == 1
scope :get_first_record, -> { find(1) }
def call_scope
# I feel using self.class shows the intent of an instance calling its own Class methods, for readability
self.class.get_first_record
end
end
# Obj.count => 100
obj = Obj.create # obj.id => 101
obj.call_scope.id # = 1
将搜索逻辑埋在类方法中可能会在以后要求重构,以允许该逻辑在其他地方重用,例如 Mrdana的答案。
因此,我们可以使用上面的示例,原始范围并使用Pavan的观点来进行重构,Mrdana的答案可以重写为范围:
class User
scope :in_good_standing, -> { where(:some_val => 'foo', :some_other_val => 'bar' }
scope :users_in_good_standing, -> (users = all) { find(users.map(&:id)).in_good_standing } # same as: User.in_good_standing.where(:id => self.id) when self is the variable
def in_good_standing?
self.class.users_in_good_standing(self).present?
end
end
这使代码DRY保持松散耦合,可重用和可扩展;从实例方法调用作用域时。