changeling 0.0.7 → 0.1.0

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.
@@ -2,11 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  module RailsApp
4
4
  class Application < Rails::Application
5
- # app config here
6
- # config.secret_token = '572c86f5ede338bd8aba8dae0fd3a326aabababc98d1e6ce34b9f5'
7
- # routes.draw do
8
- # resources :blog_posts
9
- # end
10
5
  end
11
6
 
12
7
  class ApplicationController < ActionController::Base
@@ -0,0 +1,10 @@
1
+ class AsyncBlogPost
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+ include Changeling::Async::Trackling
5
+ include Changeling::Probeling
6
+
7
+ field :title
8
+ field :content
9
+ field :public, :type => Boolean
10
+ end
@@ -0,0 +1,5 @@
1
+ # Setup for the fields etc. happens in spec_helper.
2
+ class AsyncBlogPostActiveRecord < ActiveRecord::Base
3
+ include Changeling::Async::Trackling
4
+ include Changeling::Probeling
5
+ end
@@ -0,0 +1,9 @@
1
+ class AsyncBlogPostNoTimestamp
2
+ include Mongoid::Document
3
+ include Changeling::Async::Trackling
4
+ include Changeling::Probeling
5
+
6
+ field :title
7
+ field :content
8
+ field :public, :type => Boolean
9
+ end
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+ require 'resque'
3
+ require 'sidekiq'
4
+
5
+ describe Changeling::Async::Trackling do
6
+ before(:all) do
7
+ @klass = Changeling::Models::Logling
8
+ end
9
+
10
+ async_models.each_pair do |model, args|
11
+ before(:each) do
12
+ @object = model.new(args[:options])
13
+ end
14
+
15
+ context "#{model}" do
16
+ describe "without Sidekiq or Resque" do
17
+ before(:each) do
18
+ hide_const("Resque")
19
+ hide_const("Sidekiq")
20
+ end
21
+
22
+ it "should raise the AsyncGemRequired exception" do
23
+ expect { @object.async_save_logling }.to raise_error(Changeling::Exceptions::AsyncGemRequired)
24
+ end
25
+ end
26
+
27
+ describe "with Sidekiq" do
28
+ before(:each) do
29
+ hide_const("Resque")
30
+ end
31
+
32
+ describe "callbacks" do
33
+ before(:each) do
34
+ @logling = @klass.new(@object)
35
+ end
36
+
37
+ it "should not create a logling when doing the initial save of a new object" do
38
+ @klass.should_not_receive(:new)
39
+ @object.run_callbacks(:create)
40
+ end
41
+
42
+ context "after_update" do
43
+ it "should queue a logling to be made when updating an object and changes are made" do
44
+ @object.stub(:changes).and_return({ :field => 'value' })
45
+ @klass.should_receive(:new).and_return(@logling)
46
+ Changeling::Async::SidekiqWorker.should_receive(:perform_async).with(@logling.to_indexed_json)
47
+ end
48
+
49
+ it "should not queue a logling to be made when updating an object and changes are empty" do
50
+ @object.stub(:changes).and_return({})
51
+ @klass.should_not_receive(:new)
52
+ Changeling::Async::SidekiqWorker.should_not_receive(:perform_async)
53
+ end
54
+
55
+ it "should not queue a logling to be made when updating an object and no changes have been made" do
56
+ @object.stub(:changes).and_return(nil)
57
+ @klass.should_not_receive(:create)
58
+ Changeling::Async::SidekiqWorker.should_not_receive(:perform_async)
59
+ end
60
+
61
+ after(:each) do
62
+ @object.run_callbacks(:update)
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "with Resque" do
69
+ before(:each) do
70
+ hide_const("Sidekiq")
71
+ end
72
+
73
+ describe "callbacks" do
74
+ before(:each) do
75
+ @logling = @klass.new(@object)
76
+ end
77
+
78
+ it "should not create a logling when doing the initial save of a new object" do
79
+ @klass.should_not_receive(:new)
80
+ @object.run_callbacks(:create)
81
+ end
82
+
83
+ context "after_update" do
84
+ it "should queue a logling to be made when updating an object and changes are made" do
85
+ @object.stub(:changes).and_return({ :field => 'value' })
86
+ @klass.should_receive(:new).and_return(@logling)
87
+ Resque.should_receive(:enqueue).with(Changeling::Async::ResqueWorker, @logling.to_indexed_json)
88
+ end
89
+
90
+ it "should not queue a logling to be made when updating an object and changes are empty" do
91
+ @object.stub(:changes).and_return({})
92
+ @klass.should_not_receive(:new)
93
+ Resque.should_not_receive(:enqueue)
94
+ end
95
+
96
+ it "should not queue a logling to be made when updating an object and no changes have been made" do
97
+ @object.stub(:changes).and_return(nil)
98
+ @klass.should_not_receive(:create)
99
+ Resque.should_not_receive(:enqueue)
100
+ end
101
+
102
+ after(:each) do
103
+ @object.run_callbacks(:update)
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "with Sidekiq and Resque" do
110
+ describe "callbacks" do
111
+ before(:each) do
112
+ @logling = @klass.new(@object)
113
+ end
114
+
115
+ it "should prefer Sidekiq to Resque" do
116
+ @object.stub(:changes).and_return({ :field => 'value' })
117
+ @klass.should_receive(:new).and_return(@logling)
118
+ Changeling::Async::SidekiqWorker.should_receive(:perform_async).with(@logling.to_indexed_json)
119
+ Resque.should_not_receive(:enqueue)
120
+ @object.run_callbacks(:update)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -7,8 +7,6 @@ describe Changeling::Models::Logling do
7
7
 
8
8
  # .models is defined in spec_helper.
9
9
  models.each_pair do |model, args|
10
- puts "Testing #{model} now."
11
-
12
10
  before(:each) do
13
11
  @object = model.new(args[:options])
14
12
  @changes = args[:changes]
@@ -17,287 +15,289 @@ describe Changeling::Models::Logling do
17
15
  @logling = @klass.new(@object)
18
16
  end
19
17
 
20
- context "Class Methods" do
21
- describe ".create" do
22
- before(:each) do
18
+ context "#{model}" do
19
+ context "Class Methods" do
20
+ describe ".create" do
21
+ before(:each) do
23
22
 
24
- @klass.should_receive(:new).with(@object).and_return(@logling)
25
- end
23
+ @klass.should_receive(:new).with(@object).and_return(@logling)
24
+ end
26
25
 
27
- it "should call new with it's parameters then save the initialized logling" do
28
- @logling.should_receive(:save)
26
+ it "should call new with it's parameters then save the initialized logling" do
27
+ @logling.should_receive(:save)
29
28
 
30
- @klass.create(@object)
29
+ @klass.create(@object)
30
+ end
31
31
  end
32
- end
33
32
 
34
- describe ".new" do
35
- context "when passed object is a ElasticSearch response hash" do
36
- before(:each) do
37
- @object = {
38
- "klass"=>"BlogPost",
39
- "oid"=>"50b8355f7a93d04908000001",
40
- "modifications"=>"{\"public\":[true,false]}",
41
- "modified_at"=>"2012-11-29T20:26:07-08:00"
42
- }
43
-
44
- @logling = @klass.new(@object)
45
- @changes = JSON.parse(@object['modifications'])
46
- @before, @after = @klass.parse_changes(@changes)
47
- @modified_time = DateTime.parse(@object['modified_at'])
48
- end
33
+ describe ".new" do
34
+ context "when passed object is a ElasticSearch response hash" do
35
+ before(:each) do
36
+ @object = {
37
+ "klass"=>"BlogPost",
38
+ "oid"=>"50b8355f7a93d04908000001",
39
+ "modifications"=>"{\"public\":[true,false]}",
40
+ "modified_at"=>"2012-11-29T20:26:07-08:00"
41
+ }
42
+
43
+ @logling = @klass.new(@object)
44
+ @changes = JSON.parse(@object['modifications'])
45
+ @before, @after = @klass.parse_changes(@changes)
46
+ @modified_time = DateTime.parse(@object['modified_at'])
47
+ end
49
48
 
50
- it "should set klass as the returned klass value" do
51
- @logling.klass.should == @object['klass'].constantize
52
- end
49
+ it "should set klass as the returned klass value" do
50
+ @logling.klass.should == @object['klass'].constantize
51
+ end
53
52
 
54
- it "should set oid as the returned oid value" do
55
- @logling.oid.should == @object['oid']
56
- end
53
+ it "should set oid as the returned oid value" do
54
+ @logling.oid.should == @object['oid']
55
+ end
57
56
 
58
- it "should convert oid to an integer if it's supposed to be an integer" do
59
- @object["oid"] = "1"
60
- @klass.new(@object).oid.should == @object["oid"].to_i
61
- end
57
+ it "should convert oid to an integer if it's supposed to be an integer" do
58
+ @object["oid"] = "1"
59
+ @klass.new(@object).oid.should == @object["oid"].to_i
60
+ end
62
61
 
63
- it "should set the modifications as the incoming changes parameter" do
64
- @logling.modifications.should == @changes
65
- end
62
+ it "should set the modifications as the incoming changes parameter" do
63
+ @logling.modifications.should == @changes
64
+ end
66
65
 
67
- it "should set the modified_fields as the keys of the modifications" do
68
- @logling.modified_fields.should == @changes.keys
69
- end
66
+ it "should set the modified_fields as the keys of the modifications" do
67
+ @logling.modified_fields.should == @changes.keys
68
+ end
70
69
 
71
- it "should set before and after based on .parse_changes" do
72
- @logling.before.should == @before
73
- @logling.after.should == @after
74
- end
70
+ it "should set before and after based on .parse_changes" do
71
+ @logling.before.should == @before
72
+ @logling.after.should == @after
73
+ end
75
74
 
76
- it "should set modified_at as the parsed version of the returned modified_at value" do
77
- @logling.modified_at.should == @modified_time
75
+ it "should set modified_at as the parsed version of the returned modified_at value" do
76
+ @logling.modified_at.should == @modified_time
77
+ end
78
78
  end
79
- end
80
79
 
81
- context "when passed object is a real object" do
82
- before(:each) do
83
- @before, @after = @klass.parse_changes(@changes)
84
- end
80
+ context "when passed object is a real object" do
81
+ before(:each) do
82
+ @before, @after = @klass.parse_changes(@changes)
83
+ end
85
84
 
86
- it "should set klass as the object's class" do
87
- @logling.klass.should == @object.class
88
- end
85
+ it "should set klass as the object's class" do
86
+ @logling.klass.should == @object.class
87
+ end
89
88
 
90
- it "should set oid as the object's ID" do
91
- @logling.oid.should == @object.id
92
- end
89
+ it "should set oid as the object's ID" do
90
+ @logling.oid.should == @object.id
91
+ end
93
92
 
94
- it "should set the modifications as the incoming changes parameter" do
95
- @logling.modifications.should == @changes
96
- end
93
+ it "should set the modifications as the incoming changes parameter" do
94
+ @logling.modifications.should == @changes
95
+ end
97
96
 
98
- it "should set before and after based on .parse_changes" do
99
- @logling.before.should == @before
100
- @logling.after.should == @after
101
- end
97
+ it "should set before and after based on .parse_changes" do
98
+ @logling.before.should == @before
99
+ @logling.after.should == @after
100
+ end
102
101
 
103
- it "should set the modified_fields as the keys of the modifications" do
104
- @logling.modified_fields.should == @changes.keys
105
- end
102
+ it "should set the modified_fields as the keys of the modifications" do
103
+ @logling.modified_fields.should == @changes.keys
104
+ end
106
105
 
107
- it "should ignore changes that are nil" do
108
- changes = {}
106
+ it "should ignore changes that are nil" do
107
+ changes = {}
109
108
 
110
- @changes.keys.each do |key|
111
- changes[key] = nil
112
- end
109
+ @changes.keys.each do |key|
110
+ changes[key] = nil
111
+ end
113
112
 
114
- @object.stub(:changes).and_return(changes)
113
+ @object.stub(:changes).and_return(changes)
115
114
 
116
- @klass.new(@object).modifications.should be_empty
117
- end
115
+ @klass.new(@object).modifications.should be_empty
116
+ end
118
117
 
119
- it "should set modified_at to the object's time of update if the object responds to the updated_at method" do
120
- @object.should_receive(:respond_to?).with(:updated_at).and_return(true)
118
+ it "should set modified_at to the object's time of update if the object responds to the updated_at method" do
119
+ @object.should_receive(:respond_to?).with(:updated_at).and_return(true)
121
120
 
122
- # Setting up a variable to prevent test flakiness from passing time.
123
- time = Time.now
124
- @object.stub(:updated_at).and_return(time)
121
+ # Setting up a variable to prevent test flakiness from passing time.
122
+ time = Time.now
123
+ @object.stub(:updated_at).and_return(time)
125
124
 
126
- # Create a new logling to trigger the initialize method
127
- @logling = @klass.new(@object)
128
- @logling.modified_at.should == @object.updated_at
129
- end
125
+ # Create a new logling to trigger the initialize method
126
+ @logling = @klass.new(@object)
127
+ @logling.modified_at.should == @object.updated_at
128
+ end
130
129
 
131
- it "should set modified_at to the current time if the object doesn't respond to updated_at" do
132
- @object.should_receive(:respond_to?).with(:updated_at).and_return(false)
130
+ it "should set modified_at to the current time if the object doesn't respond to updated_at" do
131
+ @object.should_receive(:respond_to?).with(:updated_at).and_return(false)
133
132
 
134
- # Setting up a variable to prevent test flakiness from passing time.
135
- time = Time.now
136
- Time.stub(:now).and_return(time)
133
+ # Setting up a variable to prevent test flakiness from passing time.
134
+ time = Time.now
135
+ Time.stub(:now).and_return(time)
137
136
 
138
- # Create a new logling to trigger the initialize method
139
- @logling = @klass.new(@object)
140
- @logling.modified_at.should == time
137
+ # Create a new logling to trigger the initialize method
138
+ @logling = @klass.new(@object)
139
+ @logling.modified_at.should == time
140
+ end
141
141
  end
142
142
  end
143
- end
144
143
 
145
- describe ".parse_changes" do
146
- before(:each) do
147
- @object.save!
144
+ describe ".parse_changes" do
145
+ before(:each) do
146
+ @object.save!
148
147
 
149
- @before = @object.attributes.select { |attr| @changes.keys.include?(attr) }
148
+ @before = @object.attributes.select { |attr| @changes.keys.include?(attr) }
150
149
 
151
- @changes.each_pair do |k, v|
152
- @object.send("#{k}=", v[1])
153
- end
154
-
155
- @after = @object.attributes.select { |attr| @changes.keys.include?(attr) }
156
- end
150
+ @changes.each_pair do |k, v|
151
+ @object.send("#{k}=", v[1])
152
+ end
157
153
 
158
- it "should correctly match the before and after states of the object" do
159
- @klass.parse_changes(@object.changes).should == [@before, @after]
160
- end
161
- end
154
+ @after = @object.attributes.select { |attr| @changes.keys.include?(attr) }
155
+ end
162
156
 
163
- describe ".klassify" do
164
- it "should return the object's class as an underscored string" do
165
- @klass.klassify(@object).should == @object.class.to_s.underscore
157
+ it "should correctly match the before and after states of the object" do
158
+ @klass.parse_changes(@object.changes).should == [@before, @after]
159
+ end
166
160
  end
167
- end
168
161
 
169
- describe ".records_for" do
170
- before(:each) do
171
- @search = Changeling::Support::Search
172
- @results = []
162
+ describe ".klassify" do
163
+ it "should return the object's class as an underscored string" do
164
+ @klass.klassify(@object).should == @object.class.to_s.underscore
165
+ end
173
166
  end
174
167
 
175
- context "length parameter" do
168
+ describe ".records_for" do
176
169
  before(:each) do
177
- @search.stub(:find_by).and_return(@results)
170
+ @search = Changeling::Support::Search
171
+ @results = []
178
172
  end
179
173
 
180
- it "should only return the amount specified" do
181
- num = 5
182
- @results.should_receive(:take).with(num).and_return([])
183
- @klass.records_for(@object, 5)
174
+ context "length parameter" do
175
+ before(:each) do
176
+ @search.stub(:find_by).and_return(@results)
177
+ end
178
+
179
+ it "should only return the amount specified" do
180
+ num = 5
181
+ @results.should_receive(:take).with(num).and_return([])
182
+ @klass.records_for(@object, 5)
183
+ end
184
+
185
+ it "should return all if no amount is specified" do
186
+ @results.should_not_receive(:take)
187
+ @klass.records_for(@object)
188
+ end
184
189
  end
185
190
 
186
- it "should return all if no amount is specified" do
187
- @results.should_not_receive(:take)
191
+ it "should search with filters on the klass and oid" do
192
+ @search.should_receive(:find_by).with(hash_including({
193
+ :filters => [
194
+ { :klass => @klass.klassify(@object) },
195
+ { :oid => @object.id.to_s }
196
+ ]
197
+ })).and_return(@results)
188
198
  @klass.records_for(@object)
189
199
  end
190
- end
191
200
 
192
- it "should search with filters on the klass and oid" do
193
- @search.should_receive(:find_by).with(hash_including({
194
- :filters => [
195
- { :klass => @klass.klassify(@object) },
196
- { :oid => @object.id.to_s }
197
- ]
198
- })).and_return(@results)
199
- @klass.records_for(@object)
200
- end
201
-
202
- it "should search with a filter on the field if one is passed in" do
203
- @search.should_receive(:find_by).with(hash_including(
204
- :filters => [
205
- { :klass => @klass.klassify(@object) },
206
- { :oid => @object.id.to_s },
207
- { :modified_fields => "field" }
208
- ]
209
- )).and_return(@results)
210
- @klass.records_for(@object, nil, "field")
211
- end
201
+ it "should search with a filter on the field if one is passed in" do
202
+ @search.should_receive(:find_by).with(hash_including(
203
+ :filters => [
204
+ { :klass => @klass.klassify(@object) },
205
+ { :oid => @object.id.to_s },
206
+ { :modified_fields => "field" }
207
+ ]
208
+ )).and_return(@results)
209
+ @klass.records_for(@object, nil, "field")
210
+ end
212
211
 
213
- it "should sort by descending modified_at" do
214
- @search.should_receive(:find_by).with(hash_including({
215
- :sort => {
216
- :field => :modified_at,
217
- :direction => :desc
218
- }
219
- })).and_return(@results)
220
- @klass.records_for(@object, nil)
212
+ it "should sort by descending modified_at" do
213
+ @search.should_receive(:find_by).with(hash_including({
214
+ :sort => {
215
+ :field => :modified_at,
216
+ :direction => :desc
217
+ }
218
+ })).and_return(@results)
219
+ @klass.records_for(@object, nil)
220
+ end
221
221
  end
222
222
  end
223
- end
224
223
 
225
- context "Instance Methods" do
226
- before(:each) do
227
- # Stub :id so that it doesn't screw up these test's expectations.
228
- @logling.stub(:id).and_return(1)
229
- end
230
-
231
- describe ".to_indexed_json" do
232
- it "should include the object's klass attribute" do
233
- @logling.should_receive(:klass)
224
+ context "Instance Methods" do
225
+ before(:each) do
226
+ # Stub :id so that it doesn't screw up these test's expectations.
227
+ @logling.stub(:id).and_return(1)
234
228
  end
235
229
 
236
- it "should include the object's oid attribute" do
237
- @logling.should_receive(:oid)
238
- end
230
+ describe ".to_indexed_json" do
231
+ it "should include the object's klass attribute" do
232
+ @logling.should_receive(:klass)
233
+ end
239
234
 
240
- it "should include the object's modifications attribute" do
241
- @logling.should_receive(:modifications)
242
- end
235
+ it "should include the object's oid attribute" do
236
+ @logling.should_receive(:oid)
237
+ end
243
238
 
244
- it "should convert the object's modifications attribute to JSON" do
245
- mods = {}
246
- @logling.should_receive(:modifications).and_return(mods)
247
- mods.should_receive(:to_json)
248
- end
239
+ it "should include the object's modifications attribute" do
240
+ @logling.should_receive(:modifications)
241
+ end
249
242
 
250
- it "should include the object's modified_at attribute" do
251
- @logling.should_receive(:modified_at)
252
- end
243
+ it "should convert the object's modifications attribute to JSON" do
244
+ mods = {}
245
+ @logling.should_receive(:modifications).and_return(mods)
246
+ mods.should_receive(:to_json)
247
+ end
253
248
 
254
- it "should include an array of the object's modified fields" do
255
- @logling.should_receive(:modified_fields)
256
- end
249
+ it "should include the object's modified_at attribute" do
250
+ @logling.should_receive(:modified_at)
251
+ end
257
252
 
258
- after(:each) do
259
- @logling.to_indexed_json
260
- end
261
- end
253
+ it "should include an array of the object's modified fields" do
254
+ @logling.should_receive(:modified_fields)
255
+ end
262
256
 
263
- describe ".as_json" do
264
- before(:each) do
265
- @json = @logling.as_json
257
+ after(:each) do
258
+ @logling.to_indexed_json
259
+ end
266
260
  end
267
261
 
268
- it "should include the object's klass attribute" do
269
- @json[:class].should == @object.class
270
- end
262
+ describe ".as_json" do
263
+ before(:each) do
264
+ @json = @logling.as_json
265
+ end
271
266
 
272
- it "should include the object's oid attribute" do
273
- @json[:oid].should == @object.id
274
- end
267
+ it "should include the object's klass attribute" do
268
+ @json[:class].should == @object.class
269
+ end
275
270
 
276
- it "should include the object's modifications attribute" do
277
- @json[:modifications].should == @changes
278
- end
271
+ it "should include the object's oid attribute" do
272
+ @json[:oid].should == @object.id
273
+ end
279
274
 
280
- it "should include the object's modified_at attribute" do
281
- @json[:modified_at].should == @logling.modified_at
282
- end
275
+ it "should include the object's modifications attribute" do
276
+ @json[:modifications].should == @changes
277
+ end
283
278
 
284
- after(:each) do
285
- @logling.to_indexed_json
286
- end
287
- end
279
+ it "should include the object's modified_at attribute" do
280
+ @json[:modified_at].should == @logling.modified_at
281
+ end
288
282
 
289
- describe ".save" do
290
- it "should update the ElasticSearch index" do
291
- @logling.should_receive(:update_index)
283
+ after(:each) do
284
+ @logling.to_indexed_json
285
+ end
292
286
  end
293
287
 
294
- it "should not update the index if there are no changes" do
295
- @logling.stub(:modifications).and_return({})
296
- @logling.should_not_receive(:_run_save_callbacks)
297
- end
288
+ describe ".save" do
289
+ it "should update the ElasticSearch index" do
290
+ @logling.should_receive(:update_index)
291
+ end
298
292
 
299
- after(:each) do
300
- @logling.save
293
+ it "should not update the index if there are no changes" do
294
+ @logling.stub(:modifications).and_return({})
295
+ @logling.should_not_receive(:_run_save_callbacks)
296
+ end
297
+
298
+ after(:each) do
299
+ @logling.save
300
+ end
301
301
  end
302
302
  end
303
303
  end