redis_lua_script 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6bff5852216da649ba066217fd1a5667703d61cdfdec54905992a38cb1bd649f
4
+ data.tar.gz: 3517ccc6e0f1afe441167d3344ccf5ded1004d897d9620ce7576edb03df6a123
5
+ SHA512:
6
+ metadata.gz: ee056fef4a44e200f854f985721789c794bf31c25d9786c93534bb48b78c72b4f570624ce126531ab0fbf3ed4a95dd9ad0b7b64c73a3ee3cc766f17becebe986
7
+ data.tar.gz: 06ad63688692bf5f0cd5064dfbbc77a53d0438295c812ef7b9e93dc6d905b58f11f5fa580ae55b486908601b12a08b8aaff8f72fd4989fa404ef3c256a8daf48
@@ -0,0 +1,5 @@
1
+ class Redis
2
+ class LuaScript
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,51 @@
1
+ require "digest"
2
+ require "redis"
3
+ require "redis/lua_script/version"
4
+
5
+ class Redis
6
+ class LuaScript
7
+ attr_reader :source
8
+
9
+ def initialize(source)
10
+ @source = source.dup.freeze
11
+ end
12
+
13
+ def sha
14
+ @sha ||= Digest::SHA1.hexdigest(minify).freeze
15
+ end
16
+
17
+ def eval(redis, *args)
18
+ redis.evalsha(sha, *args)
19
+ rescue Redis::CommandError => e
20
+ raise unless e.message.include?("NOSCRIPT")
21
+
22
+ # fall back to regular eval, which will trigger
23
+ # script to be cached for next time
24
+ redis.eval(minify, *args)
25
+ end
26
+
27
+ def load(redis)
28
+ redis.script(:load, minify).tap do |sha|
29
+ unless sha == self.sha
30
+ raise "unexpected script SHA: expected #{self.sha}, got #{sha}"
31
+ end
32
+ end
33
+ end
34
+
35
+ def loaded?(redis)
36
+ redis.script(:exists, sha)
37
+ end
38
+
39
+ def to_s
40
+ source
41
+ end
42
+
43
+ private
44
+
45
+ def minify
46
+ # trim comments (whole line and partial)
47
+ # and whitespace (prefix and empty lines)
48
+ @minify ||= source.gsub(/^\s*--.*\n|\s*--.*|^\s*|^$\n/, "").chomp.freeze
49
+ end
50
+ end
51
+ end
@@ -0,0 +1 @@
1
+ require "redis/lua_script"
@@ -0,0 +1,109 @@
1
+ describe Redis::LuaScript do
2
+ subject { Redis::LuaScript.new("return redis.call('PING')") }
3
+
4
+ before { redis.script(:flush) }
5
+
6
+ let(:redis) { Redis.new }
7
+
8
+ it { is_expected.to be_a Redis::LuaScript }
9
+
10
+ describe "#eval" do
11
+ def ping
12
+ expect(subject.eval(redis)).to eq "PONG"
13
+ end
14
+
15
+ it { ping }
16
+
17
+ it "loads the script into redis" do
18
+ expect(redis).to receive(:evalsha).once.and_call_original
19
+ expect(redis).to receive(:eval).once.and_call_original
20
+ ping
21
+ expect(subject.loaded?(redis)).to be true
22
+ end
23
+ end
24
+
25
+ describe "#load" do
26
+ it "loads script into redis" do
27
+ expect(redis.script(:exists, subject.sha)).to be false
28
+ subject.load(redis)
29
+ expect(redis.script(:exists, subject.sha)).to be true
30
+ end
31
+
32
+ it "returns the sha" do
33
+ expect(subject.load(redis)).to eq subject.sha
34
+ end
35
+
36
+ it "validates the returned sha" do
37
+ allow(redis).to receive(:script).with(:flush).and_call_original
38
+ expect(redis).to receive(:script).with(:load, String).and_return("abc")
39
+ expect { subject.load(redis) }.to raise_error(RuntimeError)
40
+ end
41
+ end
42
+
43
+ describe "#loaded?" do
44
+ it do
45
+ expect(subject.loaded?(redis)).to be false
46
+ subject.load(redis)
47
+ expect(subject.loaded?(redis)).to be true
48
+ end
49
+ end
50
+
51
+ describe "#to_s" do
52
+ it { expect(subject.to_s).to be subject.source }
53
+ end
54
+
55
+ describe "#minify" do
56
+ subject { Redis::LuaScript.new(lua) }
57
+
58
+ shared_examples "minified lua" do
59
+ it do
60
+ expect(subject.send(:minify)).to eq expected
61
+ end
62
+ end
63
+
64
+ context do
65
+ let(:lua) do <<-LUA
66
+ -- this comment gets removed
67
+ return redis.call('PING') -- this one too
68
+ LUA
69
+ end
70
+
71
+ let(:expected) { "return redis.call('PING')" }
72
+
73
+ it_behaves_like "minified lua"
74
+
75
+ it "executes properly" do
76
+ expect(subject.eval(redis)).to eq "PONG"
77
+ end
78
+ end
79
+
80
+ context "with if statement" do
81
+ let(:lua) do <<~LUA
82
+ local val = tonumber(ARGV[1])
83
+ if val > 123 then
84
+ return val
85
+ end
86
+
87
+ return 123
88
+ LUA
89
+ end
90
+
91
+ let(:expected) do
92
+ [
93
+ "local val = tonumber(ARGV[1])",
94
+ "if val > 123 then",
95
+ "return val",
96
+ "end",
97
+ "return 123",
98
+ ].join "\n"
99
+ end
100
+
101
+ it_behaves_like "minified lua"
102
+
103
+ it "executes properly" do
104
+ expect(subject.eval(redis, [], [ 0 ])).to eq 123
105
+ expect(subject.eval(redis, [], [ 999 ])).to eq 999
106
+ end
107
+ end
108
+ end
109
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis_lua_script
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Pepper
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-02-12 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: byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
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: codecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Redis Lua script optimization
84
+ email:
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - lib/redis/lua_script.rb
90
+ - lib/redis/lua_script/version.rb
91
+ - lib/redis_lua_script.rb
92
+ - spec/lua_script_spec.rb
93
+ homepage: https://github.com/dpep/redis_lua_script_rb
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubygems_version: 3.1.6
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: Redis::LuaScript
116
+ test_files:
117
+ - spec/lua_script_spec.rb