flattery 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -7,14 +7,14 @@ The two main reasons you might want to do this are probably:
7
7
 
8
8
  Hence flattery - a gem that provides a simple declarative method for caching and maintaining such values.
9
9
 
10
- Flattery is primarily intended for use with relational Active::Record storage, and is only tested with sqlite and PostgreSQL.
10
+ Flattery is primarily intended for use with relational ActiveRecord storage, and is only tested with sqlite and PostgreSQL.
11
11
  If you are using NoSQL, you probably wouldn't design your schema in a way for which flattery adds any value - but if you find a situation where this makes sense, then feel free to fork and add the support .. or lobby for it's inclusion!
12
12
 
13
13
  ## Requirements
14
14
 
15
15
  * Ruby 1.9 or 2
16
16
  * Rails 3.x/4.x
17
- * ActiveRecord (only sqlite and PostgreQL tested. Others _should_ work; raise an issue if you find problems)
17
+ * ActiveRecord (only sqlite and PostgreSQL tested. Others _should_ work; raise an issue if you find problems)
18
18
 
19
19
  ## Installation
20
20
 
@@ -34,7 +34,7 @@ Or install it yourself as:
34
34
 
35
35
  ### How to cache values from a :belongs_to association
36
36
 
37
- Given a model with a :belongs_to association, you want to store a (copy/cached) value from the associated record.
37
+ Given a model with a :belongs_to association, you want to store a (copy/cached) value from the associated record. The <tt>Flattery::ValueCache</tt> module is used to define the behaviour.
38
38
 
39
39
  class Category < ActiveRecord::Base
40
40
  has_many :notes, :inverse_of => :category
@@ -48,14 +48,14 @@ Given a model with a :belongs_to association, you want to store a (copy/cached)
48
48
  end
49
49
 
50
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).
51
+ The <tt>:category_name</tt> 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 <tt>:category_name</tt> column to the notes table (with the same type as the <tt>:name</tt> column on the Category table).
53
53
 
54
54
 
55
55
  ### How to cache the value in a specific column name
56
56
 
57
- In the usual case, the cache column name is inferred from the association (e.g. category_name in the example above).
58
- If you want to store in another column name, use the :as option on the +flatten_value+ call:
57
+ In the usual case, the cache column name is inferred from the association (e.g. <tt>:category_name</tt> in the example above).
58
+ If you want to store in another column name, use the <tt>:as</tt> option on the <tt>flatten_value</tt> call:
59
59
 
60
60
  class Note < ActiveRecord::Base
61
61
  belongs_to :category
@@ -68,8 +68,7 @@ Again, you must make sure the column is correctly defined in your schema.
68
68
 
69
69
  ### How to push updates to cached values from the source model
70
70
 
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.
71
+ Given the example above, we have a problem if Category records are updated - the <tt>:category_name</tt> value stored in Notes gets out of sync. The <tt>Flattery::ValueProvider</tt> module fixes this by propagating changes accordingly.
73
72
 
74
73
  class Category < ActiveRecord::Base
75
74
  has_many :notes
@@ -78,13 +77,13 @@ The Flattery::ValueProvider module fixes this by propagating changes accordingly
78
77
  push_flattened_values_for :name => :notes
79
78
  end
80
79
 
81
- This will push changes to Category :name to Notes records (by inference, updating the :category_name value in Notes).
80
+ This will push changes to Category <tt>:name</tt> to Notes records (by inference, updating the <tt>:category_name</tt> value in Notes).
82
81
 
83
82
  ### How to push updates to cached values from the source model to a specific cache column name
84
83
 
85
84
  If the cache column name cannot be inferred correctly, an error will be raised. Inference errors can occur if the inverse association relation cannot be determined.
86
85
 
87
- To 'help' flattery figure out the correct column name, specify the column name with an :as option:
86
+ To help flattery figure out the correct column name, specify the column name with an <tt>:as</tt> option:
88
87
 
89
88
  class Category < ActiveRecord::Base
90
89
  has_many :notes
@@ -93,6 +92,54 @@ To 'help' flattery figure out the correct column name, specify the column name w
93
92
  push_flattened_values_for :name => :notes, :as => 'cat_name'
94
93
  end
95
94
 
95
+ ### How are cached values pushed from the source model?
96
+
97
+ The default mechanism for performing the update of cached values is with the standard ActiveRecord <tt>:update_all</tt> method (scoped to only the affected records). This is done in the <tt>after_update</tt> phase of the [ActiveRecord callback lifecycle](http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html), and does not background the processing by default.
98
+
99
+ This should be fine for modest applications, but if the update will affect many records - especially if there is a high likelihood of read/write contention - then it may need finessing. Flattery allows you to define your own update procedure for these cases - see the next section.
100
+
101
+ ### How to provide a custom method for updating cached values
102
+
103
+ Use the <tt>:method</tt> option to declare the instance method to be used:
104
+
105
+ class Category < ActiveRecord::Base
106
+ has_many :notes
107
+
108
+ include Flattery::ValueProvider
109
+ push_flattened_values_for :name => :notes, :as => 'cat_name', :method => :my_custom_updater
110
+
111
+ # You custom update method definition. Parameters:
112
+ # * +attribute+ is the attribute name that the value is coming from e.g. :name
113
+ # * +new_value+ is the new value that has been set e.g. 'a value that was just set'
114
+ # * +association_name+ is the association that updates need to be pushed to set e.g. :notes
115
+ # * +target_attribute+ is the attribute name that needs to be updated e.g. :cat_name
116
+ def my_custom_updater(attribute,new_value,association_name,target_attribute)
117
+ # implement your custom update algorithm here. It could do some funky batched SQL for example.
118
+ # For now here's just a simple update_all implementation:
119
+ self.send(association_name).update_all(target_attribute => new_value)
120
+ end
121
+ end
122
+
123
+ ### How can I get cached value updates pushed in the background?
124
+
125
+ Flattery has support for getting updates done in the background. This is not the default behaviour, and must be defined for each <tt>Flattery::ValueProvider</tt> declaration.
126
+
127
+ Currently only [Delayed::Job](https://github.com/collectiveidea/delayed_job) is supported. If you want to background with another queue technology, for now the best is to implement this inside a custom update method.
128
+
129
+ #### How to background with Delayed::Job
130
+
131
+ Use the <tt>:background_with</tt> option:
132
+
133
+ class Category < ActiveRecord::Base
134
+ has_many :notes
135
+
136
+ include Flattery::ValueProvider
137
+ push_flattened_values_for :name => :notes, :as => 'cat_name', :background_with => :delayed_job
138
+
139
+ end
140
+
141
+ Note that Delayed::Job is not an explicit dependency of Flattery, so to use Delayed::Job you must have separately added and set it up in your project. Flattery will try to use it if available, and fallback to foreground processing if not.
142
+
96
143
 
97
144
  ## Contributing
98
145
 
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Flattery::VERSION
9
9
  spec.authors = ["Paul Gallagher"]
10
10
  spec.email = ["paul@evendis.com"]
11
- spec.description = %q{Flatten/normalise values from ActiveModel associations}
12
- spec.summary = %q{Flatter your nicely normalised AR models}
11
+ spec.description = %q{a gem to denormalize (flatten) selected ActiveRecord association attributes and automatically keep them in sync with the normal form}
12
+ spec.summary = %q{Flatter your nicely normalized ActiveRecord models}
13
13
  spec.homepage = "https://github.com/evendis/flattery"
14
14
  spec.license = "MIT"
15
15
 
@@ -27,6 +27,7 @@ class Flattery::Settings
27
27
  opt = options.symbolize_keys
28
28
  as_setting = opt.delete(:as).try(:to_s)
29
29
  method_setting = opt.delete(:method).try(:to_sym)
30
+ background_setting = opt.delete(:background_with).try(:to_sym)
30
31
 
31
32
  if from_entity = opt.keys.first
32
33
  cache_options[:from_entity] = from_entity
@@ -34,6 +35,7 @@ class Flattery::Settings
34
35
  end
35
36
  cache_options[:as] = as_setting
36
37
  cache_options[:method] = method_setting if method_setting
38
+ cache_options[:background_with] = background_setting if background_setting
37
39
 
38
40
  cache_options
39
41
  end
@@ -2,8 +2,6 @@ module Flattery::ValueCache
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  included do
5
- class_attribute :value_cache_options
6
- self.value_cache_options = Settings.new(self)
7
5
  before_save Processor.new
8
6
  end
9
7
 
@@ -24,6 +22,18 @@ module Flattery::ValueCache
24
22
  self.value_cache_options.add_setting(options)
25
23
  end
26
24
 
25
+ # Returns the Flattery::ValueCache options value object.
26
+ # It will inherit settings from a parent class if a model hierarchy has been defined
27
+ def value_cache_options
28
+ @value_cache_options ||= if superclass.respond_to?(:value_cache_options)
29
+ my_settings = Settings.new(self)
30
+ my_settings.raw_settings = superclass.value_cache_options.raw_settings.dup
31
+ my_settings
32
+ else
33
+ Settings.new(self)
34
+ end
35
+ end
36
+
27
37
  end
28
38
 
29
39
  end
@@ -2,9 +2,7 @@ module Flattery::ValueProvider
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  included do
5
- class_attribute :value_provider_options
6
- self.value_provider_options = Settings.new(self)
7
- before_update Processor.new
5
+ after_update Processor.new
8
6
  end
9
7
 
10
8
  module ClassMethods
@@ -24,6 +22,18 @@ module Flattery::ValueProvider
24
22
  self.value_provider_options.add_setting(options)
25
23
  end
26
24
 
25
+ # Returns the Flattery::ValueProvider options value object.
26
+ # It will inherit settings from a parent class if a model hierarchy has been defined
27
+ def value_provider_options
28
+ @value_provider_options ||= if superclass.respond_to?(:value_provider_options)
29
+ my_settings = Settings.new(self)
30
+ my_settings.raw_settings = superclass.value_provider_options.raw_settings.dup
31
+ my_settings
32
+ else
33
+ Settings.new(self)
34
+ end
35
+ end
36
+
27
37
  end
28
38
 
29
39
  end
@@ -1,14 +1,18 @@
1
1
  class Flattery::ValueProvider::Processor
2
2
 
3
3
  # Command: pushes cache updates for related changed attributes
4
- def before_update(record)
4
+ def after_update(record)
5
5
  resolved_options!(record.class).each do |key,options|
6
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})
7
+ if target_attribute = options[:as]
8
+ method = options[:method]
9
+ attribute = key.to_sym
10
+ new_value = record.send(key)
11
+ association_name = options[:to_entity]
12
+ if options[:background_with] == :delayed_job && self.respond_to?(:delay)
13
+ self.delay.apply_push(record,method,attribute,new_value,association_name,target_attribute)
14
+ else
15
+ apply_push(record,method,attribute,new_value,association_name,target_attribute)
12
16
  end
13
17
  else
14
18
  raise Flattery::CacheColumnInflectionError.new("#{record.class.name} #{key}: #{options}")
@@ -18,6 +22,16 @@ class Flattery::ValueProvider::Processor
18
22
  true
19
23
  end
20
24
 
25
+ # Command: performs an update for a specific cache setting
26
+ def apply_push(record,method,attribute,new_value,association_name,target_attribute)
27
+ case method
28
+ when :update_all
29
+ record.send(association_name).update_all({target_attribute => new_value})
30
+ else # it is a custom update method
31
+ record.send(method,attribute,new_value,association_name,target_attribute)
32
+ end
33
+ end
34
+
21
35
  # Command: resolves value provider options for +klass+ if required, and returns resolved options
22
36
  def resolved_options!(klass)
23
37
  klass.value_provider_options.settings
@@ -26,6 +26,7 @@ class Flattery::ValueProvider::Settings < Flattery::Settings
26
26
  to_entity = setting[:to_entity]
27
27
 
28
28
  push_method = setting[:method]
29
+ background_with = setting[:background_with]
29
30
  attribute_name = "#{from_entity}"
30
31
 
31
32
  assoc = klass.reflect_on_association(to_entity)
@@ -50,7 +51,8 @@ class Flattery::ValueProvider::Settings < Flattery::Settings
50
51
  {
51
52
  to_entity: to_entity,
52
53
  as: cached_attribute_name,
53
- method: push_method
54
+ method: push_method,
55
+ background_with: background_with
54
56
  }
55
57
  end
56
58
 
@@ -1,3 +1,3 @@
1
1
  module Flattery
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -8,7 +8,10 @@ ActiveRecord::Migration.suppress_messages do
8
8
  t.string :name;
9
9
  t.belongs_to :category;
10
10
  t.string :category_name;
11
+ t.string :category_description;
11
12
  t.string :cat_name;
13
+ t.belongs_to :country;
14
+ t.string :country_name;
12
15
  t.string :person_name;
13
16
  t.string :person_email;
14
17
  t.string :user_email;
@@ -16,6 +19,11 @@ ActiveRecord::Migration.suppress_messages do
16
19
 
17
20
  create_table(:categories, :force => true) do |t|
18
21
  t.string :name;
22
+ t.string :description;
23
+ end
24
+
25
+ create_table(:countries, :force => true) do |t|
26
+ t.string :name;
19
27
  end
20
28
 
21
29
  create_table(:people, :force => true) do |t|
@@ -29,6 +37,7 @@ end
29
37
  # Basic models for testing
30
38
  class Note < ActiveRecord::Base
31
39
  belongs_to :category
40
+ belongs_to :country
32
41
  belongs_to :person, primary_key: "username", foreign_key: "person_name"
33
42
  end
34
43
 
@@ -36,6 +45,10 @@ class Category < ActiveRecord::Base
36
45
  has_many :notes
37
46
  end
38
47
 
48
+ class Country < ActiveRecord::Base
49
+ has_many :notes
50
+ end
51
+
39
52
  class Person < ActiveRecord::Base
40
53
  has_many :notes, primary_key: "username", foreign_key: "person_name", inverse_of: :person
41
54
  end
@@ -51,7 +64,9 @@ module ArHelper
51
64
 
52
65
  def clear_harness_classes
53
66
  Object.send(:remove_const, :ValueProviderHarness) if Object.constants.include?(:ValueProviderHarness)
67
+ Object.send(:remove_const, :ChildValueProviderHarness) if Object.constants.include?(:ChildValueProviderHarness)
54
68
  Object.send(:remove_const, :ValueCacheHarness) if Object.constants.include?(:ValueCacheHarness)
69
+ Object.send(:remove_const, :ChildValueCacheHarness) if Object.constants.include?(:ChildValueCacheHarness)
55
70
  end
56
71
 
57
72
  end
@@ -76,9 +76,16 @@ describe Flattery::Settings do
76
76
  end
77
77
 
78
78
  context "when optional :method specified as Symbols" do
79
- before { settings.add_setting({category: :name, method: :update_all}) }
79
+ before { settings.add_setting({category: :name, method: :my_custom_updater}) }
80
80
  its(:raw_settings) { should eql([
81
- { from_entity: :category, to_entity: :name, as: nil, method: :update_all }
81
+ { from_entity: :category, to_entity: :name, as: nil, method: :my_custom_updater }
82
+ ]) }
83
+ end
84
+
85
+ context "when optional :background_with specified as Symbols" do
86
+ before { settings.add_setting({category: :name, background_with: :delayed_job}) }
87
+ its(:raw_settings) { should eql([
88
+ { from_entity: :category, to_entity: :name, as: nil, background_with: :delayed_job }
82
89
  ]) }
83
90
  end
84
91
 
@@ -29,4 +29,53 @@ describe Flattery::ValueCache::Settings do
29
29
  end
30
30
  end
31
31
 
32
+ context "with inherited model definitions and ValueCache defined in the parent" do
33
+ let!(:parent_cache_class) do
34
+ class ::ValueCacheHarness < Note
35
+ include Flattery::ValueCache
36
+ flatten_value category: :name
37
+ end
38
+ ValueCacheHarness
39
+ end
40
+ let!(:child_cache_class) do
41
+ class ::ChildValueCacheHarness < ::ValueCacheHarness
42
+ flatten_value country: :name
43
+ end
44
+ ChildValueCacheHarness
45
+ end
46
+ context "before resolution" do
47
+ describe "parent" do
48
+ let(:settings) { parent_cache_class.value_cache_options }
49
+ its(:raw_settings) { should eql([
50
+ {from_entity: :category, to_entity: :name, as: nil}
51
+ ]) }
52
+ end
53
+ describe "child" do
54
+ let(:settings) { child_cache_class.value_cache_options }
55
+ its(:raw_settings) { should eql([
56
+ {from_entity: :category, to_entity: :name, as: nil},
57
+ {from_entity: :country, to_entity: :name, as: nil}
58
+ ]) }
59
+ end
60
+ end
61
+ context "after resolution" do
62
+ before { parent_cache_class.value_cache_options.settings && child_cache_class.value_cache_options.settings}
63
+ describe "parent" do
64
+ let(:settings) { parent_cache_class.value_cache_options }
65
+ its(:resolved) { should be_true }
66
+ its(:settings) { should eql({
67
+ "category_name"=>{from_entity: :category, to_entity: :name, changed_on: ["category_id"]}
68
+ }) }
69
+ end
70
+ describe "child" do
71
+ let(:settings) { child_cache_class.value_cache_options }
72
+ its(:resolved) { should be_true }
73
+ its(:settings) { should eql({
74
+ "category_name"=>{from_entity: :category, to_entity: :name, changed_on: ["category_id"]},
75
+ "country_name"=>{from_entity: :country, to_entity: :name, changed_on: ["country_id"]}
76
+ }) }
77
+ end
78
+ end
79
+ end
80
+
32
81
  end
@@ -2,6 +2,7 @@ require 'spec_helper.rb'
2
2
 
3
3
  # Test caching in a range of actual scenarios
4
4
  describe Flattery::ValueProvider::Processor do
5
+ let(:processor_class) { Flattery::ValueProvider::Processor }
5
6
 
6
7
  context "with provider having simple has_many association and explicit cache_column name" do
7
8
  let(:provider_class) do
@@ -127,4 +128,115 @@ describe Flattery::ValueProvider::Processor do
127
128
  end
128
129
  end
129
130
 
131
+ context "with a custom update method" do
132
+ let(:provider_class) do
133
+ class ::ValueProviderHarness < Category
134
+ include Flattery::ValueProvider
135
+ push_flattened_values_for name: :notes, as: :category_name, method: :my_updater_method
136
+ def my_updater_method(attribute,new_value,association_name,target_attribute)
137
+ self.send(association_name).update_all(target_attribute => "#{new_value} (set by my_updater_method)")
138
+ end
139
+ end
140
+ ValueProviderHarness
141
+ end
142
+
143
+ let(:cache_class) do
144
+ class ::ValueCacheHarness < Note
145
+ include Flattery::ValueCache
146
+ flatten_value category: :name
147
+ end
148
+ ValueCacheHarness
149
+ end
150
+
151
+ let!(:resource) { provider_class.create(name: 'category_a') }
152
+ let!(:target_a) { cache_class.create(category_id: resource.id) }
153
+ let!(:target_other_a) { cache_class.create }
154
+ context "when cached value is updated" do
155
+ it "should push the new cache value" do
156
+ expect {
157
+ resource.update_attributes(name: 'new category name')
158
+ }.to change {
159
+ target_a.reload.category_name
160
+ }.from('category_a').to('new category name (set by my_updater_method)')
161
+ end
162
+ end
163
+ end
164
+
165
+ context "with delayed job support stubbed" do
166
+ before do
167
+ processor_class.any_instance.stub(:delay)
168
+ end
169
+
170
+ context "and background processing requested" do
171
+
172
+ let(:provider_class) do
173
+ class ::ValueProviderHarness < Category
174
+ include Flattery::ValueProvider
175
+ push_flattened_values_for name: :notes, as: :category_name, background_with: :delayed_job
176
+ end
177
+ ValueProviderHarness
178
+ end
179
+
180
+ let(:cache_class) do
181
+ class ::ValueCacheHarness < Note
182
+ include Flattery::ValueCache
183
+ flatten_value category: :name
184
+ end
185
+ ValueCacheHarness
186
+ end
187
+
188
+ let!(:resource) { provider_class.create(name: 'category_a') }
189
+ let!(:target_a) { cache_class.create(category_id: resource.id) }
190
+ let!(:target_other_a) { cache_class.create }
191
+
192
+ it "should update via delay" do
193
+ processor = double()
194
+ processor.should_receive(:apply_push)
195
+ processor_class.any_instance.should_receive(:delay).and_return(processor)
196
+ resource.update_attributes(name: 'new category name')
197
+ end
198
+ end
199
+ end
200
+
201
+ context "with delayed job support mocked" do
202
+ before do
203
+ processor_class.send(:define_method,:delay) { self }
204
+ end
205
+ after do
206
+ processor_class.send(:undef_method,:delay)
207
+ end
208
+
209
+ context "and background processing requested" do
210
+
211
+ let(:provider_class) do
212
+ class ::ValueProviderHarness < Category
213
+ include Flattery::ValueProvider
214
+ push_flattened_values_for name: :notes, as: :category_name, background_with: :delayed_job
215
+ end
216
+ ValueProviderHarness
217
+ end
218
+
219
+ let(:cache_class) do
220
+ class ::ValueCacheHarness < Note
221
+ include Flattery::ValueCache
222
+ flatten_value category: :name
223
+ end
224
+ ValueCacheHarness
225
+ end
226
+
227
+ let!(:resource) { provider_class.create(name: 'category_a') }
228
+ let!(:target_a) { cache_class.create(category_id: resource.id) }
229
+ let!(:target_other_a) { cache_class.create }
230
+
231
+ it "should push the new cache value" do
232
+ expect {
233
+ resource.update_attributes(name: 'new category name')
234
+ }.to change {
235
+ target_a.reload.category_name
236
+ }.from('category_a').to('new category name')
237
+ end
238
+
239
+ end
240
+ end
241
+
130
242
  end
@@ -25,10 +25,58 @@ describe Flattery::ValueProvider::Settings do
25
25
  before { settings.settings }
26
26
  its(:resolved) { should be_true }
27
27
  its(:settings) { should eql({
28
- "name"=>{to_entity: :notes, as: :category_name, method: :update_all}
28
+ "name"=>{to_entity: :notes, as: :category_name, method: :update_all, background_with: nil}
29
29
  }) }
30
30
  end
31
31
  end
32
32
 
33
+ context "with inherited model definitions and ValueProvider defined in the parent" do
34
+ let!(:parent_provider_class) do
35
+ class ::ValueProviderHarness < Category
36
+ include Flattery::ValueProvider
37
+ push_flattened_values_for name: :notes, as: :category_name
38
+ end
39
+ ValueProviderHarness
40
+ end
41
+ let!(:child_provider_class) do
42
+ class ::ChildValueProviderHarness < ::ValueProviderHarness
43
+ push_flattened_values_for description: :notes, as: :category_description
44
+ end
45
+ ChildValueProviderHarness
46
+ end
47
+ context "before resolution" do
48
+ describe "parent" do
49
+ let(:settings) { parent_provider_class.value_provider_options }
50
+ its(:raw_settings) { should eql([
51
+ {from_entity: :name, to_entity: :notes, as: 'category_name', method: :update_all}
52
+ ]) }
53
+ end
54
+ describe "child" do
55
+ let(:settings) { child_provider_class.value_provider_options }
56
+ its(:raw_settings) { should eql([
57
+ {from_entity: :name, to_entity: :notes, as: 'category_name', method: :update_all},
58
+ {from_entity: :description, to_entity: :notes, as: 'category_description', method: :update_all}
59
+ ]) }
60
+ end
61
+ end
62
+ context "after resolution" do
63
+ before { parent_provider_class.value_provider_options.settings && child_provider_class.value_provider_options.settings}
64
+ describe "parent" do
65
+ let(:settings) { parent_provider_class.value_provider_options }
66
+ its(:resolved) { should be_true }
67
+ its(:settings) { should eql({
68
+ "name"=>{to_entity: :notes, as: :category_name, method: :update_all, background_with: nil}
69
+ }) }
70
+ end
71
+ describe "child" do
72
+ let(:settings) { child_provider_class.value_provider_options }
73
+ its(:resolved) { should be_true }
74
+ its(:settings) { should eql({
75
+ "name"=>{to_entity: :notes, as: :category_name, method: :update_all, background_with: nil},
76
+ "description"=>{to_entity: :notes, as: :category_description, method: :update_all, background_with: nil}
77
+ }) }
78
+ end
79
+ end
80
+ end
33
81
 
34
82
  end
@@ -14,15 +14,15 @@ describe Flattery::ValueProvider do
14
14
  its(:included_modules) { should include(Flattery::ValueProvider) }
15
15
  its(:value_provider_options) { should be_a(Flattery::ValueProvider::Settings) }
16
16
 
17
- describe "#before_update" do
17
+ describe "#after_update" do
18
18
  let(:processor_class) { Flattery::ValueProvider::Processor }
19
19
  it "should not be called when record created" do
20
- processor_class.any_instance.should_receive(:before_update).never
20
+ processor_class.any_instance.should_receive(:after_update).never
21
21
  provider_class.create!
22
22
  end
23
23
  it "should be called when record updated" do
24
24
  instance = provider_class.create!
25
- processor_class.any_instance.should_receive(:before_update).and_return(true)
25
+ processor_class.any_instance.should_receive(:after_update).and_return(true)
26
26
  instance.save
27
27
  end
28
28
  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.3
4
+ version: 0.0.4
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-10 00:00:00.000000000 Z
12
+ date: 2013-09-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -155,7 +155,8 @@ dependencies:
155
155
  - - ! '>='
156
156
  - !ruby/object:Gem::Version
157
157
  version: 1.3.2
158
- description: Flatten/normalise values from ActiveModel associations
158
+ description: a gem to denormalize (flatten) selected ActiveRecord association attributes
159
+ and automatically keep them in sync with the normal form
159
160
  email:
160
161
  - paul@evendis.com
161
162
  executables: []
@@ -215,7 +216,7 @@ rubyforge_project:
215
216
  rubygems_version: 1.8.25
216
217
  signing_key:
217
218
  specification_version: 3
218
- summary: Flatter your nicely normalised AR models
219
+ summary: Flatter your nicely normalized ActiveRecord models
219
220
  test_files:
220
221
  - spec/spec_helper.rb
221
222
  - spec/support/active_record_fixtures.rb