trifle-stats 0.4.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.devcontainer.json +6 -0
- data/.devops/docker/codespaces/Dockerfile +1 -0
- data/.devops/docker/codespaces/docker-compose.yml +28 -0
- data/.devops/docker/environment/.p10k.zsh +1626 -0
- data/.devops/docker/environment/.zshrc +13 -0
- data/.devops/docker/environment/Dockerfile +54 -0
- data/{.gitpod → .devops/docker/gitpod}/Dockerfile +0 -0
- data/{.gitpod → .devops/docker/gitpod}/base/Dockerfile +0 -0
- data/.github/workflows/ruby.yml +16 -17
- data/.gitpod.yml +1 -1
- data/Gemfile.lock +5 -5
- data/README.md +67 -9
- data/lib/trifle/stats/configuration.rb +2 -1
- data/lib/trifle/stats/designator/custom.rb +22 -0
- data/lib/trifle/stats/designator/geometric.rb +25 -0
- data/lib/trifle/stats/designator/linear.rb +24 -0
- data/lib/trifle/stats/driver/README.md +3 -3
- data/lib/trifle/stats/driver/mongo.rb +16 -14
- data/lib/trifle/stats/driver/postgres.rb +18 -12
- data/lib/trifle/stats/driver/process.rb +20 -14
- data/lib/trifle/stats/driver/redis.rb +18 -12
- data/lib/trifle/stats/operations/timeseries/classify.rb +48 -0
- data/lib/trifle/stats/operations/timeseries/increment.rb +9 -7
- data/lib/trifle/stats/operations/timeseries/set.rb +9 -7
- data/lib/trifle/stats/operations/timeseries/values.rb +8 -7
- data/lib/trifle/stats/version.rb +1 -1
- data/lib/trifle/stats.rb +13 -0
- data/trifle-stats.gemspec +5 -4
- metadata +47 -36
@@ -0,0 +1,13 @@
|
|
1
|
+
# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
|
2
|
+
# Initialization code that may require console input (password prompts, [y/n]
|
3
|
+
# confirmations, etc.) must go above this block; everything else may go below.
|
4
|
+
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
|
5
|
+
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
|
6
|
+
fi
|
7
|
+
|
8
|
+
source /root/powerlevel10k/powerlevel10k.zsh-theme
|
9
|
+
|
10
|
+
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.
|
11
|
+
[[ ! -f /root/.p10k.zsh ]] || source /root/.p10k.zsh
|
12
|
+
|
13
|
+
POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true
|
@@ -0,0 +1,54 @@
|
|
1
|
+
FROM debian:latest
|
2
|
+
|
3
|
+
ENV TZ=Europe/Bratislava
|
4
|
+
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
5
|
+
|
6
|
+
# install ubuntu packages
|
7
|
+
RUN apt-get update -q \
|
8
|
+
&& apt-get install -y \
|
9
|
+
build-essential \
|
10
|
+
apt-transport-https \
|
11
|
+
zsh \
|
12
|
+
libpq-dev \
|
13
|
+
git \
|
14
|
+
curl \
|
15
|
+
wget \
|
16
|
+
unzip \
|
17
|
+
gpg \
|
18
|
+
gnupg2 \
|
19
|
+
locales \
|
20
|
+
autoconf \
|
21
|
+
libssl-dev \
|
22
|
+
libreadline-dev \
|
23
|
+
zlib1g-dev \
|
24
|
+
tmux \
|
25
|
+
htop \
|
26
|
+
vim \
|
27
|
+
&& apt-get clean
|
28
|
+
|
29
|
+
#set the locale
|
30
|
+
RUN locale-gen en_US.UTF-8
|
31
|
+
ENV LANG en_US.UTF-8
|
32
|
+
ENV LANGUAGE en_US:en
|
33
|
+
ENV LC_ALL en_US.UTF-8
|
34
|
+
|
35
|
+
RUN wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | zsh || true
|
36
|
+
RUN git clone --depth=1 https://github.com/romkatv/powerlevel10k.git /root/powerlevel10k
|
37
|
+
COPY .zshrc /root/.zshrc
|
38
|
+
COPY .p10k.zsh /root/p10k.zsh
|
39
|
+
|
40
|
+
#install asdf
|
41
|
+
ENV ASDF_ROOT /root/.asdf
|
42
|
+
ENV PATH "${ASDF_ROOT}/bin:${ASDF_ROOT}/shims:$PATH"
|
43
|
+
RUN git clone https://github.com/asdf-vm/asdf.git ${ASDF_ROOT} --branch v0.10.0
|
44
|
+
|
45
|
+
RUN asdf plugin-add ruby https://github.com/asdf-vm/asdf-ruby.git
|
46
|
+
|
47
|
+
# install ruby
|
48
|
+
ENV RUBY_VERSION 3.0.0
|
49
|
+
RUN ASDF_RUBY_BUILD_VERSION=v20201225 asdf install ruby ${RUBY_VERSION} \
|
50
|
+
&& asdf global ruby ${RUBY_VERSION}
|
51
|
+
|
52
|
+
RUN gem install bundler
|
53
|
+
|
54
|
+
CMD ["zsh"]
|
File without changes
|
File without changes
|
data/.github/workflows/ruby.yml
CHANGED
@@ -9,29 +9,28 @@ name: Ruby
|
|
9
9
|
|
10
10
|
on:
|
11
11
|
push:
|
12
|
-
branches: [
|
12
|
+
branches: [main]
|
13
13
|
pull_request:
|
14
|
-
branches: [
|
14
|
+
branches: [main]
|
15
15
|
|
16
16
|
jobs:
|
17
17
|
test:
|
18
|
-
|
19
18
|
runs-on: ubuntu-latest
|
20
19
|
strategy:
|
21
20
|
matrix:
|
22
|
-
ruby-version: [
|
21
|
+
ruby-version: ["3.0"]
|
23
22
|
|
24
23
|
steps:
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
24
|
+
- uses: actions/checkout@v2
|
25
|
+
- name: Set up Ruby
|
26
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
27
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
28
|
+
# uses: ruby/setup-ruby@v1
|
29
|
+
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
30
|
+
with:
|
31
|
+
ruby-version: ${{ matrix.ruby-version }}
|
32
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
33
|
+
- name: Rspec
|
34
|
+
run: bundle exec rspec
|
35
|
+
- name: Rubocop
|
36
|
+
run: bundle exec rubocop
|
data/.gitpod.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
trifle-stats (
|
5
|
-
mongo (>= 2.14.0)
|
6
|
-
pg (>= 1.2)
|
7
|
-
redis (>= 4.2)
|
4
|
+
trifle-stats (1.1.1)
|
8
5
|
tzinfo (~> 2.0)
|
9
6
|
|
10
7
|
GEM
|
@@ -13,7 +10,7 @@ GEM
|
|
13
10
|
ast (2.4.2)
|
14
11
|
bson (4.12.1)
|
15
12
|
byebug (11.1.3)
|
16
|
-
concurrent-ruby (1.1.
|
13
|
+
concurrent-ruby (1.1.10)
|
17
14
|
diff-lcs (1.4.4)
|
18
15
|
dotenv (2.7.6)
|
19
16
|
mongo (2.14.0)
|
@@ -64,7 +61,10 @@ DEPENDENCIES
|
|
64
61
|
bundler (~> 2.1)
|
65
62
|
byebug
|
66
63
|
dotenv
|
64
|
+
mongo (>= 2.14.0)
|
65
|
+
pg (>= 1.2)
|
67
66
|
rake (~> 12.0)
|
67
|
+
redis (>= 4.2)
|
68
68
|
rspec (~> 3.0)
|
69
69
|
rubocop (= 1.0.0)
|
70
70
|
trifle-stats!
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ Simple analytics backed by Redis, Postgres, MongoDB, Google Analytics, Segment,
|
|
8
8
|
|
9
9
|
`Trifle::Stats` is a _way too_ simple timeline analytics that helps you track custom metrics. Automatically increments counters for each enabled range. It supports timezones and different week beginning.
|
10
10
|
|
11
|
-
[^1]: TBH only Redis for now 💔.
|
11
|
+
[^1]: TBH only Redis, Postgres and MongoDB for now 💔.
|
12
12
|
|
13
13
|
## Documentation
|
14
14
|
|
@@ -24,11 +24,22 @@ gem 'trifle-stats'
|
|
24
24
|
|
25
25
|
And then execute:
|
26
26
|
|
27
|
-
|
27
|
+
```sh
|
28
|
+
$ bundle install
|
29
|
+
```
|
28
30
|
|
29
31
|
Or install it yourself as:
|
30
32
|
|
31
|
-
|
33
|
+
```sh
|
34
|
+
$ gem install trifle-stats
|
35
|
+
```
|
36
|
+
|
37
|
+
Depending on driver you would like to use, make sure you add required gems into your `Gemfile`.
|
38
|
+
```ruby
|
39
|
+
gem 'mongo', '>= 2.14.0'
|
40
|
+
gem 'pg', '>= 1.2'
|
41
|
+
gem 'redis', '>= 4.2'
|
42
|
+
```
|
32
43
|
|
33
44
|
## Usage
|
34
45
|
|
@@ -45,25 +56,72 @@ end
|
|
45
56
|
|
46
57
|
### Track values
|
47
58
|
|
48
|
-
|
59
|
+
Track your first metrics
|
49
60
|
|
50
|
-
Now track your first metrics
|
51
61
|
```ruby
|
52
62
|
Trifle::Stats.track(key: 'event::logs', at: Time.now, values: {count: 1, duration: 2, lines: 241})
|
53
63
|
=> [{2021-01-25 16:00:00 +0100=>{:count=>1, :duration=>2, :lines=>241}}, {2021-01-25 00:00:00 +0100=>{:count=>1, :duration=>2, :lines=>241}}]
|
54
|
-
|
64
|
+
```
|
65
|
+
|
66
|
+
Then do it few more times
|
67
|
+
|
68
|
+
```ruby
|
55
69
|
Trifle::Stats.track(key: 'event::logs', at: Time.now, values: {count: 1, duration: 1, lines: 56})
|
56
70
|
=> [{2021-01-25 16:00:00 +0100=>{:count=>1, :duration=>1, :lines=>56}}, {2021-01-25 00:00:00 +0100=>{:count=>1, :duration=>1, :lines=>56}}]
|
57
71
|
Trifle::Stats.track(key: 'event::logs', at: Time.now, values: {count: 1, duration: 5, lines: 361})
|
58
72
|
=> [{2021-01-25 16:00:00 +0100=>{:count=>1, :duration=>5, :lines=>361}}, {2021-01-25 00:00:00 +0100=>{:count=>1, :duration=>5, :lines=>361}}]
|
59
73
|
```
|
60
74
|
|
61
|
-
|
75
|
+
You can also store nested counters like
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Trifle::Stats.track(key: 'event::logs', at: Time.now, values: {
|
79
|
+
count: 1,
|
80
|
+
duration: {
|
81
|
+
parsing: 21,
|
82
|
+
compression: 8,
|
83
|
+
upload: 1
|
84
|
+
},
|
85
|
+
lines: 25432754
|
86
|
+
})
|
87
|
+
```
|
88
|
+
|
89
|
+
#### Get values
|
90
|
+
|
91
|
+
Retrieve your values for specific `range`. Adding increments above will return sum of all the values you've tracked.
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
Trifle::Stats.values(key: 'event::logs', from: Time.now, to: Time.now, range: :day)
|
95
|
+
=> {:at=>[2021-01-25 00:00:00 +0200], :values=>[{"count"=>3, "duration"=>8, "lines"=>658}]}
|
96
|
+
```
|
97
|
+
|
98
|
+
### Assert values
|
99
|
+
|
100
|
+
Asserting values works same way like incrementing, but instead of increment, it sets the value. Duh.
|
101
|
+
|
102
|
+
Set your first metrics
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
Trifle::Stats.assert(key: 'event::logs', at: Time.now, values: {count: 1, duration: 2, lines: 241})
|
106
|
+
=> [{2021-01-25 16:00:00 +0100=>{:count=>1, :duration=>2, :lines=>241}}, {2021-01-25 00:00:00 +0100=>{:count=>1, :duration=>2, :lines=>241}}]
|
107
|
+
```
|
108
|
+
|
109
|
+
Then do it few more times
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
Trifle::Stats.assert(key: 'event::logs', at: Time.now, values: {count: 1, duration: 1, lines: 56})
|
113
|
+
=> [{2021-01-25 16:00:00 +0100=>{:count=>1, :duration=>1, :lines=>56}}, {2021-01-25 00:00:00 +0100=>{:count=>1, :duration=>1, :lines=>56}}]
|
114
|
+
Trifle::Stats.assert(key: 'event::logs', at: Time.now, values: {count: 1, duration: 5, lines: 361})
|
115
|
+
=> [{2021-01-25 16:00:00 +0100=>{:count=>1, :duration=>5, :lines=>361}}, {2021-01-25 00:00:00 +0100=>{:count=>1, :duration=>5, :lines=>361}}]
|
116
|
+
```
|
117
|
+
|
118
|
+
#### Get values
|
119
|
+
|
120
|
+
Retrieve your values for specific `range`. As you just used `assert` above, it will return latest value you've asserted.
|
62
121
|
|
63
|
-
Retrieve your values for specific `range`.
|
64
122
|
```ruby
|
65
123
|
Trifle::Stats.values(key: 'event::logs', from: Time.now, to: Time.now, range: :day)
|
66
|
-
=> [
|
124
|
+
=> {:at=>[2021-01-25 00:00:00 +0200], :values=>[{"count"=>1, "duration"=>5, "lines"=>361}]}
|
67
125
|
```
|
68
126
|
|
69
127
|
## Contributing
|
@@ -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,9 +2,9 @@
|
|
2
2
|
|
3
3
|
Driver is a wrapper class that persists and retrieves values from backend. It needs to implement:
|
4
4
|
|
5
|
-
- `inc(
|
6
|
-
- `set(
|
7
|
-
- `get(
|
5
|
+
- `inc(keys:, **values)` method increment values
|
6
|
+
- `set(keys:, **values)` method set values
|
7
|
+
- `get(keys:)` method to retrieve values
|
8
8
|
|
9
9
|
## Documentation
|
10
10
|
|
@@ -16,24 +16,27 @@ module Trifle
|
|
16
16
|
@separator = '::'
|
17
17
|
end
|
18
18
|
|
19
|
-
def inc(
|
20
|
-
|
19
|
+
def inc(keys:, **values)
|
20
|
+
data = self.class.pack(hash: { data: values })
|
21
21
|
|
22
22
|
collection.bulk_write(
|
23
|
-
|
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(
|
28
|
-
|
29
|
+
def set(keys:, **values)
|
30
|
+
data = self.class.pack(hash: { data: values })
|
29
31
|
|
30
32
|
collection.bulk_write(
|
31
|
-
|
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:,
|
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 },
|
@@ -43,13 +46,12 @@ module Trifle
|
|
43
46
|
}
|
44
47
|
end
|
45
48
|
|
46
|
-
def get(
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
return {} if data.nil? || data['data'].nil?
|
49
|
+
def get(keys:)
|
50
|
+
pkeys = keys.map { |key| key.join(separator) }
|
51
|
+
data = collection.find(key: { '$in' => pkeys })
|
52
|
+
map = data.inject({}) { |o, d| o.merge(d['key'] => d['data']) }
|
51
53
|
|
52
|
-
|
54
|
+
pkeys.map { |pkey| map[pkey] || {} }
|
53
55
|
end
|
54
56
|
|
55
57
|
private
|
@@ -16,11 +16,13 @@ module Trifle
|
|
16
16
|
@separator = '::'
|
17
17
|
end
|
18
18
|
|
19
|
-
def inc(
|
20
|
-
|
19
|
+
def inc(keys:, **values)
|
20
|
+
keys.map do |key|
|
21
|
+
pkey = key.join(separator)
|
21
22
|
|
22
|
-
|
23
|
-
|
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(
|
35
|
-
|
36
|
+
def set(keys:, **values)
|
37
|
+
keys.map do |key|
|
38
|
+
pkey = key.join(separator)
|
36
39
|
|
37
|
-
|
40
|
+
_set_all(key: pkey, **values)
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
44
|
def _set_all(key:, **values)
|
@@ -44,13 +48,15 @@ module Trifle
|
|
44
48
|
client.exec(query)
|
45
49
|
end
|
46
50
|
|
47
|
-
def get(
|
48
|
-
|
51
|
+
def get(keys:)
|
52
|
+
keys.map do |key|
|
53
|
+
pkey = key.join(separator)
|
49
54
|
|
50
|
-
|
51
|
-
|
55
|
+
data = _get(key: pkey)
|
56
|
+
return {} if data.nil?
|
52
57
|
|
53
|
-
|
58
|
+
self.class.unpack(hash: data)
|
59
|
+
end
|
54
60
|
end
|
55
61
|
|
56
62
|
def _get(key:)
|
@@ -12,26 +12,32 @@ module Trifle
|
|
12
12
|
@separator = '::'
|
13
13
|
end
|
14
14
|
|
15
|
-
def inc(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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(
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
|
31
|
-
def get(
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
+
def get(keys:)
|
36
|
+
keys.map do |key|
|
37
|
+
self.class.unpack(
|
38
|
+
hash: @data.fetch(key.join(@separator), {})
|
39
|
+
)
|
40
|
+
end
|
35
41
|
end
|
36
42
|
end
|
37
43
|
end
|
@@ -16,26 +16,32 @@ module Trifle
|
|
16
16
|
@separator = '::'
|
17
17
|
end
|
18
18
|
|
19
|
-
def inc(
|
20
|
-
|
19
|
+
def inc(keys:, **values)
|
20
|
+
keys.map do |key|
|
21
|
+
pkey = ([prefix] + key).join(separator)
|
21
22
|
|
22
|
-
|
23
|
-
|
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(
|
28
|
-
|
29
|
+
def set(keys:, **values)
|
30
|
+
keys.map do |key|
|
31
|
+
pkey = ([prefix] + key).join(separator)
|
29
32
|
|
30
|
-
|
33
|
+
client.hmset(pkey, *self.class.pack(hash: values))
|
34
|
+
end
|
31
35
|
end
|
32
36
|
|
33
|
-
def get(
|
34
|
-
|
37
|
+
def get(keys:)
|
38
|
+
keys.map do |key|
|
39
|
+
pkey = ([prefix] + key).join(separator)
|
35
40
|
|
36
|
-
|
37
|
-
|
38
|
-
|
41
|
+
self.class.unpack(
|
42
|
+
hash: client.hgetall(pkey)
|
43
|
+
)
|
44
|
+
end
|
39
45
|
end
|
40
46
|
end
|
41
47
|
end
|
@@ -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.
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|