class Riak::CRDT::TGCounter

Attributes

actor[RW]
counts[RW]
history_length[RW]

Public Class Methods

from_hash(h, options) click to toggle source
# File lib/crdt/tgcounter.rb, line 38
def self.from_hash(h, options)
  gc = new(options)

  h['c'].each do |a, values|
    gc.counts[a] = Hash.new() unless gc.counts[a]
    gc.counts[a]["total"] = values["total"]
    gc.counts[a]["txns"] = TransactionArray.new(values["txns"])
  end

  return gc
end
from_json(json, options) click to toggle source
# File lib/crdt/tgcounter.rb, line 50
def self.from_json(json, options)
  h = JSON.parse json
  raise ArgumentError.new 'unexpected type field in JSON' unless h['type'] == 'TGCounter'

  from_hash(h, options)
end
new(options) click to toggle source

Create a new Transaction GCounter @param [Hash] options

{
  :actor [String]
  :history_length [Integer]
}
# File lib/crdt/tgcounter.rb, line 11
def initialize(options)
  self.actor = options[:actor]
  self.history_length = options[:history_length]
  self.counts = Hash.new()
  self.counts[self.actor] = Hash.new()
  self.counts[self.actor]["total"] = 0
  self.counts[self.actor]["txns"] = TransactionArray.new()
end

Public Instance Methods

compress_history() click to toggle source

Compress this actor’s data based on history_length

# File lib/crdt/tgcounter.rb, line 169
def compress_history()
  total = 0

  duplicates = self.duplicate_transactions()

  if self.counts[actor]["txns"].length > self.history_length
    to_delete = self.counts[actor]["txns"].length - self.history_length
    self.counts[actor]["txns"].arr.slice!(0..to_delete - 1).each do |arr|
      txn, val = arr
      total += val unless duplicates.member? txn
    end
  end

  self.counts[actor]["total"] += total
end
duplicate_transactions() click to toggle source

Get unique list of all duplicate transactions for all actors other than self @return [Hash]

# File lib/crdt/tgcounter.rb, line 98
def duplicate_transactions()
  duplicates = Hash.new()

  self.duplicate_transactions_by_actor().each do |a, txns|
    txns.each do |txn, val|
      duplicates[txn] = val
    end
  end

  duplicates
end
duplicate_transactions_by_actor() click to toggle source

Get unique list of all duplicate transactions per actor other than self @return [Hash]

# File lib/crdt/tgcounter.rb, line 82
def duplicate_transactions_by_actor()
  actor_txns = Hash.new()

  my_transactions = self.unique_transactions(self.actor).keys

  self.counts.keys.each do |a|
    next if a == self.actor
    uniques = self.unique_transactions(a).keys
    actor_txns[a] = (my_transactions & uniques)
  end

  actor_txns
end
has_transaction?(transaction) click to toggle source
# File lib/crdt/tgcounter.rb, line 110
def has_transaction?(transaction)
  self.unique_transactions().keys.member?(transaction)
end
increment(transaction, value) click to toggle source

Increment this actor’s transaction array, overwriting if the value exists @param [String] transaction @param [Integer] value

# File lib/crdt/tgcounter.rb, line 60
def increment(transaction, value)
  self.counts[actor]["txns"][transaction] = value
end
merge(other) click to toggle source

Merge actor data from a sibling into self, additionally remove duplicate transactions and compress oldest transactions that exceed the :history_length param into actor’s total @param [TGCounter] other

# File lib/crdt/tgcounter.rb, line 130
def merge(other)
  self.merge_actors(other)
  self.remove_duplicates()
  self.compress_history()
end
merge_actors(other) click to toggle source

Combine all actors’ data

# File lib/crdt/tgcounter.rb, line 137
def merge_actors(other)
  other.counts.each do |other_actor, other_values|
    if self.counts[other_actor]
      # Max of totals
      mine = self.counts[other_actor]["total"]
      self.counts[other_actor]["total"] = [mine, other_values["total"]].max

      # Max of unique transactions
      other_values["txns"].arr.each do |arr|
        other_txn, other_value = arr
        mine = (self.counts[other_actor]["txns"][other_txn]) ?
            self.counts[other_actor]["txns"][other_txn] : 0
        self.counts[other_actor]["txns"][other_txn] = [mine, other_value].max
      end
    else
      self.counts[other_actor] = other_values
    end
  end
end
remove_duplicates() click to toggle source

Remove duplicate transactions if other actors have claimed them

# File lib/crdt/tgcounter.rb, line 158
def remove_duplicates()
  self.duplicate_transactions_by_actor().each do |a, txns|
    # Spaceship operator, if my actor is of greater value than theirs, skip because they should remove the dupe
    next if (self.actor <=> a) == 1
    txns.each do |txn|
      self.counts[self.actor]["txns"].delete(txn)
    end
  end
end
to_hash() click to toggle source
# File lib/crdt/tgcounter.rb, line 20
def to_hash
  c = Hash.new()
  self.counts.each do |a, values|
    c[a] = Hash.new()
    c[a]["total"] = values["total"]
    c[a]["txns"] = values["txns"].arr
  end

  {
      type: 'TGCounter',
      c: c
  }
end
to_json() click to toggle source
# File lib/crdt/tgcounter.rb, line 34
def to_json
  self.to_hash.to_json
end
unique_transactions(for_actor=nil) click to toggle source

Get unique list of all transactions and values across all known actors, or optionally for a single actor @param [String] for_actor @return [Hash]

# File lib/crdt/tgcounter.rb, line 67
def unique_transactions(for_actor=nil)
  txns = Hash.new()

  self.counts.each do |a, values|
    next if for_actor && a != for_actor
    values["txns"].arr.each do |arr|
      txns[arr[0]] = arr[1]
    end
  end

  txns
end
value() click to toggle source

Sum of totals and currently tracked transactions @return [Integer]

# File lib/crdt/tgcounter.rb, line 116
def value()
  total = self.unique_transactions().values.inject(0, &:+)

  self.counts.values.each do |a|
    total += a["total"]
  end

  total
end