single_increase_by 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|