require 'redis_counters/base_counter' require 'redis_counters/clusterize_and_partitionize' require 'string_tools' module RedisCounters # Счетчик на основе redis-hash, с возможностью партиционирования и кластеризации значений. class HashCounter < BaseCounter include ClusterizeAndPartitionize alias_method :increment, :process protected def process_value redis.hincrbyfloat(key, field, params.fetch(:value, 1.0)) end def field if group_keys.present? group_params = group_keys.map { |key| params.fetch(key) } else group_params = [field_name] end if value_delimiter.is_a?(Array) group_params.join(value_delimiter.first) else group_params.join(value_delimiter) end end def field_name @field_name ||= options.fetch(:field_name) end def group_keys @group_keys ||= Array.wrap(options.fetch(:group_keys, [])) end # Protected: Возвращает данные партиции в виде массива хешей. # # Каждый элемент массива, представлен в виде хеша, содержащего все параметры кластеризации и # значение счетчика в ключе :value. # # cluster - Array - листовой кластер - массив параметров однозначно идентифицирующий кластер. # partition - Array - листовая партиция - массив параметров однозначно идентифицирующий партицию. # # Returns Array of HashWithIndifferentAccess. def partition_data(cluster, partition) keys = group_keys.dup.unshift(:value) if delimiter_is_ary = value_delimiter.is_a?(Array) new_delim, old_delim = value_delimiter end redis.hgetall(key(partition, cluster)).inject(Array.new) do |result, (key, value)| key = key.dup.to_utf8 values = if delimiter_is_ary if key.include?(new_delim) key.split(new_delim, -1) else key.split(old_delim, -1) end else key.split(value_delimiter, -1) end values = values.map(&:presence).unshift(format_value(value)) values.delete_at(1) unless group_keys.present? result << Hash[keys.zip(values)].with_indifferent_access end end def format_value(value) if float_mode? value.to_f else value.to_i end end def float_mode? @float_mode ||= options.fetch(:float_mode, false) end end end