Создание области Active Record "утвержденных редактором" версий пользовательского контента

Я запускаю сайт, похожий на Stackru, где пользователи могут публиковать контент (сообщения). На главной странице также размещены избранные сообщения, выбранные нашими редакторами.

Я хочу, чтобы наши пользователи могли редактировать свои сообщения в любое время, при этом следя за тем, чтобы на нашей главной странице ничего не отображалось без предварительного просмотра нашими редакторами.

Чтобы решить эту проблему, я решил, что мы могли бы внедрить систему управления версиями, такую ​​как paper_trail и сохранить текущий version_id всякий раз, когда мы показываем пост. Таким образом, мы можем просто получить проверенный редактором контент на главной странице, при этом показывая самую последнюю версию в менее важных частях сайта, таких как профиль пользователя. Наши редакторы могут периодически просматривать любые изменения и одобрять их.

Мне интересно, какой самый чистый подход позволяет мне выбирать проверенные версии в одних контроллерах и последние версии в других, сохраняя при этом общий интерфейс и минимально дублированный код.


Идеальное решение будет включать в себя reviewed сфера, чтобы я мог просто сделать Post.reviewed и получить обратно проверенные версии, и Post.all чтобы получить последние версии.

Я не знаю, как это сделать, поскольку получение проверенной версии требует десериализации объекта, хранящегося в PaperTrail (version.reify) и это не представляется возможным в рамках.

Я мог бы использовать метод класса вместо этого:

def self.reviewed all.map do |post| Version.find(post.version_id).reify end.compact! || Post.none end

Тем не менее, это далеко не идеально, так как это не реальная область применения и, следовательно, не является цепью и т. Д.

В качестве альтернативы я мог бы использовать метод Post instance, например:

def reviewed_version Version.find(version_id).reify end

Это работает в теории, но это означает, что теперь мне нужно вызывать этот метод во всех моих представлениях, в то время как ответственность за выбор нужных данных лежит на контроллере. Допустим, у меня есть render collection: @posts как на главной странице, так и в профиле пользователя, как мой _post.html.erb частично знаю, стоит ли звонить reviewed_version или нет.


Я не привязан к PaperTrail, но у него есть много тонкостей, которые я предпочитаю не дублировать. Я открыт для изучения других направлений, если PaperTrail окажется слишком негибким.

FWIW, я также посмотрел на Draftsman, но он рассматривает "черновик" как исключение (т. Е. В большинстве случаев ты не показываешь черновик), тогда как в моем случае я хочу показать самую последнюю версию на большинстве страниц, за исключением некоторых, как на главной странице.

1 ответ

Решение

Это звучит слишком сложно для того, чтобы сделать что-то очень простое само по себе

  • у тебя есть Post модель, которая содержит общие атрибуты (например, автор и дата создания)
  • Post has_many :revisions
  • Revision имеет reviewed логический атрибут

Если вы хотите найти последнюю проверенную ревизию для Post, вы можете иметь:

class Post < ActiveRecord::Base
  has_many :revisions

  delegate :content, to: :current_revision # you can even have those to transparently delegate

  def current_revision
    @current_revision ||= revisions.where( reviewed: true ).order( "created_at desc" ).first
  end
end

если вы хотите получить только Posts, которые рассмотрели изменения:

class Post < ActiveRecord::Base
  has_many :revisions

  scope :reviewed, ->{ group( 'posts.id' ) ).having( 'count(revisions.id) > 0' ).joins( :revisions ).where( "revisions.reviewed = ?", true ) }
end

Вот как бы я это сделал, пусть люди более знакомы с paper_trail расскажу о своем пути:)

РЕДАКТИРОВАТЬ

Как обсуждалось в комментариях, декоратор может также помочь дифференцировать ревизию и делегировать ей методы, в то же время имея возможность передавать отношение activerecord. Которые дают что-то таким образом:

class Post < ActiveRecord::Base
  has_many :revisions

  scope :reviewed, ->{ group( 'posts.id' ) ).having( 'count(revisions.id) > 0' ).joins( :revisions ).where( "revisions.reviewed = ?", true ) }

  def reviewed_revision
    @reviewed_revision ||= revisions.where( reviewed: true ).order( "created_at desc" ).first
  end

  def latest_revision
    @latest_revision ||= revisions.order( "created_at desc" ).first
  end
end

class PostDecorator
  attr_reader :post, :revision
  delegate :content, :updated_at, to: :revision

  def self.items( scope, revision_method )
    revision_method = :reviewed_revision unless %i[ reviewed_revision latest_revision ].include?( revision_method )

    scope.map do |post|
      PostDecorator.new( post, post.send( revision_method ) )
    end
  end

  def initalize( post, revision )
    @post, @revision = post, revision
  end

  def method_missing( action_name, *args, &block )
    post.send( action_name, *args, &block )
  end
end

# view
#
# <% PostDecorator.items( Post.reviewed.limit(10), :reviewed_revision ).each do |post| %>
# <p><%= post.author.name %></p>
# <p><%= simple_format post.content %></p>
# <% end %>
Другие вопросы по тегам