require "set"

module Ohm
  class Transaction
    attr_accessor :observed_keys
    attr_accessor :reading_procs
    attr_accessor :writing_procs
    attr_accessor :before_procs
    attr_accessor :after_procs

    def self.define(&block)
      new.tap(&block)
    end

    def initialize(*transactions)
      @observed_keys = ::Set.new
      @reading_procs = ::Set.new
      @writing_procs = ::Set.new
      @before_procs  = ::Set.new
      @after_procs   = ::Set.new

      transactions.each do |t|
        append(t)
      end
    end

    def append(t)
      @observed_keys += t.observed_keys
      @reading_procs += t.reading_procs
      @writing_procs += t.writing_procs
      @before_procs  += t.before_procs
      @after_procs   += t.after_procs
    end

    def watch(*keys)
      @observed_keys += keys
    end

    def read(&block)
      @reading_procs << block
    end

    def write(&block)
      @writing_procs << block
    end

    def before(&block)
      @before_procs << block
    end

    def after(&block)
      @after_procs << block
    end

    def commit(db)
      run(before_procs)

      loop do
        if observed_keys.any?
          db.watch(*observed_keys)
        end

        run(reading_procs)

        break if db.multi do
          run(writing_procs)
        end
      end

      run(after_procs)
    end

    def run(procs)
      procs.each { |p| p.call }
    end
  end
end
require "set"

module Ohm
  class Transaction
    attr_accessor :blocks

    def self.define(&block)
      new.tap(&block)
    end

    def initialize(*transactions)
      @blocks = Hash.new { |h, k| h[k] = ::Set.new }

      transactions.each do |t|
        append(t)
      end
    end

    def append(t)
      t.blocks.each do |key, block|
        blocks[key] += block
      end
    end

    def watch(*keys)
      @blocks[:watch] += keys
    end

    def read(&block)
      @blocks[:read] << block
    end

    def write(&block)
      @blocks[:write] << block
    end

    def before(&block)
      @blocks[:before] << block
    end

    def after(&block)
      @blocks[:after] << block
    end

    def commit(db)
      run(blocks[:before])

      loop do
        if blocks[:watch].any?
          db.watch(*blocks[:watch])
        end

        run(blocks[:read])

        break if db.multi do
          run(blocks[:write])
        end
      end

      run(blocks[:after])
    end

    def run(procs)
      procs.each { |p| p.call }
    end
  end
end

Remove the repetition in Ohm::Transaction
Remove the repetition in Ohm::Transaction

- use a #blocks Hash to store all Sets
