cache_crispies 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cache_crispies.rb +11 -3
- data/lib/cache_crispies/attribute.rb +40 -5
- data/lib/cache_crispies/base.rb +68 -13
- data/lib/cache_crispies/collection.rb +16 -1
- data/lib/cache_crispies/condition.rb +16 -4
- data/lib/cache_crispies/controller.rb +16 -1
- data/lib/cache_crispies/hash_builder.rb +11 -1
- data/lib/cache_crispies/memoizer.rb +11 -1
- data/lib/cache_crispies/plan.rb +39 -1
- data/lib/cache_crispies/version.rb +5 -2
- data/spec/attribute_spec.rb +2 -2
- data/spec/collection_spec.rb +1 -3
- data/spec/memoizer_spec.rb +0 -2
- data/spec/plan_spec.rb +19 -1
- data/spec/spec_helper.rb +6 -87
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af451e5837f96664e141a147df118fbfffb7ee4fedc71cd5e6c34438b9069461
|
4
|
+
data.tar.gz: ffe23f3217c50b5c6bbbd183127a2274b6c4afb59ad93e50a271c880d20968f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8208c9ef13c4f66e9d64c7b8a5394e7d2907b894ff4ca6617357ca785ede999048d3ef4ffc00d900a30e5ac284401a5b145fb2271764f878d16603017ac08d1
|
7
|
+
data.tar.gz: 167cf43b28151ec4d6c65c0badfcbe73a871f7a102bc916f04eb0e889069843e518db1a608da31c854096a60843bd50a69ab99a2b4b784a89ce1d5ecd5e549fe
|
data/lib/cache_crispies.rb
CHANGED
@@ -1,12 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support/dependencies'
|
2
4
|
require 'oj'
|
3
5
|
|
6
|
+
# The top level namespace module for the gem
|
4
7
|
module CacheCrispies
|
5
|
-
|
6
|
-
|
8
|
+
# A prefix used in building cache key. This should be extra insurance against
|
9
|
+
# key conflicts and also provides an easy way to search for keys in Redis.
|
10
|
+
CACHE_KEY_PREFIX = 'cache-crispies'
|
11
|
+
|
12
|
+
# The string to use to join parts of the cache keys together
|
13
|
+
CACHE_KEY_SEPARATOR = '+'
|
7
14
|
|
8
15
|
require 'cache_crispies/version'
|
9
16
|
|
17
|
+
# Use autoload for better Rails development
|
10
18
|
autoload :Attribute, 'cache_crispies/attribute'
|
11
19
|
autoload :Base, 'cache_crispies/base'
|
12
20
|
autoload :Collection, 'cache_crispies/collection'
|
@@ -15,4 +23,4 @@ module CacheCrispies
|
|
15
23
|
autoload :Memoizer, 'cache_crispies/memoizer'
|
16
24
|
autoload :Controller, 'cache_crispies/controller'
|
17
25
|
autoload :Plan, 'cache_crispies/plan'
|
18
|
-
end
|
26
|
+
end
|
@@ -1,8 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CacheCrispies
|
4
|
+
# Reperesents a single serialized attribute in a serializer. It's generated
|
5
|
+
# by a call to either {CacheCrispies::Base.serialize} or
|
6
|
+
# {CacheCrispies::Base.merge}.
|
2
7
|
class Attribute
|
3
|
-
|
8
|
+
# Represents an invalid option passed in the to: argument
|
9
|
+
class InvalidCoercionType < ArgumentError; end
|
4
10
|
|
5
|
-
|
11
|
+
# Initializes a new CacheCrispies::Attribute instance
|
12
|
+
#
|
13
|
+
# @param key [Symbol] the JSON key for this attribute
|
14
|
+
# @param from [Symbol] the method on the model to call to get the value
|
15
|
+
# @param with [CacheCrispies::Base] a serializer to use to serialize the
|
16
|
+
# @param to [Class, Symbol] the data type to coerce the value into
|
17
|
+
# @param nesting [Array<Symbol>] the JSON keys this attribute will be
|
18
|
+
# nested inside
|
19
|
+
# @param conditions [Array<CacheCrispies::Condition>] the show_if condition
|
20
|
+
# blocks this attribute is nested inside. These will be evaluated for
|
21
|
+
# thruthiness and must all be true for this attribute to reneder.
|
22
|
+
# argument's value
|
23
|
+
def initialize(
|
24
|
+
key,
|
25
|
+
from: nil, with: nil, to: nil, nesting: [], conditions: []
|
26
|
+
)
|
6
27
|
@key = key
|
7
28
|
@method_name = from || key || :itself
|
8
29
|
@serializer = with
|
@@ -11,8 +32,22 @@ module CacheCrispies
|
|
11
32
|
@conditions = Array(conditions)
|
12
33
|
end
|
13
34
|
|
14
|
-
attr_reader
|
35
|
+
attr_reader(
|
36
|
+
:method_name,
|
37
|
+
:key,
|
38
|
+
:serializer,
|
39
|
+
:coerce_to,
|
40
|
+
:nesting,
|
41
|
+
:conditions
|
42
|
+
)
|
15
43
|
|
44
|
+
# Gets the value of the attribute for the given model and options
|
45
|
+
#
|
46
|
+
# @param model [Object] typically ActiveRecord::Base, but could be anything
|
47
|
+
# @param options [Hash] any optional values from the serializer instance
|
48
|
+
# @return the value for the attribute for the given model and options
|
49
|
+
# @raise [InvalidCoercionType] when an invalid argument is passed in the
|
50
|
+
# to: argument
|
16
51
|
def value_for(model, options)
|
17
52
|
value = model.public_send(method_name)
|
18
53
|
|
@@ -51,7 +86,7 @@ module CacheCrispies
|
|
51
86
|
!!value
|
52
87
|
else
|
53
88
|
raise(
|
54
|
-
|
89
|
+
InvalidCoercionType,
|
55
90
|
"#{coerce_to} has no registered coercion strategy"
|
56
91
|
)
|
57
92
|
end
|
@@ -63,4 +98,4 @@ module CacheCrispies
|
|
63
98
|
).public_send(method_name)
|
64
99
|
end
|
65
100
|
end
|
66
|
-
end
|
101
|
+
end
|
data/lib/cache_crispies/base.rb
CHANGED
@@ -1,50 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'digest'
|
2
4
|
require 'rails'
|
3
5
|
|
4
6
|
module CacheCrispies
|
7
|
+
# The Base class that all serializer classes should inherit from
|
5
8
|
class Base
|
6
9
|
attr_reader :model, :options
|
7
10
|
|
11
|
+
# Define class-level instance variables and their default values when the
|
12
|
+
# class is inherited by another class. This is not meant to be called
|
13
|
+
# directly. It is called internally by Ruby.
|
14
|
+
#
|
15
|
+
# @param other [Class] the inheriting child class
|
16
|
+
# @return [void]
|
17
|
+
def self.inherited(other)
|
18
|
+
other.instance_variable_set(:@attributes, [])
|
19
|
+
other.instance_variable_set(:@nesting, [])
|
20
|
+
other.instance_variable_set(:@conditions, [])
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
attr_reader :attributes
|
25
|
+
end
|
26
|
+
delegate :attributes, to: :class
|
27
|
+
|
28
|
+
# Initializes a new instance of CacheCrispies::Base, or really, it should
|
29
|
+
# always be a subclass of CacheCrispies::Base.
|
30
|
+
#
|
31
|
+
# @param model [Object] typically ActiveRecord::Base, but could be anything
|
32
|
+
# @param options [Hash] any optional custom values you want to be
|
33
|
+
# accessible in your subclass.
|
8
34
|
def initialize(model, options = {})
|
9
35
|
@model = model
|
10
36
|
@options = options
|
11
37
|
end
|
12
38
|
|
39
|
+
# Renders the serializer instance to a JSON-ready Hash
|
40
|
+
#
|
41
|
+
# @return [Hash] a JSON-ready hash
|
13
42
|
def as_json
|
14
43
|
HashBuilder.new(self).call
|
15
44
|
end
|
16
45
|
|
46
|
+
# Whether or not this serializer class should allow caching of results.
|
47
|
+
# It is set to false by default, but can be overridden in child classes.
|
48
|
+
#
|
49
|
+
# @return [Boolean]
|
17
50
|
def self.do_caching?
|
18
51
|
false
|
19
52
|
end
|
20
53
|
|
54
|
+
# A JSON key to use as a root key on a non-collection serializable. by
|
55
|
+
# default it's the name of the class without the "Serializer" part. But it
|
56
|
+
# can be overridden in a subclass to be anything.
|
57
|
+
#
|
58
|
+
# @return [Symbol] a symbol to be used as a key for a JSON-ready Hash
|
21
59
|
def self.key
|
22
60
|
to_s.demodulize.chomp('Serializer').underscore.to_sym
|
23
61
|
end
|
24
62
|
|
63
|
+
# A JSON key to use as a root key on a collection-type serializable. By
|
64
|
+
# deafult it's the plural version of .key, but it can be overridden in a
|
65
|
+
# subclass to be anything.
|
66
|
+
#
|
67
|
+
# @return [Symbol] a symbol to be used as a key for a JSON-ready Hash
|
25
68
|
def self.collection_key
|
26
69
|
return nil unless key
|
27
70
|
|
28
71
|
key.to_s.pluralize.to_sym
|
29
72
|
end
|
30
73
|
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
74
|
+
# An array of strings that should be added to the cache key for an instance
|
75
|
+
# of this serializer. Typically you'd add in the #cache_key or string value
|
76
|
+
# for any extra models or data passed in through the options hash here. But
|
77
|
+
# it could also contain any custom logic about how to construct a cache
|
78
|
+
# key. This method is meant to be overridden in subclasses.
|
79
|
+
#
|
80
|
+
# @example cache based off models provided in options
|
81
|
+
# def self.cache_key_addons(options)
|
82
|
+
# [options[:current_user].cache_key]
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# @example time-based caching
|
86
|
+
# def self.cache_key_addons(_options)
|
87
|
+
# [Date.today.to_s]
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# @param options [Hash] the options hash passed to the serializer, will be
|
91
|
+
# passed in here as well so you can refernce it if needed.
|
92
|
+
# @return [Array<String>]
|
34
93
|
def self.cache_key_addons(_options = {})
|
35
94
|
[]
|
36
95
|
end
|
37
96
|
|
97
|
+
# Return a cache key string for the serializer class to be included in the
|
98
|
+
# cache key for the instances. The key includes the name of the class, and
|
99
|
+
# a digest of the contents of the main class file.
|
100
|
+
#
|
101
|
+
# @return [String] a cache key for the class
|
38
102
|
def self.cache_key_base
|
39
103
|
# TODO: we may need to get a cache key from nested serializers as well :(
|
40
104
|
@cache_key_base ||= "#{self}-#{file_hash}"
|
41
105
|
end
|
42
106
|
|
43
|
-
def self.attributes
|
44
|
-
@attributes || []
|
45
|
-
end
|
46
|
-
delegate :attributes, to: :class
|
47
|
-
|
48
107
|
private
|
49
108
|
|
50
109
|
def self.file_hash
|
@@ -63,7 +122,6 @@ module CacheCrispies
|
|
63
122
|
private_class_method :path
|
64
123
|
|
65
124
|
def self.nest_in(key, &block)
|
66
|
-
@nesting ||= []
|
67
125
|
@nesting << key
|
68
126
|
|
69
127
|
block.call
|
@@ -73,7 +131,6 @@ module CacheCrispies
|
|
73
131
|
private_class_method :nest_in
|
74
132
|
|
75
133
|
def self.show_if(condition_proc, &block)
|
76
|
-
@conditions ||= []
|
77
134
|
@conditions << Condition.new(condition_proc)
|
78
135
|
|
79
136
|
block.call
|
@@ -83,8 +140,6 @@ module CacheCrispies
|
|
83
140
|
private_class_method :show_if
|
84
141
|
|
85
142
|
def self.serialize(*attribute_names, from: nil, with: nil, to: nil)
|
86
|
-
@attributes ||= []
|
87
|
-
|
88
143
|
attribute_names.flatten.map { |att| att&.to_sym }.map do |attrib|
|
89
144
|
current_nesting = Array(@nesting).dup
|
90
145
|
current_conditions = Array(@conditions).dup
|
@@ -107,4 +162,4 @@ module CacheCrispies
|
|
107
162
|
end
|
108
163
|
private_class_method :merge
|
109
164
|
end
|
110
|
-
end
|
165
|
+
end
|
@@ -1,13 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rails'
|
2
4
|
|
3
5
|
module CacheCrispies
|
6
|
+
# Handles rendering and possibly caching a collection of models using a
|
7
|
+
# Serializer
|
4
8
|
class Collection
|
9
|
+
# Initializes a new instance of CacheCrispies::Collection
|
10
|
+
#
|
11
|
+
# @param colleciton [Object] typically an enumerable containing instances of
|
12
|
+
# ActiveRecord::Base, but could be any enumerable
|
13
|
+
# @param serializer [CacheCrispies::Base] a class inheriting from
|
14
|
+
# CacheCrispies::Base
|
15
|
+
# @param options [Hash] any optional values from the serializer instance
|
5
16
|
def initialize(collection, serializer, options = {})
|
6
17
|
@collection = collection
|
7
18
|
@serializer = serializer
|
8
19
|
@options = options
|
9
20
|
end
|
10
21
|
|
22
|
+
# Renders the collection to a JSON-ready Hash trying to cache the hash
|
23
|
+
# along the way
|
24
|
+
#
|
25
|
+
# @return [Hash] the JSON-ready Hash
|
11
26
|
def as_json
|
12
27
|
if serializer.do_caching? && collection.respond_to?(:cache_key)
|
13
28
|
cached_json
|
@@ -40,4 +55,4 @@ module CacheCrispies
|
|
40
55
|
end
|
41
56
|
end
|
42
57
|
end
|
43
|
-
end
|
58
|
+
end
|
@@ -1,17 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CacheCrispies
|
2
|
-
# Represents an instance of a conditional built by a
|
4
|
+
# Represents an instance of a conditional built by a
|
5
|
+
# {CacheCrispies::Base.show_if} call
|
3
6
|
class Condition
|
7
|
+
# Returns a new instance of Condition
|
8
|
+
#
|
9
|
+
# @param block [Proc] a block containing the logic for the condition
|
4
10
|
def initialize(block)
|
5
11
|
@block = block
|
6
12
|
end
|
7
13
|
|
8
|
-
#
|
9
|
-
#
|
14
|
+
# A system-wide unique ID used for memoizaiton
|
15
|
+
#
|
16
|
+
# @eturn [Integer] the unique ID for this condition
|
10
17
|
def uid
|
11
18
|
# Just reusing the block's object_id seems to make sense
|
12
19
|
block.object_id
|
13
20
|
end
|
14
21
|
|
22
|
+
# Test the truthiness of the condition against a model and options
|
23
|
+
#
|
24
|
+
# @param model [Object] typically ActiveRecord::Base, but could be anything
|
25
|
+
# @param options [Hash] any optional values from the serializer instance
|
26
|
+
# @return [Boolean] the condition's truthiness
|
15
27
|
def true_for?(model, options = {})
|
16
28
|
!!case block.arity
|
17
29
|
when 0
|
@@ -27,4 +39,4 @@ module CacheCrispies
|
|
27
39
|
|
28
40
|
attr_reader :block
|
29
41
|
end
|
30
|
-
end
|
42
|
+
end
|
@@ -1,9 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CacheCrispies
|
4
|
+
# A Rails concern designed to be used in Rails controllers to provide access to
|
5
|
+
# the #cache_render method
|
2
6
|
module Controller
|
3
7
|
extend ActiveSupport::Concern
|
4
8
|
|
9
|
+
# The serialization mode that should be used with the oj gem
|
5
10
|
OJ_MODE = :rails
|
6
11
|
|
12
|
+
# Renders the provided cacheable object to JSON using the provided
|
13
|
+
# serializer
|
14
|
+
#
|
15
|
+
# @param serializer [CacheCrispies::Base] a class inheriting from
|
16
|
+
# CacheCrispies::Base
|
17
|
+
# @param cacheable [Object] can be any object. But is typically a Rails
|
18
|
+
# model inheriting from ActiveRecord::Base
|
19
|
+
# @param options [Hash] any hash of custom options that should be passed
|
20
|
+
# to the serializer instance
|
21
|
+
# @return [void]
|
7
22
|
def cache_render(serializer, cacheable, options = {})
|
8
23
|
plan = CacheCrispies::Plan.new(serializer, cacheable, options)
|
9
24
|
|
@@ -23,4 +38,4 @@ module CacheCrispies
|
|
23
38
|
render json: Oj.dump(plan.wrap(serializer_json), mode: OJ_MODE)
|
24
39
|
end
|
25
40
|
end
|
26
|
-
end
|
41
|
+
end
|
@@ -1,10 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CacheCrispies
|
4
|
+
# Builds out a JSON-ready Hash using the attributes in a Serializer
|
2
5
|
class HashBuilder
|
6
|
+
# Initializes a new instance of CacheCrispies::HashBuilder
|
7
|
+
#
|
8
|
+
# @param serializer [CacheCrispies::Base] an instance of a subclass of
|
9
|
+
# CacheCrispies::Base
|
3
10
|
def initialize(serializer)
|
4
11
|
@serializer = serializer
|
5
12
|
@condition_results = Memoizer.new
|
6
13
|
end
|
7
14
|
|
15
|
+
# Builds the Hash
|
16
|
+
#
|
17
|
+
# @return [Hash]
|
8
18
|
def call
|
9
19
|
hash = {}
|
10
20
|
|
@@ -58,4 +68,4 @@ module CacheCrispies
|
|
58
68
|
attribute.value_for(target, serializer.options)
|
59
69
|
end
|
60
70
|
end
|
61
|
-
end
|
71
|
+
end
|
@@ -1,9 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CacheCrispies
|
4
|
+
# Simple class to handle memoizing values by a key. This really just provides
|
5
|
+
# a bit of wrapper around doing this yourself with a hash.
|
2
6
|
class Memoizer
|
3
7
|
def initialize
|
4
8
|
@cache = {}
|
5
9
|
end
|
6
10
|
|
11
|
+
# Fetches a cached value for the given key, if it exists. Otherwise it calls
|
12
|
+
# the block and caches that value
|
13
|
+
#
|
14
|
+
# @param key [Object] the value to use as a cache key
|
15
|
+
# @yield the value to cache and return if there is a cache miss
|
16
|
+
# @return [Object] either the cached value or the block's value
|
7
17
|
def fetch(key, &_block)
|
8
18
|
# Avoid ||= because we need to memoize falsey values.
|
9
19
|
return cache[key] if cache.key?(key)
|
@@ -15,4 +25,4 @@ module CacheCrispies
|
|
15
25
|
|
16
26
|
attr_reader :cache
|
17
27
|
end
|
18
|
-
end
|
28
|
+
end
|
data/lib/cache_crispies/plan.rb
CHANGED
@@ -1,7 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CacheCrispies
|
4
|
+
# Represents a plan on how to cache a given cacheable with a given serializer
|
2
5
|
class Plan
|
3
6
|
attr_reader :serializer, :cacheable, :options
|
4
7
|
|
8
|
+
# Initializes a new instance of CacheCrispies::Plan
|
9
|
+
#
|
10
|
+
# @param serializer [CacheCrispies::Base] a class inheriting from
|
11
|
+
# CacheCrispies::Base
|
12
|
+
# @param cacheable [Object] typically ActiveRecord::Base or an enumerable
|
13
|
+
# containing instances of ActiveRecord::Base, but could be anything
|
14
|
+
# @param options [Hash] any optional values from the serializer instance
|
5
15
|
def initialize(serializer, cacheable, options = {})
|
6
16
|
@serializer = serializer
|
7
17
|
@cacheable = cacheable
|
@@ -9,14 +19,30 @@ module CacheCrispies
|
|
9
19
|
@options = options
|
10
20
|
end
|
11
21
|
|
22
|
+
# Whether or not the cacheable should be treated like a collection
|
23
|
+
#
|
24
|
+
# @return [Boolean] true if cacheable is a collection
|
12
25
|
def collection?
|
13
26
|
cacheable.respond_to?(:each)
|
14
27
|
end
|
15
28
|
|
29
|
+
# Returns the cache_key in a format suitable for an ETag header
|
30
|
+
#
|
31
|
+
# @return [String] an MD5 digest of cache_key
|
16
32
|
def etag
|
17
33
|
Digest::MD5.hexdigest(cache_key)
|
18
34
|
end
|
19
35
|
|
36
|
+
# Returns a string of cache keys for all dependent objects. Changes to any
|
37
|
+
# of keys should bust the overall key for this plan. The key consists of:
|
38
|
+
# - a global key for this gem
|
39
|
+
# - the serializers class name
|
40
|
+
# - a digest of the contents of the of serializer class file
|
41
|
+
# - any addon keys the serializer may define
|
42
|
+
# - the #cache_key method on the cacheable (ActiveRecord provides this by
|
43
|
+
# default)
|
44
|
+
#
|
45
|
+
# @return [String] a suitable cache key
|
20
46
|
def cache_key
|
21
47
|
@cache_key ||=
|
22
48
|
[
|
@@ -27,6 +53,11 @@ module CacheCrispies
|
|
27
53
|
].flatten.compact.join(CACHE_KEY_SEPARATOR)
|
28
54
|
end
|
29
55
|
|
56
|
+
# Caches the contents of the block, if the plan is cacheable, otherwise
|
57
|
+
# calls yields to the block directly
|
58
|
+
#
|
59
|
+
# @yield calls the block that should return a value to be cached
|
60
|
+
# @return whatever the provided block returns
|
30
61
|
def cache
|
31
62
|
if cache?
|
32
63
|
Rails.cache.fetch(cache_key) { yield }
|
@@ -35,6 +66,13 @@ module CacheCrispies
|
|
35
66
|
end
|
36
67
|
end
|
37
68
|
|
69
|
+
# Wraps a value in a JSON key/object. Returns json_hash directly if there
|
70
|
+
# is no key.
|
71
|
+
#
|
72
|
+
# @param json_hash [Hash, Array, Object] typically a JSON-ready Hash or
|
73
|
+
# Array, but could be anything really
|
74
|
+
# @return [Hash, Object] will return a hash with a single key of #key,
|
75
|
+
# unless there is no #key, then returns the json_hash directly.
|
38
76
|
def wrap(json_hash)
|
39
77
|
return json_hash unless key?
|
40
78
|
|
@@ -65,4 +103,4 @@ module CacheCrispies
|
|
65
103
|
Digest::MD5.hexdigest(addons.join('|'))
|
66
104
|
end
|
67
105
|
end
|
68
|
-
end
|
106
|
+
end
|
data/spec/attribute_spec.rb
CHANGED
@@ -151,9 +151,9 @@ describe CacheCrispies::Attribute do
|
|
151
151
|
let(:to) { OpenStruct }
|
152
152
|
|
153
153
|
it 'raises an exception' do
|
154
|
-
expect { subject }.to raise_exception CacheCrispies::Attribute::
|
154
|
+
expect { subject }.to raise_exception CacheCrispies::Attribute::InvalidCoercionType
|
155
155
|
end
|
156
156
|
end
|
157
157
|
end
|
158
158
|
end
|
159
|
-
end
|
159
|
+
end
|
data/spec/collection_spec.rb
CHANGED
@@ -20,9 +20,7 @@ describe CacheCrispies::Collection do
|
|
20
20
|
let(:uncacheable_models) { [model1, model2] }
|
21
21
|
let(:cacheable_models) {
|
22
22
|
[model1, model2].tap do |models|
|
23
|
-
def models.cache_key
|
24
|
-
'cereals-key'
|
25
|
-
end
|
23
|
+
def models.cache_key() end
|
26
24
|
end
|
27
25
|
}
|
28
26
|
let(:collection) { cacheable_models }
|
data/spec/memoizer_spec.rb
CHANGED
@@ -3,8 +3,6 @@ require 'spec_helper'
|
|
3
3
|
describe CacheCrispies::Memoizer do
|
4
4
|
describe '#fetch' do
|
5
5
|
it 'only calls the block once per key' do
|
6
|
-
block = -> {}
|
7
|
-
|
8
6
|
expect { |block| subject.fetch 1, &block }.to yield_with_no_args
|
9
7
|
expect { |block| subject.fetch 1, &block }.to_not yield_with_no_args
|
10
8
|
expect { |block| subject.fetch 2, &block }.to yield_with_no_args
|
data/spec/plan_spec.rb
CHANGED
@@ -49,7 +49,10 @@ describe CacheCrispies::Plan do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
describe '#etag' do
|
52
|
-
|
52
|
+
it 'generates an MD5 digest of the cache_key' do
|
53
|
+
expect(subject).to receive(:cache_key).and_return 'foo'
|
54
|
+
expect(subject.etag).to eq Digest::MD5::hexdigest('foo')
|
55
|
+
end
|
53
56
|
end
|
54
57
|
|
55
58
|
describe '#cache_key' do
|
@@ -104,6 +107,21 @@ describe CacheCrispies::Plan do
|
|
104
107
|
end
|
105
108
|
|
106
109
|
describe '#cache' do
|
110
|
+
context 'when the plan is not cacheable' do
|
111
|
+
it "doesn't cache the results" do
|
112
|
+
expect(Rails).to_not receive(:cache)
|
113
|
+
subject.cache {}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when the plan is not cacheable' do
|
118
|
+
it "doesn't cache the results" do
|
119
|
+
expect(subject).to receive(:cache?).and_return true
|
120
|
+
expect(subject).to receive(:cache_key).and_return 'bar'
|
121
|
+
expect(Rails).to receive_message_chain(:cache, :fetch).with('bar')
|
122
|
+
subject.cache {}
|
123
|
+
end
|
124
|
+
end
|
107
125
|
end
|
108
126
|
|
109
127
|
describe '#wrap' do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,102 +1,21 @@
|
|
1
|
-
require '
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
2
3
|
|
4
|
+
require 'byebug'
|
3
5
|
require_relative '../lib/cache_crispies'
|
4
6
|
|
5
|
-
# This file was generated by the `rspec --init` command. Conventionally, all
|
6
|
-
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
7
|
-
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
8
|
-
# this file to always be loaded, without a need to explicitly require it in any
|
9
|
-
# files.
|
10
|
-
#
|
11
|
-
# Given that it is always loaded, you are encouraged to keep this file as
|
12
|
-
# light-weight as possible. Requiring heavyweight dependencies from this file
|
13
|
-
# will add to the boot time of your test suite on EVERY test run, even for an
|
14
|
-
# individual file that may not need all of that loaded. Instead, consider making
|
15
|
-
# a separate helper file that requires the additional dependencies and performs
|
16
|
-
# the additional setup, and require it from the spec files that actually need
|
17
|
-
# it.
|
18
|
-
#
|
19
|
-
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
20
7
|
RSpec.configure do |config|
|
21
|
-
# rspec-expectations config goes here. You can use an alternate
|
22
|
-
# assertion/expectation library such as wrong or the stdlib/minitest
|
23
|
-
# assertions if you prefer.
|
24
8
|
config.expect_with :rspec do |expectations|
|
25
|
-
# This option will default to `true` in RSpec 4. It makes the `description`
|
26
|
-
# and `failure_message` of custom matchers include text for helper methods
|
27
|
-
# defined using `chain`, e.g.:
|
28
|
-
# be_bigger_than(2).and_smaller_than(4).description
|
29
|
-
# # => "be bigger than 2 and smaller than 4"
|
30
|
-
# ...rather than:
|
31
|
-
# # => "be bigger than 2"
|
32
9
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
33
10
|
end
|
34
11
|
|
35
|
-
# rspec-mocks config goes here. You can use an alternate test double
|
36
|
-
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
37
12
|
config.mock_with :rspec do |mocks|
|
38
|
-
# Prevents you from mocking or stubbing a method that does not exist on
|
39
|
-
# a real object. This is generally recommended, and will default to
|
40
|
-
# `true` in RSpec 4.
|
41
13
|
mocks.verify_partial_doubles = true
|
42
14
|
end
|
43
15
|
|
44
|
-
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
45
|
-
# have no way to turn it off -- the option exists only for backwards
|
46
|
-
# compatibility in RSpec 3). It causes shared context metadata to be
|
47
|
-
# inherited by the metadata hash of host groups and examples, rather than
|
48
|
-
# triggering implicit auto-inclusion in groups with matching metadata.
|
49
16
|
config.shared_context_metadata_behavior = :apply_to_host_groups
|
50
17
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
# # you care about by tagging them with `:focus` metadata. When nothing
|
55
|
-
# # is tagged with `:focus`, all examples get run. RSpec also provides
|
56
|
-
# # aliases for `it`, `describe`, and `context` that include `:focus`
|
57
|
-
# # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
58
|
-
# config.filter_run_when_matching :focus
|
59
|
-
#
|
60
|
-
# # Allows RSpec to persist some state between runs in order to support
|
61
|
-
# # the `--only-failures` and `--next-failure` CLI options. We recommend
|
62
|
-
# # you configure your source control system to ignore this file.
|
63
|
-
# config.example_status_persistence_file_path = "spec/examples.txt"
|
64
|
-
#
|
65
|
-
# # Limits the available syntax to the non-monkey patched syntax that is
|
66
|
-
# # recommended. For more details, see:
|
67
|
-
# # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
68
|
-
# # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
69
|
-
# # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
70
|
-
# config.disable_monkey_patching!
|
71
|
-
#
|
72
|
-
# # This setting enables warnings. It's recommended, but in some cases may
|
73
|
-
# # be too noisy due to issues in dependencies.
|
74
|
-
# config.warnings = true
|
75
|
-
#
|
76
|
-
# # Many RSpec users commonly either run the entire suite or an individual
|
77
|
-
# # file, and it's useful to allow more verbose output when running an
|
78
|
-
# # individual spec file.
|
79
|
-
# if config.files_to_run.one?
|
80
|
-
# # Use the documentation formatter for detailed output,
|
81
|
-
# # unless a formatter has already been configured
|
82
|
-
# # (e.g. via a command-line flag).
|
83
|
-
# config.default_formatter = "doc"
|
84
|
-
# end
|
85
|
-
#
|
86
|
-
# # Print the 10 slowest examples and example groups at the
|
87
|
-
# # end of the spec run, to help surface which specs are running
|
88
|
-
# # particularly slow.
|
89
|
-
# config.profile_examples = 10
|
90
|
-
#
|
91
|
-
# # Run specs in random order to surface order dependencies. If you find an
|
92
|
-
# # order dependency and want to debug it, you can fix the order by providing
|
93
|
-
# # the seed, which is printed after each run.
|
94
|
-
# # --seed 1234
|
95
|
-
# config.order = :random
|
96
|
-
#
|
97
|
-
# # Seed global randomization in this process using the `--seed` CLI option.
|
98
|
-
# # Setting this allows you to use `--seed` to deterministically reproduce
|
99
|
-
# # test failures related to randomization by passing the same `--seed` value
|
100
|
-
# # as the one that triggered the failure.
|
101
|
-
# Kernel.srand config.seed
|
18
|
+
config.warnings = true
|
19
|
+
config.order = :random
|
20
|
+
Kernel.srand config.seed
|
102
21
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cache_crispies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Crownoble
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 3.8.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.17'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.17'
|
83
97
|
description:
|
84
98
|
email: adam@codenoble.com
|
85
99
|
executables: []
|