flattery 0.0.2 → 0.0.3
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 +16 -11
- data/lib/flattery.rb +1 -0
- data/lib/flattery/settings.rb +71 -0
- data/lib/flattery/value_cache.rb +6 -44
- data/lib/flattery/value_cache/processor.rb +18 -0
- data/lib/flattery/value_cache/settings.rb +44 -0
- data/lib/flattery/value_provider.rb +6 -73
- data/lib/flattery/value_provider/processor.rb +26 -0
- data/lib/flattery/value_provider/settings.rb +74 -0
- data/lib/flattery/version.rb +1 -1
- data/spec/spec_helper.rb +4 -0
- data/spec/support/active_record_fixtures.rb +5 -0
- data/spec/unit/settings_spec.rb +123 -0
- data/spec/unit/value_cache/processor_spec.rb +52 -0
- data/spec/unit/value_cache/settings_spec.rb +32 -0
- data/spec/unit/value_cache_spec.rb +14 -140
- data/spec/unit/value_provider/processor_spec.rb +130 -0
- data/spec/unit/value_provider/settings_spec.rb +34 -0
- data/spec/unit/value_provider_spec.rb +14 -179
- metadata +17 -2
data/README.md
CHANGED
@@ -32,20 +32,26 @@ Or install it yourself as:
|
|
32
32
|
|
33
33
|
## Usage
|
34
34
|
|
35
|
-
### How to
|
35
|
+
### How to cache values from a :belongs_to association
|
36
36
|
|
37
|
-
Given a model with a :
|
37
|
+
Given a model with a :belongs_to association, you want to store a (copy/cached) value from the associated record.
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
class Category < ActiveRecord::Base
|
40
|
+
has_many :notes, :inverse_of => :category
|
41
|
+
end
|
41
42
|
|
42
43
|
class Note < ActiveRecord::Base
|
43
|
-
belongs_to :category
|
44
|
+
belongs_to :category, :inverse_of => :notes
|
44
45
|
|
45
46
|
include Flattery::ValueCache
|
46
47
|
flatten_value :category => :name
|
47
48
|
end
|
48
49
|
|
50
|
+
In this case, when you save an instance of Note, it will store the instance.category.name value as instance.category_name.
|
51
|
+
The :category_name attribute is inferred from the relationship, and is assumed to be present in the schema.
|
52
|
+
So before you can use this, you must add a migration to add the :category_name column to the notes table (with the same type as the :name column on the Category table).
|
53
|
+
|
54
|
+
|
49
55
|
### How to cache the value in a specific column name
|
50
56
|
|
51
57
|
In the usual case, the cache column name is inferred from the association (e.g. category_name in the example above).
|
@@ -58,12 +64,12 @@ If you want to store in another column name, use the :as option on the +flatten_
|
|
58
64
|
flatten_value :category => :name, :as => 'cat_name'
|
59
65
|
end
|
60
66
|
|
61
|
-
|
67
|
+
Again, you must make sure the column is correctly defined in your schema.
|
62
68
|
|
63
|
-
|
64
|
-
you want the category_name cached value updated if the category.name changes.
|
69
|
+
### How to push updates to cached values from the source model
|
65
70
|
|
66
|
-
|
71
|
+
Given the example above, we have a problem if Category records are updated - the :category_name value stored in Notes gets out of sync.
|
72
|
+
The Flattery::ValueProvider module fixes this by propagating changes accordingly.
|
67
73
|
|
68
74
|
class Category < ActiveRecord::Base
|
69
75
|
has_many :notes
|
@@ -72,7 +78,7 @@ This is achieved by adding the Flattery::ValueProvider to the source model and d
|
|
72
78
|
push_flattened_values_for :name => :notes
|
73
79
|
end
|
74
80
|
|
75
|
-
This will
|
81
|
+
This will push changes to Category :name to Notes records (by inference, updating the :category_name value in Notes).
|
76
82
|
|
77
83
|
### How to push updates to cached values from the source model to a specific cache column name
|
78
84
|
|
@@ -88,7 +94,6 @@ To 'help' flattery figure out the correct column name, specify the column name w
|
|
88
94
|
end
|
89
95
|
|
90
96
|
|
91
|
-
|
92
97
|
## Contributing
|
93
98
|
|
94
99
|
1. Fork it
|
data/lib/flattery.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
class Flattery::Settings
|
2
|
+
|
3
|
+
attr_accessor :klass
|
4
|
+
attr_accessor :raw_settings
|
5
|
+
attr_accessor :resolved_settings
|
6
|
+
attr_accessor :resolved
|
7
|
+
|
8
|
+
def initialize(klass=nil)
|
9
|
+
self.klass = klass
|
10
|
+
reset!
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_setting(options={})
|
14
|
+
if options.nil?
|
15
|
+
reset!
|
16
|
+
return
|
17
|
+
end
|
18
|
+
return if options.empty?
|
19
|
+
unresolved!
|
20
|
+
self.raw_settings << parse_option_setting(options)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Process +options+ and return a standardised raw_setting hash
|
24
|
+
def parse_option_setting(options)
|
25
|
+
cache_options = setting_template
|
26
|
+
|
27
|
+
opt = options.symbolize_keys
|
28
|
+
as_setting = opt.delete(:as).try(:to_s)
|
29
|
+
method_setting = opt.delete(:method).try(:to_sym)
|
30
|
+
|
31
|
+
if from_entity = opt.keys.first
|
32
|
+
cache_options[:from_entity] = from_entity
|
33
|
+
cache_options[:to_entity] = opt[from_entity].try(:to_sym)
|
34
|
+
end
|
35
|
+
cache_options[:as] = as_setting
|
36
|
+
cache_options[:method] = method_setting if method_setting
|
37
|
+
|
38
|
+
cache_options
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the basic settings template
|
42
|
+
def setting_template
|
43
|
+
{}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Command: mark settings as unresolved
|
47
|
+
def unresolved!
|
48
|
+
self.resolved = false
|
49
|
+
end
|
50
|
+
|
51
|
+
# Command: clear/reset all settings
|
52
|
+
def reset!
|
53
|
+
self.raw_settings = []
|
54
|
+
self.resolved_settings = {}
|
55
|
+
self.resolved = false
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns resolved settings
|
59
|
+
def settings
|
60
|
+
unless resolved
|
61
|
+
self.resolved = resolve_settings!
|
62
|
+
end
|
63
|
+
self.resolved_settings
|
64
|
+
end
|
65
|
+
|
66
|
+
# Command: sets resolved_settings. Returns true if resolution was success (which will set the resolution status)
|
67
|
+
def resolve_settings!
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/lib/flattery/value_cache.rb
CHANGED
@@ -3,9 +3,8 @@ module Flattery::ValueCache
|
|
3
3
|
|
4
4
|
included do
|
5
5
|
class_attribute :value_cache_options
|
6
|
-
self.value_cache_options =
|
7
|
-
|
8
|
-
before_save :resolve_value_cache
|
6
|
+
self.value_cache_options = Settings.new(self)
|
7
|
+
before_save Processor.new
|
9
8
|
end
|
10
9
|
|
11
10
|
module ClassMethods
|
@@ -22,49 +21,12 @@ module Flattery::ValueCache
|
|
22
21
|
# When explicitly passed nil, it clears all existing settings
|
23
22
|
#
|
24
23
|
def flatten_value(options={})
|
25
|
-
|
26
|
-
self.value_cache_options = {}
|
27
|
-
return
|
28
|
-
end
|
29
|
-
|
30
|
-
self.value_cache_options ||= {}
|
31
|
-
opt = options.symbolize_keys
|
32
|
-
as_setting = opt.delete(:as)
|
33
|
-
association_name = opt.keys.first
|
34
|
-
association_method = opt[association_name].try(:to_sym)
|
35
|
-
cache_attribute = (as_setting || "#{association_name}_#{association_method}").to_s
|
36
|
-
|
37
|
-
assoc = reflect_on_association(association_name)
|
38
|
-
cache_options = if assoc && assoc.belongs_to? && assoc.klass.column_names.include?("#{association_method}")
|
39
|
-
{
|
40
|
-
association_name: association_name,
|
41
|
-
association_method: association_method,
|
42
|
-
changed_on: [assoc.foreign_key]
|
43
|
-
}
|
44
|
-
end
|
45
|
-
|
46
|
-
if cache_options
|
47
|
-
self.value_cache_options[cache_attribute] = cache_options
|
48
|
-
else
|
49
|
-
self.value_cache_options.delete(cache_attribute)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Returns the cache_column name given +association_name+ and +association_method+
|
54
|
-
def cache_attribute_for_association(association_name,association_method)
|
55
|
-
value_cache_options.detect{|k,v| v[:association_name] == association_name.to_sym && v[:association_method] == association_method.to_sym }.first
|
24
|
+
self.value_cache_options.add_setting(options)
|
56
25
|
end
|
57
26
|
|
58
27
|
end
|
59
28
|
|
60
|
-
# Command: updates cached values for related changed attributes
|
61
|
-
def resolve_value_cache
|
62
|
-
self.class.value_cache_options.each do |key,options|
|
63
|
-
if changed & options[:changed_on]
|
64
|
-
self.send("#{key}=", self.send(options[:association_name]).try(:send,options[:association_method]))
|
65
|
-
end
|
66
|
-
end
|
67
|
-
true
|
68
|
-
end
|
69
|
-
|
70
29
|
end
|
30
|
+
|
31
|
+
require "flattery/value_cache/settings"
|
32
|
+
require "flattery/value_cache/processor"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Flattery::ValueCache::Processor
|
2
|
+
|
3
|
+
# Command: updates cached values for related changed attributes
|
4
|
+
def before_save(record)
|
5
|
+
resolved_options!(record.class).each do |key,options|
|
6
|
+
if record.changed & options[:changed_on]
|
7
|
+
record.send("#{key}=", record.send(options[:from_entity]).try(:send,options[:to_entity]))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
# Command: resolves value cache options for +klass+ if required, and returns resolved options
|
14
|
+
def resolved_options!(klass)
|
15
|
+
klass.value_cache_options.settings
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Flattery::ValueCache::Settings < Flattery::Settings
|
2
|
+
|
3
|
+
# Command: sets resolved_settings. Returns true if resolution was success (which will set the resolution status)
|
4
|
+
#
|
5
|
+
# Given raw settings: [{ from_entity: :category, to_entity: :name, as: 'cat_name' }]
|
6
|
+
# Resolved settings: { 'cat_name' => { from_entity: :category, to_entity: :name, changed_on: ['category_id'] } }
|
7
|
+
#
|
8
|
+
# In the ValueCache context:
|
9
|
+
# * +from_entity+ is the association from which the cache value is obtained
|
10
|
+
# * +to_entity+ is the column that the cache value will be stored in
|
11
|
+
# * +as+ is the column name on +from_entity+ from which the cache value is to be read
|
12
|
+
#
|
13
|
+
# Validations/transformations performed:
|
14
|
+
# * from_entity is a valid association
|
15
|
+
# * cache attribute name derived from :as or inferred from association/attribute names
|
16
|
+
# * cache attribute is a valid column
|
17
|
+
#
|
18
|
+
# If any of these fail, the setting is excluded from the resolved options.
|
19
|
+
#
|
20
|
+
def resolve_settings!
|
21
|
+
self.resolved_settings = raw_settings.each_with_object({}) do |setting,memo|
|
22
|
+
from_entity = setting[:from_entity]
|
23
|
+
to_entity = setting[:to_entity]
|
24
|
+
cache_attribute = (setting[:as] || "#{from_entity}_#{to_entity}").to_s
|
25
|
+
|
26
|
+
assoc = klass.reflect_on_association(from_entity)
|
27
|
+
cache_options = if assoc && assoc.belongs_to? && klass.column_names.include?("#{cache_attribute}")
|
28
|
+
{
|
29
|
+
from_entity: from_entity,
|
30
|
+
to_entity: to_entity,
|
31
|
+
changed_on: [assoc.foreign_key]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
if cache_options
|
36
|
+
memo[cache_attribute] = cache_options
|
37
|
+
else
|
38
|
+
memo.delete(cache_attribute)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -3,9 +3,8 @@ module Flattery::ValueProvider
|
|
3
3
|
|
4
4
|
included do
|
5
5
|
class_attribute :value_provider_options
|
6
|
-
self.value_provider_options =
|
7
|
-
|
8
|
-
before_update :resolve_value_provision
|
6
|
+
self.value_provider_options = Settings.new(self)
|
7
|
+
before_update Processor.new
|
9
8
|
end
|
10
9
|
|
11
10
|
module ClassMethods
|
@@ -22,78 +21,12 @@ module Flattery::ValueProvider
|
|
22
21
|
# When explicitly passed nil, it clears all existing settings
|
23
22
|
#
|
24
23
|
def push_flattened_values_for(options={})
|
25
|
-
|
26
|
-
self.value_provider_options = {}
|
27
|
-
return
|
28
|
-
end
|
29
|
-
|
30
|
-
self.value_provider_options ||= {}
|
31
|
-
opt = options.symbolize_keys
|
32
|
-
as_setting = opt.delete(:as)
|
33
|
-
|
34
|
-
attribute_key = opt.keys.first
|
35
|
-
association_name = opt[attribute_key]
|
36
|
-
attribute_name = "#{attribute_key}"
|
37
|
-
|
38
|
-
cached_attribute_name = (as_setting || "inflect").to_sym
|
39
|
-
|
40
|
-
assoc = reflect_on_association(association_name)
|
41
|
-
cache_options = if assoc && assoc.macro == :has_many
|
42
|
-
{
|
43
|
-
association_name: association_name,
|
44
|
-
cached_attribute_name: cached_attribute_name,
|
45
|
-
method: :update_all
|
46
|
-
}
|
47
|
-
end
|
48
|
-
|
49
|
-
if cache_options
|
50
|
-
self.value_provider_options[attribute_name] = cache_options
|
51
|
-
else
|
52
|
-
self.value_provider_options.delete(attribute_name)
|
53
|
-
end
|
24
|
+
self.value_provider_options.add_setting(options)
|
54
25
|
end
|
55
26
|
|
56
27
|
end
|
57
28
|
|
58
|
-
# Command: pushes cache updates for related changed attributes
|
59
|
-
def resolve_value_provision
|
60
|
-
self.class.value_provider_options.each do |key,options|
|
61
|
-
if changed.include?(key)
|
62
|
-
|
63
|
-
association_name = options[:association_name]
|
64
|
-
|
65
|
-
cache_column = if options[:cached_attribute_name] == :inflect
|
66
|
-
name = nil
|
67
|
-
if assoc = self.class.reflect_on_association(association_name)
|
68
|
-
other_assoc_name = if assoc.inverse_of
|
69
|
-
assoc.inverse_of.name
|
70
|
-
else
|
71
|
-
end
|
72
|
-
if other_assoc_name
|
73
|
-
if assoc.klass.respond_to?(:cache_attribute_for_association)
|
74
|
-
name = assoc.klass.cache_attribute_for_association(other_assoc_name,key)
|
75
|
-
end
|
76
|
-
name ||= "#{other_assoc_name}_#{key}"
|
77
|
-
end
|
78
|
-
name = nil unless name && assoc.klass.column_names.include?(name)
|
79
|
-
end
|
80
|
-
name
|
81
|
-
else
|
82
|
-
options[:cached_attribute_name]
|
83
|
-
end
|
84
|
-
|
85
|
-
if cache_column
|
86
|
-
case options[:method]
|
87
|
-
when :update_all
|
88
|
-
new_value = self.send(key)
|
89
|
-
self.send(association_name).update_all({cache_column => new_value})
|
90
|
-
end
|
91
|
-
else
|
92
|
-
raise Flattery::CacheColumnInflectionError.new("#{self.class.name} #{key}: #{options}")
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
true
|
97
|
-
end
|
98
|
-
|
99
29
|
end
|
30
|
+
|
31
|
+
require "flattery/value_provider/settings"
|
32
|
+
require "flattery/value_provider/processor"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Flattery::ValueProvider::Processor
|
2
|
+
|
3
|
+
# Command: pushes cache updates for related changed attributes
|
4
|
+
def before_update(record)
|
5
|
+
resolved_options!(record.class).each do |key,options|
|
6
|
+
if record.changed.include?(key)
|
7
|
+
if cache_column = options[:as]
|
8
|
+
case options[:method]
|
9
|
+
when :update_all
|
10
|
+
new_value = record.send(key)
|
11
|
+
record.send(options[:to_entity]).update_all({cache_column => new_value})
|
12
|
+
end
|
13
|
+
else
|
14
|
+
raise Flattery::CacheColumnInflectionError.new("#{record.class.name} #{key}: #{options}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
# Command: resolves value provider options for +klass+ if required, and returns resolved options
|
22
|
+
def resolved_options!(klass)
|
23
|
+
klass.value_provider_options.settings
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class Flattery::ValueProvider::Settings < Flattery::Settings
|
2
|
+
|
3
|
+
# Returns the basic settings template
|
4
|
+
def setting_template
|
5
|
+
{method: :update_all}
|
6
|
+
end
|
7
|
+
|
8
|
+
# Command: sets resolved_settings. Returns true if resolution was success (which will set the resolution status)
|
9
|
+
#
|
10
|
+
# Given raw settings: [{ from_entity: :name, to_entity: :notes, as: 'cat_name', method: :update_all }]
|
11
|
+
# Resolved settings: { 'name' => { to_entity: :notes, as: 'cat_name', method: :update_all } }
|
12
|
+
#
|
13
|
+
# In the ValueProvider context:
|
14
|
+
# * +from_entity+ is the column that provides the cache value
|
15
|
+
# * +to_entity+ is the association to which the cache value is pushed
|
16
|
+
# * +as+ is the column name on +to_entity+ from which the cache value is to be stored
|
17
|
+
#
|
18
|
+
# Validations/transformations performed:
|
19
|
+
# * to_entity is a valid association
|
20
|
+
#
|
21
|
+
# If any of these fail, the setting is excluded from the resolved options.
|
22
|
+
#
|
23
|
+
def resolve_settings!
|
24
|
+
self.resolved_settings = raw_settings.each_with_object({}) do |setting,memo|
|
25
|
+
from_entity = setting[:from_entity]
|
26
|
+
to_entity = setting[:to_entity]
|
27
|
+
|
28
|
+
push_method = setting[:method]
|
29
|
+
attribute_name = "#{from_entity}"
|
30
|
+
|
31
|
+
assoc = klass.reflect_on_association(to_entity)
|
32
|
+
cache_options = if assoc && assoc.macro == :has_many
|
33
|
+
|
34
|
+
cached_attribute_name = if setting[:as].present?
|
35
|
+
setting[:as].to_sym
|
36
|
+
else
|
37
|
+
name = nil
|
38
|
+
other_assoc_name = if assoc.inverse_of
|
39
|
+
assoc.inverse_of.name
|
40
|
+
else
|
41
|
+
end
|
42
|
+
if other_assoc_name
|
43
|
+
name = cache_attribute_for_association(assoc.klass,other_assoc_name,attribute_name)
|
44
|
+
name ||= "#{other_assoc_name}_#{attribute_name}"
|
45
|
+
end
|
46
|
+
name = nil unless name && assoc.klass.column_names.include?(name)
|
47
|
+
name
|
48
|
+
end
|
49
|
+
|
50
|
+
{
|
51
|
+
to_entity: to_entity,
|
52
|
+
as: cached_attribute_name,
|
53
|
+
method: push_method
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
if cache_options
|
58
|
+
memo[attribute_name] = cache_options
|
59
|
+
else
|
60
|
+
memo.delete(attribute_name)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the cache_column name given +association_name+ and +association_method+
|
68
|
+
def cache_attribute_for_association(klass,association_name,association_method)
|
69
|
+
if klass.respond_to?(:value_cache_options)
|
70
|
+
klass.value_cache_options.settings.detect{|k,v| v[:from_entity] == association_name.to_sym && v[:to_entity] == association_method.to_sym }.first
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/lib/flattery/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -49,6 +49,11 @@ module ArHelper
|
|
49
49
|
Person.delete_all
|
50
50
|
end
|
51
51
|
|
52
|
+
def clear_harness_classes
|
53
|
+
Object.send(:remove_const, :ValueProviderHarness) if Object.constants.include?(:ValueProviderHarness)
|
54
|
+
Object.send(:remove_const, :ValueCacheHarness) if Object.constants.include?(:ValueCacheHarness)
|
55
|
+
end
|
56
|
+
|
52
57
|
end
|
53
58
|
|
54
59
|
RSpec.configure do |conf|
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Flattery::Settings do
|
4
|
+
let(:settings_class) { Flattery::Settings }
|
5
|
+
let(:settings) { settings_class.new }
|
6
|
+
subject { settings }
|
7
|
+
|
8
|
+
describe "#initialize" do
|
9
|
+
its(:klass) { should be_nil }
|
10
|
+
its(:raw_settings) { should eql([]) }
|
11
|
+
its(:resolved_settings) { should eql({}) }
|
12
|
+
its(:resolved) { should be_false}
|
13
|
+
context "when given class parameter" do
|
14
|
+
let(:klass) { String }
|
15
|
+
let(:settings) { settings_class.new(klass) }
|
16
|
+
its(:klass) { should eql(klass) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_dummy_settings_values
|
21
|
+
settings.raw_settings = [1,2,3,4]
|
22
|
+
settings.resolved_settings = {a: :b}
|
23
|
+
settings.resolved = true
|
24
|
+
settings
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#reset!" do
|
28
|
+
before do
|
29
|
+
add_dummy_settings_values
|
30
|
+
settings.reset!
|
31
|
+
end
|
32
|
+
its(:raw_settings) { should eql([]) }
|
33
|
+
its(:resolved_settings) { should eql({}) }
|
34
|
+
its(:resolved) { should be_false}
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#add_setting" do
|
38
|
+
|
39
|
+
context "when given empty hash" do
|
40
|
+
before { add_dummy_settings_values }
|
41
|
+
it "should not do anything" do
|
42
|
+
expect { settings.add_setting({}) }.to_not change { settings.raw_settings }.from([1,2,3,4])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "when given nil" do
|
47
|
+
it "should cause a reset!" do
|
48
|
+
settings.should_receive(:reset!)
|
49
|
+
settings.add_setting(nil)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when given options as Symbols" do
|
54
|
+
before { settings.add_setting({category: :name}) }
|
55
|
+
its(:raw_settings) { should eql([
|
56
|
+
{ from_entity: :category, to_entity: :name, as: nil }
|
57
|
+
]) }
|
58
|
+
context "and then given another definition" do
|
59
|
+
before { settings.add_setting({person: :email}) }
|
60
|
+
its(:raw_settings) { should eql([
|
61
|
+
{ from_entity: :category, to_entity: :name, as: nil },
|
62
|
+
{ from_entity: :person, to_entity: :email, as: nil }
|
63
|
+
]) }
|
64
|
+
context "and then reset with nil" do
|
65
|
+
before { settings.add_setting nil }
|
66
|
+
its(:raw_settings) { should eql([]) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when optional :as specified as Symbols" do
|
72
|
+
before { settings.add_setting({category: :name, as: :cat_name}) }
|
73
|
+
its(:raw_settings) { should eql([
|
74
|
+
{ from_entity: :category, to_entity: :name, as: 'cat_name' }
|
75
|
+
]) }
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when optional :method specified as Symbols" do
|
79
|
+
before { settings.add_setting({category: :name, method: :update_all}) }
|
80
|
+
its(:raw_settings) { should eql([
|
81
|
+
{ from_entity: :category, to_entity: :name, as: nil, method: :update_all }
|
82
|
+
]) }
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when given options as String" do
|
86
|
+
before { settings.add_setting({'category' => 'name'}) }
|
87
|
+
its(:raw_settings) { should eql([
|
88
|
+
{ from_entity: :category, to_entity: :name, as: nil }
|
89
|
+
]) }
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when optional :as specified as String" do
|
93
|
+
before { settings.add_setting({'category' => 'name', 'as' => 'cat_name'}) }
|
94
|
+
its(:raw_settings) { should eql([
|
95
|
+
{ from_entity: :category, to_entity: :name, as: 'cat_name' }
|
96
|
+
]) }
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "#settings" do
|
102
|
+
context "when initialially not resolved" do
|
103
|
+
it "should invoke resolve_settings!" do
|
104
|
+
settings.should_receive(:resolve_settings!)
|
105
|
+
settings.settings
|
106
|
+
end
|
107
|
+
it "should mark as resolved" do
|
108
|
+
expect { settings.settings }.to change { settings.resolved }.from(false).to(true)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
context "when initialially resolved" do
|
112
|
+
before { add_dummy_settings_values }
|
113
|
+
it "should not invoke resolve_settings!" do
|
114
|
+
settings.should_receive(:resolve_settings!).never
|
115
|
+
settings.settings
|
116
|
+
end
|
117
|
+
it "should not change resolved status" do
|
118
|
+
expect { settings.settings }.to_not change { settings.resolved }.from(true)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
# Now test caching in a range of actual scenarios...
|
4
|
+
describe Flattery::ValueCache::Processor do
|
5
|
+
|
6
|
+
context "with simple belongs_to associations with cached values" do
|
7
|
+
let(:cache_class) do
|
8
|
+
class ::ValueCacheHarness < Note
|
9
|
+
include Flattery::ValueCache
|
10
|
+
flatten_value category: :name
|
11
|
+
end
|
12
|
+
ValueCacheHarness
|
13
|
+
end
|
14
|
+
let!(:resource) { cache_class.create }
|
15
|
+
|
16
|
+
let!(:category) { Category.create(name: 'category_a') }
|
17
|
+
|
18
|
+
context "when association is changed by id" do
|
19
|
+
it "should cache the new value" do
|
20
|
+
expect {
|
21
|
+
resource.update_attributes(category_id: category.id)
|
22
|
+
}.to change {
|
23
|
+
resource.category_name
|
24
|
+
}.from(nil).to(category.name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
context "when already set" do
|
28
|
+
before { resource.update_attributes(category_id: category.id) }
|
29
|
+
context "then set to nil" do
|
30
|
+
it "should cache the new value" do
|
31
|
+
expect {
|
32
|
+
resource.update_attributes(category_id: nil)
|
33
|
+
}.to change {
|
34
|
+
resource.category_name
|
35
|
+
}.from(category.name).to(nil)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
context "and associated record is destroyed" do
|
39
|
+
before { category.destroy }
|
40
|
+
it "should not recache the value when other values updated" do
|
41
|
+
expect {
|
42
|
+
resource.update_attributes(name: 'a new name')
|
43
|
+
}.to_not change {
|
44
|
+
resource.category_name
|
45
|
+
}.from(category.name)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Flattery::ValueCache::Settings do
|
4
|
+
let(:settings_class) { Flattery::ValueCache::Settings }
|
5
|
+
let(:settings) { cache_class.value_cache_options }
|
6
|
+
subject { settings }
|
7
|
+
|
8
|
+
context "with a standard belongs_to association" do
|
9
|
+
let(:cache_class) do
|
10
|
+
class ::ValueCacheHarness < Note
|
11
|
+
include Flattery::ValueCache
|
12
|
+
flatten_value category: :name
|
13
|
+
end
|
14
|
+
ValueCacheHarness
|
15
|
+
end
|
16
|
+
context "before resolution" do
|
17
|
+
it { should be_a(settings_class) }
|
18
|
+
its(:raw_settings) { should eql([
|
19
|
+
{from_entity: :category, to_entity: :name, as: nil}
|
20
|
+
]) }
|
21
|
+
its(:resolved) { should be_false }
|
22
|
+
end
|
23
|
+
context "after resolution" do
|
24
|
+
before { settings.settings }
|
25
|
+
its(:resolved) { should be_true }
|
26
|
+
its(:settings) { should eql({
|
27
|
+
"category_name"=>{from_entity: :category, to_entity: :name, changed_on: ["category_id"]}
|
28
|
+
}) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -1,156 +1,30 @@
|
|
1
1
|
require 'spec_helper.rb'
|
2
2
|
|
3
|
-
class FlatteryValueCacheTestHarness < Note
|
4
|
-
include Flattery::ValueCache
|
5
|
-
end
|
6
|
-
|
7
3
|
describe Flattery::ValueCache do
|
8
4
|
|
9
|
-
let(:
|
10
|
-
|
11
|
-
|
12
|
-
describe "##included_modules" do
|
13
|
-
subject { resource_class.included_modules }
|
14
|
-
it { should include(Flattery::ValueCache) }
|
15
|
-
end
|
16
|
-
|
17
|
-
describe "##value_cache_options" do
|
18
|
-
before { resource_class.flatten_value flatten_value_options }
|
19
|
-
subject { resource_class.value_cache_options }
|
20
|
-
|
21
|
-
context "when set to empty" do
|
22
|
-
let(:flatten_value_options) { {} }
|
23
|
-
it { should be_empty }
|
24
|
-
end
|
25
|
-
|
26
|
-
context "when reset with nil" do
|
27
|
-
let(:flatten_value_options) { {category: :name} }
|
28
|
-
it "should clear all settings" do
|
29
|
-
expect {
|
30
|
-
resource_class.flatten_value nil
|
31
|
-
}.to change {
|
32
|
-
resource_class.value_cache_options
|
33
|
-
}.to({})
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context "with simple belongs_to association" do
|
38
|
-
|
39
|
-
context "when set by association name and attribute value" do
|
40
|
-
let(:flatten_value_options) { {category: :name} }
|
41
|
-
it { should eql({
|
42
|
-
"category_name" => {
|
43
|
-
association_name: :category,
|
44
|
-
association_method: :name,
|
45
|
-
changed_on: ["category_id"]
|
46
|
-
}
|
47
|
-
}) }
|
48
|
-
end
|
49
|
-
|
50
|
-
context "when given a cache column override" do
|
51
|
-
let(:flatten_value_options) { {category: :name, as: :cat_name} }
|
52
|
-
it { should eql({
|
53
|
-
"cat_name" => {
|
54
|
-
association_name: :category,
|
55
|
-
association_method: :name,
|
56
|
-
changed_on: ["category_id"]
|
57
|
-
}
|
58
|
-
}) }
|
59
|
-
end
|
60
|
-
|
61
|
-
context "when set using Strings" do
|
62
|
-
let(:flatten_value_options) { {'category' => 'name', 'as' => 'cat_name'} }
|
63
|
-
it { should eql({
|
64
|
-
"cat_name" => {
|
65
|
-
association_name: :category,
|
66
|
-
association_method: :name,
|
67
|
-
changed_on: ["category_id"]
|
68
|
-
}
|
69
|
-
}) }
|
70
|
-
end
|
71
|
-
|
72
|
-
context "when set by association name and invalid attribute value" do
|
73
|
-
let(:flatten_value_options) { {category: :bogative} }
|
74
|
-
it { should be_empty }
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
context "with a belongs_to association having non-standard primary and foreign keys" do
|
80
|
-
|
81
|
-
context "when set by association name and attribute value" do
|
82
|
-
let(:flatten_value_options) { {person: :email} }
|
83
|
-
it { should eql({
|
84
|
-
"person_email" => {
|
85
|
-
association_name: :person,
|
86
|
-
association_method: :email,
|
87
|
-
changed_on: ["person_name"]
|
88
|
-
}
|
89
|
-
}) }
|
90
|
-
end
|
91
|
-
|
92
|
-
context "when set by association name and invalid attribute value" do
|
93
|
-
let(:flatten_value_options) { {person: :bogative} }
|
94
|
-
it { should be_empty }
|
95
|
-
end
|
96
|
-
|
5
|
+
let(:cache_class) do
|
6
|
+
class ::ValueCacheHarness < Note
|
7
|
+
include Flattery::ValueCache
|
97
8
|
end
|
9
|
+
ValueCacheHarness
|
10
|
+
end
|
98
11
|
|
12
|
+
subject { cache_class }
|
99
13
|
|
100
|
-
|
14
|
+
its(:included_modules) { should include(Flattery::ValueCache) }
|
15
|
+
its(:value_cache_options) { should be_a(Flattery::ValueCache::Settings) }
|
101
16
|
|
102
|
-
describe "#
|
17
|
+
describe "#before_save" do
|
18
|
+
let(:processor_class) { Flattery::ValueCache::Processor }
|
103
19
|
it "should be called when record created" do
|
104
|
-
|
105
|
-
|
20
|
+
processor_class.any_instance.should_receive(:before_save).and_return(true)
|
21
|
+
subject.create!
|
106
22
|
end
|
107
23
|
it "should be called when record updated" do
|
108
|
-
instance =
|
109
|
-
|
24
|
+
instance = subject.create!
|
25
|
+
processor_class.any_instance.should_receive(:before_save).and_return(true)
|
110
26
|
instance.save
|
111
27
|
end
|
112
28
|
end
|
113
29
|
|
114
|
-
describe "#before_save" do
|
115
|
-
let!(:resource) { resource_class.create }
|
116
|
-
|
117
|
-
context "with simple belongs_to associations with cached values" do
|
118
|
-
before { resource_class.flatten_value category: :name }
|
119
|
-
let!(:category) { Category.create(name: 'category_a') }
|
120
|
-
|
121
|
-
context "when association is changed by id" do
|
122
|
-
it "should cache the new value" do
|
123
|
-
expect {
|
124
|
-
resource.update_attributes(category_id: category.id)
|
125
|
-
}.to change {
|
126
|
-
resource.category_name
|
127
|
-
}.from(nil).to(category.name)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
context "when already set" do
|
131
|
-
before { resource.update_attributes(category_id: category.id) }
|
132
|
-
context "then set to nil" do
|
133
|
-
it "should cache the new value" do
|
134
|
-
expect {
|
135
|
-
resource.update_attributes(category_id: nil)
|
136
|
-
}.to change {
|
137
|
-
resource.category_name
|
138
|
-
}.from(category.name).to(nil)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
context "and associated record is destroyed" do
|
142
|
-
before { category.destroy }
|
143
|
-
it "should not recache the value when other values updated" do
|
144
|
-
expect {
|
145
|
-
resource.update_attributes(name: 'a new name')
|
146
|
-
}.to_not change {
|
147
|
-
resource.category_name
|
148
|
-
}.from(category.name)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
30
|
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
# Test caching in a range of actual scenarios
|
4
|
+
describe Flattery::ValueProvider::Processor do
|
5
|
+
|
6
|
+
context "with provider having simple has_many association and explicit cache_column name" do
|
7
|
+
let(:provider_class) do
|
8
|
+
class ::ValueProviderHarness < Category
|
9
|
+
include Flattery::ValueProvider
|
10
|
+
push_flattened_values_for name: :notes, as: :category_name
|
11
|
+
end
|
12
|
+
ValueProviderHarness
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:cache_class) do
|
16
|
+
class ::ValueCacheHarness < Note
|
17
|
+
include Flattery::ValueCache
|
18
|
+
flatten_value category: :name
|
19
|
+
end
|
20
|
+
ValueCacheHarness
|
21
|
+
end
|
22
|
+
|
23
|
+
let!(:resource) { provider_class.create(name: 'category_a') }
|
24
|
+
let!(:target_a) { cache_class.create(category_id: resource.id) }
|
25
|
+
let!(:target_other_a) { cache_class.create }
|
26
|
+
context "when cached value is updated" do
|
27
|
+
it "should push the new cache value" do
|
28
|
+
expect {
|
29
|
+
resource.update_attributes(name: 'new category name')
|
30
|
+
}.to change {
|
31
|
+
target_a.reload.category_name
|
32
|
+
}.from('category_a').to('new category name')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "with provider that cannot correctly infer the cache column name" do
|
38
|
+
let(:provider_class) do
|
39
|
+
class ::ValueProviderHarness < Category
|
40
|
+
include Flattery::ValueProvider
|
41
|
+
push_flattened_values_for name: :notes
|
42
|
+
end
|
43
|
+
ValueProviderHarness
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:cache_class) do
|
47
|
+
class ::ValueCacheHarness < Note
|
48
|
+
include Flattery::ValueCache
|
49
|
+
flatten_value category: :name
|
50
|
+
end
|
51
|
+
ValueCacheHarness
|
52
|
+
end
|
53
|
+
|
54
|
+
let!(:resource) { provider_class.create(name: 'category_a') }
|
55
|
+
let!(:target_a) { cache_class.create(category_id: resource.id) }
|
56
|
+
let!(:target_other_a) { cache_class.create }
|
57
|
+
context "when cached value is updated" do
|
58
|
+
it "should push the new cache value" do
|
59
|
+
expect {
|
60
|
+
resource.update_attributes(name: 'new category name')
|
61
|
+
}.to raise_error(Flattery::CacheColumnInflectionError)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "with provider having has_many association with cache name inflected via inverse relation" do
|
67
|
+
let(:provider_class) do
|
68
|
+
class ::ValueProviderHarness < Person
|
69
|
+
include Flattery::ValueProvider
|
70
|
+
has_many :harness_notes, class_name: 'NoteTestHarness', primary_key: "username", foreign_key: "person_name", inverse_of: :person
|
71
|
+
push_flattened_values_for email: :notes
|
72
|
+
end
|
73
|
+
ValueProviderHarness
|
74
|
+
end
|
75
|
+
|
76
|
+
let(:cache_class) do
|
77
|
+
class ::ValueCacheHarness < Note
|
78
|
+
include Flattery::ValueCache
|
79
|
+
flatten_value person: :email
|
80
|
+
end
|
81
|
+
ValueCacheHarness
|
82
|
+
end
|
83
|
+
|
84
|
+
let!(:resource) { provider_class.create(username: 'user_a', email: 'email1') }
|
85
|
+
let!(:target_a) { cache_class.create(person_name: resource.username) }
|
86
|
+
let!(:target_other_a) { cache_class.create }
|
87
|
+
context "when cached value is updated" do
|
88
|
+
it "should push the new cache value" do
|
89
|
+
expect {
|
90
|
+
resource.update_attributes(email: 'email2')
|
91
|
+
}.to change {
|
92
|
+
target_a.reload.person_email
|
93
|
+
}.from('email1').to('email2')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "with provider having has_many association with cache name inflected via inverse relation with custom cache column name" do
|
99
|
+
let(:provider_class) do
|
100
|
+
class ::ValueProviderHarness < Person
|
101
|
+
include Flattery::ValueProvider
|
102
|
+
has_many :harness_notes, class_name: 'ValueCacheHarness', primary_key: "username", foreign_key: "person_name", inverse_of: :person
|
103
|
+
push_flattened_values_for email: :harness_notes
|
104
|
+
end
|
105
|
+
ValueProviderHarness
|
106
|
+
end
|
107
|
+
|
108
|
+
let(:cache_class) do
|
109
|
+
class ::ValueCacheHarness < Note
|
110
|
+
include Flattery::ValueCache
|
111
|
+
flatten_value person: :email, as: :user_email
|
112
|
+
end
|
113
|
+
ValueCacheHarness
|
114
|
+
end
|
115
|
+
|
116
|
+
let!(:resource) { provider_class.create(username: 'user_a', email: 'email1') }
|
117
|
+
let!(:target_a) { cache_class.create(person_name: resource.username) }
|
118
|
+
let!(:target_other_a) { cache_class.create }
|
119
|
+
context "when cached value is updated" do
|
120
|
+
it "should push the new cache value" do
|
121
|
+
expect {
|
122
|
+
resource.update_attributes(email: 'email2')
|
123
|
+
}.to change {
|
124
|
+
target_a.reload.user_email
|
125
|
+
}.from('email1').to('email2')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Flattery::ValueProvider::Settings do
|
4
|
+
let(:settings_class) { Flattery::ValueProvider::Settings }
|
5
|
+
let(:settings) { provider_class.value_provider_options }
|
6
|
+
|
7
|
+
subject { settings }
|
8
|
+
|
9
|
+
context "with a standard has_many association" do
|
10
|
+
let(:provider_class) do
|
11
|
+
class ::ValueProviderHarness < Category
|
12
|
+
include Flattery::ValueProvider
|
13
|
+
push_flattened_values_for name: :notes, as: :category_name
|
14
|
+
end
|
15
|
+
ValueProviderHarness
|
16
|
+
end
|
17
|
+
context "before resolution" do
|
18
|
+
it { should be_a(settings_class) }
|
19
|
+
its(:raw_settings) { should eql([
|
20
|
+
{from_entity: :name, to_entity: :notes, as: 'category_name', method: :update_all}
|
21
|
+
]) }
|
22
|
+
its(:resolved) { should be_false }
|
23
|
+
end
|
24
|
+
context "after resolution" do
|
25
|
+
before { settings.settings }
|
26
|
+
its(:resolved) { should be_true }
|
27
|
+
its(:settings) { should eql({
|
28
|
+
"name"=>{to_entity: :notes, as: :category_name, method: :update_all}
|
29
|
+
}) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
end
|
@@ -1,195 +1,30 @@
|
|
1
1
|
require 'spec_helper.rb'
|
2
2
|
|
3
|
-
class FlatteryValueProviderTestHarness < Category
|
4
|
-
include Flattery::ValueProvider
|
5
|
-
end
|
6
|
-
|
7
|
-
class NoteTestHarness < Note
|
8
|
-
include Flattery::ValueCache
|
9
|
-
end
|
10
|
-
|
11
|
-
class CategoryTestHarness < Category
|
12
|
-
include Flattery::ValueProvider
|
13
|
-
end
|
14
|
-
|
15
|
-
class PersonTestHarness < Person
|
16
|
-
include Flattery::ValueProvider
|
17
|
-
has_many :harness_notes, class_name: 'NoteTestHarness', primary_key: "username", foreign_key: "person_name", inverse_of: :person
|
18
|
-
end
|
19
|
-
|
20
3
|
describe Flattery::ValueProvider do
|
21
4
|
|
22
|
-
let(:
|
23
|
-
|
24
|
-
|
25
|
-
describe "##included_modules" do
|
26
|
-
subject { resource_class.included_modules }
|
27
|
-
it { should include(Flattery::ValueProvider) }
|
28
|
-
end
|
29
|
-
|
30
|
-
describe "##value_provider_options" do
|
31
|
-
before { resource_class.push_flattened_values_for push_flattened_values_for_options }
|
32
|
-
subject { resource_class.value_provider_options }
|
33
|
-
|
34
|
-
context "when set to empty" do
|
35
|
-
let(:push_flattened_values_for_options) { {} }
|
36
|
-
it { should be_empty }
|
5
|
+
let(:provider_class) do
|
6
|
+
class ::ValueProviderHarness < Category
|
7
|
+
include Flattery::ValueProvider
|
37
8
|
end
|
9
|
+
ValueProviderHarness
|
10
|
+
end
|
38
11
|
|
39
|
-
|
40
|
-
let(:push_flattened_values_for_options) { {name: :notes} }
|
41
|
-
it "should clear all settings" do
|
42
|
-
expect {
|
43
|
-
resource_class.push_flattened_values_for nil
|
44
|
-
}.to change {
|
45
|
-
resource_class.value_provider_options
|
46
|
-
}.to({})
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
context "with simple has_many association" do
|
51
|
-
|
52
|
-
context "when set by association name and attribute value" do
|
53
|
-
let(:push_flattened_values_for_options) { {name: :notes} }
|
54
|
-
it { should eql({
|
55
|
-
"name" => {
|
56
|
-
association_name: :notes,
|
57
|
-
cached_attribute_name: :inflect,
|
58
|
-
method: :update_all
|
59
|
-
}
|
60
|
-
}) }
|
61
|
-
end
|
62
|
-
|
63
|
-
context "when given a cache column override" do
|
64
|
-
let(:push_flattened_values_for_options) { {name: :notes, as: :category_name} }
|
65
|
-
it { should eql({
|
66
|
-
"name" => {
|
67
|
-
association_name: :notes,
|
68
|
-
cached_attribute_name: :category_name,
|
69
|
-
method: :update_all
|
70
|
-
}
|
71
|
-
}) }
|
72
|
-
end
|
73
|
-
|
74
|
-
context "when set by association name and invalid attribute value" do
|
75
|
-
let(:push_flattened_values_for_options) { {name: :bogative} }
|
76
|
-
it { should be_empty }
|
77
|
-
end
|
12
|
+
subject { provider_class }
|
78
13
|
|
79
|
-
|
80
|
-
|
14
|
+
its(:included_modules) { should include(Flattery::ValueProvider) }
|
15
|
+
its(:value_provider_options) { should be_a(Flattery::ValueProvider::Settings) }
|
81
16
|
|
82
|
-
describe "#
|
17
|
+
describe "#before_update" do
|
18
|
+
let(:processor_class) { Flattery::ValueProvider::Processor }
|
83
19
|
it "should not be called when record created" do
|
84
|
-
|
85
|
-
|
20
|
+
processor_class.any_instance.should_receive(:before_update).never
|
21
|
+
provider_class.create!
|
86
22
|
end
|
87
23
|
it "should be called when record updated" do
|
88
|
-
instance =
|
89
|
-
|
24
|
+
instance = provider_class.create!
|
25
|
+
processor_class.any_instance.should_receive(:before_update).and_return(true)
|
90
26
|
instance.save
|
91
27
|
end
|
92
28
|
end
|
93
29
|
|
94
|
-
describe "#before_update" do
|
95
|
-
|
96
|
-
context "with provider having simple has_many association and explicit cache_column name" do
|
97
|
-
let(:provider_class) { CategoryTestHarness }
|
98
|
-
let(:cache_class) { NoteTestHarness }
|
99
|
-
before do
|
100
|
-
provider_class.push_flattened_values_for name: :notes, as: :category_name
|
101
|
-
cache_class.flatten_value category: :name
|
102
|
-
end
|
103
|
-
after do
|
104
|
-
provider_class.value_provider_options = {}
|
105
|
-
cache_class.value_cache_options = {}
|
106
|
-
end
|
107
|
-
let!(:resource) { provider_class.create(name: 'category_a') }
|
108
|
-
let!(:target_a) { cache_class.create(category_id: resource.id) }
|
109
|
-
let!(:target_other_a) { cache_class.create }
|
110
|
-
context "when cached value is updated" do
|
111
|
-
it "should push the new cache value" do
|
112
|
-
expect {
|
113
|
-
resource.update_attributes(name: 'new category name')
|
114
|
-
}.to change {
|
115
|
-
target_a.reload.category_name
|
116
|
-
}.from('category_a').to('new category name')
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
context "with provider that cannot correctly infer the cache column name" do
|
122
|
-
let(:provider_class) { CategoryTestHarness }
|
123
|
-
let(:cache_class) { NoteTestHarness }
|
124
|
-
before do
|
125
|
-
provider_class.push_flattened_values_for name: :notes
|
126
|
-
cache_class.flatten_value category: :name
|
127
|
-
end
|
128
|
-
after do
|
129
|
-
provider_class.value_provider_options = {}
|
130
|
-
cache_class.value_cache_options = {}
|
131
|
-
end
|
132
|
-
let!(:resource) { provider_class.create(name: 'category_a') }
|
133
|
-
let!(:target_a) { cache_class.create(category_id: resource.id) }
|
134
|
-
let!(:target_other_a) { cache_class.create }
|
135
|
-
context "when cached value is updated" do
|
136
|
-
it "should push the new cache value" do
|
137
|
-
expect {
|
138
|
-
resource.update_attributes(name: 'new category name')
|
139
|
-
}.to raise_error(Flattery::CacheColumnInflectionError)
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
context "with provider having has_many association with cache name inflected via inverse relation" do
|
145
|
-
let(:provider_class) { PersonTestHarness }
|
146
|
-
let(:cache_class) { NoteTestHarness }
|
147
|
-
before do
|
148
|
-
provider_class.push_flattened_values_for email: :notes
|
149
|
-
cache_class.flatten_value person: :email
|
150
|
-
end
|
151
|
-
after do
|
152
|
-
provider_class.value_provider_options = {}
|
153
|
-
cache_class.value_cache_options = {}
|
154
|
-
end
|
155
|
-
let!(:resource) { provider_class.create(username: 'user_a', email: 'email1') }
|
156
|
-
let!(:target_a) { cache_class.create(person_name: resource.username) }
|
157
|
-
let!(:target_other_a) { cache_class.create }
|
158
|
-
context "when cached value is updated" do
|
159
|
-
it "should push the new cache value" do
|
160
|
-
expect {
|
161
|
-
resource.update_attributes(email: 'email2')
|
162
|
-
}.to change {
|
163
|
-
target_a.reload.person_email
|
164
|
-
}.from('email1').to('email2')
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
context "with provider having has_many association with cache name inflected via inverse relation with custom cache column name" do
|
170
|
-
let(:provider_class) { PersonTestHarness }
|
171
|
-
let(:cache_class) { NoteTestHarness }
|
172
|
-
before do
|
173
|
-
provider_class.push_flattened_values_for email: :harness_notes
|
174
|
-
cache_class.flatten_value person: :email, as: :user_email
|
175
|
-
end
|
176
|
-
after do
|
177
|
-
provider_class.value_provider_options = {}
|
178
|
-
cache_class.value_cache_options = {}
|
179
|
-
end
|
180
|
-
let!(:resource) { provider_class.create(username: 'user_a', email: 'email1') }
|
181
|
-
let!(:target_a) { cache_class.create(person_name: resource.username) }
|
182
|
-
let!(:target_other_a) { cache_class.create }
|
183
|
-
context "when cached value is updated" do
|
184
|
-
it "should push the new cache value" do
|
185
|
-
expect {
|
186
|
-
resource.update_attributes(email: 'email2')
|
187
|
-
}.to change {
|
188
|
-
target_a.reload.user_email
|
189
|
-
}.from('email1').to('email2')
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
end
|
195
30
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flattery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-09-
|
12
|
+
date: 2013-09-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -173,13 +173,23 @@ files:
|
|
173
173
|
- flattery.gemspec
|
174
174
|
- lib/flattery.rb
|
175
175
|
- lib/flattery/exception.rb
|
176
|
+
- lib/flattery/settings.rb
|
176
177
|
- lib/flattery/value_cache.rb
|
178
|
+
- lib/flattery/value_cache/processor.rb
|
179
|
+
- lib/flattery/value_cache/settings.rb
|
177
180
|
- lib/flattery/value_provider.rb
|
181
|
+
- lib/flattery/value_provider/processor.rb
|
182
|
+
- lib/flattery/value_provider/settings.rb
|
178
183
|
- lib/flattery/version.rb
|
179
184
|
- spec/spec_helper.rb
|
180
185
|
- spec/support/active_record_fixtures.rb
|
181
186
|
- spec/unit/exception_spec.rb
|
187
|
+
- spec/unit/settings_spec.rb
|
188
|
+
- spec/unit/value_cache/processor_spec.rb
|
189
|
+
- spec/unit/value_cache/settings_spec.rb
|
182
190
|
- spec/unit/value_cache_spec.rb
|
191
|
+
- spec/unit/value_provider/processor_spec.rb
|
192
|
+
- spec/unit/value_provider/settings_spec.rb
|
183
193
|
- spec/unit/value_provider_spec.rb
|
184
194
|
homepage: https://github.com/evendis/flattery
|
185
195
|
licenses:
|
@@ -210,5 +220,10 @@ test_files:
|
|
210
220
|
- spec/spec_helper.rb
|
211
221
|
- spec/support/active_record_fixtures.rb
|
212
222
|
- spec/unit/exception_spec.rb
|
223
|
+
- spec/unit/settings_spec.rb
|
224
|
+
- spec/unit/value_cache/processor_spec.rb
|
225
|
+
- spec/unit/value_cache/settings_spec.rb
|
213
226
|
- spec/unit/value_cache_spec.rb
|
227
|
+
- spec/unit/value_provider/processor_spec.rb
|
228
|
+
- spec/unit/value_provider/settings_spec.rb
|
214
229
|
- spec/unit/value_provider_spec.rb
|