Мы недавно начали нажатие соответствия в нашей компании и обязаны сохранять полную историю изменений в наших данных, которыми в настоящее время управляют в приложении направляющих. Нам дали OK для простого продвижения чего-то описательного для каждого действия к файлу журнала, который является довольно незаметным способом пойти.
Мой наклон состоит в том, чтобы выполнить в чем-то вроде этого ApplicationController
:
around_filter :set_logger_username
def set_logger_username
Thread.current["username"] = current_user.login || "guest"
yield
Thread.current["username"] = nil
end
Тогда создайте наблюдателя, который выглядит примерно так:
class AuditObserver < ActiveRecord::Observer
observe ... #all models that need to be observed
def after_create(auditable)
AUDIT_LOG.info "[#{username}][ADD][#{auditable.class.name}][#{auditable.id}]:#{auditable.inspect}"
end
def before_update(auditable)
AUDIT_LOG.info "[#{username}][MOD][#{auditable.class.name}][#{auditable.id}]:#{auditable.changed.inspect}"
end
def before_destroy(auditable)
AUDIT_LOG.info "[#{username}][DEL][#{auditable.class.name}][#{auditable.id}]:#{auditable.inspect}"
end
def username
(Thread.current['username'] || "UNKNOWN").ljust(30)
end
end
и в целом это работает отлично, но это перестало работать при использовании "волшебства" <association>_ids
метод, который прикрепляется к has_many: через => ассоциации.
Например:
# model
class MyModel
has_many :runway_models, :dependent => :destroy
has_many :runways, :through => :runway_models
end
#controller
class MyModelController < ApplicationController
# ...
# params => {:my_model => {:runways_ids => ['1', '2', '3', '5', '8']}}
def update
respond_to do |format|
if @my_model.update_attributes(params[:my_model])
flash[:notice] = 'My Model was successfully updated.'
format.html { redirect_to(@my_model) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @my_model.errors, :status => :unprocessable_entity }
end
end
end
# ...
end
Это закончит тем, что инициировало after_create
когда новый Runway
записи связаны, но не инициируют before_destroy
когда a RunwayModel
удален.
Мой вопрос... Существует ли способ заставить его работать так, чтобы это наблюдало те изменения (и/или потенциально другой удаляет)?
Существует ли лучшее решение, которое все еще относительно незаметно?
У меня было аналогичное требование в недавнем проекте. Я закончил использовать гем plays_as_audited , и он отлично нам помог.
В моем контроллере приложения у меня есть строка, подобная следующей
audit RunWay,RunWayModel,OtherModelName
, и она берет на себя всю магию, она также ведет журнал всех изменений, которые были сделаны и кто их сделал - это довольно гладко.
Надеюсь, это поможет
Используйте плагин Vestal versions для этого:
См. этот экран для более подробной информации. Посмотрите на аналогичный вопрос , на который недавно был дан ответ.
Плагин Vestal versions
- самый активный плагин, он хранит только дельту. Дельта, принадлежащая разным моделям, хранится в одной таблице.
class User < ActiveRecord::Base
versioned
end
# following lines of code is from the readme
>> u = User.create(:first_name => "Steve", :last_name => "Richert")
=> #<User first_name: "Steve", last_name: "Richert">
>> u.version
=> 1
>> u.update_attribute(:first_name, "Stephen")
=> true
>> u.name
=> "Stephen Richert"
>> u.version
=> 2
>> u.revert_to(10.seconds.ago)
=> 1
>> u.name
=> "Steve Richert"
>> u.version
=> 1
>> u.save
=> true
>> u.version
=> 3
Добавил эту обезьянью заплатку в наш lib/core_extensions.rb
ActiveRecord::Associations::HasManyThroughAssociation.class_eval do
def delete_records(records)
klass = @reflection.through_reflection.klass
records.each do |associate|
klass.destroy_all(construct_join_attributes(associate))
end
end
end
Это удар по производительности(!), но удовлетворяет требованию, и учитывая тот факт, что этот destroy_all вызывается не часто, он подходит для наших нужд - хотя я собираюсь проверить acts_as_versioned и acts_as_audited
Вы также можете использовать что-то вроде actions_as_versioned http://github.com/technoweenie/acts_as_versioned
. Он редактирует ваши записи таблицы и создает копировать каждый раз, когда что-то меняется (например, в вики)
Это было бы легче контролировать (показывать различия в интерфейсе и т. д.), чем файл журнала