具有 ActiveResource 和 ActiveRecord 的 Rails 审计系统

发布于 2024-11-06 19:28:26 字数 711 浏览 4 评论 0 原文

我有一个包含 ActiveRecord 和 ActiveResource 模型的庞大项目。我需要使用这些模型实现用户活动的日志记录,并记录模型属性的更改(保存对象状态或类似的东西)。用户或 cron rake 任务可以进行更改。

我还必须能够按日期、任何字段搜索任何数据..等

,例如,通过上次活动生成可读消息也很好

  • 用户 Bob 将密码更改为 * 并于 2011 年 8 月 12 日 08:12 发送电子邮件至 **
  • 员工 Jeff 添加了新合作伙伴:公司名称,时间为 2011 年 8 月 12 日 08:13
  • 管理员 Jack 已删除产品:产品名称于 2011-09-12 11:11
  • 客户 Sam 订购了新服务:服务名称于 2011-09-12 11:12

有人实现这样的日志记录吗?有想法吗?建议?

我应该使用 gems 还是我可以在观察者不改变模型的情况下完成所有逻辑?


我喜欢 gem https://github.com/airblade/paper_trail 谁能告诉我如何让它工作与活动资源?

I have a huge project with both of ActiveRecord and ActiveResource models. I need to implement logging of user activity with these models and also to log changes of model attributes (save object state or somthing like that). Changes can made by users or cron rake tasks.

I also must have possibility to search any data by date , any field ..etc

Will be nice also to generate readable messages with last activity , for example

  • User Bob change his password to * and email to ** at 2011-08-12 08:12
  • Staff Jeff added new partner: Company name at 2011-08-12 08:13
  • Admin Jack deleted product : Product name at 2011-09-12 11:11
  • Client Sam ordered new service : Service name at 2011-09-12 11:12

Does anybody implement such logging? Ideas? Advices?

should I use gems or can I do all the logic with observers not changing models?


I liked gem https://github.com/airblade/paper_trail can anybody say how can I make it work with activeresource ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

生活了然无味 2024-11-13 19:28:26

您正在寻找

https://github.com/collectiveidea/acts_as_audited

很少有开源项目使用该插件我想想红矿以及工头

编辑:不幸的是它只能做ActiveRecord,不能做ActiveResource。

You are looking for

https://github.com/collectiveidea/acts_as_audited

Few open source projects use that plugin I think Red Mine as well as The Foreman.

Edit: Unfortunately it can do only ActiveRecord, not ActiveResource.

一场信仰旅途 2024-11-13 19:28:26

Fivell,我刚刚看到这个问题,今晚在赏金到期之前没有时间进行修改,所以我会给你我的审计代码,它可以与 ActiveRecord 一起使用,并且应该与 ActiveResource 一起使用,也许需要一些调整(我不要经常使用 ARes 来立即了解)。我知道我们使用的回调在那里,但我不确定 ARes 是否具有 ActiveRecord 的脏属性 changes 跟踪。

此代码记录所有模型上的每个 CREATE/UPDATE/DELETE(审核日志模型上的 CREATE 和您指定的任何其他例外除外),并将更改存储为 JSON。还存储清理后的回溯,以便您可以确定哪些代码进行了更改(这捕获了 MVC 中的任何点以及 rake 任务和控制台使用情况)。

此代码适用于控制台使用、rake 任务和 http 请求,尽管通常只有最后一个记录当前用户。 (如果我没记错的话,它所替换的 ActiveRecord 观察器在 rake 任务或控制台中不起作用。)哦,这段代码来自 Rails 2.3 应用程序 - 我有几个 Rails 3 应用程序,但我不需要这种类型还没有对他们进行审计。

我没有可以很好地显示此信息的代码(我们仅在需要研究问题时才深入研究数据),但由于更改存储为 JSON,因此应该相当简单。

首先,我们将当前用户存储在 User.current 中,以便可以在任何地方访问它,因此在 app/models/user.rb 中:

Class User < ActiveRecord::Base
  cattr_accessor :current
  ...
end

当前用户在每个请求的应用程序控制器中设置,如下所示(并且不会导致并发问题):

def current_user
  User.current = session[:user_id] ? User.find_by_id(session[:user_id]) : nil
end

如果有意义的话,您可以在 rake 任务中设置 User.current

接下来,我们定义模型来存储审核信息 app/models/audit_log_entry.rb - 您需要自定义 IgnoreClassesRegEx 以适应您不想审核的任何模型:

# == Schema Information
#
# Table name: audit_log_entries
#
#  id         :integer         not null, primary key
#  class_name :string(255)
#  entity_id  :integer
#  user_id    :integer
#  action     :string(255)
#  data       :text
#  call_chain :text
#  created_at :datetime
#  updated_at :datetime
#

class AuditLogEntry < ActiveRecord::Base
  IgnoreClassesRegEx = /^ActiveRecord::Acts::Versioned|ActiveRecord.*::Session|Session|Sequence|SchemaMigration|CronRun|CronRunMessage|FontMetric$/
  belongs_to :user

  def entity (reload = false)
    @entity = nil if reload
    begin
      @entity ||= Kernel.const_get(class_name).find_by_id(entity_id)
    rescue
      nil
    end
  end

  def call_chain
    return if call_chain_before_type_cast.blank?
    if call_chain_before_type_cast.instance_of?(Array)
      call_chain_before_type_cast
    else
      JSON.parse(call_chain_before_type_cast)
    end
  end
  def data
    return if data_before_type_cast.blank?
    if data_before_type_cast.instance_of?(Hash)
      data_before_type_cast
    else
      JSON.parse(data_before_type_cast)
    end
  end

  def self.debug_entity(class_name, entity_id)
    require 'fastercsv'
    FasterCSV.generate do |csv|
      csv << %w[class_name entity_id date action first_name last_name data]
      find_all_by_class_name_and_entity_id(class_name, entity_id,
                                           :order => 'created_at').each do |a|
        csv << [a.class_name, a.entity_id, a.created_at, a.action, 
          (a.user && a.user.first_name), (a.user && a.user.last_name), a.data]
      end
    end
  end
end

接下来我们向 ActiveRecord::Base 添加一些方法以使审核工作。您需要查看 audit_log_clean_backtrace 方法并根据您的需要进行修改。 (FWIW,我们在 lib/extensions/*.rb 中添加了现有类,这些类在初始化程序中加载。)在 lib/extensions/active_record.rb 中:

class ActiveRecord::Base
  cattr_accessor :audit_log_backtrace_cleaner
  after_create  :audit_log_on_create
  before_update :save_audit_log_update_diff
  after_update  :audit_log_on_update
  after_destroy :audit_log_on_destroy
  def audit_log_on_create
    return if self.class.name =~ /AuditLogEntry/
    return if self.class.name =~ AuditLogEntry::IgnoreClassesRegEx
    audit_log_create 'CREATE', self, caller
  end
  def save_audit_log_update_diff
    @audit_log_update_diff = changes.reject{ |k,v| 'updated_at' == k }
  end
  def audit_log_on_update
    return if self.class.name =~ AuditLogEntry::IgnoreClassesRegEx
    return if @audit_log_update_diff.empty?
    audit_log_create 'UPDATE', @audit_log_update_diff, caller
  end
  def audit_log_on_destroy
    return if self.class.name =~ AuditLogEntry::IgnoreClassesRegEx
    audit_log_create 'DESTROY', self, caller
  end
  def audit_log_create (action, data, call_chain)
    AuditLogEntry.create :user       => User.current,
                         :action     => action,
                         :class_name => self.class.name,
                         :entity_id  => id,
                         :data       => data.to_json,
                         :call_chain => audit_log_clean_backtrace(call_chain).to_json
  end
  def audit_log_clean_backtrace (backtrace)
    if !ActiveRecord::Base.audit_log_backtrace_cleaner
      ActiveRecord::Base.audit_log_backtrace_cleaner = ActiveSupport::BacktraceCleaner.new
      ActiveRecord::Base.audit_log_backtrace_cleaner.add_silencer { |line| line =~ /\/lib\/rake\.rb/ }
      ActiveRecord::Base.audit_log_backtrace_cleaner.add_silencer { |line| line =~ /\/bin\/rake/ }
      ActiveRecord::Base.audit_log_backtrace_cleaner.add_silencer { |line| line =~ /\/lib\/(action_controller|active_(support|record)|hoptoad_notifier|phusion_passenger|rack|ruby|sass)\// }
      ActiveRecord::Base.audit_log_backtrace_cleaner.add_filter   { |line| line.gsub(RAILS_ROOT, '') }
    end
    ActiveRecord::Base.audit_log_backtrace_cleaner.clean backtrace
  end
end

最后,这是我们对此进行的测试 - 当然,您需要修改实际的测试操作。 test/integration/audit_log_test.rb

require File.dirname(__FILE__) + '/../test_helper'

class AuditLogTest < ActionController::IntegrationTest
  def setup
  end

  def test_audit_log
    u = users(:manager)
    log_in u
    a = Alert.first :order => 'id DESC'
    visit 'alerts/new'
    fill_in 'alert_note'
    click_button 'Send Alert'
    a = Alert.first :order => 'id DESC', :conditions => ['id > ?', a ? a.id : 0]
    ale = AuditLogEntry.first :conditions => {:class_name => 'Alert', :entity_id => a.id }
    assert_equal 'Alert',  ale.class_name
    assert_equal 'CREATE', ale.action
  end

private

  def log_in (user, password = 'test', initial_url = home_path)
    visit initial_url
    assert_contain 'I forgot my password'
    fill_in 'email',    :with => user.email
    fill_in 'password', :with => password
    click_button 'Log In'
  end

  def log_out
    visit logout_path
    assert_contain 'I forgot my password'
  end
end

test/unit/audit_log_entry_test.rb

# == Schema Information
#
# Table name: audit_log_entries
#
#  id         :integer         not null, primary key
#  class_name :string(255)
#  action     :string(255)
#  data       :text
#  user_id    :integer
#  created_at :datetime
#  updated_at :datetime
#  entity_id  :integer
#  call_chain :text
#

require File.dirname(__FILE__) + '/../test_helper'

class AuditLogEntryTest < ActiveSupport::TestCase
  test 'should handle create update and delete' do
    record = Alert.new :note => 'Test Alert'
    assert_difference 'Alert.count' do
      assert_difference 'AuditLogEntry.count' do
        record.save
        ale = AuditLogEntry.first :order => 'created_at DESC'
        assert ale
        assert_equal 'CREATE', ale.action, 'AuditLogEntry.action should be CREATE'
        assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name'
        assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id'
      end
    end
    assert_difference 'AuditLogEntry.count' do
      record.update_attribute 'note', 'Test Update'
      ale = AuditLogEntry.first :order => 'created_at DESC'
      expected_data = {'note' => ['Test Alert', 'Test Update']}
      assert ale
      assert_equal 'UPDATE', ale.action, 'AuditLogEntry.action should be UPDATE'
      assert_equal expected_data, ale.data
      assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name'
      assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id'
    end
    assert_difference 'AuditLogEntry.count' do
      record.destroy
      ale = AuditLogEntry.first :order => 'created_at DESC'
      assert ale
      assert_equal 'DESTROY', ale.action, 'AuditLogEntry.action should be CREATE'
      assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name'
      assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id'
      assert_nil Alert.find_by_id(record.id), 'Alert should be deleted'
    end
  end

  test 'should not log AuditLogEntry create entry and block on update and delete' do
    record = Alert.new :note => 'Test Alert'
    assert_difference 'Alert.count' do
      assert_difference 'AuditLogEntry.count' do
        record.save
      end
    end
    ale = AuditLogEntry.first :order => 'created_at DESC'
    assert_equal 'CREATE', ale.action, 'AuditLogEntry.action should be CREATE'
    assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name'
    assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id'
    assert_nil AuditLogEntry.first(:conditions => { :class_name => 'AuditLogEntry', :entity_id => ale.id })

    if ale.user_id.nil?
      u = User.first
    else
      u = User.first :conditions => ['id != ?', ale.user_id]
    end
    ale.user_id = u.id
    assert !ale.save

    assert !ale.destroy
  end
end

Fivell, I just saw this question and don't have time to work up alterations this evening before the bounty expires, so I'll give you my auditing code that works with ActiveRecord and should work with ActiveResource, perhaps with a few tweaks (I don't use ARes often enough to know offhand). I know the callbacks we use are there, but I'm not sure if ARes has ActiveRecord's dirty attribute changes tracking.

This code logs each CREATE/UPDATE/DELETE on all models (excepting CREATEs on the audit log model and any other exceptions you specify) with the changes stored as JSON. A cleaned backtrace is also stored so you can determine what code made the change (this captures any point in your MVC as well as rake tasks and console usage).

This code works for console usage, rake tasks, and http requests, although generally only the last one logs the current user. (If I recall correctly, the ActiveRecord observer that this replaced did not work in rake tasks or the console.) Oh, this code comes from a Rails 2.3 app - I have a couple Rails 3 apps, but I haven't needed this kind of auditing for them yet.

I don't have code that builds a nice display of this information (we only dig into the data when we need to look into an issue), but since the changes are stored as JSON it should be fairly straightforward.

First, we store the current user in User.current so it is accessible everywhere, so in app/models/user.rb:

Class User < ActiveRecord::Base
  cattr_accessor :current
  ...
end

The current user is set in the application controller for each request like so (and does not cause concurrency issues):

def current_user
  User.current = session[:user_id] ? User.find_by_id(session[:user_id]) : nil
end

You could set User.current in your rake tasks if it made sense to.

Next, we define the model to store the audit info app/models/audit_log_entry.rb - you'll want to customize IgnoreClassesRegEx to fit any models you don't want audited:

# == Schema Information
#
# Table name: audit_log_entries
#
#  id         :integer         not null, primary key
#  class_name :string(255)
#  entity_id  :integer
#  user_id    :integer
#  action     :string(255)
#  data       :text
#  call_chain :text
#  created_at :datetime
#  updated_at :datetime
#

class AuditLogEntry < ActiveRecord::Base
  IgnoreClassesRegEx = /^ActiveRecord::Acts::Versioned|ActiveRecord.*::Session|Session|Sequence|SchemaMigration|CronRun|CronRunMessage|FontMetric$/
  belongs_to :user

  def entity (reload = false)
    @entity = nil if reload
    begin
      @entity ||= Kernel.const_get(class_name).find_by_id(entity_id)
    rescue
      nil
    end
  end

  def call_chain
    return if call_chain_before_type_cast.blank?
    if call_chain_before_type_cast.instance_of?(Array)
      call_chain_before_type_cast
    else
      JSON.parse(call_chain_before_type_cast)
    end
  end
  def data
    return if data_before_type_cast.blank?
    if data_before_type_cast.instance_of?(Hash)
      data_before_type_cast
    else
      JSON.parse(data_before_type_cast)
    end
  end

  def self.debug_entity(class_name, entity_id)
    require 'fastercsv'
    FasterCSV.generate do |csv|
      csv << %w[class_name entity_id date action first_name last_name data]
      find_all_by_class_name_and_entity_id(class_name, entity_id,
                                           :order => 'created_at').each do |a|
        csv << [a.class_name, a.entity_id, a.created_at, a.action, 
          (a.user && a.user.first_name), (a.user && a.user.last_name), a.data]
      end
    end
  end
end

Next we add some methods to ActiveRecord::Base to make the audits work. You'll want to look at the audit_log_clean_backtrace method and modify for your needs. (FWIW, we put additions to existing classes in lib/extensions/*.rb which are loaded in an initializer.) In lib/extensions/active_record.rb:

class ActiveRecord::Base
  cattr_accessor :audit_log_backtrace_cleaner
  after_create  :audit_log_on_create
  before_update :save_audit_log_update_diff
  after_update  :audit_log_on_update
  after_destroy :audit_log_on_destroy
  def audit_log_on_create
    return if self.class.name =~ /AuditLogEntry/
    return if self.class.name =~ AuditLogEntry::IgnoreClassesRegEx
    audit_log_create 'CREATE', self, caller
  end
  def save_audit_log_update_diff
    @audit_log_update_diff = changes.reject{ |k,v| 'updated_at' == k }
  end
  def audit_log_on_update
    return if self.class.name =~ AuditLogEntry::IgnoreClassesRegEx
    return if @audit_log_update_diff.empty?
    audit_log_create 'UPDATE', @audit_log_update_diff, caller
  end
  def audit_log_on_destroy
    return if self.class.name =~ AuditLogEntry::IgnoreClassesRegEx
    audit_log_create 'DESTROY', self, caller
  end
  def audit_log_create (action, data, call_chain)
    AuditLogEntry.create :user       => User.current,
                         :action     => action,
                         :class_name => self.class.name,
                         :entity_id  => id,
                         :data       => data.to_json,
                         :call_chain => audit_log_clean_backtrace(call_chain).to_json
  end
  def audit_log_clean_backtrace (backtrace)
    if !ActiveRecord::Base.audit_log_backtrace_cleaner
      ActiveRecord::Base.audit_log_backtrace_cleaner = ActiveSupport::BacktraceCleaner.new
      ActiveRecord::Base.audit_log_backtrace_cleaner.add_silencer { |line| line =~ /\/lib\/rake\.rb/ }
      ActiveRecord::Base.audit_log_backtrace_cleaner.add_silencer { |line| line =~ /\/bin\/rake/ }
      ActiveRecord::Base.audit_log_backtrace_cleaner.add_silencer { |line| line =~ /\/lib\/(action_controller|active_(support|record)|hoptoad_notifier|phusion_passenger|rack|ruby|sass)\// }
      ActiveRecord::Base.audit_log_backtrace_cleaner.add_filter   { |line| line.gsub(RAILS_ROOT, '') }
    end
    ActiveRecord::Base.audit_log_backtrace_cleaner.clean backtrace
  end
end

Finally, here are the tests we have on this - you'll need to modify the actual test actions of course. test/integration/audit_log_test.rb

require File.dirname(__FILE__) + '/../test_helper'

class AuditLogTest < ActionController::IntegrationTest
  def setup
  end

  def test_audit_log
    u = users(:manager)
    log_in u
    a = Alert.first :order => 'id DESC'
    visit 'alerts/new'
    fill_in 'alert_note'
    click_button 'Send Alert'
    a = Alert.first :order => 'id DESC', :conditions => ['id > ?', a ? a.id : 0]
    ale = AuditLogEntry.first :conditions => {:class_name => 'Alert', :entity_id => a.id }
    assert_equal 'Alert',  ale.class_name
    assert_equal 'CREATE', ale.action
  end

private

  def log_in (user, password = 'test', initial_url = home_path)
    visit initial_url
    assert_contain 'I forgot my password'
    fill_in 'email',    :with => user.email
    fill_in 'password', :with => password
    click_button 'Log In'
  end

  def log_out
    visit logout_path
    assert_contain 'I forgot my password'
  end
end

And test/unit/audit_log_entry_test.rb:

# == Schema Information
#
# Table name: audit_log_entries
#
#  id         :integer         not null, primary key
#  class_name :string(255)
#  action     :string(255)
#  data       :text
#  user_id    :integer
#  created_at :datetime
#  updated_at :datetime
#  entity_id  :integer
#  call_chain :text
#

require File.dirname(__FILE__) + '/../test_helper'

class AuditLogEntryTest < ActiveSupport::TestCase
  test 'should handle create update and delete' do
    record = Alert.new :note => 'Test Alert'
    assert_difference 'Alert.count' do
      assert_difference 'AuditLogEntry.count' do
        record.save
        ale = AuditLogEntry.first :order => 'created_at DESC'
        assert ale
        assert_equal 'CREATE', ale.action, 'AuditLogEntry.action should be CREATE'
        assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name'
        assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id'
      end
    end
    assert_difference 'AuditLogEntry.count' do
      record.update_attribute 'note', 'Test Update'
      ale = AuditLogEntry.first :order => 'created_at DESC'
      expected_data = {'note' => ['Test Alert', 'Test Update']}
      assert ale
      assert_equal 'UPDATE', ale.action, 'AuditLogEntry.action should be UPDATE'
      assert_equal expected_data, ale.data
      assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name'
      assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id'
    end
    assert_difference 'AuditLogEntry.count' do
      record.destroy
      ale = AuditLogEntry.first :order => 'created_at DESC'
      assert ale
      assert_equal 'DESTROY', ale.action, 'AuditLogEntry.action should be CREATE'
      assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name'
      assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id'
      assert_nil Alert.find_by_id(record.id), 'Alert should be deleted'
    end
  end

  test 'should not log AuditLogEntry create entry and block on update and delete' do
    record = Alert.new :note => 'Test Alert'
    assert_difference 'Alert.count' do
      assert_difference 'AuditLogEntry.count' do
        record.save
      end
    end
    ale = AuditLogEntry.first :order => 'created_at DESC'
    assert_equal 'CREATE', ale.action, 'AuditLogEntry.action should be CREATE'
    assert_equal record.class.name, ale.class_name, 'AuditLogEntry.class_name should match record.class.name'
    assert_equal record.id, ale.entity_id, 'AuditLogEntry.entity_id should match record.id'
    assert_nil AuditLogEntry.first(:conditions => { :class_name => 'AuditLogEntry', :entity_id => ale.id })

    if ale.user_id.nil?
      u = User.first
    else
      u = User.first :conditions => ['id != ?', ale.user_id]
    end
    ale.user_id = u.id
    assert !ale.save

    assert !ale.destroy
  end
end
弱骨蛰伏 2024-11-13 19:28:26

https://github.com/collectiveidea/acts_as_audited

https://github.com/airblade/paper_trail

都是仅适用于 ActiveRecord 的出色解决方案,但由于 ActiveRecord 的大部分内容 已被提取到 ActiveModel,因此扩展其中一个以支持 ActiveResource 可能是合理的,至少对于只读支持而言。我浏览了 Github 网络图并用 google 搜索了一下,似乎没有任何正在进行的此类解决方案的开发,尽管如此,我预计在这两个插件之一上实现比从头开始更容易。 paper_trail 似乎正在更积极地开发中,并且对 Rails 3.1 有一些提交,因此它可能与 Rails 内部结构更同步并且更容易扩展,但这只是一种直觉 - 我不是熟悉两者的内部结构。

https://github.com/collectiveidea/acts_as_audited

and

https://github.com/airblade/paper_trail

are both great solutions for ActiveRecord only, but since much of ActiveRecord has been extracted to ActiveModel, it's likely to be reasonable to extend either to support ActiveResource as well, at least for read-only support. I looked through the Github network graphs and googled around and there doesn't appear to be any ongoing development of such a solution, nevertheless I expect it will be easier to implement on top of one of these two plugins than starting from scratch. paper_trail appears to be under more active development and has some commits for Rails 3.1, so it may be more up to date with Rails internals and easier to extend, but that's just a gut instinct—I'm not familiar with the internals of either.

终遇你 2024-11-13 19:28:26

acts_as_audited gem 应该很适合您:
https://github.com/collectiveidea/acts_as_audited

而就 ActiveResource 而言,它也会其他应用程序中的模型。您可以在服务器端使用gem,并且不需要在客户端对其进行审核。所有使用 ActiveResource 的 CRUD 操作最终都会转换为 ActiveRecord(服务器端)上的 CRUD 操作。

因此,您可能需要从远处看它,并且相同的解决方案适用于这两种情况,但在不同的地方。

The acts_as_audited gem should work well for you:
https://github.com/collectiveidea/acts_as_audited

And as far as ActiveResource is considered, it will also be a model in some other application. You can use the gem at the server side, and you don't need to audit it at the client side. All the CRUD operations using ActiveResource would finally translate to CRUD operations on the ActiveRecord (on server side).

So probably you need to look at it from a distance, and the same solution would apply in both the cases, but at different places.

煞人兵器 2024-11-13 19:28:26

为了跟踪用户活动(CRUD),我创建了一个继承自 Logger 的类,现在我计划编写一个小插件来跟踪用户,我可以将其用于构建的任何 ROR 应用程序。我已经检查过是否有类似的插件,但我没有看到。我想有很多像 paper-trail、acts_as_audited 或 itslog 这样的 gem,但我更喜欢使用插件。有什么建议吗?
这是一个可能对您有帮助的链接:http://robaldred.co.uk/2009/01/custom-log-files-for-your-ruby-on-rails-applications/comment-page-1 /#comment-342

不错的编码

for tracking user activity(CRUD ), i've created a class inherits from Logger, and now I am planing to write a litle plugin for tracking user that i can use for any ROR application built. I have already checked if there is a plugin like that but I didn’t see. I guess there are many gem like paper-trail, acts_as_audited or itslog but i prefer to use a plugin. Any suggestions?
Here is a link that might help you : http://robaldred.co.uk/2009/01/custom-log-files-for-your-ruby-on-rails-applications/comment-page-1/#comment-342

nice coding

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文