Rubernate

{Dynamic Persistence
for Dynamic Language}

#
# This is advansed example - demonstrates how to use Callbacks to extend basic functionality.
# It introduce History facility to track all changes of Peristent objects.
#
require 'ex0_config'

# Represents history item
class HistoryItem
  persistent :pk, :klass, :type, :time, :event_id, :p_name, :old_value, :new_value
 
  def initialize object, type, p_name=nil, old_value=nil, new_value=nil
    self.pk = object.primary_key
    self.klass = object.class.name
    self.type = type.to_s
    self.time = Time.now
    self.event_id = runtime.event_id
    self.p_name = p_name.to_s
    self.old_value = old_value.to_s
    self.new_value = new_value.to_s
  end
  
  def to_s
    res = "#{self.event_id}, #{self.time}, #{self.klass}: #{self.pk}, event: #{self.type}"
    res+= " property: #{self.p_name}, old: #{self.old_value}, new: #{self.new_value}" if
      type == "on_change"
    res
  end
end

$session_id = 0

# Represent handler for interesting events
module Rubernate
module Callbacks
  # Defines extenisions to all persistent classes
  module Persistent
    def on_create
      return if is_a? HistoryItem # Ignore changes on HistoryItem
      runtime.history << HistoryItem.new(self, :on_create)
    end
    
    def on_remove
      return if is_a? HistoryItem # Ignore changes on HistoryItem
      runtime.history << HistoryItem.new(self, :on_remove)
    end
    
    def on_modify property, old_value, new_value
      return if is_a? HistoryItem # Ignore changes on HistoryItem
      runtime.history << HistoryItem.new(self, :on_modify, property, old_value, new_value)
    end
    
    def on_change property, old_value, new_value
      return if is_a? HistoryItem # Ignore changes on HistoryItem
      runtime.history << HistoryItem.new(self, :on_change, property, old_value, new_value)
    end
  end

  # Defindes extensions to +Runtime+
  module Runtime
    # Defines history items buffers
    attr_reader :history

    def on_begin
      @history = []
      @session_id = $session_id+= 1 # TODO: make it synchronized
      @event_id = 0
    end
    
    def before_flush modified
      before_commit
    end
   
    def before_commit
      history.shift.attach until history.empty? # Attach history item.
    end
    
    def event_id
      @event_id += 1
      "#{@session_id}/#{@event_id}"
    end
  end
end
end

class SampleClass
  persistent :p
end  

#Session 1 create object and modify it 2 times
Rubernate.session do
  o = SampleClass.new.attach
  for i in 1..2
    o.p = i
  end
end

#Session 2 modify and remove object
Rubernate.session do
  objs = Rubernate.find_by_query 'Select :o; Where o.derived(SampleClass)'
  for o in objs
    o.p = o.p * o.p
    o.remove!
  end
end

# Find all history HistoryItem objects
Rubernate.session do
  history = Rubernate.find_by_query 'Select :h; Where h.derived(HistoryItem);
        OrderBy h.event_id.str'
  for h in history
    puts h
    h.remove!
  end
end