class Redstruct::Script

Utility class to interact with Lua scripts on Redis. It is recommended you flush your script cache on the redis server every once in a while to remove scripts that are not used anymore.

Constants

ERROR_MESSAGE_PATTERN

Redis returns an error starting with NOSCRIPT when we try to evaluate am unknown script using its sha1.

Attributes

connection[R]

@return [Redstruct::ConnectionProxy] the connection used for script commands

script[R]

@return [String] the Lua script to evaluate

Public Class Methods

new(script:, connection:, sha1: nil) click to toggle source

@param [String] script the lua source code for the script @param [Redstruct::ConnectionProxy] connection connection to use for script commands @param [String] sha1 the sha1 representation of the script; optional

# File lib/redstruct/script.rb, line 25
def initialize(script:, connection:, sha1: nil)
  self.script = script
  @connection = connection
  @sha1 = sha1 unless sha1.nil?

  raise ArgumentError, 'requires a connection proxy' unless @connection.is_a?(Redstruct::ConnectionProxy)
end

Public Instance Methods

eval(keys: [], argv: []) click to toggle source

Evaluates the script using the given keys and argv arrays, and returns the unparsed result. Caller is in charge of interpreting the result. NOTE: To minimize the number of redis commands, this always first assumes that the script was already loaded using its sha1 representation, and tells redis to execute the script cached by `sha1`. If it receives as error that the script does not exist, only then will it send the source to be executed. So in the worst case you get 2 redis commands, but in the average case you get 1, and it's much faster as redis does not have to reparse the script, and we don't need to send the lua source every time. @param [Array<String>] keys the KEYS array as described in the Redis doc for eval @param [Array<String>] argv the ARGV array as described in the Redis doc for eval @return [nil, Boolean, String, Numeric] returns whatever redis returns

# File lib/redstruct/script.rb, line 76
def eval(keys: [], argv: [])
  keys = [keys] unless keys.is_a?(Array)
  argv = [argv] unless argv.is_a?(Array)
  argv = normalize(argv)

  @connection.evalsha(self.sha1, keys, argv)
rescue Redis::CommandError => err
  raise unless ERROR_MESSAGE_PATTERN.match(err.message)
  @connection.eval(@script, keys, argv)
end
exists?() click to toggle source

Checks if the script was already loaded for the given redis db using sha1 @return [Boolean] true if the script was already loaded, false otherwise

# File lib/redstruct/script.rb, line 55
def exists?
  return @connection.script(:exists, self.sha1)
end
load() click to toggle source

Loads the given script to redis (i.e. sends the source, which gets compiled and saved) and saves the returned sha1 @return [String] the new sha1

# File lib/redstruct/script.rb, line 61
def load
  @sha1 = @connection.script(:load, @script)
  return @sha1
end
script=(script) click to toggle source

Duplicates and freezes the given script, and reinitializes the sha1 (which later gets lazily computed) @param [String] script the lua source code

# File lib/redstruct/script.rb, line 35
def script=(script)
  script = script&.strip
  raise ArgumentError, 'No source script given' if script.empty?

  @sha1 = nil
  @script = script.dup.freeze
end
sha1() click to toggle source

Returns the sha1 representation of the source code at `script` When running a lua script, redis will compile it once and cache the bytecode, using the sha1 of the source code as the cache key. @return [String] sha1 representation of `script`

# File lib/redstruct/script.rb, line 47
def sha1
  return @sha1 ||= begin
    Digest::SHA1.hexdigest(@script)
  end
end

Private Instance Methods

normalize(values) click to toggle source
# File lib/redstruct/script.rb, line 93
def normalize(values)
  values.map do |value|
    case value
    when true then 1
    when false then 0
    else
      value.to_s
    end
  end
end