Создание области 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
если вы хотите получить только Post
s, которые рассмотрели изменения:
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 %>