def load(string_or_io, opts={}, &block)

      s = string_or_io
      s = s.read if s.respond_to?(:read)
      string_or_io.close if string_or_io.respond_to?(:close) && opts[:close]
      h = JSON.load(s)

      mks = DUMP_KEYS - h.keys
      fail Flor::FlorError.new("missing keys #{mks.inspect}") if mks.any?

      o = lambda { |k| v = opts[k] || opts["#{k}s".to_sym]; v ? Array(v) : nil }
        #
      exis = o[:exid]
      doms = o[:domain]
      sdms = o[:strict_domain] || o[:sdomain]
        #
      doms = doms.collect { |d| /\A#{d}(\.#{Flor::NAME_REX})*\z/ } if doms

      counts = { executions: 0, timers: 0, traps: 0, pointers: 0, total: 0 }

      storage.db.transaction do

        (DUMP_KEYS - %w[ timestamp ]).each do |k|

          y = k.to_sym
          cla = storage.send(k)
          cols = cla.columns

          rows = h[k]
            .inject([]) { |a, hh|

              next a if exis && ! exis.include?(hh['exid'])
              next a if doms && ! doms.find { |d| d.match(hh['domain']) }
              next a if sdms && ! sdms.include?(hh['domain'])

              counts[y] += 1
              counts[:total] += 1

              vals = cla.from_h(hh)
              a << cols.collect { |c| vals[c] } }

          cla.import(cols, rows) if rows.any?
        end

        block.call(h) if block
      end

      counts
    end
def load(string_or_io, opts={}, &block)

      s = string_or_io
      s = s.read if s.respond_to?(:read)
      string_or_io.close if string_or_io.respond_to?(:close) && opts[:close]
      h = JSON.load(s)

      mks = DUMP_KEYS - h.keys
      fail Flor::FlorError.new("missing keys #{mks.inspect}") if mks.any?

      o = lambda { |k| v = opts[k] || opts["#{k}s".to_sym]; v ? Array(v) : nil }
        #
      exis = o[:exid]
      doms = o[:domain]
      sdms = o[:strict_domain] || o[:sdomain]
        #
      doms = doms.collect { |d| /\A#{d}(\.#{Flor::NAME_REX})*\z/ } if doms

      counts = { executions: 0, timers: 0, traps: 0, pointers: 0, total: 0 }

      storage.db.transaction do

        (DUMP_KEYS - %w[ timestamp ]).each do |k|

          y = k.to_sym
          cla = storage.send(k)
          cols = cla.columns

          rows = h[k]
            .inject([]) { |a, hh|

              next a if exis && ! exis.include?(hh['exid'])
              next a if doms && ! doms.find { |d| d.match(hh['domain']) }
              next a if sdms && ! sdms.include?(hh['domain'])

              counts[y] += 1
              counts[:total] += 1

              vals = cla.from_h(hh)
              a << cols.collect { |c| vals[c] } }

          cla.import(cols, rows) if rows.any?
        end

        block.call(h) if block
      end

      counts
    end
Read a previous JSON dump and loads it into the storage.
 Can be useful when testing, dumping once and reloading multiple times
 to test variants.

 load(string) -> h  # load all executions from given JSON string
                    # returns object inserted stat hash
 load(io)  # load all executions from the given IO
 load(io, close: true)  # load from the given IO and close it after read

 load(x, exid: i)            # load only given executions,
 load(x, exids: [ i0, i1 ])  # ignore the rest of the data in the source
 load(x, domain: d)            # load only exes from given domains,
 load(x, domains: [ d0, d1 ])  # and their subdomains
 load(x, sdomain: d)            # load only exes from strict domains,
 load(x, sdomains: [ d0, d1 ])  # ignores exes in their subdomains