redis_support 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +58 -0
 - data/lib/redis_support/class_extensions.rb +56 -0
 - data/lib/redis_support/locks.rb +98 -0
 - data/lib/redis_support.rb +41 -0
 - data/test/helper.rb +70 -0
 - data/test/test_redis_support.rb +54 -0
 - metadata +88 -0
 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,58 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Redis Support
         
     | 
| 
      
 2 
     | 
    
         
            +
            =============
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            redis_support is a small library which provides simple support for
         
     | 
| 
      
 5 
     | 
    
         
            +
            common actions for the Redis key-value store. It is not an
         
     | 
| 
      
 6 
     | 
    
         
            +
            object-relational mapper, does not attempt to comprehensively solve
         
     | 
| 
      
 7 
     | 
    
         
            +
            all of your storage problems, and does not make julienne fries.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            Key Support
         
     | 
| 
      
 10 
     | 
    
         
            +
            -----------
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            Redis provides a global keyspace. Most projects specify their keys
         
     | 
| 
      
 13 
     | 
    
         
            +
            using namespaces delimited by colons (:), with variables mixed in at
         
     | 
| 
      
 14 
     | 
    
         
            +
            particular points. For example:
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                users:1:email
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            `redis_support`, when used as a mixin, will provide a simple method to
         
     | 
| 
      
 19 
     | 
    
         
            +
            declare that namespace and access it later.
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                class User
         
     | 
| 
      
 22 
     | 
    
         
            +
                  include RedisSupport
         
     | 
| 
      
 23 
     | 
    
         
            +
                  redis_key :email, "users:USER_ID:email"
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  attr_accessor :id
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def email
         
     | 
| 
      
 28 
     | 
    
         
            +
                    redis.get Keys.email( self.id )
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            Helpful exceptions are raised if you try to declare the same namespace
         
     | 
| 
      
 33 
     | 
    
         
            +
            twice - RedisSupport keeps track of all the key definitions your app
         
     | 
| 
      
 34 
     | 
    
         
            +
            wants to use.
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            Locking Support
         
     | 
| 
      
 37 
     | 
    
         
            +
            ---------------
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            There is also a simple locking mechanism based on SETNX. Locking is
         
     | 
| 
      
 40 
     | 
    
         
            +
            usually unnecessary since Redis operations are atomic and Redis'
         
     | 
| 
      
 41 
     | 
    
         
            +
            MULTI/EXEC/DISCARD gives you the ability to make multiple operations
         
     | 
| 
      
 42 
     | 
    
         
            +
            atomic. But sometimes it's useful - a contrived example -
         
     | 
| 
      
 43 
     | 
    
         
            +
            major_operation will block for up to 30 seconds if another process
         
     | 
| 
      
 44 
     | 
    
         
            +
            attempts to run it at the same time:
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                class User
         
     | 
| 
      
 47 
     | 
    
         
            +
                  include RedisSupport
         
     | 
| 
      
 48 
     | 
    
         
            +
                  redis_key :email, "users:USER_ID:email"
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  attr_accessor :id
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  def major_operation
         
     | 
| 
      
 53 
     | 
    
         
            +
                    redis_lock Keys.email( self.id ) do
         
     | 
| 
      
 54 
     | 
    
         
            +
                      # do my expensive stuff
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module RedisSupport
         
     | 
| 
      
 2 
     | 
    
         
            +
              class RedisKeyError < StandardError ; end
         
     | 
| 
      
 3 
     | 
    
         
            +
              class DuplicateRedisKeyDefinitionError < RedisKeyError ; end
         
     | 
| 
      
 4 
     | 
    
         
            +
              class InvalidRedisKeyDefinitionError < RedisKeyError ; end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              VAR_PATTERN = /^[A-Z]+(_[A-Z]+)*$/
         
     | 
| 
      
 7 
     | 
    
         
            +
              STR_PATTERN = /^[a-z_]+$/
         
     | 
| 
      
 8 
     | 
    
         
            +
              module ClassMethods
         
     | 
| 
      
 9 
     | 
    
         
            +
                # Goal is to allow a class to declare a redis key/property 
         
     | 
| 
      
 10 
     | 
    
         
            +
                # The key is a colon delimited string where variables
         
     | 
| 
      
 11 
     | 
    
         
            +
                # are listed are upper case (underscores inbetween) and
         
     | 
| 
      
 12 
     | 
    
         
            +
                # non variables are completely lower case (underscores inbetween or appending/prepending)
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                # variables cannot be repeated and must start with a letter
         
     | 
| 
      
 15 
     | 
    
         
            +
                # the key must also start with a nonvariable
         
     | 
| 
      
 16 
     | 
    
         
            +
                #
         
     | 
| 
      
 17 
     | 
    
         
            +
                # Examples
         
     | 
| 
      
 18 
     | 
    
         
            +
                #
         
     | 
| 
      
 19 
     | 
    
         
            +
                #   redis_key :workpools, "job:JOB_ID:workpools"
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                # Returns the redis key.
         
     | 
| 
      
 22 
     | 
    
         
            +
                def redis_key( name, keystruct )
         
     | 
| 
      
 23 
     | 
    
         
            +
                  if Keys.methods.include? name.to_s
         
     | 
| 
      
 24 
     | 
    
         
            +
                    raise DuplicateRedisKeyDefinitionError
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  
         
     | 
| 
      
 27 
     | 
    
         
            +
                  key = keystruct.split(":")
         
     | 
| 
      
 28 
     | 
    
         
            +
                  
         
     | 
| 
      
 29 
     | 
    
         
            +
                  unless (first = key.shift) =~ STR_PATTERN
         
     | 
| 
      
 30 
     | 
    
         
            +
                    raise InvalidRedisKeyDefinitionError.new "keys must begin with lowercase letters"
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  vars, strs = key.inject([[],[]]) do |(vs, ss), token|
         
     | 
| 
      
 34 
     | 
    
         
            +
                    case token
         
     | 
| 
      
 35 
     | 
    
         
            +
                    when VAR_PATTERN
         
     | 
| 
      
 36 
     | 
    
         
            +
                      var = token.downcase
         
     | 
| 
      
 37 
     | 
    
         
            +
                      ss << "\#{#{var}}"
         
     | 
| 
      
 38 
     | 
    
         
            +
                      vs << var
         
     | 
| 
      
 39 
     | 
    
         
            +
                    when STR_PATTERN
         
     | 
| 
      
 40 
     | 
    
         
            +
                      ss << token
         
     | 
| 
      
 41 
     | 
    
         
            +
                    else
         
     | 
| 
      
 42 
     | 
    
         
            +
                      raise InvalidRedisKeyDefinitionError.new "Internal error parsing #{keystruct} : last token : #{token}"
         
     | 
| 
      
 43 
     | 
    
         
            +
                    end
         
     | 
| 
      
 44 
     | 
    
         
            +
                    [vs, ss]
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  strs.unshift(first)
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  RedisSupport::Keys.class_eval <<-RUBY, __FILE__, __LINE__ + 1
         
     | 
| 
      
 50 
     | 
    
         
            +
                    def self.#{name.to_s}( #{vars.map {|x| x.to_s }.join(', ')} )
         
     | 
| 
      
 51 
     | 
    
         
            +
                      "#{strs.join(":")}"
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  RUBY
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,98 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Locking support
         
     | 
| 
      
 2 
     | 
    
         
            +
            #
         
     | 
| 
      
 3 
     | 
    
         
            +
            module RedisSupport
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              # Lock a block of code so it can only be accessed by one thread in
         
     | 
| 
      
 6 
     | 
    
         
            +
              # our system at a time.
         
     | 
| 
      
 7 
     | 
    
         
            +
              #
         
     | 
| 
      
 8 
     | 
    
         
            +
              # See 'acquire_redis_lock' for details on parameters.
         
     | 
| 
      
 9 
     | 
    
         
            +
              #
         
     | 
| 
      
 10 
     | 
    
         
            +
              # Returns nothing.
         
     | 
| 
      
 11 
     | 
    
         
            +
              def redis_lock( key_to_lock, expiration = 30, interval = 1 )
         
     | 
| 
      
 12 
     | 
    
         
            +
                acquire_redis_lock( key_to_lock, expiration, interval )
         
     | 
| 
      
 13 
     | 
    
         
            +
                yield
         
     | 
| 
      
 14 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 15 
     | 
    
         
            +
                release_redis_lock( key_to_lock )
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              # Acquire a lock on a key in our Redis database. This is a blocking
         
     | 
| 
      
 19 
     | 
    
         
            +
              # call. It sleeps until the lock has been successfully acquired.
         
     | 
| 
      
 20 
     | 
    
         
            +
              #
         
     | 
| 
      
 21 
     | 
    
         
            +
              # Basic usage:
         
     | 
| 
      
 22 
     | 
    
         
            +
              #
         
     | 
| 
      
 23 
     | 
    
         
            +
              #   acquire_redis_lock( key.my_key )
         
     | 
| 
      
 24 
     | 
    
         
            +
              #   # do some stuff on my_key
         
     | 
| 
      
 25 
     | 
    
         
            +
              #   release_redis_lock( key.my_key )
         
     | 
| 
      
 26 
     | 
    
         
            +
              #
         
     | 
| 
      
 27 
     | 
    
         
            +
              # Described in detail here:
         
     | 
| 
      
 28 
     | 
    
         
            +
              #
         
     | 
| 
      
 29 
     | 
    
         
            +
              #   http://code.google.com/p/redis/wiki/SetnxCommand
         
     | 
| 
      
 30 
     | 
    
         
            +
              #
         
     | 
| 
      
 31 
     | 
    
         
            +
              # key_to_lock - the key to lock. the actual key for the lock in redis will
         
     | 
| 
      
 32 
     | 
    
         
            +
              #               be this value with 'lock.' prepended, which lets this whole
         
     | 
| 
      
 33 
     | 
    
         
            +
              #               acquire_lock business act like a standard ruby object or
         
     | 
| 
      
 34 
     | 
    
         
            +
              #               synchronize lock. Also it ensures that all locks in the database
         
     | 
| 
      
 35 
     | 
    
         
            +
              #               can be easily viewed using redis.keys("lock.*")
         
     | 
| 
      
 36 
     | 
    
         
            +
              #
         
     | 
| 
      
 37 
     | 
    
         
            +
              # expiration  - the expiration for the lock, expressed as an Integer. default is
         
     | 
| 
      
 38 
     | 
    
         
            +
              #               30 seconds from when the lock is acquired. Note that this is the
         
     | 
| 
      
 39 
     | 
    
         
            +
              #               amount of time others will wait for you, not the amount of time
         
     | 
| 
      
 40 
     | 
    
         
            +
              #               you will wait to acquire the lock.
         
     | 
| 
      
 41 
     | 
    
         
            +
              #
         
     | 
| 
      
 42 
     | 
    
         
            +
              # interval    - sleep interval for checking the lock's status.
         
     | 
| 
      
 43 
     | 
    
         
            +
              #   
         
     | 
| 
      
 44 
     | 
    
         
            +
              # Returns nothing.
         
     | 
| 
      
 45 
     | 
    
         
            +
              def acquire_redis_lock( key_to_lock, expiration = 30, interval = 1 )
         
     | 
| 
      
 46 
     | 
    
         
            +
                key = lock_key( key_to_lock )
         
     | 
| 
      
 47 
     | 
    
         
            +
                until redis.setnx key, timeout_i( expiration )
         
     | 
| 
      
 48 
     | 
    
         
            +
                  if redis.get( key ).to_i < Time.now.to_i
         
     | 
| 
      
 49 
     | 
    
         
            +
                    old_timeout = redis.getset( key, timeout_i( expiration ) ).to_i
         
     | 
| 
      
 50 
     | 
    
         
            +
                    if old_timeout < Time.now.to_i
         
     | 
| 
      
 51 
     | 
    
         
            +
                      return # got it!
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  else
         
     | 
| 
      
 54 
     | 
    
         
            +
                    sleep interval
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
              # Acquire a redis lock only if it can be acquired
         
     | 
| 
      
 60 
     | 
    
         
            +
              # is a nonblocking action
         
     | 
| 
      
 61 
     | 
    
         
            +
              #
         
     | 
| 
      
 62 
     | 
    
         
            +
              # Returns true on success and false on failure
         
     | 
| 
      
 63 
     | 
    
         
            +
              def acquire_redis_lock_nonblock( key_to_lock, expiration = 30 ) 
         
     | 
| 
      
 64 
     | 
    
         
            +
                key = lock_key( key_to_lock ) 
         
     | 
| 
      
 65 
     | 
    
         
            +
                redis.setnx key, timeout_i( expiration )
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
              
         
     | 
| 
      
 68 
     | 
    
         
            +
              # See docs for acquire_redis_lock above
         
     | 
| 
      
 69 
     | 
    
         
            +
              #
         
     | 
| 
      
 70 
     | 
    
         
            +
              # Returns nothing.
         
     | 
| 
      
 71 
     | 
    
         
            +
              def release_redis_lock( locked_key )
         
     | 
| 
      
 72 
     | 
    
         
            +
                redis.del lock_key( locked_key )
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
              def has_redis_lock?( locked_key )
         
     | 
| 
      
 76 
     | 
    
         
            +
                redis.exists lock_key(locked_key) 
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
              private
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
              def lock_key( key_to_lock )
         
     | 
| 
      
 82 
     | 
    
         
            +
                "lock.#{key_to_lock}"
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
              # Converts an Integer number of seconds into a future timestamp that
         
     | 
| 
      
 86 
     | 
    
         
            +
              # can be used with Redis.
         
     | 
| 
      
 87 
     | 
    
         
            +
              #
         
     | 
| 
      
 88 
     | 
    
         
            +
              # Examples
         
     | 
| 
      
 89 
     | 
    
         
            +
              # 
         
     | 
| 
      
 90 
     | 
    
         
            +
              #   timeout_i(expiration)
         
     | 
| 
      
 91 
     | 
    
         
            +
              #   # => 1274955869
         
     | 
| 
      
 92 
     | 
    
         
            +
              #
         
     | 
| 
      
 93 
     | 
    
         
            +
              # Returns the timestamp.
         
     | 
| 
      
 94 
     | 
    
         
            +
              def timeout_i( timeout )
         
     | 
| 
      
 95 
     | 
    
         
            +
                timeout.seconds.from_now.to_i
         
     | 
| 
      
 96 
     | 
    
         
            +
              end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,41 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'redis'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "/"))
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'redis_support/class_extensions'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'redis_support/locks'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module RedisSupport
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              # Inspired/take from the redis= in Resque
         
     | 
| 
      
 10 
     | 
    
         
            +
              #
         
     | 
| 
      
 11 
     | 
    
         
            +
              # Accepts:
         
     | 
| 
      
 12 
     | 
    
         
            +
              #   1. A 'hostname:port' string
         
     | 
| 
      
 13 
     | 
    
         
            +
              #   2. A 'hostname:port:db' string (to select the Redis db)
         
     | 
| 
      
 14 
     | 
    
         
            +
              #   3. An instance of `Redis`, `Redis::Client`
         
     | 
| 
      
 15 
     | 
    
         
            +
              def redis=(connection)
         
     | 
| 
      
 16 
     | 
    
         
            +
                if connection.respond_to? :split
         
     | 
| 
      
 17 
     | 
    
         
            +
                  host, port, db = connection.split(':')
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @@redis = Redis.new(:host => host,:port => port,:thread_safe => true,:db => db)
         
     | 
| 
      
 19 
     | 
    
         
            +
                else
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @@redis = connection
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              def redis
         
     | 
| 
      
 25 
     | 
    
         
            +
                return @@redis if @@redis
         
     | 
| 
      
 26 
     | 
    
         
            +
                self.redis = @@redis || 'localhost:6379'
         
     | 
| 
      
 27 
     | 
    
         
            +
                self.redis
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              def keys
         
     | 
| 
      
 31 
     | 
    
         
            +
                Keys
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              module Keys ; end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              def self.included(model)
         
     | 
| 
      
 37 
     | 
    
         
            +
                model.extend ClassMethods
         
     | 
| 
      
 38 
     | 
    
         
            +
                model.extend RedisSupport
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
    
        data/test/helper.rb
    ADDED
    
    | 
         @@ -0,0 +1,70 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'test/unit'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            dir = File.dirname(__FILE__)
         
     | 
| 
      
 5 
     | 
    
         
            +
            $LOAD_PATH.unshift(File.join(dir, '..', 'lib'))
         
     | 
| 
      
 6 
     | 
    
         
            +
            $LOAD_PATH.unshift(dir)
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'redis_support'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ##
         
     | 
| 
      
 10 
     | 
    
         
            +
            # much of this was taken directly from the resque test suite
         
     | 
| 
      
 11 
     | 
    
         
            +
            #
         
     | 
| 
      
 12 
     | 
    
         
            +
            #
         
     | 
| 
      
 13 
     | 
    
         
            +
            if !system("which redis-server")
         
     | 
| 
      
 14 
     | 
    
         
            +
              puts '', "** can't find `redis-server` in your path"
         
     | 
| 
      
 15 
     | 
    
         
            +
              puts "** try running `sudo rake install`"
         
     | 
| 
      
 16 
     | 
    
         
            +
              abort ''
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            #
         
     | 
| 
      
 20 
     | 
    
         
            +
            # start our own redis when the tests start,
         
     | 
| 
      
 21 
     | 
    
         
            +
            # kill it when they end
         
     | 
| 
      
 22 
     | 
    
         
            +
            #
         
     | 
| 
      
 23 
     | 
    
         
            +
            at_exit do
         
     | 
| 
      
 24 
     | 
    
         
            +
              next if $!
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              if defined?(MiniTest)
         
     | 
| 
      
 27 
     | 
    
         
            +
                exit_code = MiniTest::Unit.new.run(ARGV)
         
     | 
| 
      
 28 
     | 
    
         
            +
              else
         
     | 
| 
      
 29 
     | 
    
         
            +
                exit_code = Test::Unit::AutoRunner.run
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              pid = `ps -A -o pid,command | grep [r]edis-test`.split(" ")[0]
         
     | 
| 
      
 33 
     | 
    
         
            +
              puts "Killing test redis server..."
         
     | 
| 
      
 34 
     | 
    
         
            +
              `rm -f #{dir}/dump.rdb`
         
     | 
| 
      
 35 
     | 
    
         
            +
              Process.kill("KILL", pid.to_i)
         
     | 
| 
      
 36 
     | 
    
         
            +
              exit exit_code
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            puts "Starting redis for testing at localhost:9736..."
         
     | 
| 
      
 40 
     | 
    
         
            +
            `redis-server #{dir}/redis-test.conf`
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            class TestClass
         
     | 
| 
      
 43 
     | 
    
         
            +
              include RedisSupport
         
     | 
| 
      
 44 
     | 
    
         
            +
              
         
     | 
| 
      
 45 
     | 
    
         
            +
              redis_key :test_novar, "test:redis"
         
     | 
| 
      
 46 
     | 
    
         
            +
              redis_key :test_var, "test:redis:VAR"
         
     | 
| 
      
 47 
     | 
    
         
            +
              redis_key :test_vars, "test:redis:VAR_ONE:VAR_TWO:append"
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            TestClass.redis = "localhost:9736"
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            ##
         
     | 
| 
      
 53 
     | 
    
         
            +
            # test/spec/mini 3
         
     | 
| 
      
 54 
     | 
    
         
            +
            # http://gist.github.com/25455
         
     | 
| 
      
 55 
     | 
    
         
            +
            # chris@ozmm.org
         
     | 
| 
      
 56 
     | 
    
         
            +
            #
         
     | 
| 
      
 57 
     | 
    
         
            +
            def context(*args, &block)
         
     | 
| 
      
 58 
     | 
    
         
            +
              return super unless (name = args.first) && block
         
     | 
| 
      
 59 
     | 
    
         
            +
              require 'test/unit'
         
     | 
| 
      
 60 
     | 
    
         
            +
              klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
         
     | 
| 
      
 61 
     | 
    
         
            +
                def self.test(name, &block)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
                def self.xtest(*args) end
         
     | 
| 
      
 65 
     | 
    
         
            +
                def self.setup(&block) define_method(:setup, &block) end
         
     | 
| 
      
 66 
     | 
    
         
            +
                def self.teardown(&block) define_method(:teardown, &block) end
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
              (class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
         
     | 
| 
      
 69 
     | 
    
         
            +
              klass.class_eval &block
         
     | 
| 
      
 70 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,54 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.dirname(__FILE__) + '/helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            context "Redis Support" do
         
     | 
| 
      
 4 
     | 
    
         
            +
              setup do
         
     | 
| 
      
 5 
     | 
    
         
            +
                TestClass.redis.flushall
         
     | 
| 
      
 6 
     | 
    
         
            +
                @test_class = TestClass.new
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              test "redis connection works as expected" do
         
     | 
| 
      
 10 
     | 
    
         
            +
                assert_equal @test_class.redis, TestClass.redis
         
     | 
| 
      
 11 
     | 
    
         
            +
                assert_equal "OK", @test_class.redis.set("superman", 1)
         
     | 
| 
      
 12 
     | 
    
         
            +
                assert_equal "1", @test_class.redis.get("superman")
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              test "redis connections changes as expected" do
         
     | 
| 
      
 16 
     | 
    
         
            +
                TestClass.redis = "localhost:6379"
         
     | 
| 
      
 17 
     | 
    
         
            +
                assert_equal @test_class.redis, TestClass.redis
         
     | 
| 
      
 18 
     | 
    
         
            +
                @test_class.redis = "localhost:9736"
         
     | 
| 
      
 19 
     | 
    
         
            +
                assert_equal @test_class.redis, TestClass.redis
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              test "redis keys are created correctly in normal conditions" do
         
     | 
| 
      
 23 
     | 
    
         
            +
                assert_equal "test:redis", TestClass::Keys.test_novar
         
     | 
| 
      
 24 
     | 
    
         
            +
                assert_equal "test:redis:variable", TestClass::Keys.test_var("variable")
         
     | 
| 
      
 25 
     | 
    
         
            +
                assert_equal "test:redis:variable:id:append", TestClass::Keys.test_vars("variable", "id")
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              test "redis key should be able to create key" do
         
     | 
| 
      
 29 
     | 
    
         
            +
                assert_nothing_raised do
         
     | 
| 
      
 30 
     | 
    
         
            +
                  TestClass.redis_key :whatever, "this_should_work"
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              test "redis keys are not created if the keyname was previously used" do
         
     | 
| 
      
 35 
     | 
    
         
            +
                assert_raise(RedisSupport::DuplicateRedisKeyDefinitionError) do
         
     | 
| 
      
 36 
     | 
    
         
            +
                  TestClass.redis_key :test_var, "this:should:fail"
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              test "redis key should fail when given incorrect syntax" do
         
     | 
| 
      
 41 
     | 
    
         
            +
                failure_keys = %w{oh:WE_should:fail oh:WE_:fail oh:_WE:fail oh:WE_903:fail FAILPART:oh:no}
         
     | 
| 
      
 42 
     | 
    
         
            +
                failure_keys.each do |failure_key|
         
     | 
| 
      
 43 
     | 
    
         
            +
                  assert_raise(RedisSupport::InvalidRedisKeyDefinitionError) do
         
     | 
| 
      
 44 
     | 
    
         
            +
                    TestClass.redis_key :failure, failure_key
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                end    
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
              test "redis keys fails gracefully, syntax error, when key space is fucked" do
         
     | 
| 
      
 50 
     | 
    
         
            +
                assert_raise(SyntaxError) do
         
     | 
| 
      
 51 
     | 
    
         
            +
                  TestClass.redis_key :failure, "test:redis:VAR:VAR:oops"
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,88 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification 
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: redis_support
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version 
         
     | 
| 
      
 4 
     | 
    
         
            +
              hash: 29
         
     | 
| 
      
 5 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 6 
     | 
    
         
            +
              segments: 
         
     | 
| 
      
 7 
     | 
    
         
            +
              - 0
         
     | 
| 
      
 8 
     | 
    
         
            +
              - 0
         
     | 
| 
      
 9 
     | 
    
         
            +
              - 1
         
     | 
| 
      
 10 
     | 
    
         
            +
              version: 0.0.1
         
     | 
| 
      
 11 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 12 
     | 
    
         
            +
            authors: 
         
     | 
| 
      
 13 
     | 
    
         
            +
            - dolores
         
     | 
| 
      
 14 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 15 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 16 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            date: 2010-06-18 00:00:00 -07:00
         
     | 
| 
      
 19 
     | 
    
         
            +
            default_executable: 
         
     | 
| 
      
 20 
     | 
    
         
            +
            dependencies: 
         
     | 
| 
      
 21 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 22 
     | 
    
         
            +
              name: redis
         
     | 
| 
      
 23 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 24 
     | 
    
         
            +
              requirement: &id001 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 25 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 26 
     | 
    
         
            +
                requirements: 
         
     | 
| 
      
 27 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 28 
     | 
    
         
            +
                  - !ruby/object:Gem::Version 
         
     | 
| 
      
 29 
     | 
    
         
            +
                    hash: 31
         
     | 
| 
      
 30 
     | 
    
         
            +
                    segments: 
         
     | 
| 
      
 31 
     | 
    
         
            +
                    - 1
         
     | 
| 
      
 32 
     | 
    
         
            +
                    - 0
         
     | 
| 
      
 33 
     | 
    
         
            +
                    - 4
         
     | 
| 
      
 34 
     | 
    
         
            +
                    version: 1.0.4
         
     | 
| 
      
 35 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: *id001
         
     | 
| 
      
 37 
     | 
    
         
            +
            description: "Module for adding redis functionality to classes: simple key namespacing and locking and connections"
         
     | 
| 
      
 38 
     | 
    
         
            +
            email: dolores@doloreslabs.com
         
     | 
| 
      
 39 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            extra_rdoc_files: 
         
     | 
| 
      
 44 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 45 
     | 
    
         
            +
            files: 
         
     | 
| 
      
 46 
     | 
    
         
            +
            - lib/redis_support.rb
         
     | 
| 
      
 47 
     | 
    
         
            +
            - lib/redis_support/class_extensions.rb
         
     | 
| 
      
 48 
     | 
    
         
            +
            - lib/redis_support/locks.rb
         
     | 
| 
      
 49 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 50 
     | 
    
         
            +
            - test/helper.rb
         
     | 
| 
      
 51 
     | 
    
         
            +
            - test/test_redis_support.rb
         
     | 
| 
      
 52 
     | 
    
         
            +
            has_rdoc: true
         
     | 
| 
      
 53 
     | 
    
         
            +
            homepage: http://github.com/dolores/redis_support
         
     | 
| 
      
 54 
     | 
    
         
            +
            licenses: []
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 57 
     | 
    
         
            +
            rdoc_options: 
         
     | 
| 
      
 58 
     | 
    
         
            +
            - --charset=UTF-8
         
     | 
| 
      
 59 
     | 
    
         
            +
            require_paths: 
         
     | 
| 
      
 60 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 61 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 62 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 63 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 64 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 65 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 66 
     | 
    
         
            +
                  hash: 3
         
     | 
| 
      
 67 
     | 
    
         
            +
                  segments: 
         
     | 
| 
      
 68 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 69 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 70 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 71 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 72 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 73 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 74 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 75 
     | 
    
         
            +
                  hash: 3
         
     | 
| 
      
 76 
     | 
    
         
            +
                  segments: 
         
     | 
| 
      
 77 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 78 
     | 
    
         
            +
                  version: "0"
         
     | 
| 
      
 79 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 82 
     | 
    
         
            +
            rubygems_version: 1.3.7
         
     | 
| 
      
 83 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 84 
     | 
    
         
            +
            specification_version: 3
         
     | 
| 
      
 85 
     | 
    
         
            +
            summary: A Redis Support module
         
     | 
| 
      
 86 
     | 
    
         
            +
            test_files: 
         
     | 
| 
      
 87 
     | 
    
         
            +
            - test/helper.rb
         
     | 
| 
      
 88 
     | 
    
         
            +
            - test/test_redis_support.rb
         
     |