我有一个问题,允许我给后端用户分类元素的能力。我用它来做几个不同的元素。 rails社区似乎非常反对关注和回调,我想就如何更好地建模以下代码提出一些建议:
require 'active_support/concern'
module Rankable
extend ActiveSupport::Concern
included do
validates :row_order, :presence => true
scope :next_rank, lambda { |rank| where('row_order > ?',rank).order("row_order asc").limit(1)}
scope :previous_rank, lambda { |rank| where('row_order < ?',rank).order("row_order desc").limit(1)}
scope :bigger_rank, order("row_order desc").limit('1')
before_validation :assign_rank
end
def invert(target)
a = self.row_order
b = target.row_order
self.row_order = target.row_order
target.row_order = a
if self.save
if target.save
true
else
self.row_order = a
self.save
false
end
else
false
end
end
def increase_rank
return false unless self.next_rank.first && self.invert(self.next_rank.first)
end
def decrease_rank
return false unless self.previous_rank.first && self.invert(self.previous_rank.first)
end
private
def assign_default_rank
if !self.row_order
if self.class.bigger_rank.first
self.row_order = self.class.bigger_rank.first.row_order + 1
else
self.row_order=0
end
end
end
end
答案 0 :(得分:2)
我认为关注是你想要完成的事情的一个很好的选择(特别是对于验证和范围,因为ActiveRecord非常好地完成了这两个)。但是,如果您确实希望将事情从关注点移出,除了验证和范围之外,还有可能。只看代码,看起来你有一个等级的概念,它由一个整数表示,但可以成为它自己的对象:
class Rank
def initialize(rankable)
@rankable = rankable
@klass = rankable.class
end
def number
@rankable.row_order
end
def increase
next_rank ? RankableInversionService.call(@rankable, next_rank) : false
end
def decrease
previous_rank ? RankableInversionService.call(@rankable, previous_rank) : false
end
private
def next_rank
@next_rank ||= @klass.next_rank.first
end
def previous_rank
@previous_rank ||= @klass.previous_rank.first
end
end
要提取#invert方法,我们可以创建一个RankableInversionService(上面引用):
class RankableInversionService
def self.call(rankable, other)
new(rankable, other).call
end
def initialize(rankable, other)
@rankable = rankable
@other = other
@original_rankable_rank = rankable.rank
@original_other_rank = other.rank
end
def call
@rankable.rank = @other.rank
@other.rank = @rankable.rank
if @rankable.save && @other.save
true
else
@rankable.rank = @original_rankable_rank
@other.rank = @original_other_rank
@rankable.save
@other.save
false
end
end
end
要提取回调,您可以使用RankableUpdateService,在保存对象之前分配默认排名:
class RankableUpdateService
def self.call(rankable)
new(rankable).call
end
def initialize(rankable)
@rankable = rankable
@klass = rankable.class
end
def call
@rankable.rank = bigger_rank unless @rankable.ranked?
@rankable.save
end
private
def bigger_rank
@bigger_rank ||= @klass.bigger_rank.first.try(:rank)
end
end
现在你关注的是:
module Rankable
extend ActiveSupport::Concern
included do
# validations
# scopes
end
def rank
@rank ||= Rank.new(self)
end
def rank=(rank)
self.row_order = rank.number; @rank = rank
end
def ranked?
rank.number.present?
end
end
我确定如果您按原样使用此代码存在问题,但您可以获得该概念。总的来说,我认为在这里做的唯一好处就是提取一个Rank对象,除了它可能太复杂,关注封装得非常好。