trifle-stats 1.0.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43ed8b8a05c933e5397b59bce9dc18eaee146ac62d2df67eccfbd9a53caad619
4
- data.tar.gz: 59f6d4ceba6de8977fcedf6f153fd621bd78a7055102080475c0d1f0e98f952f
3
+ metadata.gz: d2ab7e87008ccd472bcbb2f821db69a304c0d2be078827150d828459c62b9ffa
4
+ data.tar.gz: 7e6b5b77c70845d2c2d7b5b29f7a9811bba32f166f3a6204fb13e931c29ab90e
5
5
  SHA512:
6
- metadata.gz: 3a6fc973aaedd3167ea5c1b4d2f2cb7ebd8376de153f212005aae43320e59acb992d1034f8a83c5d6063e9b314f916603cd98714ecb9d0a66b02e4dd8dc8fb33
7
- data.tar.gz: adfbb9e6625f1f05b20c7f420c6a029b94086882ebd7e02fb3223449fbbc22c6b42c2ad49218b76ad8406570b1c9e2263b677ae93150fe1a6200e00dadddc8e1
6
+ metadata.gz: 49870c3899c775ce1a91a857cc39f9cc92c05b6bb7ffe211ecf52adf033436942f5db44942cd4e2f84143b8111e586b25254f88f4d9c4cf8c9123adac20503e8
7
+ data.tar.gz: 3dee093eed89e39a2dbc0c867dba0529359a9cba84f11c021fad8a78246e2e0d1115119ee896658e8ed917546757423432078faece8c582f559923b973170938
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- trifle-stats (1.0.0)
4
+ trifle-stats (1.1.1)
5
5
  tzinfo (~> 2.0)
6
6
 
7
7
  GEM
@@ -6,12 +6,13 @@ module Trifle
6
6
  module Stats
7
7
  class Configuration
8
8
  attr_writer :driver
9
- attr_accessor :track_ranges, :time_zone, :beginning_of_week
9
+ attr_accessor :track_ranges, :time_zone, :beginning_of_week, :designator
10
10
 
11
11
  def initialize
12
12
  @ranges = %i[minute hour day week month quarter year]
13
13
  @beginning_of_week = :monday
14
14
  @time_zone = 'GMT'
15
+ @designator = nil
15
16
  end
16
17
 
17
18
  def tz
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trifle
4
+ module Stats
5
+ class Designator
6
+ class Custom
7
+ attr_reader :buckets
8
+
9
+ def initialize(buckets:)
10
+ @buckets = buckets.sort
11
+ end
12
+
13
+ def designate(value:)
14
+ return buckets.first.to_s if value <= buckets.first
15
+ return "#{buckets.last}+" if value > buckets.last
16
+
17
+ (buckets.find { |b| value.ceil < b }).to_s
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trifle
4
+ module Stats
5
+ class Designator
6
+ class Geometric
7
+ attr_reader :min, :max
8
+
9
+ def initialize(min:, max:)
10
+ @min = min.negative? ? 0 : min
11
+ @max = max
12
+ end
13
+
14
+ def designate(value:) # rubocop:disable Metrics/AbcSize
15
+ return min.to_f.to_s if value <= min
16
+ return "#{max.to_f}+" if value > max
17
+ return (10**value.floor.to_s.length).to_f.to_s if value > 1
18
+ return 1.0.to_s if value > 0.1 # ugh?
19
+
20
+ (1.0 / 10**value.to_s.gsub('0.', '').split(/[1-9]/).first.length).to_s
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trifle
4
+ module Stats
5
+ class Designator
6
+ class Linear
7
+ attr_reader :min, :max, :step
8
+
9
+ def initialize(min:, max:, step:)
10
+ @min = min
11
+ @max = max
12
+ @step = step.to_i
13
+ end
14
+
15
+ def designate(value:) # rubocop:disable Metrics/AbcSize
16
+ return min.to_s if value <= min
17
+ return "#{max}+" if value > max
18
+
19
+ (value.ceil / step * step + ((value.ceil % step).zero? ? 0 : step)).to_s
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -2,8 +2,8 @@
2
2
 
3
3
  Driver is a wrapper class that persists and retrieves values from backend. It needs to implement:
4
4
 
5
- - `inc(key:, **values)` method increment values
6
- - `set(key:, **values)` method set values
5
+ - `inc(keys:, **values)` method increment values
6
+ - `set(keys:, **values)` method set values
7
7
  - `get(keys:)` method to retrieve values
8
8
 
9
9
  ## Documentation
@@ -16,24 +16,27 @@ module Trifle
16
16
  @separator = '::'
17
17
  end
18
18
 
19
- def inc(key:, **values)
20
- pkey = key.join(separator)
19
+ def inc(keys:, **values)
20
+ data = self.class.pack(hash: { data: values })
21
21
 
22
22
  collection.bulk_write(
23
- [upsert_operation('$inc', pkey: pkey, values: values)]
23
+ keys.map do |key|
24
+ upsert_operation('$inc', pkey: key.join(separator), data: data)
25
+ end
24
26
  )
25
27
  end
26
28
 
27
- def set(key:, **values)
28
- pkey = key.join(separator)
29
+ def set(keys:, **values)
30
+ data = self.class.pack(hash: { data: values })
29
31
 
30
32
  collection.bulk_write(
31
- [upsert_operation('$set', pkey: pkey, values: values)]
33
+ keys.map do |key|
34
+ upsert_operation('$set', pkey: key.join(separator), data: data)
35
+ end
32
36
  )
33
37
  end
34
38
 
35
- def upsert_operation(operation, pkey:, values:)
36
- data = self.class.pack(hash: { data: values })
39
+ def upsert_operation(operation, pkey:, data:)
37
40
  {
38
41
  update_many: {
39
42
  filter: { key: pkey },
@@ -16,11 +16,13 @@ module Trifle
16
16
  @separator = '::'
17
17
  end
18
18
 
19
- def inc(key:, **values)
20
- pkey = key.join(separator)
19
+ def inc(keys:, **values)
20
+ keys.map do |key|
21
+ pkey = key.join(separator)
21
22
 
22
- self.class.pack(hash: values).each do |k, c|
23
- _inc_one(key: pkey, name: k, value: c)
23
+ self.class.pack(hash: values).each do |k, c|
24
+ _inc_one(key: pkey, name: k, value: c)
25
+ end
24
26
  end
25
27
  end
26
28
 
@@ -31,10 +33,12 @@ module Trifle
31
33
  client.exec(query)
32
34
  end
33
35
 
34
- def set(key:, **values)
35
- pkey = key.join(separator)
36
+ def set(keys:, **values)
37
+ keys.map do |key|
38
+ pkey = key.join(separator)
36
39
 
37
- _set_all(key: pkey, **values)
40
+ _set_all(key: pkey, **values)
41
+ end
38
42
  end
39
43
 
40
44
  def _set_all(key:, **values)
@@ -12,19 +12,23 @@ module Trifle
12
12
  @separator = '::'
13
13
  end
14
14
 
15
- def inc(key:, **values)
16
- self.class.pack(hash: values).each do |k, c|
17
- d = @data.fetch(key.join(@separator), {})
18
- d[k] = d[k].to_i + c
19
- @data[key.join(@separator)] = d
15
+ def inc(keys:, **values)
16
+ keys.map do |key|
17
+ self.class.pack(hash: values).each do |k, c|
18
+ d = @data.fetch(key.join(@separator), {})
19
+ d[k] = d[k].to_i + c
20
+ @data[key.join(@separator)] = d
21
+ end
20
22
  end
21
23
  end
22
24
 
23
- def set(key:, **values)
24
- self.class.pack(hash: values).each do |k, c|
25
- d = @data.fetch(key.join(@separator), {})
26
- d[k] = c
27
- @data[key.join(@separator)] = d
25
+ def set(keys:, **values)
26
+ keys.map do |key|
27
+ self.class.pack(hash: values).each do |k, c|
28
+ d = @data.fetch(key.join(@separator), {})
29
+ d[k] = c
30
+ @data[key.join(@separator)] = d
31
+ end
28
32
  end
29
33
  end
30
34
 
@@ -16,18 +16,22 @@ module Trifle
16
16
  @separator = '::'
17
17
  end
18
18
 
19
- def inc(key:, **values)
20
- pkey = ([prefix] + key).join(separator)
19
+ def inc(keys:, **values)
20
+ keys.map do |key|
21
+ pkey = ([prefix] + key).join(separator)
21
22
 
22
- self.class.pack(hash: values).each do |k, c|
23
- client.hincrby(pkey, k, c)
23
+ self.class.pack(hash: values).each do |k, c|
24
+ client.hincrby(pkey, k, c)
25
+ end
24
26
  end
25
27
  end
26
28
 
27
- def set(key:, **values)
28
- pkey = ([prefix] + key).join(separator)
29
+ def set(keys:, **values)
30
+ keys.map do |key|
31
+ pkey = ([prefix] + key).join(separator)
29
32
 
30
- client.hmset(pkey, *self.class.pack(hash: values))
33
+ client.hmset(pkey, *self.class.pack(hash: values))
34
+ end
31
35
  end
32
36
 
33
37
  def get(keys:)
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trifle
4
+ module Stats
5
+ module Operations
6
+ module Timeseries
7
+ class Classify
8
+ attr_reader :key, :values
9
+
10
+ def initialize(**keywords)
11
+ @key = keywords.fetch(:key)
12
+ @at = keywords.fetch(:at)
13
+ @values = keywords.fetch(:values)
14
+ @config = keywords[:config]
15
+ end
16
+
17
+ def config
18
+ @config || Trifle::Stats.default
19
+ end
20
+
21
+ def deep_classify(hash)
22
+ hash.transform_values do |value|
23
+ next deep_classify(value) if value.is_a?(Hash)
24
+
25
+ { classify(value) => 1 }
26
+ end
27
+ end
28
+
29
+ def classify(value)
30
+ config.designator.designate(value: value).to_s.gsub('.', '_')
31
+ end
32
+
33
+ def key_for(range:)
34
+ at = Nocturnal.new(@at, config: config).send(range)
35
+ [key, range, at.to_i]
36
+ end
37
+
38
+ def perform
39
+ config.driver.inc(
40
+ keys: config.ranges.map { |range| key_for(range: range) },
41
+ **deep_classify(values)
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -18,14 +18,16 @@ module Trifle
18
18
  @config || Trifle::Stats.default
19
19
  end
20
20
 
21
+ def key_for(range:)
22
+ at = Nocturnal.new(@at, config: config).send(range)
23
+ [key, range, at.to_i]
24
+ end
25
+
21
26
  def perform
22
- config.ranges.map do |range|
23
- at = Nocturnal.new(@at, config: config).send(range)
24
- config.driver.inc(
25
- key: [key, range, at.to_i],
26
- **values
27
- )
28
- end
27
+ config.driver.inc(
28
+ keys: config.ranges.map { |range| key_for(range: range) },
29
+ **values
30
+ )
29
31
  end
30
32
  end
31
33
  end
@@ -18,14 +18,16 @@ module Trifle
18
18
  @config || Trifle::Stats.default
19
19
  end
20
20
 
21
+ def key_for(range:)
22
+ at = Nocturnal.new(@at, config: config).send(range)
23
+ [key, range, at.to_i]
24
+ end
25
+
21
26
  def perform
22
- config.ranges.map do |range|
23
- at = Nocturnal.new(@at, config: config).send(range)
24
- config.driver.set(
25
- key: [key, range, at.to_i],
26
- **values
27
- )
28
- end
27
+ config.driver.set(
28
+ keys: config.ranges.map { |range| key_for(range: range) },
29
+ **values
30
+ )
29
31
  end
30
32
  end
31
33
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Trifle
4
4
  module Stats
5
- VERSION = '1.0.0'
5
+ VERSION = '1.1.1'
6
6
  end
7
7
  end
data/lib/trifle/stats.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'trifle/stats/designator/custom'
4
+ require 'trifle/stats/designator/geometric'
5
+ require 'trifle/stats/designator/linear'
3
6
  require 'trifle/stats/driver/mongo'
4
7
  require 'trifle/stats/driver/postgres'
5
8
  require 'trifle/stats/driver/process'
@@ -7,6 +10,7 @@ require 'trifle/stats/driver/redis'
7
10
  require 'trifle/stats/mixins/packer'
8
11
  require 'trifle/stats/nocturnal'
9
12
  require 'trifle/stats/configuration'
13
+ require 'trifle/stats/operations/timeseries/classify'
10
14
  require 'trifle/stats/operations/timeseries/increment'
11
15
  require 'trifle/stats/operations/timeseries/set'
12
16
  require 'trifle/stats/operations/timeseries/values'
@@ -45,6 +49,15 @@ module Trifle
45
49
  ).perform
46
50
  end
47
51
 
52
+ def self.assort(key:, at:, values:, config: nil)
53
+ Trifle::Stats::Operations::Timeseries::Classify.new(
54
+ key: key,
55
+ at: at,
56
+ values: values,
57
+ config: config
58
+ ).perform
59
+ end
60
+
48
61
  def self.values(key:, from:, to:, range:, config: nil)
49
62
  Trifle::Stats::Operations::Timeseries::Values.new(
50
63
  key: key,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trifle-stats
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jozef Vaclavik
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-06-18 00:00:00.000000000 Z
11
+ date: 2022-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -183,6 +183,9 @@ files:
183
183
  - bin/setup
184
184
  - lib/trifle/stats.rb
185
185
  - lib/trifle/stats/configuration.rb
186
+ - lib/trifle/stats/designator/custom.rb
187
+ - lib/trifle/stats/designator/geometric.rb
188
+ - lib/trifle/stats/designator/linear.rb
186
189
  - lib/trifle/stats/driver/README.md
187
190
  - lib/trifle/stats/driver/mongo.rb
188
191
  - lib/trifle/stats/driver/postgres.rb
@@ -190,6 +193,7 @@ files:
190
193
  - lib/trifle/stats/driver/redis.rb
191
194
  - lib/trifle/stats/mixins/packer.rb
192
195
  - lib/trifle/stats/nocturnal.rb
196
+ - lib/trifle/stats/operations/timeseries/classify.rb
193
197
  - lib/trifle/stats/operations/timeseries/increment.rb
194
198
  - lib/trifle/stats/operations/timeseries/set.rb
195
199
  - lib/trifle/stats/operations/timeseries/values.rb