Class: RedisScripts

Inherits:
Object
  • Object
show all
Defined in:
lib/redis_scripts.rb,
lib/redis_scripts/version.rb

Overview

Adapter for elegant Redis scripting.

This is usually accessed as redis.scripts, although can also be instantiated as RedisScripts.new(redis).

Defined Under Namespace

Modules: Mixin Classes: Script

Constant Summary collapse

SHAMismatch =

Raised when Redis returns an unexpected SHA when loading a script into the redis script cache.

Should never happen.

Class.new(RuntimeError)
VERSION =
[0, 0, 2]

Class Attribute Summary collapse

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(redis) ⇒ RedisScripts

Create a RedisScripts adapter for the given redis handle.



20
21
22
23
# File 'lib/redis_scripts.rb', line 20

def initialize(redis)
  @redis = redis
  @load_path = RedisScripts.load_path
end

Class Attribute Details

.load_pathObject

Global load path for redis scripts.

redis.scripts.load_path defaults to this value for all redis clients.



16
17
18
# File 'lib/redis_scripts.rb', line 16

def load_path
  @load_path
end

Instance Attribute Details

#load_pathObject

Paths to look for Redis scripts.

These directories are searched recursively for all .lua files. Defaults to RedisScripts.load_path, which itself has no default, so one of these needs to be set.

Like the ruby load path, earlier directories shadow later directories in the event two directories contain scripts with the same name.



36
37
38
# File 'lib/redis_scripts.rb', line 36

def load_path
  @load_path
end

#redisObject (readonly)

The adapter’s redis handle.



26
27
28
# File 'lib/redis_scripts.rb', line 26

def redis
  @redis
end

Instance Method Details

#eval(name, *args) ⇒ Object

Call EVAL for the named script.

Raises ArgumentError if no such script exists.



72
73
74
# File 'lib/redis_scripts.rb', line 72

def eval(name, *args)
  redis.eval script(name).content, *args
end

#evalsha(name, *args) ⇒ Object

Call EVALSHA for the named script.

Raises ArgumentError if no such script exists.



104
105
106
# File 'lib/redis_scripts.rb', line 104

def evalsha(name, *args)
  redis.evalsha script(name).sha, *args
end

#exists(name) ⇒ Object

Call SCRIPT EXISTS for the named script.

Raises ArgumentError if no such script exists.



97
98
99
# File 'lib/redis_scripts.rb', line 97

def exists(name)
  redis.script 'exists', script(name).sha
end

#load(name) ⇒ Object

Call SCRIPT LOAD for the named script.

Raises ArgumentError if no such script exists.



79
80
81
# File 'lib/redis_scripts.rb', line 79

def load(name)
  redis.script 'load', script(name).content
end

#load_allObject

Call SCRIPT LOAD for all scripts.

This effectively primes the script cache with all your scripts. It does not remove any scripts - use redis.script(‘flush’) to empty the script cache first if that is required.



88
89
90
91
92
# File 'lib/redis_scripts.rb', line 88

def load_all
  scripts.each do |name, script|
    redis.script 'load', script.content
  end
end

#run(name, *args) ⇒ Object

Run the script named name with the given args.

name is the path of the script relative to the load_path, minus the ‘.lua’ extension. So if the load_path contains ‘scripts’, and the script is at ‘scripts/foo/bar.lua’, then name should be ‘foo/bar’. name may be a string or symbol.

args are passed to Redis#evalsha (see documentation for the Redis gem). If the script is not yet loaded in the redis script cache, it is loaded and called again. Note that this means this should not be called inside a MULTI transaction - this is usually not a problem, since the purpose of scripting is to perform a sequence of atomic operations in a single command.

Raises ArgumentError if no such script exists.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/redis_scripts.rb', line 52

def run(name, *args)
  script = script(name)
  begin
    redis.evalsha script.sha, *args
  rescue Redis::CommandError => error
    error.message.include?('NOSCRIPT') or
      raise
    sha, value = redis.pipelined do
      redis.script 'load', script.content
      redis.evalsha(script.sha, *args)
    end
    sha == script.sha or
      raise SHAMismatch, "SHA mismatch for #{name}: expected #{script.sha}, got #{sha}"
    value
  end
end

#script(name) ⇒ Object

Return the named script, as a Script object.

Raises ArgumentError if no such script exists.



111
112
113
114
# File 'lib/redis_scripts.rb', line 111

def script(name)
  scripts[name.to_s] or
    raise ArgumentError, "no such script: #{name}"
end