prosto_cache 0.1.3 → 0.2.0
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/Gemfile +3 -5
- data/Gemfile.lock +13 -15
- data/Rakefile +3 -0
- data/lib/prosto_cache.rb +129 -63
- data/prosto_cache.gemspec +10 -13
- data/spec/prosto_cache_spec.rb +86 -65
- data/spec/spec_helper.rb +1 -1
- metadata +9 -28
    
        data/Gemfile
    CHANGED
    
    
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,23 +1,21 @@ | |
| 1 1 | 
             
            GEM
         | 
| 2 2 | 
             
              remote: http://rubygems.org/
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                diff-lcs (1. | 
| 5 | 
            -
                 | 
| 6 | 
            -
                 | 
| 7 | 
            -
             | 
| 8 | 
            -
                  rspec- | 
| 9 | 
            -
                  rspec- | 
| 10 | 
            -
             | 
| 11 | 
            -
                rspec- | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
                rspec-mocks (2.3.0)
         | 
| 4 | 
            +
                diff-lcs (1.2.4)
         | 
| 5 | 
            +
                rake (10.0.4)
         | 
| 6 | 
            +
                rspec (2.13.0)
         | 
| 7 | 
            +
                  rspec-core (~> 2.13.0)
         | 
| 8 | 
            +
                  rspec-expectations (~> 2.13.0)
         | 
| 9 | 
            +
                  rspec-mocks (~> 2.13.0)
         | 
| 10 | 
            +
                rspec-core (2.13.1)
         | 
| 11 | 
            +
                rspec-expectations (2.13.0)
         | 
| 12 | 
            +
                  diff-lcs (>= 1.1.3, < 2.0)
         | 
| 13 | 
            +
                rspec-mocks (2.13.1)
         | 
| 15 14 |  | 
| 16 15 | 
             
            PLATFORMS
         | 
| 17 16 | 
             
              ruby
         | 
| 18 17 |  | 
| 19 18 | 
             
            DEPENDENCIES
         | 
| 20 | 
            -
              bundler | 
| 21 | 
            -
               | 
| 22 | 
            -
               | 
| 23 | 
            -
              rspec (~> 2.3.0)
         | 
| 19 | 
            +
              bundler
         | 
| 20 | 
            +
              rake
         | 
| 21 | 
            +
              rspec
         | 
    
        data/Rakefile
    CHANGED
    
    
    
        data/lib/prosto_cache.rb
    CHANGED
    
    | @@ -1,63 +1,117 @@ | |
| 1 1 | 
             
            =begin
         | 
| 2 2 | 
             
            This library provides a simple way to cache model and to access this cache in some canonical way.
         | 
| 3 3 | 
             
            Any changes to the model's objects will automatically result in cache reload.
         | 
| 4 | 
            -
            Cache reload in other ruby processes of same app will be triggered as well, but | 
| 5 | 
            -
             | 
| 4 | 
            +
            Cache reload in other ruby processes of same app will be triggered as well, but
         | 
| 5 | 
            +
            with some delay (currently up to 60 seconds).
         | 
| 6 | 
            +
            If delay in cache reloading is not an option, well, this simply library will
         | 
| 7 | 
            +
            not work for you, and you have to use something fancier, like Memcached.
         | 
| 6 8 |  | 
| 7 9 | 
             
            Usage:
         | 
| 8 | 
            -
             | 
| 10 | 
            +
             | 
| 11 | 
            +
            * Add ProstoCache mixin to your model
         | 
| 9 12 | 
             
              class YourModel < ActiveRecord::Base
         | 
| 10 13 | 
             
                include ProstoCache
         | 
| 11 | 
            -
             | 
| 14 | 
            +
             | 
| 15 | 
            +
            * Configure cache access keys (optional step, by default cache is accessed by key 'name')
         | 
| 12 16 | 
             
              cache_accessor_keys %w(scope name)
         | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 17 | 
            +
             | 
| 18 | 
            +
            * Your model must have non-nullable column updated_at, add it in migration if
         | 
| 19 | 
            +
              it is missing (this field is used for invalidating cache in other ruby processes).
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            * Access cached model object in your code like this
         | 
| 15 22 | 
             
              Simple case of one key
         | 
| 16 23 | 
             
                YourModel.cache[:key1]
         | 
| 17 | 
            -
              Case of 2 or more keys | 
| 18 | 
            -
                YourModel.cache[:key1 | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 24 | 
            +
              Case of 2 or more keys
         | 
| 25 | 
            +
                YourModel.cache[:key1, :key2, :key3]
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            * Handling of non-existing cache values.
         | 
| 28 | 
            +
              If cache is accessed using symbol key and value not found, it will raise BadCacheKeyError.
         | 
| 29 | 
            +
              If cache is accessed using string key and value not found, it will return nil.
         | 
| 30 | 
            +
              For complex keys type of last key component is the one taken into account.
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            * If you want to, you can add extra lookup helpers to the objects that relate
         | 
| 33 | 
            +
              to the cached object, that will allow those objects to update 'string'
         | 
| 34 | 
            +
              attribute, and that will result in database reference change.
         | 
| 22 35 | 
             
                class OtherModel < ActiveRecord::Base
         | 
| 23 36 | 
             
                  belongs_to :your_model
         | 
| 24 37 | 
             
                  lookup_enum_for :your_model
         | 
| 25 38 | 
             
                end
         | 
| 26 | 
            -
                
         | 
| 27 | 
            -
                This lookup was intertionally not integrated with ActiveRecord since not everybody would want that, and monkey-patching other library (AR) is not really a good thing, even if it results in more magically 'smooth' experience.
         | 
| 28 | 
            -
                Principle of least surprise, right?
         | 
| 29 | 
            -
            =end
         | 
| 30 39 |  | 
| 31 | 
            -
             | 
| 40 | 
            +
              This lookup was intertionally not integrated 'seamlessly' with ActiveRecord since not
         | 
| 41 | 
            +
              everybody would want that, and monkey-patching other library (AR) is not really
         | 
| 42 | 
            +
              a good thing, even if it results in more 'smooth' experience where everything works as if by magic.
         | 
| 43 | 
            +
            =end
         | 
| 32 44 |  | 
| 33 45 | 
             
            module ProstoCache
         | 
| 46 | 
            +
              class BadCacheKeyError < StandardError; end
         | 
| 47 | 
            +
              class BadCacheValuesError < StandardError; end
         | 
| 48 | 
            +
             | 
| 34 49 | 
             
              # cache itself, contains pretty much all the logic
         | 
| 35 50 | 
             
              class ProstoModelCache
         | 
| 36 | 
            -
             | 
| 51 | 
            +
             | 
| 52 | 
            +
                class ProstoHash
         | 
| 53 | 
            +
                  extend Forwardable
         | 
| 54 | 
            +
                  def initialize(hash = {})
         | 
| 55 | 
            +
                    @hash = hash.each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v }
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def [](key)
         | 
| 59 | 
            +
                    raise ArgumentError unless key
         | 
| 60 | 
            +
                    hash[key.to_sym]
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def []=(key, value)
         | 
| 64 | 
            +
                    raise ArgumentError unless key
         | 
| 65 | 
            +
                    hash[key.to_sym] = value
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  def_delegators :hash, :to_s, :inspect
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  private
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  attr_reader :hash
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def self.fail_on_missing_value?(litmus)
         | 
| 76 | 
            +
                  case litmus
         | 
| 77 | 
            +
                  when Symbol
         | 
| 78 | 
            +
                    true
         | 
| 79 | 
            +
                  when String
         | 
| 80 | 
            +
                    false
         | 
| 81 | 
            +
                  else
         | 
| 82 | 
            +
                    raise ArgumentError, "Unknown type of cache key #{litmus.inspect}"
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                MAX_CACHE_LIFE = 60 # seconds
         | 
| 37 87 |  | 
| 38 88 | 
             
                def initialize(model_class, accessor_keys)
         | 
| 39 | 
            -
                   | 
| 40 | 
            -
             | 
| 89 | 
            +
                  raise ArgumentError, "No model class provided" unless model_class
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  @model_class = model_class
         | 
| 92 | 
            +
                  @accessor_keys = [*(accessor_keys || :name)]
         | 
| 41 93 | 
             
                end
         | 
| 42 94 |  | 
| 43 95 | 
             
                def invalidate
         | 
| 44 96 | 
             
                  self.cache = self.signature = self.validated_at = nil
         | 
| 45 97 | 
             
                end
         | 
| 46 98 |  | 
| 47 | 
            -
                def [](keys)
         | 
| 48 | 
            -
                   | 
| 49 | 
            -
                     | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
                         | 
| 99 | 
            +
                def [](*keys)
         | 
| 100 | 
            +
                  unless keys.length == accessor_keys.length
         | 
| 101 | 
            +
                    raise BadCacheKeyError, "Cached accessed by #{keys.length} keys, expected #{accessor_keys.length}"
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  keys.zip((1..keys.length)).inject(safe_cache) do |memo, (key, index)|
         | 
| 105 | 
            +
                    value = memo[key]
         | 
| 106 | 
            +
                    unless value
         | 
| 107 | 
            +
                      if ProstoModelCache.fail_on_missing_value?(keys.last)
         | 
| 108 | 
            +
                        raise BadCacheKeyError, key
         | 
| 109 | 
            +
                      else
         | 
| 110 | 
            +
                        value = ProstoHash.new unless index == keys.length
         | 
| 57 111 | 
             
                      end
         | 
| 58 112 | 
             
                    end
         | 
| 59 | 
            -
             | 
| 60 | 
            -
                     | 
| 113 | 
            +
             | 
| 114 | 
            +
                    value
         | 
| 61 115 | 
             
                  end
         | 
| 62 116 | 
             
                end
         | 
| 63 117 |  | 
| @@ -71,56 +125,71 @@ module ProstoCache | |
| 71 125 |  | 
| 72 126 | 
             
                private
         | 
| 73 127 |  | 
| 128 | 
            +
                attr_reader :model_class, :cache, :signature, :validated_at, :accessor_keys
         | 
| 129 | 
            +
                attr_writer :cache, :signature, :validated_at
         | 
| 130 | 
            +
             | 
| 74 131 | 
             
                def safe_cache
         | 
| 75 | 
            -
                   | 
| 76 | 
            -
             | 
| 132 | 
            +
                  time = Time.now.to_i
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                  if cache && validated_at < time - MAX_CACHE_LIFE
         | 
| 135 | 
            +
                    current_cache_signature = validate_cache_signature(time)
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                  unless cache
         | 
| 139 | 
            +
                    load_cache(time, current_cache_signature)
         | 
| 140 | 
            +
                  end
         | 
| 77 141 |  | 
| 78 | 
            -
                   | 
| 79 | 
            -
             | 
| 142 | 
            +
                  cache
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                def validate_cache_signature(time)
         | 
| 146 | 
            +
                  query_cache_signature.tap { |current_cache_signature|
         | 
| 80 147 | 
             
                    if current_cache_signature == signature
         | 
| 81 | 
            -
                      self.validated_at =  | 
| 148 | 
            +
                      self.validated_at = time
         | 
| 82 149 | 
             
                    else
         | 
| 83 150 | 
             
                      invalidate
         | 
| 84 151 | 
             
                    end
         | 
| 85 | 
            -
                   | 
| 152 | 
            +
                  }
         | 
| 153 | 
            +
                end
         | 
| 86 154 |  | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 155 | 
            +
                def load_cache(time, current_cache_signature = nil)
         | 
| 156 | 
            +
                  fail "Can not load already loaded cache" if cache
         | 
| 89 157 |  | 
| 90 | 
            -
             | 
| 91 | 
            -
                    self.validated_at = current_time
         | 
| 92 | 
            -
                    self.signature = current_cache_signature
         | 
| 93 | 
            -
                  end
         | 
| 158 | 
            +
                  current_cache_signature ||= query_cache_signature
         | 
| 94 159 |  | 
| 95 | 
            -
                   | 
| 160 | 
            +
                  self.cache = build_cache(model_class.all, accessor_keys)
         | 
| 161 | 
            +
                  self.validated_at = time
         | 
| 162 | 
            +
                  self.signature = current_cache_signature
         | 
| 96 163 | 
             
                end
         | 
| 97 164 |  | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
                   | 
| 165 | 
            +
             | 
| 166 | 
            +
                def build_cache(objects, attributes=[])
         | 
| 167 | 
            +
                  attributes = [*attributes]
         | 
| 168 | 
            +
                  if attributes.empty?
         | 
| 101 169 | 
             
                    # terminal case
         | 
| 102 | 
            -
                    raise "No cache entry found" if objects.nil? || objects.empty?
         | 
| 103 | 
            -
                    raise "Non deterministic search result, more then one cache entry found" if objects.size > 1
         | 
| 170 | 
            +
                    raise BadCacheValuesError, "No cache entry found" if objects.nil? || objects.empty?
         | 
| 171 | 
            +
                    raise BadCacheValuesError, "Non deterministic search result, more then one cache entry found" if objects.size > 1
         | 
| 104 172 | 
             
                    return objects.first
         | 
| 105 173 | 
             
                  else
         | 
| 106 174 | 
             
                    reduced_attributes = attributes.dup
         | 
| 107 175 | 
             
                    attribute = reduced_attributes.delete_at(0).to_sym
         | 
| 108 | 
            -
                     | 
| 176 | 
            +
                    # first, bucketize to reduce problem's complexity
         | 
| 177 | 
            +
                    array_map = objects.each_with_object({}) do |o, memo|
         | 
| 109 178 | 
             
                      key = o.send(attribute).to_s
         | 
| 110 179 | 
             
                      memo[key] ||= []
         | 
| 111 180 | 
             
                      memo[key] << o
         | 
| 112 | 
            -
                      memo
         | 
| 113 181 | 
             
                    end
         | 
| 114 | 
            -
                     | 
| 115 | 
            -
             | 
| 116 | 
            -
                      memo[ | 
| 117 | 
            -
                      memo
         | 
| 182 | 
            +
                    # second, recurse and build cache from those reduced buckets!
         | 
| 183 | 
            +
                    array_map.each_with_object(ProstoHash.new) do |(attr_value, attr_bucket), memo|
         | 
| 184 | 
            +
                      memo[attr_value] = build_cache(attr_bucket, reduced_attributes)
         | 
| 118 185 | 
             
                    end
         | 
| 119 186 | 
             
                  end
         | 
| 120 187 | 
             
                end
         | 
| 121 188 |  | 
| 122 189 | 
             
                def query_cache_signature
         | 
| 123 | 
            -
                  raw_result = ActiveRecord::Base.connection.execute | 
| 190 | 
            +
                  raw_result = ActiveRecord::Base.connection.execute(
         | 
| 191 | 
            +
                    "select max(updated_at) as max_updated_at, max(id) as max_id, count(id) as count from #{model_class.table_name}"
         | 
| 192 | 
            +
                  )
         | 
| 124 193 | 
             
                  array_result = case raw_result.class.name
         | 
| 125 194 | 
             
                  when 'Mysql::Result'
         | 
| 126 195 | 
             
                    [].tap { |rows| raw_result.each_hash { |h| rows << h } }
         | 
| @@ -129,7 +198,7 @@ module ProstoCache | |
| 129 198 | 
             
                  when 'PGresult'
         | 
| 130 199 | 
             
                    raw_result.map(&:to_hash)
         | 
| 131 200 | 
             
                  else
         | 
| 132 | 
            -
                     | 
| 201 | 
            +
                    fail "Result class #{raw_result.class.name} in unsupported"
         | 
| 133 202 | 
             
                  end
         | 
| 134 203 | 
             
                  array_result.map(&:symbolize_keys).first
         | 
| 135 204 | 
             
                end
         | 
| @@ -150,9 +219,10 @@ module ProstoCache | |
| 150 219 | 
             
                  end
         | 
| 151 220 | 
             
                end
         | 
| 152 221 | 
             
              end
         | 
| 153 | 
            -
             | 
| 222 | 
            +
             | 
| 223 | 
            +
              module Extensions
         | 
| 154 224 | 
             
                def lookup_enum_for(name, enum_class=nil)
         | 
| 155 | 
            -
                  raise unless name
         | 
| 225 | 
            +
                  raise ArgumentError, "No name provided" unless name
         | 
| 156 226 | 
             
                  enum_class = name.to_s.classify.constantize unless enum_class
         | 
| 157 227 | 
             
                  define_method("#{name}_with_lookup=") do |o|
         | 
| 158 228 | 
             
                    new_value = o
         | 
| @@ -166,7 +236,3 @@ module ProstoCache | |
| 166 236 | 
             
                end
         | 
| 167 237 | 
             
              end
         | 
| 168 238 | 
             
            end
         | 
| 169 | 
            -
             | 
| 170 | 
            -
             | 
| 171 | 
            -
             | 
| 172 | 
            -
             | 
    
        data/prosto_cache.gemspec
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            Gem::Specification.new do |s|
         | 
| 4 4 | 
             
              s.name = "prosto_cache"
         | 
| 5 | 
            -
              s.version = "0. | 
| 5 | 
            +
              s.version = "0.2.0"
         | 
| 6 6 |  | 
| 7 7 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 8 8 | 
             
              s.authors = ["Olek Poplavsky"]
         | 
| @@ -35,20 +35,17 @@ Gem::Specification.new do |s| | |
| 35 35 | 
             
                s.specification_version = 3
         | 
| 36 36 |  | 
| 37 37 | 
             
                if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
         | 
| 38 | 
            -
                  s. | 
| 39 | 
            -
                  s.add_development_dependency(%q< | 
| 40 | 
            -
                  s.add_development_dependency(%q< | 
| 41 | 
            -
                  s.add_development_dependency(%q<rake>, ["= 0.8.7"])
         | 
| 38 | 
            +
                  s.add_development_dependency(%q<rspec>, ["~> 2.3"])
         | 
| 39 | 
            +
                  s.add_development_dependency(%q<bundler>, ["~> 1.1"])
         | 
| 40 | 
            +
                  s.add_development_dependency(%q<rake>)
         | 
| 42 41 | 
             
                else
         | 
| 43 | 
            -
                  s.add_dependency(%q< | 
| 44 | 
            -
                  s.add_dependency(%q< | 
| 45 | 
            -
                  s.add_dependency(%q< | 
| 46 | 
            -
                  s.add_dependency(%q<rake>, ["= 0.8.7"])
         | 
| 42 | 
            +
                  s.add_dependency(%q<rspec>, ["~> 2.3"])
         | 
| 43 | 
            +
                  s.add_dependency(%q<bundler>, ["~> 1.1"])
         | 
| 44 | 
            +
                  s.add_dependency(%q<rake>)
         | 
| 47 45 | 
             
                end
         | 
| 48 46 | 
             
              else
         | 
| 49 | 
            -
                s.add_dependency(%q< | 
| 50 | 
            -
                s.add_dependency(%q< | 
| 51 | 
            -
                s.add_dependency(%q< | 
| 52 | 
            -
                s.add_dependency(%q<rake>, ["= 0.8.7"])
         | 
| 47 | 
            +
                s.add_dependency(%q<rspec>, ["~> 2.3"])
         | 
| 48 | 
            +
                s.add_dependency(%q<bundler>, ["~> 1.1"])
         | 
| 49 | 
            +
                s.add_dependency(%q<rake>)
         | 
| 53 50 | 
             
              end
         | 
| 54 51 | 
             
            end
         | 
    
        data/spec/prosto_cache_spec.rb
    CHANGED
    
    | @@ -5,69 +5,80 @@ describe "ProstoCache" do | |
| 5 5 | 
             
                Object.send(:remove_const, 'Foo') if Object.const_defined? 'Foo'
         | 
| 6 6 |  | 
| 7 7 | 
             
                class Foo; end
         | 
| 8 | 
            -
                Foo. | 
| 9 | 
            -
                class Foo
         | 
| 10 | 
            -
                  include ProstoCache
         | 
| 11 | 
            -
                end
         | 
| 8 | 
            +
                Foo.stub(:after_save)
         | 
| 12 9 | 
             
              end
         | 
| 13 10 |  | 
| 14 | 
            -
               | 
| 15 | 
            -
             | 
| 11 | 
            +
              describe '#cache' do
         | 
| 12 | 
            +
                before do
         | 
| 13 | 
            +
                  class Foo
         | 
| 14 | 
            +
                    include ProstoCache
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 16 17 |  | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 18 | 
            +
                it "should add a cache method" do
         | 
| 19 | 
            +
                  Foo.cache.should_not be_nil
         | 
| 20 | 
            +
                  Foo.cache.should be_an_instance_of(ProstoCache::ProstoModelCache)
         | 
| 21 | 
            +
                end
         | 
| 21 22 |  | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 23 | 
            +
                it "should not load cache if it was never accessed" do
         | 
| 24 | 
            +
                  Foo.cache.should_not_receive(:query_cache_signature)
         | 
| 25 | 
            +
                  Foo.should_not_receive(:all)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  Foo.cache.should_not be_nil
         | 
| 28 | 
            +
                end
         | 
| 26 29 | 
             
              end
         | 
| 27 30 |  | 
| 28 31 | 
             
              context "[] method with single key access" do
         | 
| 29 32 | 
             
                before do
         | 
| 30 | 
            -
                  Object.send(:remove_const, 'Foo') if Object.const_defined? 'Foo'
         | 
| 31 | 
            -
             | 
| 32 33 | 
             
                  class Foo
         | 
| 34 | 
            +
                    include ProstoCache
         | 
| 35 | 
            +
             | 
| 33 36 | 
             
                    attr_accessor :name
         | 
| 34 37 | 
             
                    def initialize(name)
         | 
| 35 38 | 
             
                      self.name = name
         | 
| 36 39 | 
             
                    end
         | 
| 37 40 | 
             
                  end
         | 
| 38 41 |  | 
| 39 | 
            -
                  Foo. | 
| 40 | 
            -
             | 
| 41 | 
            -
                  class Foo
         | 
| 42 | 
            -
                    include ProstoCache
         | 
| 43 | 
            -
                  end
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                  Foo.cache.should_not be_nil
         | 
| 46 | 
            -
                  Foo.cache.should_receive(:query_cache_signature).once.and_return(:foo)
         | 
| 47 | 
            -
                  Foo.should_receive(:all).once.and_return(%w(foo bar).map { |n| Foo.new(n) })
         | 
| 42 | 
            +
                  Foo.cache.stub(:query_cache_signature).once.and_return(:foo)
         | 
| 43 | 
            +
                  Foo.stub(:all).once.and_return(%w(foo bar).map { |n| Foo.new(n) })
         | 
| 48 44 | 
             
                end
         | 
| 49 45 |  | 
| 50 46 | 
             
                it "should load cache when it is accessed" do
         | 
| 51 | 
            -
                  Foo. | 
| 47 | 
            +
                  Foo.should_receive(:all).once.and_return(%w(foo bar).map { |n| Foo.new(n) })
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  Foo.cache[:foo]
         | 
| 52 50 | 
             
                end
         | 
| 53 51 |  | 
| 54 | 
            -
                 | 
| 55 | 
            -
                   | 
| 52 | 
            +
                context 'when key is symbol' do
         | 
| 53 | 
            +
                  it "should raise an error for key that was not found" do
         | 
| 54 | 
            +
                    expect { Foo.cache[:nondef] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  it "should return proper object for key that was found" do
         | 
| 58 | 
            +
                    Foo.cache[:foo].should_not be_nil
         | 
| 59 | 
            +
                    Foo.cache[:foo].name.should == 'foo'
         | 
| 60 | 
            +
                  end
         | 
| 56 61 | 
             
                end
         | 
| 57 62 |  | 
| 58 | 
            -
                 | 
| 59 | 
            -
                   | 
| 60 | 
            -
             | 
| 61 | 
            -
                   | 
| 63 | 
            +
                context 'when key is string' do
         | 
| 64 | 
            +
                  it "should return nil for key that was not found" do
         | 
| 65 | 
            +
                    Foo.cache['nondef'].should be_nil
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  it "should return proper object for key that was found" do
         | 
| 69 | 
            +
                    Foo.cache['foo'].should_not be_nil
         | 
| 70 | 
            +
                    Foo.cache['foo'].name.should == 'foo'
         | 
| 71 | 
            +
                  end
         | 
| 62 72 | 
             
                end
         | 
| 63 73 | 
             
              end
         | 
| 64 74 |  | 
| 65 75 | 
             
              context "[] method with composite key access" do
         | 
| 66 76 | 
             
                before do
         | 
| 67 | 
            -
                  Object.send(:remove_const, 'Foo') if Object.const_defined? 'Foo'
         | 
| 68 | 
            -
             | 
| 69 77 | 
             
                  class Foo
         | 
| 70 | 
            -
                     | 
| 78 | 
            +
                    include ProstoCache
         | 
| 79 | 
            +
                    cache_accessor_keys %w(key1 key2)
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    attr_accessor :name, :key1, :key2
         | 
| 71 82 | 
             
                    def initialize(name, key1, key2)
         | 
| 72 83 | 
             
                      self.name = name
         | 
| 73 84 | 
             
                      self.key1 = key1
         | 
| @@ -75,46 +86,56 @@ describe "ProstoCache" do | |
| 75 86 | 
             
                    end
         | 
| 76 87 | 
             
                  end
         | 
| 77 88 |  | 
| 78 | 
            -
                  Foo. | 
| 79 | 
            -
             | 
| 80 | 
            -
                  class Foo
         | 
| 81 | 
            -
                    include ProstoCache
         | 
| 82 | 
            -
                    cache_accessor_keys %w(key1 key2)
         | 
| 83 | 
            -
                  end
         | 
| 84 | 
            -
             | 
| 85 | 
            -
                  Foo.cache.should_not be_nil
         | 
| 86 | 
            -
                  Foo.cache.should_receive(:query_cache_signature).once.and_return(:foo)
         | 
| 87 | 
            -
                  Foo.should_receive(:all).once.and_return(%w(foo bar).map { |n| Foo.new(n, n + '1', n + '2') })
         | 
| 89 | 
            +
                  Foo.cache.stub(:query_cache_signature).once.and_return(:foo)
         | 
| 90 | 
            +
                  Foo.stub(:all).once.and_return(%w(foo bar).map { |n| Foo.new(n, n + '1', n + '2') })
         | 
| 88 91 | 
             
                end
         | 
| 89 92 |  | 
| 90 | 
            -
                it "should  | 
| 91 | 
            -
                  Foo.cache[:nondef]. | 
| 93 | 
            +
                it "should raise an error when not enough keys provided" do
         | 
| 94 | 
            +
                  expect { Foo.cache[:nondef] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 95 | 
            +
                  expect { Foo.cache[:foo1] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 96 | 
            +
                  expect { Foo.cache['nondef'] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 97 | 
            +
                  expect { Foo.cache['foo1'] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 92 98 | 
             
                end
         | 
| 93 99 |  | 
| 94 | 
            -
                it "should  | 
| 95 | 
            -
                  Foo.cache[: | 
| 100 | 
            +
                it "should raise an error when too many keys provided" do
         | 
| 101 | 
            +
                  expect { Foo.cache[:nondef1, :nondef2, :nondef3] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 102 | 
            +
                  expect { Foo.cache[:foo1, :foo2, :nondef] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 103 | 
            +
                  expect { Foo.cache['nondef1', 'nondef2', 'nondef3'] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 104 | 
            +
                  expect { Foo.cache['foo1', 'foo2', 'nondef'] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 96 105 | 
             
                end
         | 
| 97 106 |  | 
| 98 | 
            -
                 | 
| 99 | 
            -
                   | 
| 100 | 
            -
             | 
| 107 | 
            +
                context 'when last key is symbol' do
         | 
| 108 | 
            +
                  it "should raise an error for first key that was not found" do
         | 
| 109 | 
            +
                    expect { Foo.cache[:undef, :foo2] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 110 | 
            +
                    expect { Foo.cache['undef', :foo2] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 111 | 
            +
                  end
         | 
| 101 112 |  | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
                   | 
| 106 | 
            -
                end
         | 
| 113 | 
            +
                  it "should raise an error for last key that was not found" do
         | 
| 114 | 
            +
                    expect { Foo.cache[:foo1, :nondef] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 115 | 
            +
                    expect { Foo.cache['foo1', :nondef] }.to raise_error ProstoCache::BadCacheKeyError
         | 
| 116 | 
            +
                  end
         | 
| 107 117 |  | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
                   | 
| 118 | 
            +
                  it "should return proper object for key that was found" do
         | 
| 119 | 
            +
                    Foo.cache[:foo1, :foo2].should_not be_nil
         | 
| 120 | 
            +
                    Foo.cache[:foo1, :foo2].name.should == 'foo'
         | 
| 121 | 
            +
                  end
         | 
| 112 122 | 
             
                end
         | 
| 113 123 |  | 
| 114 | 
            -
                 | 
| 115 | 
            -
                   | 
| 116 | 
            -
                    Foo.cache[ | 
| 117 | 
            -
             | 
| 124 | 
            +
                context 'when last key is string' do
         | 
| 125 | 
            +
                  it "should return nil for first level key that was not found" do
         | 
| 126 | 
            +
                    Foo.cache['nondef', 'foo2'].should be_nil
         | 
| 127 | 
            +
                    Foo.cache[:nondef, 'foo2'].should be_nil
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  it "should return nil for second level key that was not found" do
         | 
| 131 | 
            +
                    Foo.cache['foo1', 'nondef'].should be_nil
         | 
| 132 | 
            +
                    Foo.cache[:foo1, 'nondef'].should be_nil
         | 
| 133 | 
            +
                  end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                  it "should return proper object for key that was found" do
         | 
| 136 | 
            +
                    Foo.cache['foo1', 'foo2'].should_not be_nil
         | 
| 137 | 
            +
                    Foo.cache['foo1', 'foo2'].name.should == 'foo'
         | 
| 138 | 
            +
                  end
         | 
| 118 139 | 
             
                end
         | 
| 119 140 | 
             
              end
         | 
| 120 141 | 
             
            end
         | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    | @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
         | 
| 2 2 | 
             
            $LOAD_PATH.unshift(File.dirname(__FILE__))
         | 
| 3 | 
            +
             | 
| 3 4 | 
             
            require 'rspec'
         | 
| 4 5 | 
             
            require 'prosto_cache'
         | 
| 5 6 |  | 
| @@ -8,5 +9,4 @@ require 'prosto_cache' | |
| 8 9 | 
             
            Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
         | 
| 9 10 |  | 
| 10 11 | 
             
            RSpec.configure do |config|
         | 
| 11 | 
            -
              
         | 
| 12 12 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: prosto_cache
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.2.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -11,22 +11,6 @@ bindir: bin | |
| 11 11 | 
             
            cert_chain: []
         | 
| 12 12 | 
             
            date: 2011-05-23 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 15 | 
            -
              name: hashie
         | 
| 16 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 17 | 
            -
                none: false
         | 
| 18 | 
            -
                requirements:
         | 
| 19 | 
            -
                - - ! '>='
         | 
| 20 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 21 | 
            -
                    version: 1.0.0
         | 
| 22 | 
            -
              type: :runtime
         | 
| 23 | 
            -
              prerelease: false
         | 
| 24 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 25 | 
            -
                none: false
         | 
| 26 | 
            -
                requirements:
         | 
| 27 | 
            -
                - - ! '>='
         | 
| 28 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 29 | 
            -
                    version: 1.0.0
         | 
| 30 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 31 15 | 
             
              name: rspec
         | 
| 32 16 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -34,7 +18,7 @@ dependencies: | |
| 34 18 | 
             
                requirements:
         | 
| 35 19 | 
             
                - - ~>
         | 
| 36 20 | 
             
                  - !ruby/object:Gem::Version
         | 
| 37 | 
            -
                    version: 2.3 | 
| 21 | 
            +
                    version: '2.3'
         | 
| 38 22 | 
             
              type: :development
         | 
| 39 23 | 
             
              prerelease: false
         | 
| 40 24 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| @@ -42,7 +26,7 @@ dependencies: | |
| 42 26 | 
             
                requirements:
         | 
| 43 27 | 
             
                - - ~>
         | 
| 44 28 | 
             
                  - !ruby/object:Gem::Version
         | 
| 45 | 
            -
                    version: 2.3 | 
| 29 | 
            +
                    version: '2.3'
         | 
| 46 30 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 47 31 | 
             
              name: bundler
         | 
| 48 32 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -50,7 +34,7 @@ dependencies: | |
| 50 34 | 
             
                requirements:
         | 
| 51 35 | 
             
                - - ~>
         | 
| 52 36 | 
             
                  - !ruby/object:Gem::Version
         | 
| 53 | 
            -
                    version: 1.1 | 
| 37 | 
            +
                    version: '1.1'
         | 
| 54 38 | 
             
              type: :development
         | 
| 55 39 | 
             
              prerelease: false
         | 
| 56 40 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| @@ -58,23 +42,23 @@ dependencies: | |
| 58 42 | 
             
                requirements:
         | 
| 59 43 | 
             
                - - ~>
         | 
| 60 44 | 
             
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            -
                    version: 1.1 | 
| 45 | 
            +
                    version: '1.1'
         | 
| 62 46 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 63 47 | 
             
              name: rake
         | 
| 64 48 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 65 49 | 
             
                none: false
         | 
| 66 50 | 
             
                requirements:
         | 
| 67 | 
            -
                - - ' | 
| 51 | 
            +
                - - ! '>='
         | 
| 68 52 | 
             
                  - !ruby/object:Gem::Version
         | 
| 69 | 
            -
                    version: 0 | 
| 53 | 
            +
                    version: '0'
         | 
| 70 54 | 
             
              type: :development
         | 
| 71 55 | 
             
              prerelease: false
         | 
| 72 56 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 73 57 | 
             
                none: false
         | 
| 74 58 | 
             
                requirements:
         | 
| 75 | 
            -
                - - ' | 
| 59 | 
            +
                - - ! '>='
         | 
| 76 60 | 
             
                  - !ruby/object:Gem::Version
         | 
| 77 | 
            -
                    version: 0 | 
| 61 | 
            +
                    version: '0'
         | 
| 78 62 | 
             
            description: Use this gem if you want a simple 'enum-like' cache for your models that
         | 
| 79 63 | 
             
              does not restrict updates, but will stay current with them.
         | 
| 80 64 | 
             
            email: olek@woodenbits.com
         | 
| @@ -107,9 +91,6 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 107 91 | 
             
              - - ! '>='
         | 
| 108 92 | 
             
                - !ruby/object:Gem::Version
         | 
| 109 93 | 
             
                  version: '0'
         | 
| 110 | 
            -
                  segments:
         | 
| 111 | 
            -
                  - 0
         | 
| 112 | 
            -
                  hash: 849790085734229441
         | 
| 113 94 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 114 95 | 
             
              none: false
         | 
| 115 96 | 
             
              requirements:
         |