Module: Sequel::Plugins::QueryCache::DatasetMethods

Defined in:
lib/sequel-query-cache/dataset_methods.rb

Constant Summary collapse

CACHE_BY_DEFAULT_PROC =
lambda do |ds, opts|
  if ds.opts[:limit] && opts[:if_limit]
    return true if
      (opts[:if_limit] == true) ||
      (opts[:if_limit] >= ds.opts[:limit])
  end

  false
end

Instance Method Summary collapse

Instance Method Details

#cache_clear_on_updateObject



176
177
178
# File 'lib/sequel-query-cache/dataset_methods.rb', line 176

def cache_clear_on_update
  @cache_clear_on_update.nil? ? true : @cache_clear_on_update
end

#cache_clear_on_update=(v) ⇒ Object



180
181
182
# File 'lib/sequel-query-cache/dataset_methods.rb', line 180

def cache_clear_on_update=(v)
  @cache_clear_on_update = !!v
end

#cache_delObject

Deletes the cache value using the current dataset’s key and logs the action.



171
172
173
174
# File 'lib/sequel-query-cache/dataset_methods.rb', line 171

def cache_del
  db.log_info("CACHE DEL: #{cache_key}")
  cache_driver.del(cache_key)
end

#cache_driverObject

Returns the model’s cache driver. – TODO: Caching should be modified to be a database/dataset extension with a tiny plugin for the models. However, this works just fine for now. ++



22
23
24
# File 'lib/sequel-query-cache/dataset_methods.rb', line 22

def cache_driver
  model.cache_driver
end

#cache_getObject

Gets the cache value using the current dataset’s key and logs the action. The underlying driver should return nil in the event that there is no cached data. Also logs whether there was a hit or miss on the cache.



149
150
151
152
153
154
# File 'lib/sequel-query-cache/dataset_methods.rb', line 149

def cache_get
  db.log_info("CACHE GET: #{cache_key}")
  cached_rows = cache_driver.get(cache_key)
  db.log_info("CACHE #{cached_rows ? 'HIT' : 'MISS'}: #{cache_key}")
  cached_rows
end

#cache_keyObject

Returns the default cache key if a manual cache key has not been set. The cache key is used by the storage engines to retrieve cached data. The default will suffice in almost all instances.



123
124
125
# File 'lib/sequel-query-cache/dataset_methods.rb', line 123

def cache_key
  @cache_key || default_cache_key
end

#cache_key=(cache_key) ⇒ Object

Sets a manual cache key for a dataset that overrides the default MD5 hash. This key has no Sequel: prefix, so if that’s important, remember to add it manually.

Note: Setting the cache key manually is NOT inherited by cloned datasets since keys are presumed to be for the current dataset and any changes, such as where clauses or limits, should result in a new key. In general, you shouldn’t change the cache key unless you have a really good reason for doing so.



136
137
138
# File 'lib/sequel-query-cache/dataset_methods.rb', line 136

def cache_key=(cache_key)
  @cache_key = cache_key ? cache_key.to_s : nil
end

#cache_options(opts = nil) ⇒ Object

Copies the model’s cache_options and merges them with options provided by opts if any are provided. Returns the current cache_options.

@cache_options is cloned when the dataset is cloned.



30
31
32
33
34
# File 'lib/sequel-query-cache/dataset_methods.rb', line 30

def cache_options(opts=nil)
  @cache_options ||= model.cache_options
  @cache_options = @cache_options.merge(opts) if opts
  @cache_options
end

#cache_set(value, opts = {}) ⇒ Object

Sets the cache value using the current dataset’s key and logs the action. In general, this method should not be called directly. It’s exposed because model instances need access to it.

An opts hash can be passed to override any default options being sent to the driver. The most common use for this would be to modify the ttl for a cache. However, this should probably be done using #cached rather than doing anything directly via this method.



164
165
166
167
# File 'lib/sequel-query-cache/dataset_methods.rb', line 164

def cache_set(value, opts={})
  db.log_info("CACHE SET: #{cache_key}")
  cache_driver.set(cache_key, value, opts.merge(cache_options))
end

#cached(opts = nil) ⇒ Object

Clones the current dataset, sets it to be cached and returns the new dataset. This is useful for chaining purposes:

dataset.where(column1: true).order(:column2).cached.all

In the above example, the data would always be pulled from the cache or cached if it wasn’t already. The value of @is_cacheable is cloned when a dataset is cloned, so the following example would also have the same result:

dataset.cached.where(column1: true).order(:column2).all

opts is passed to #cache_options on the new dataset.



81
82
83
84
85
86
# File 'lib/sequel-query-cache/dataset_methods.rb', line 81

def cached(opts=nil)
  c = clone
  c.cache_options(opts)
  c.is_cacheable = true
  c
end

#clear_cache_keys!Object



140
141
142
143
# File 'lib/sequel-query-cache/dataset_methods.rb', line 140

def clear_cache_keys!
  remove_instance_variable(:@default_cache_key) if defined? @default_cache_key
  remove_instance_variable(:@cache_key) if defined? @cache_key
end

#clone(opts = nil) ⇒ Object

Overrides the dataset’s existing clone method. Clones the existing dataset but clears any manually set cache key and the memoized default cache key to ensure it’s regenerated by the new dataset.



247
248
249
250
251
# File 'lib/sequel-query-cache/dataset_methods.rb', line 247

def clone(opts=nil)
  c = super(opts)
  c.clear_cache_keys!
  c
end

#default_cache_keyObject

Creates a default cache key, which is an MD5 base64 digest of the the literal select SQL with Sequel: added as a prefix. This value is memoized because assembling the SQL string and hashing it every time this method gets called is obnoxious.



116
117
118
# File 'lib/sequel-query-cache/dataset_methods.rb', line 116

def default_cache_key
  @default_cache_key ||= "Sequel:#{Digest::MD5.base64digest(sql)}"
end

#default_cachedObject

Clones the current dataset, returns the caching state to whatever would be default for that dataset and returns the new dataset. See #cached for further details and examples.

Note: This is the “proper” way to clear @is_cacheable once it’s been set.



102
103
104
105
106
107
108
109
110
# File 'lib/sequel-query-cache/dataset_methods.rb', line 102

def default_cached
  if defined? @is_cacheable
    c = clone
    c.remove_instance_variable(:@is_cacheable)
    c
  else
    self
  end
end

#delete(&block) ⇒ Object

Overrides the dataset’s existing delete method. Deletes an existing cache after a successful delete.



194
195
196
197
198
# File 'lib/sequel-query-cache/dataset_methods.rb', line 194

def delete(&block)
  result = super
  cache_del if is_cacheable?
  result
end

#eachObject

Sets self as the source_dataset on a result if that result supports source datasets. While it can almost certainly be presumed the result will, if the dataset’s row_proc has been modified for some reason the result might be different than expected.



237
238
239
240
241
242
# File 'lib/sequel-query-cache/dataset_methods.rb', line 237

def each
  super do |r|
    r.source_dataset = self if r.respond_to? :source_dataset=
    yield r
  end
end

#fetch_rows(sql) ⇒ Object

Overrides the dataset’s existing fetch_rows method. If the dataset is cacheable it will do one of two things:

If a cache exists it will yield the cached rows rather query the database.

If a cache does not exist it will query the database, store the results in an array, cache those and then yield the results like the original method would have.

Note: If you’re using PostgreSQL, or another database where each iterates with the cursor rather over the dataset results, you’ll lose that functionality when caching is enabled for a query since the entire result is iterated first before it is yielded. If that behavior is important, remember to disable caching for that particular query.



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/sequel-query-cache/dataset_methods.rb', line 215

def fetch_rows(sql)
  if is_cacheable?
    if cached_rows = cache_get
      # Symbolize the row keys before yielding as they're often strings
      # when the data is deserialized. Sequel doesn't play nice with
      # string keys.
      cached_rows.each{|r| yield r.reduce({}){|h,v| h[v[0].to_sym]=v[1]; h}}
    else
      cached_rows = []
      super(sql){|r| cached_rows << r}
      cache_set(cached_rows)
      cached_rows.each{|r| yield r}
    end
  else
    super
  end
end

#is_cacheable=(is_cacheable) ⇒ Object

Sets the value for @is_cacheable which is used as the return value from #is_cacheable?. @is_cacheable is cloned when the dataset is cloned.

Note: In general, #cached and #not_cached should be used to set this value. This method exists primarily for their use.



64
65
66
# File 'lib/sequel-query-cache/dataset_methods.rb', line 64

def is_cacheable=(is_cacheable)
  @is_cacheable = !!is_cacheable
end

#is_cacheable?Boolean

Determines whether or not a dataset should be cached. If @is_cacheable is set that value will be returned, otherwise the default value will be returned by #is_cacheable_by_default?

Returns:

  • (Boolean)


54
55
56
# File 'lib/sequel-query-cache/dataset_methods.rb', line 54

def is_cacheable?
  defined?(@is_cacheable) ? @is_cacheable : is_cacheable_by_default?
end

#is_cacheable_by_default?Boolean

Determines whether or not to cache a dataset based on the configuration settings of the plugin. – TODO: Specify a place to find those settings. However, where those are applied is currently in flux. Also, further document how this process actually works. ++

Returns:

  • (Boolean)


43
44
45
46
47
48
49
# File 'lib/sequel-query-cache/dataset_methods.rb', line 43

def is_cacheable_by_default?
  cache_by_default = cache_options[:cache_by_default]
  return false unless cache_by_default
  return true if cache_by_default[:always]
  proc = cache_by_default[:proc] || CACHE_BY_DEFAULT_PROC
  proc.call(self, cache_by_default)
end

#not_cachedObject

Clones the current dataset, sets it to not be cached and returns the new dataset. See #cached for further details and examples.



90
91
92
93
94
# File 'lib/sequel-query-cache/dataset_methods.rb', line 90

def not_cached
  c = clone
  c.is_cacheable = false
  c
end

#update(values = {}, &block) ⇒ Object

Overrides the dataset’s existing update method. Deletes an existing cache after a successful update.



186
187
188
189
190
# File 'lib/sequel-query-cache/dataset_methods.rb', line 186

def update(values={}, &block)
  result = super
  cache_del if is_cacheable? && cache_clear_on_update
  result
end