single_increase_by 0.0.4
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 +7 -0
- data/.circleci/config.yml +70 -0
- data/.github/workflows/gem-push.yml +32 -0
- data/.gitignore +24 -0
- data/.tool-versions +1 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +67 -0
- data/README.md +33 -0
- data/app/wolverine/model/_base.lua +61 -0
- data/app/wolverine/model/get.lua +7 -0
- data/app/wolverine/model/incr_amount.lua +9 -0
- data/app/wolverine/model/set.lua +8 -0
- data/app/wolverine/model/set_expire.lua +8 -0
- data/app/wolverine/shared/_number.lua +93 -0
- data/github/workflows/publish-gem.yml +32 -0
- data/lib/single_increase_by/model.rb +32 -0
- data/lib/single_increase_by/version.rb +3 -0
- data/lib/single_increase_by.rb +98 -0
- data/single_increase_by.gemspec +24 -0
- data/spec/lib/single_increase_by/model/incr_total_spec.rb +53 -0
- data/spec/lib/single_increase_by/model/set_expire_spec.rb +18 -0
- data/spec/lib/single_increase_by/model/set_spec.rb +17 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/redis_test.rb +34 -0
- data/spec/support/waiter.rb +10 -0
- metadata +115 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ae57378362a4e4032e09fbfb2d1ae6a6fcb57deeac03d8c19cd1ba21875360b9
|
4
|
+
data.tar.gz: a1cfce6a2e86bf725d556708badf9dfcb07acfd5613e7988a494d7394cb6df05
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e66bc9d7f842e46b5a4780de93b1384a1c4bebab7a65aa23b1d4e918717fe77f36fb23e0f5eafce7df11b53dbf18370c1e352750ab03836a9bde1ea2ead77c0f
|
7
|
+
data.tar.gz: d1343610cb5e8ce9d519b3389546c5e0f75fc3898de21aa27047eb165e0b74190d6c0339af636525b203071e322dfc2eeee08c199be4dea261093e53abba6827
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Ruby CircleCI 2.0 configuration file
|
2
|
+
#
|
3
|
+
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
|
4
|
+
#
|
5
|
+
version: 2
|
6
|
+
|
7
|
+
template:
|
8
|
+
setup-cache-version: &setup-cache-version
|
9
|
+
run:
|
10
|
+
name: Write CACHE_VERSION to file
|
11
|
+
command: echo $CACHE_VERSION > ~/cache_version.txt && echo CACHE_VERSION=$CACHE_VERSION
|
12
|
+
restore-bundle: &restore-bundle
|
13
|
+
restore_cache:
|
14
|
+
name: Restore bundled gem
|
15
|
+
key: gem-bundle-{{ checksum "Gemfile.lock" }}-v-{{ checksum "~/cache_version.txt" }}
|
16
|
+
save-bundle: &save-bundle
|
17
|
+
save_cache:
|
18
|
+
name: Save bundled gem
|
19
|
+
key: gem-bundle-{{ checksum "Gemfile.lock" }}-v-{{ checksum "~/cache_version.txt" }}
|
20
|
+
paths:
|
21
|
+
- ./vendor/bundle
|
22
|
+
- /usr/local/bundle/config
|
23
|
+
|
24
|
+
|
25
|
+
jobs:
|
26
|
+
build: &containerized
|
27
|
+
docker:
|
28
|
+
- image: cimg/ruby:2.7.6-node
|
29
|
+
environment:
|
30
|
+
- REDIS_URL: redis://127.0.0.1:6379
|
31
|
+
- image: redis:3.2
|
32
|
+
|
33
|
+
working_directory: ~/repo
|
34
|
+
|
35
|
+
steps:
|
36
|
+
- checkout
|
37
|
+
- <<: *setup-cache-version
|
38
|
+
- <<: *restore-bundle
|
39
|
+
- run:
|
40
|
+
name: install dependencies
|
41
|
+
command: |
|
42
|
+
bundle install --jobs=4 --retry=3 --path vendor/bundle
|
43
|
+
- <<: *save-bundle
|
44
|
+
|
45
|
+
- run: dockerize -wait tcp://localhost:6379 -timeout 5s
|
46
|
+
|
47
|
+
- run:
|
48
|
+
name: run tests
|
49
|
+
command: |
|
50
|
+
mkdir /tmp/test-results
|
51
|
+
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
|
52
|
+
|
53
|
+
bundle exec rspec --format progress \
|
54
|
+
--format RspecJunitFormatter \
|
55
|
+
--out /tmp/test-results/rspec.xml \
|
56
|
+
--format progress \
|
57
|
+
$TEST_FILES
|
58
|
+
|
59
|
+
# collect reports
|
60
|
+
- store_test_results:
|
61
|
+
path: /tmp/test-results
|
62
|
+
- store_artifacts:
|
63
|
+
path: /tmp/test-results
|
64
|
+
destination: test-results
|
65
|
+
|
66
|
+
workflows:
|
67
|
+
version: 2
|
68
|
+
build:
|
69
|
+
jobs:
|
70
|
+
- build
|
@@ -0,0 +1,32 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
name: Build + Publish
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
permissions:
|
12
|
+
contents: read
|
13
|
+
packages: write
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v3
|
17
|
+
- uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: '3.2'
|
20
|
+
bundler-cache: true
|
21
|
+
|
22
|
+
- name: Publish to GPR
|
23
|
+
run: |
|
24
|
+
mkdir -p $HOME/.gem
|
25
|
+
touch $HOME/.gem/credentials
|
26
|
+
chmod 0600 $HOME/.gem/credentials
|
27
|
+
printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
28
|
+
gem build *.gemspec
|
29
|
+
gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
|
30
|
+
env:
|
31
|
+
GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}"
|
32
|
+
OWNER: ${{ github.repository_owner }}
|
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/_yardoc/
|
4
|
+
/coverage/
|
5
|
+
/doc/
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/tmp/
|
9
|
+
*.bundle
|
10
|
+
*.so
|
11
|
+
*.o
|
12
|
+
*.a
|
13
|
+
mkmf.log
|
14
|
+
.env
|
15
|
+
uploads
|
16
|
+
tags
|
17
|
+
.byebug_history
|
18
|
+
.DS_Store
|
19
|
+
log/redis.9736.log
|
20
|
+
.pryrc
|
21
|
+
.pry_history
|
22
|
+
log/
|
23
|
+
node_modules/
|
24
|
+
output/
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 2.7.6
|
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in single_increase_by.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'thor'
|
7
|
+
gem 'parallel'
|
8
|
+
gem "bundler"
|
9
|
+
gem "rspec"
|
10
|
+
gem "byebug"
|
11
|
+
gem "redis_test", "~> 0.4.0"
|
12
|
+
gem "rspec_junit_formatter"
|
13
|
+
gem "activesupport"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
single_increase_by (0.0.4)
|
5
|
+
hashie
|
6
|
+
redis
|
7
|
+
wolverine
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activesupport (7.0.5)
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
i18n (>= 1.6, < 2)
|
15
|
+
minitest (>= 5.1)
|
16
|
+
tzinfo (~> 2.0)
|
17
|
+
byebug (11.1.3)
|
18
|
+
concurrent-ruby (1.2.2)
|
19
|
+
connection_pool (2.4.1)
|
20
|
+
diff-lcs (1.5.0)
|
21
|
+
hashie (5.0.0)
|
22
|
+
i18n (1.13.0)
|
23
|
+
concurrent-ruby (~> 1.0)
|
24
|
+
minitest (5.18.0)
|
25
|
+
parallel (1.23.0)
|
26
|
+
redis (5.2.0)
|
27
|
+
redis-client (>= 0.22.0)
|
28
|
+
redis-client (0.22.2)
|
29
|
+
connection_pool
|
30
|
+
redis_test (0.4.1)
|
31
|
+
rspec (3.12.0)
|
32
|
+
rspec-core (~> 3.12.0)
|
33
|
+
rspec-expectations (~> 3.12.0)
|
34
|
+
rspec-mocks (~> 3.12.0)
|
35
|
+
rspec-core (3.12.2)
|
36
|
+
rspec-support (~> 3.12.0)
|
37
|
+
rspec-expectations (3.12.3)
|
38
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
39
|
+
rspec-support (~> 3.12.0)
|
40
|
+
rspec-mocks (3.12.5)
|
41
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
42
|
+
rspec-support (~> 3.12.0)
|
43
|
+
rspec-support (3.12.0)
|
44
|
+
rspec_junit_formatter (0.6.0)
|
45
|
+
rspec-core (>= 2, < 4, != 2.12.0)
|
46
|
+
thor (1.2.2)
|
47
|
+
tzinfo (2.0.6)
|
48
|
+
concurrent-ruby (~> 1.0)
|
49
|
+
wolverine (0.3.5)
|
50
|
+
redis (>= 3.0.0)
|
51
|
+
|
52
|
+
PLATFORMS
|
53
|
+
ruby
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
activesupport
|
57
|
+
bundler
|
58
|
+
byebug
|
59
|
+
parallel
|
60
|
+
redis_test (~> 0.4.0)
|
61
|
+
rspec
|
62
|
+
rspec_junit_formatter
|
63
|
+
single_increase_by!
|
64
|
+
thor
|
65
|
+
|
66
|
+
BUNDLED WITH
|
67
|
+
2.4.12
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# SingleIncreaseBy
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```
|
8
|
+
source "https://rubygems.pkg.github.com/remitano" do
|
9
|
+
gem "single_increase_by", "0.0.1"
|
10
|
+
end
|
11
|
+
```
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
## Initialize
|
16
|
+
|
17
|
+
```
|
18
|
+
SingleIncreaseBy.redis = redis
|
19
|
+
|
20
|
+
```
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
```
|
27
|
+
object = SingleIncreaseBy::Model.new("User:1:PointCount")
|
28
|
+
object.get
|
29
|
+
|
30
|
+
ret = object.incr_amount(amount: 0.1, operation: "support_st01_get_point")
|
31
|
+
|
32
|
+
```
|
33
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
local Model = {}
|
2
|
+
|
3
|
+
function Model.get(attribute_key)
|
4
|
+
return redis.call("GET", attribute_key)
|
5
|
+
end
|
6
|
+
|
7
|
+
function Model.set(attribute_key, value)
|
8
|
+
redis.call("SET", attribute_key, value)
|
9
|
+
return value
|
10
|
+
end
|
11
|
+
|
12
|
+
function Model.incr_amount(attribute_key, change, operation)
|
13
|
+
local amount_str = Model.get(attribute_key)
|
14
|
+
|
15
|
+
local message, ok = Model._check_operation(attribute_key, operation, change, amount_str)
|
16
|
+
if not ok then
|
17
|
+
return message, ok
|
18
|
+
elseif message ~= nil then
|
19
|
+
return message, ok
|
20
|
+
end
|
21
|
+
|
22
|
+
if number.is_zero(change) then
|
23
|
+
return redis.error_reply("change must not == 0"), false
|
24
|
+
end
|
25
|
+
|
26
|
+
local amount = number.tofloat(amount_str)
|
27
|
+
local new_amount = number.add(amount, change)
|
28
|
+
|
29
|
+
Model._store_operation(attribute_key, operation, change)
|
30
|
+
redis.call("SET", attribute_key, new_amount)
|
31
|
+
return tostring(new_amount), true
|
32
|
+
end
|
33
|
+
|
34
|
+
function Model.set_expire(attribute_key, time_in_second)
|
35
|
+
redis.call("EXPIRE", attribute_key, time_in_second)
|
36
|
+
end
|
37
|
+
|
38
|
+
function Model._store_operation(attribute_key, operation, change)
|
39
|
+
local six_months = 6 * 30 * 24 * 60 * 60
|
40
|
+
redis.call("SETEX", Model._operation_key(attribute_key, operation), six_months, change)
|
41
|
+
end
|
42
|
+
|
43
|
+
function Model._check_operation(attribute_key, operation, change, default)
|
44
|
+
if operation ~= "reconcile" then
|
45
|
+
local existing = redis.call("GET", Model._operation_key(attribute_key, operation))
|
46
|
+
if existing ~= false then
|
47
|
+
if tonumber(existing) == change then
|
48
|
+
return default, true
|
49
|
+
else
|
50
|
+
local error = redis.error_reply(string.format("operation executed with change=%s", existing));
|
51
|
+
return error, false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
return nil, true
|
57
|
+
end
|
58
|
+
|
59
|
+
function Model._operation_key(attribute_key, operation)
|
60
|
+
return string.format("single-increase-by:%s:%s", attribute_key, operation)
|
61
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%= include_partial 'shared/_number.lua' %>
|
2
|
+
<%= include_partial 'model/_base.lua' %>
|
3
|
+
redis.replicate_commands()
|
4
|
+
|
5
|
+
local attribute_key = ARGV[1]
|
6
|
+
local change = tonumber(ARGV[2])
|
7
|
+
local operation = ARGV[3]
|
8
|
+
local result, ok = Model.incr_amount(attribute_key, change, operation)
|
9
|
+
return result
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%= include_partial 'shared/_number.lua' %>
|
2
|
+
<%= include_partial 'model/_base.lua' %>
|
3
|
+
redis.replicate_commands()
|
4
|
+
|
5
|
+
local attribute_key = ARGV[1]
|
6
|
+
local time_in_second = tonumber(ARGV[2])
|
7
|
+
local result = Model.set_expire(attribute_key, time_in_second)
|
8
|
+
return result
|
@@ -0,0 +1,93 @@
|
|
1
|
+
local number = {}
|
2
|
+
|
3
|
+
function number.tofloat(value)
|
4
|
+
if value then
|
5
|
+
return tonumber(value)
|
6
|
+
else
|
7
|
+
return 0
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
function number.tostring(number)
|
12
|
+
return string.format("%12.12f", number)
|
13
|
+
end
|
14
|
+
|
15
|
+
function number.round(value, scale)
|
16
|
+
return number.tofloat(string.format("%."..scale.."f", value))
|
17
|
+
end
|
18
|
+
|
19
|
+
function number.floor(value, scale)
|
20
|
+
local value_str10 = string.format("%.10f", value)
|
21
|
+
local value_str = string.sub(value_str10, 1, - 10 + scale - 1)
|
22
|
+
return number.tofloat(value_str)
|
23
|
+
end
|
24
|
+
|
25
|
+
function number.round8(value)
|
26
|
+
return number.round(value, 8)
|
27
|
+
end
|
28
|
+
|
29
|
+
function number.round9(value)
|
30
|
+
return number.round(value, 9)
|
31
|
+
end
|
32
|
+
|
33
|
+
function number.floor8(value)
|
34
|
+
return number.floor(value, 8)
|
35
|
+
end
|
36
|
+
|
37
|
+
function number.floor4(value)
|
38
|
+
return number.floor(value, 4)
|
39
|
+
end
|
40
|
+
|
41
|
+
-- return the possible rounding scale because lua floating number can hold only 15 significant digits
|
42
|
+
-- update: reduce rounding scale to 14 since with 15, there is some case the add method does not work well
|
43
|
+
function number.rounding_scale(value)
|
44
|
+
return 14 - math.ceil(math.log(math.abs(value)) / math.log(10))
|
45
|
+
end
|
46
|
+
|
47
|
+
function number.add(a, b)
|
48
|
+
local rounding_scale = math.min(
|
49
|
+
math.max(
|
50
|
+
number.rounding_scale(a),
|
51
|
+
number.rounding_scale(b)
|
52
|
+
),
|
53
|
+
9
|
54
|
+
)
|
55
|
+
|
56
|
+
return number.round(a + b, rounding_scale)
|
57
|
+
end
|
58
|
+
|
59
|
+
function number.is_zero(a)
|
60
|
+
return number.round9(a) == 0
|
61
|
+
end
|
62
|
+
|
63
|
+
function number.is_less_than(a, b)
|
64
|
+
return number.compare(a, b) < 0
|
65
|
+
end
|
66
|
+
|
67
|
+
function number.is_less_than_or_equal(a, b)
|
68
|
+
return number.compare(a, b) <= 0
|
69
|
+
end
|
70
|
+
|
71
|
+
function number.is_greater_than(a, b)
|
72
|
+
return number.compare(a, b) > 0
|
73
|
+
end
|
74
|
+
|
75
|
+
function number.is_greater_than_or_equal(a, b)
|
76
|
+
return number.compare(a, b) >= 0
|
77
|
+
end
|
78
|
+
|
79
|
+
function number.is_equal(a, b)
|
80
|
+
return number.compare(a, b) == 0
|
81
|
+
end
|
82
|
+
|
83
|
+
-- deal with lua only have 15 significant digits for floating number
|
84
|
+
function number.compare(a, b)
|
85
|
+
local scale = 9
|
86
|
+
if a ~= 0 then
|
87
|
+
scale = number.rounding_scale(a)
|
88
|
+
if scale > 9 then
|
89
|
+
scale = 9
|
90
|
+
end
|
91
|
+
end
|
92
|
+
return number.round(a - b, scale)
|
93
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
name: Build + Publish
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
permissions:
|
12
|
+
contents: read
|
13
|
+
packages: write
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v3
|
17
|
+
- uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: '3.2'
|
20
|
+
bundler-cache: true
|
21
|
+
|
22
|
+
- name: Publish to GPR
|
23
|
+
run: |
|
24
|
+
mkdir -p $HOME/.gem
|
25
|
+
touch $HOME/.gem/credentials
|
26
|
+
chmod 0600 $HOME/.gem/credentials
|
27
|
+
printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
28
|
+
gem build *.gemspec
|
29
|
+
gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
|
30
|
+
env:
|
31
|
+
GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}"
|
32
|
+
OWNER: ${{ github.repository_owner }}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SingleIncreaseBy
|
2
|
+
class Model
|
3
|
+
attr_reader :attribute_key
|
4
|
+
|
5
|
+
def initialize(attribute_key)
|
6
|
+
@attribute_key = attribute_key
|
7
|
+
end
|
8
|
+
|
9
|
+
def get
|
10
|
+
SingleIncreaseBy.wolverine.model.get(argv: [attribute_key])
|
11
|
+
end
|
12
|
+
|
13
|
+
def set(value)
|
14
|
+
SingleIncreaseBy.wolverine.model.set(argv: [attribute_key, value])
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_expire(time_in_second)
|
18
|
+
SingleIncreaseBy.wolverine.model.set_expire(argv: [attribute_key, time_in_second])
|
19
|
+
end
|
20
|
+
|
21
|
+
def incr_amount(amount:, operation:)
|
22
|
+
require_identifier!(operation)
|
23
|
+
SingleIncreaseBy.wolverine.model.incr_amount(argv: [attribute_key, amount, operation])
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def require_identifier!(identifier)
|
29
|
+
raise OperationIdentifierMissing if identifier.blank?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require "logger"
|
2
|
+
require 'wolverine'
|
3
|
+
require 'shellwords'
|
4
|
+
|
5
|
+
module SingleIncreaseBy
|
6
|
+
class OperationIdentifierMissing < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
module LuaErrorExt
|
10
|
+
def generate_backtrace(file, line_number)
|
11
|
+
content = Wolverine::Script.new(file).instance_variable_get('@content')
|
12
|
+
puts "===========evaluated #{file}=========="
|
13
|
+
system "echo #{Shellwords.escape(content)} | cat -n"
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ScriptExt
|
19
|
+
def call redis, *args
|
20
|
+
keys = (args.first[:keys] || []).map { |k| Shellwords.escape(k) }
|
21
|
+
argv = (args.first[:argv] || []).map { |v| Shellwords.escape(v) }
|
22
|
+
|
23
|
+
cmd = [
|
24
|
+
"redis-cli",
|
25
|
+
"-h", redis.connection[:host],
|
26
|
+
"-p", redis.connection[:port],
|
27
|
+
"--ldb-sync-mode --eval #{tmp_file}",
|
28
|
+
*keys,
|
29
|
+
",",
|
30
|
+
*argv,
|
31
|
+
].join(" ")
|
32
|
+
|
33
|
+
puts cmd
|
34
|
+
|
35
|
+
system(cmd)
|
36
|
+
end
|
37
|
+
|
38
|
+
def tmp_file
|
39
|
+
@tmp_file ||= File.expand_path("../../tmp/lua-#{@digest}.lua", __FILE__).tap do |file_name|
|
40
|
+
File.write(file_name, @content)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class << self
|
46
|
+
attr_reader :read_wolverines
|
47
|
+
|
48
|
+
def redis=(redis)
|
49
|
+
wolverine.config.redis = redis
|
50
|
+
end
|
51
|
+
|
52
|
+
def redis
|
53
|
+
wolverine.config.redis
|
54
|
+
end
|
55
|
+
|
56
|
+
def read_redises=(redises)
|
57
|
+
return (@read_wolverines = nil) unless redises.present?
|
58
|
+
@read_wolverines = redises.map do |redis|
|
59
|
+
Wolverine.new.tap do |wolf|
|
60
|
+
wolf.config.redis = redis
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def enable_verbose_lua_error
|
66
|
+
Wolverine::LuaError.prepend(LuaErrorExt)
|
67
|
+
end
|
68
|
+
|
69
|
+
def enable_ldb_debugging
|
70
|
+
Wolverine::Script.prepend(ScriptExt)
|
71
|
+
end
|
72
|
+
|
73
|
+
def array_to_ostruct(arr)
|
74
|
+
OpenStruct.new(Hash[arr.each_slice(2).to_a])
|
75
|
+
end
|
76
|
+
|
77
|
+
def wolverine
|
78
|
+
@wolverine ||= Wolverine.new.tap do |wolf|
|
79
|
+
wolf.config.script_path = Pathname.new(File.expand_path("../../app/wolverine", __FILE__))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def read_wolverine
|
84
|
+
if read_wolverines.blank?
|
85
|
+
wolverine
|
86
|
+
else
|
87
|
+
read_wolverines.sample
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def with_readonly
|
92
|
+
yield read_wolverine
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
Dir[File.expand_path("../single_increase_by/*.rb", __FILE__)].each { |f| require f }
|
98
|
+
Dir[File.expand_path("../single_increase_by/**/*.rb", __FILE__)].each { |f| require f }
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'single_increase_by/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "single_increase_by"
|
8
|
+
spec.version = "#{SingleIncreaseBy::VERSION}"
|
9
|
+
spec.authors = ["Tan Nguyen"]
|
10
|
+
spec.email = ["tannguyenanh@gmail.com"]
|
11
|
+
spec.summary = %q{Perform single_increase_by operation}
|
12
|
+
spec.description = %q{Perform single_increase_by operation}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "redis"
|
22
|
+
spec.add_dependency "wolverine"
|
23
|
+
spec.add_dependency "hashie"
|
24
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe "Model#incr_amount" do
|
4
|
+
context "operation is nil" do
|
5
|
+
it "raise error" do
|
6
|
+
model = SingleIncreaseBy::Model.new("coin:1")
|
7
|
+
expect {
|
8
|
+
model.incr_amount(amount: 0.1, operation: nil)
|
9
|
+
}.to raise_error(SingleIncreaseBy::OperationIdentifierMissing)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "operation is empty string" do
|
14
|
+
it "raise error" do
|
15
|
+
expect {
|
16
|
+
model = SingleIncreaseBy::Model.new("coin:1")
|
17
|
+
model.incr_amount(amount: 0.1, operation: "")
|
18
|
+
}.to raise_error(SingleIncreaseBy::OperationIdentifierMissing)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "positive incr" do
|
23
|
+
it "incr the balance" do
|
24
|
+
model = SingleIncreaseBy::Model.new("coin:1")
|
25
|
+
ret = model.incr_amount(amount: 0.1, operation: "operation1")
|
26
|
+
expect(ret.to_d).to eq 0.1
|
27
|
+
amount = model.get
|
28
|
+
expect(amount.to_d.round(8)).to eq 0.1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "negative incr" do
|
33
|
+
it "decr the balance" do
|
34
|
+
model = SingleIncreaseBy::Model.new("coin:1")
|
35
|
+
ret = model.incr_amount(amount: 0.1, operation: "operation1")
|
36
|
+
expect(ret.to_d).to eq 0.1
|
37
|
+
ret = model.incr_amount(amount: -0.1, operation: "operation2")
|
38
|
+
expect(ret.to_d).to eq 0.0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "redo action" do
|
43
|
+
it "keeps same value" do
|
44
|
+
model = SingleIncreaseBy::Model.new("coin:1")
|
45
|
+
ret = model.incr_amount(amount: 0.1, operation: "operation1")
|
46
|
+
expect(ret.to_d).to eq 0.1
|
47
|
+
ret = model.incr_amount(amount: 0.1, operation: "operation2")
|
48
|
+
expect(ret.to_d.round(8)).to eq 0.2
|
49
|
+
ret = model.incr_amount(amount: 0.1, operation: "operation1")
|
50
|
+
expect(ret.to_d.round(8)).to eq 0.2
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe "Model#set_expire" do
|
4
|
+
context "set expire key" do
|
5
|
+
it "set expire successfully" do
|
6
|
+
model = SingleIncreaseBy::Model.new("coin:1")
|
7
|
+
ret = model.incr_amount(amount: 0.1, operation: "operation1")
|
8
|
+
expect(ret.to_d).to eq 0.1
|
9
|
+
amount = model.get
|
10
|
+
expect(amount.to_d.round(8)).to eq 0.1
|
11
|
+
|
12
|
+
model.set_expire(60) # 60 seconds
|
13
|
+
expect(SingleIncreaseBy.redis.ttl("coin:1")).to be_between(40.seconds, 60.seconds)
|
14
|
+
model.set_expire(120) # 60 seconds
|
15
|
+
expect(SingleIncreaseBy.redis.ttl("coin:1")).to be_between(90.seconds, 120.seconds)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe "Model#set" do
|
4
|
+
context "value is already have" do
|
5
|
+
it "raise error" do
|
6
|
+
model = SingleIncreaseBy::Model.new("coin:1")
|
7
|
+
ret = model.incr_amount(amount: 0.1, operation: "operation1")
|
8
|
+
expect(ret.to_d).to eq 0.1
|
9
|
+
amount = model.get
|
10
|
+
expect(amount.to_d.round(8)).to eq 0.1
|
11
|
+
|
12
|
+
model.set(5)
|
13
|
+
amount = model.get
|
14
|
+
expect(amount.to_d.round(8)).to eq 5
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'single_increase_by'
|
4
|
+
require 'byebug'
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/core_ext'
|
7
|
+
|
8
|
+
# Requires helpers
|
9
|
+
Dir[File.expand_path("./spec/helpers/**/*.rb")].each { |f| require f }
|
10
|
+
|
11
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
12
|
+
# in spec/support/ and its subdirectories.
|
13
|
+
Dir[File.expand_path("./spec/support/**/*.rb")].each { |f| require f }
|
14
|
+
|
15
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
18
|
+
config.run_all_when_everything_filtered = true
|
19
|
+
config.filter_run :focus
|
20
|
+
|
21
|
+
# Run specs in random order to surface order dependencies. If you find an
|
22
|
+
# order dependency and want to debug it, you can fix the order by providing
|
23
|
+
# the seed, which is printed after each run.
|
24
|
+
# --seed 1234
|
25
|
+
config.order = 'random'
|
26
|
+
end
|
27
|
+
|
28
|
+
SingleIncreaseBy.enable_verbose_lua_error
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'redis'
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
if ENV["CI"]
|
5
|
+
config.after(:each) do
|
6
|
+
SingleIncreaseBy.redis.flushdb
|
7
|
+
end
|
8
|
+
else
|
9
|
+
require 'redis_test'
|
10
|
+
config.before(:suite) do
|
11
|
+
ENV['TEST_REDIS_PORT'] = '6380'
|
12
|
+
|
13
|
+
RedisTest.start
|
14
|
+
ENV['REDIS_URL'] = "" # make sure default redis url is unset
|
15
|
+
#RedisTest.start(log_to_stdout: true)
|
16
|
+
SingleIncreaseBy.redis = Redis.new(url: RedisTest.server_url)
|
17
|
+
end
|
18
|
+
|
19
|
+
config.before(:each) do
|
20
|
+
SingleIncreaseBy.redis.set("ENVIRONMENT", "test")
|
21
|
+
end
|
22
|
+
|
23
|
+
config.after(:each) do
|
24
|
+
RedisTest.clear
|
25
|
+
# notice that will flush the Redis db, so it's less
|
26
|
+
# desirable to put that in a config.before(:each) since it may clean any
|
27
|
+
# data that you try to put in redis prior to that
|
28
|
+
end
|
29
|
+
|
30
|
+
config.after(:suite) do
|
31
|
+
RedisTest.stop
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: single_increase_by
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tan Nguyen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-09-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redis
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: wolverine
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: hashie
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Perform single_increase_by operation
|
56
|
+
email:
|
57
|
+
- tannguyenanh@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".circleci/config.yml"
|
63
|
+
- ".github/workflows/gem-push.yml"
|
64
|
+
- ".gitignore"
|
65
|
+
- ".tool-versions"
|
66
|
+
- Gemfile
|
67
|
+
- Gemfile.lock
|
68
|
+
- README.md
|
69
|
+
- app/wolverine/model/_base.lua
|
70
|
+
- app/wolverine/model/get.lua
|
71
|
+
- app/wolverine/model/incr_amount.lua
|
72
|
+
- app/wolverine/model/set.lua
|
73
|
+
- app/wolverine/model/set_expire.lua
|
74
|
+
- app/wolverine/shared/_number.lua
|
75
|
+
- github/workflows/publish-gem.yml
|
76
|
+
- lib/single_increase_by.rb
|
77
|
+
- lib/single_increase_by/model.rb
|
78
|
+
- lib/single_increase_by/version.rb
|
79
|
+
- single_increase_by.gemspec
|
80
|
+
- spec/lib/single_increase_by/model/incr_total_spec.rb
|
81
|
+
- spec/lib/single_increase_by/model/set_expire_spec.rb
|
82
|
+
- spec/lib/single_increase_by/model/set_spec.rb
|
83
|
+
- spec/spec_helper.rb
|
84
|
+
- spec/support/redis_test.rb
|
85
|
+
- spec/support/waiter.rb
|
86
|
+
homepage: ''
|
87
|
+
licenses:
|
88
|
+
- MIT
|
89
|
+
metadata: {}
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
requirements: []
|
105
|
+
rubygems_version: 3.4.10
|
106
|
+
signing_key:
|
107
|
+
specification_version: 4
|
108
|
+
summary: Perform single_increase_by operation
|
109
|
+
test_files:
|
110
|
+
- spec/lib/single_increase_by/model/incr_total_spec.rb
|
111
|
+
- spec/lib/single_increase_by/model/set_expire_spec.rb
|
112
|
+
- spec/lib/single_increase_by/model/set_spec.rb
|
113
|
+
- spec/spec_helper.rb
|
114
|
+
- spec/support/redis_test.rb
|
115
|
+
- spec/support/waiter.rb
|