flattery 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|