<commit_before>module PaperTrail

  def self.included(base)
    base.send :extend, ClassMethods
  end


  module ClassMethods
    # Options:
    # :ignore    an array of attributes for which a new +Version+ will not be created if only they change.
    # :meta      a hash of extra data to store.  You must add a column to the versions table for each key.
    #            Values are objects or procs (which are called with +self+, i.e. the model with the paper
    #            trail).
    def has_paper_trail(options = {})
      send :include, InstanceMethods

      cattr_accessor :ignore
      self.ignore = (options[:ignore] || []).map &:to_s

      cattr_accessor :meta
      self.meta = options[:meta] || {}
      
      cattr_accessor :paper_trail_active
      self.paper_trail_active = true

      has_many :versions, :as => :item, :order => 'created_at ASC, id ASC'

      after_create  :record_create
      before_update :record_update
      after_destroy :record_destroy
    end

    def paper_trail_off
      self.paper_trail_active = false
    end

    def paper_trail_on
      self.paper_trail_active = true
    end
  end


  module InstanceMethods
    def record_create
      if self.class.paper_trail_active
        versions.create merge_metadata(:event => 'create', :whodunnit => PaperTrail.whodunnit)
      end
    end

    def record_update
      if changed_and_we_care? and self.class.paper_trail_active
        versions.build merge_metadata(:event     => 'update',
                                      :object    => object_to_string(previous_version),
                                      :whodunnit => PaperTrail.whodunnit)
      end
    end

    def record_destroy
      if self.class.paper_trail_active
        versions.create merge_metadata(:event     => 'destroy',
                                       :object    => object_to_string(previous_version),
                                       :whodunnit => PaperTrail.whodunnit)
      end
    end
    
    # Returns the object at the version that was valid at the given timestamp.
    def version_at(timestamp)
      # short-circuit if the current state is valid
      return self if self.updated_at <= timestamp
      
      version = versions.first(
        :conditions => ['created_at > ?', timestamp], 
        :order => 'created_at ASC')
      version.reify if version
    end

    private

    def merge_metadata(data)
      meta.each do |k,v|
        data[k] = v.respond_to?(:call) ? v.call(self) : v
      end
      data
    end<commit_msg>Added comments.<commit_after>module PaperTrail

  def self.included(base)
    base.send :extend, ClassMethods
  end


  module ClassMethods
    # Options:
    # :ignore    an array of attributes for which a new +Version+ will not be created if only they change.
    # :meta      a hash of extra data to store.  You must add a column to the versions table for each key.
    #            Values are objects or procs (which are called with +self+, i.e. the model with the paper
    #            trail).
    def has_paper_trail(options = {})
      send :include, InstanceMethods

      cattr_accessor :ignore
      self.ignore = (options[:ignore] || []).map &:to_s

      cattr_accessor :meta
      self.meta = options[:meta] || {}

      cattr_accessor :paper_trail_active
      self.paper_trail_active = true

      has_many :versions, :as => :item, :order => 'created_at ASC, id ASC'

      after_create  :record_create
      before_update :record_update
      after_destroy :record_destroy
    end

    def paper_trail_off
      self.paper_trail_active = false
    end

    def paper_trail_on
      self.paper_trail_active = true
    end
  end


  module InstanceMethods
    def record_create
      if self.class.paper_trail_active
        versions.create merge_metadata(:event => 'create', :whodunnit => PaperTrail.whodunnit)
      end
    end

    def record_update
      if changed_and_we_care? and self.class.paper_trail_active
        versions.build merge_metadata(:event     => 'update',
                                      :object    => object_to_string(previous_version),
                                      :whodunnit => PaperTrail.whodunnit)
      end
    end

    def record_destroy
      if self.class.paper_trail_active
        versions.create merge_metadata(:event     => 'destroy',
                                       :object    => object_to_string(previous_version),
                                       :whodunnit => PaperTrail.whodunnit)
      end
    end

    # Returns the object (not a Version) as it was at the given timestamp.
    def version_at(timestamp)
      # Short-circuit if the current state is applicable.
      return self if self.updated_at <= timestamp
      # Look for the first version created after, rather than before, the
      # timestamp because a version stores how the object looked before the
      # change.
      version = versions.first :conditions => ['created_at > ?', timestamp],
                               :order      => 'created_at ASC'
      version.reify if version
    end

    private

    def merge_metadata(data)
      meta.each do |k,v|
        data[k] = v.respond_to?(:call) ? v.call(self) : v
      end
      data
    end
{'fraction_non_alphanumeric': 0.07506702412868632, 'fraction_numerical': 0.0, 'mean_word_length': 2.092362344582593, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5812014', 'n_tokens_mistral': 1510, 'n_tokens_neox': 1462, 'n_words': 433}