Skip to content

Commit

Permalink
feat: support two value delimiters
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel Galkin committed Apr 27, 2017
1 parent a4f458c commit d64da5e
Show file tree
Hide file tree
Showing 12 changed files with 96 additions and 34 deletions.
3 changes: 0 additions & 3 deletions .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ build:
volumes:
- /home/data/drone/images:/images
- /home/data/drone/gems:/bundle
- /home/data/drone/key_cache:/ssh_keys
environment:
- COMPOSE_FILE_EXT=drone
- POSTGRES_IMAGE_TAG=9.3-latest
Expand All @@ -15,11 +14,9 @@ build:
- wrapdocker docker -v

- fetch-images
--image whilp/ssh-agent
--image abakpress/ruby-app:$RUBY_IMAGE_TAG
--image abakpress/postgres-db:$POSTGRES_IMAGE_TAG
--image abakpress/sphinx-index:$SPHINX_IMAGE_TAG

- dip ssh add -T -v /ssh_keys -k /ssh_keys/id_rsa
- dip provision
- dip rspec
2 changes: 0 additions & 2 deletions dip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ environment:
RUBY_IMAGE_TAG: 2.2-latest
COMPOSE_FILE_EXT: development
RAILS_ENV: test
APRESS_GEMS_CREDENTIALS: ""

compose:
files:
Expand Down Expand Up @@ -38,6 +37,5 @@ interaction:

provision:
- docker volume create --name bundler_data
- dip bundle config --local https://gems.railsc.ru/ ${APRESS_GEMS_CREDENTIALS}
- dip clean
- dip bundle install
5 changes: 0 additions & 5 deletions docker-compose.development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@ services:
volumes:
- .:/app
- ../:/localgems
- ssh-data:/ssh:ro
- bundler-data:/bundle

volumes:
bundler-data:
external:
name: bundler_data

ssh-data:
external:
name: ssh_data
6 changes: 0 additions & 6 deletions docker-compose.drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,3 @@ services:
volumes:
- .:/app
- /bundle:/bundle
- ssh-data:/ssh:ro

volumes:
ssh-data:
external:
name: ssh_data
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ services:
app:
image: abakpress/ruby-app:$RUBY_IMAGE_TAG
environment:
- SSH_AUTH_SOCK=/ssh/auth/sock
- BUNDLE_PATH=/bundle/$DOCKER_RUBY_VERSION
- BUNDLE_CONFIG=/app/.bundle/config
command: bash
4 changes: 1 addition & 3 deletions lib/redis_counters.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# encoding: utf-8
require 'redis_counters/version'
require 'redis_counters/base_counter'
require 'redis_counters/hash_counter'
Expand All @@ -12,10 +11,9 @@
require 'active_support/core_ext'

module RedisCounters

def create_counter(redis, opts)
BaseCounter.create(redis, opts)
end

module_function :create_counter
end
end
13 changes: 6 additions & 7 deletions lib/redis_counters/base_counter.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# coding: utf-8
require 'forwardable'
require 'active_support/core_ext/class/attribute'

module RedisCounters

# Базовый класс счетчика на основе Redis.

class BaseCounter
extend Forwardable

Expand All @@ -22,10 +19,13 @@ class BaseCounter
# opts - Hash - хеш опций счетчика:
# counter_name - Symbol/String - идентификатор счетчика.
# key_delimiter - String - разделитель ключа (опционально).
# value_delimiter - String - разделитель значений (опционально).
# value_delimiter - Array[String] или String, разделитель значений. Если это массив, то первый элемент будет
# считатся новым разделителем, а второй старым. Все данные будут записываться, используя
# новый. Старый будет использоваться только для старых данных. Так что если надо сменить
# делимитр у счётчика, например с ':' на '�', то сперва надо будет установить его на ['�',
# ':'], а после дампа старых данных в БД, на '�'.
#
# Returns RedisCounters::BaseCounter.
#
def self.create(redis, opts)
counter_class = opts.fetch(:counter_class).to_s.constantize
counter_class.new(redis, opts)
Expand Down Expand Up @@ -80,5 +80,4 @@ def value_delimiter

def_delegator :redis, :multi, :transaction
end

end
end
2 changes: 1 addition & 1 deletion lib/redis_counters/clusterize_and_partitionize.rb
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,4 @@ def partitions_keys(params = {})
result
end
end
end
end
26 changes: 22 additions & 4 deletions lib/redis_counters/hash_counter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ def field
group_params = [field_name]
end

group_params.join(value_delimiter)
if value_delimiter.is_a?(Array)
group_params.join(value_delimiter.first)
else
group_params.join(value_delimiter)
end
end

def field_name
Expand All @@ -35,17 +39,31 @@ def group_keys
# Protected: Возвращает данные партиции в виде массива хешей.
#
# Каждый элемент массива, представлен в виде хеша, содержащего все параметры кластеризации и
# значение счетчика в ключе :value. Данные в счётчике могут эскейпиться бекслэшем: "a:b:c\\:d"
# значение счетчика в ключе :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)|
# values = key.split(/(?<!\\)#{value_delimiter}/, -1).unshift(format_value(value))
values = key.split(value_delimiter, -1).unshift(format_value(value))
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
Expand Down
1 change: 1 addition & 0 deletions redis_counters.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'simplecov'
spec.add_development_dependency 'cane', '>= 2.6.0'
spec.add_development_dependency 'bundler-audit'
spec.add_development_dependency 'pry-byebug'
end
66 changes: 64 additions & 2 deletions spec/redis_counters/hash_counter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@
it { expect(counter.data(partitions).first[:param4]).to eq '1' }
it { expect(counter.data(partitions).second[:value]).to eq 5 }
it { expect(counter.data(partitions).second[:param3]).to eq '31' }
it { expect(counter.data(partitions).second[:param4]).to eq '' }
it { expect(counter.data(partitions).second[:param4]).to eq nil }
end

context 'when group_keys given' do
Expand Down Expand Up @@ -446,6 +446,68 @@
it { expect(counter.data.third[:value]).to eq 3 }
end

context 'two delimiters' do
let(:options) do
{
counter_name: :test_counter,
group_keys: [:title, :url],
partition_keys: [:date],
key_delimiter: '&',
value_delimiter: %W(\uFFFD :)
}
end
let(:partition) { {title: 'Main', url: 'http://example.com', date: '2017-04-21'} }

before { counter.increment(partition) }

it 'uses the first (new) delimiter for writing' do
expect(counter.send(:field)).to eq('Main�http://example.com')
expect(counter.data).to eq([{'value' => 1, 'title' => 'Main', 'url' => 'http://example.com'}])
end
end

context 'new delimiter added when there is data separated by the old one' do
let(:options) do
{
counter_name: :test_counter,
group_keys: [:title, :url],
partition_keys: [:date],
key_delimiter: '&',
value_delimiter: %W(\uFFFD :)
}
end
let(:partition) { {title: 'Main', url: 'http://example.com', date: '2017-04-21'} }

before do
redis.hincrbyfloat('test_counter&2017-04-21', 'Main:/about', 1.0)
end

it 'understands the old delimiter' do
expect(counter.data).to eq([{'value' => 1, 'title' => 'Main', 'url' => '/about'}])
end
end

context 'new delimiter added and there is data separated by it but contains the old delimiter' do
let(:options) do
{
counter_name: :test_counter,
group_keys: [:title, :url],
partition_keys: [:date],
key_delimiter: '&',
value_delimiter: %W(\uFFFD :)
}
end
let(:partition) { {title: 'Main', url: 'http://example.com', date: '2017-04-21'} }

before do
redis.hincrbyfloat('test_counter&2017-04-21', 'Main�http://example.com', 1.0)
end

it 'uses the new delimiter' do
expect(counter.data).to eq([{'value' => 1, 'title' => 'Main', 'url' => 'http://example.com'}])
end
end

context 'when check custom increment' do
let(:options) { {
:counter_name => :test_counter,
Expand All @@ -459,4 +521,4 @@
it { expect(redis.hexists('test_counter', 'test_field')).to be_true }
it { expect(redis.hget('test_counter', 'test_field').to_f).to be_within(0.001).of value*0.2.to_f }
end
end
end
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'timecop'
require 'codeclimate-test-reporter'
require 'simplecov'
require 'pry-byebug'

SimpleCov.start('test_frameworks')

Expand Down

0 comments on commit d64da5e

Please sign in to comment.