1

我有一個擔心,允許我給後端用戶排序元素的能力。我將它用於幾個不同的元素。 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 

回答

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 

把解壓出來,我們可以創建一個RankableInversionService的#invert方法(上面提到的) :

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對象,除了它可能太複雜以至於關注封裝得非常好。

相關問題