Как создать полный Контрольный журнал в направляющих для каждой таблицы?

Мы недавно начали нажатие соответствия в нашей компании и обязаны сохранять полную историю изменений в наших данных, которыми в настоящее время управляют в приложении направляющих. Нам дали 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 удален.

Мой вопрос... Существует ли способ заставить его работать так, чтобы это наблюдало те изменения (и/или потенциально другой удаляет)?
Существует ли лучшее решение, которое все еще относительно незаметно?

20
задан Mark S. 4 March 2010 в 16:46
поделиться

4 ответа

У меня было аналогичное требование в недавнем проекте. Я закончил использовать гем plays_as_audited , и он отлично нам помог.

В моем контроллере приложения у меня есть строка, подобная следующей

audit RunWay,RunWayModel,OtherModelName

, и она берет на себя всю магию, она также ведет журнал всех изменений, которые были сделаны и кто их сделал - это довольно гладко.

Надеюсь, это поможет

10
ответ дан 30 November 2019 в 01:16
поделиться

Используйте плагин 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
6
ответ дан 30 November 2019 в 01:16
поделиться

Добавил эту обезьянью заплатку в наш 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

2
ответ дан 30 November 2019 в 01:16
поделиться

Вы также можете использовать что-то вроде actions_as_versioned http://github.com/technoweenie/acts_as_versioned
. Он редактирует ваши записи таблицы и создает копировать каждый раз, когда что-то меняется (например, в вики)
Это было бы легче контролировать (показывать различия в интерфейсе и т. д.), чем файл журнала

0
ответ дан 30 November 2019 в 01:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: