class Redis::Prescription

Lua script executor for redis.

Instead of executing script with `EVAL` everytime - loads script once and then runs it with `EVALSHA`.

@example Usage

script = Redis::Prescription.new("return ARGV[1] + ARGV[2]")
script.eval(Redis.current, :argv => [2, 2]) # => 2

Constants

LOAD

Script load command.

NOSCRIPT

Redis error fired when script ID is unkown.

VERSION

Gem version.

Attributes

digest[R]

LUA script SHA1 digest. @return [String]

source[R]

LUA script source. @return [String]

Public Class Methods

new(source) click to toggle source

@param source [#to_s] Lua script.

# File lib/redis/prescription.rb, line 34
def initialize(source)
  @source = source.to_s.strip.freeze
  @digest = Digest::SHA1.hexdigest(@source).freeze
end
read(file) click to toggle source

Reads given file and returns new {Prescription} with its contents. @param file [String] @return [Prescription]

# File lib/redis/prescription.rb, line 71
def self.read(file)
  new File.read file
end

Public Instance Methods

bootstrap!(redis) click to toggle source

Loads script to redis. @param redis (see namespaceless) @return [void]

# File lib/redis/prescription.rb, line 42
def bootstrap!(redis)
  digest = namespaceless(redis).script(LOAD, @source)
  return if @digest == digest

  # XXX: this may happen **ONLY** if script digesting will be
  #   changed in redis, which is not likely gonna happen.
  warn "[#{self.class}] Unexpected digest: " \
    "#{digest.inspect} (expected: #{@digest.inspect})"

  @digest = digest.freeze
end
eval(redis, keys: [], argv: []) click to toggle source

Executes script and returns result of execution. @param redis (see namespaceless) @param keys [Array] keys to pass to the script @param argv [Array] arguments to pass to the script @return depends on the script

# File lib/redis/prescription.rb, line 59
def eval(redis, keys: [], argv: [])
  redis.evalsha(@digest, keys, argv)
rescue => e
  raise unless e.message.include? NOSCRIPT

  bootstrap!(redis)
  redis.evalsha(@digest, keys, argv)
end

Private Instance Methods

namespaceless(redis) click to toggle source

Yields real namespace-less redis client. @param redis [Redis, Redis::Namespace] @return [Redis]

# File lib/redis/prescription.rb, line 80
def namespaceless(redis)
  if redis.is_a?(Redis)
    redis
  elsif defined?(Redis::Namespace) && redis.is_a?(Redis::Namespace)
    redis.redis
  else
    raise TypeError, "Unsupported redis client type: #{redis.class}"
  end
end