mongar 0.0.4

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.
@@ -0,0 +1,373 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "Mongar::Replica" do
4
+ before do
5
+ @replica = Mongar::Replica.new
6
+ end
7
+
8
+ describe "#run" do
9
+ before do
10
+ class Client
11
+ attr_accessor :name, :employee_count
12
+ def initialize(args)
13
+ args.each do |key, value|
14
+ self.send(:"#{key}=", value)
15
+ end
16
+ end
17
+ end
18
+
19
+ @collection = Mongar::Mongo::Collection.new(:name => "clients")
20
+ @replica = Mongar::Replica.new(:source => Client, :destination => @collection)
21
+ @replica.column :name do
22
+ primary_index
23
+ end
24
+ @replica.column :employee_count
25
+
26
+ @mongo = Mongar::Mongo.new
27
+ Mongar::Mongo.databases[:default] = @mongo
28
+
29
+ @last_replicated_time = Time.now - 86400
30
+ @collection.stub!(:last_replicated_at).and_return(@last_replicated_time)
31
+
32
+ @created_client1 = Client.new(:name => "Otis Co", :employee_count => 600)
33
+ end
34
+
35
+ context "requiring a full refresh" do
36
+ before do
37
+ @replica.stub!(:find).with(:created, Time.parse("1/1/1902 00:00:00")).and_return([@created_client1])
38
+ @replica.stub!(:do_full_refresh?).and_return(true)
39
+
40
+ @collection.stub!(:create_or_update!)
41
+ @collection.stub!(:mark_all_items_pending_deletion!)
42
+ @collection.stub!(:delete_all_items_pending_deletion!)
43
+ @collection.stub!(:last_replicated_at=)
44
+ end
45
+ it "should create or update the items in the destination database" do
46
+ @collection.should_receive(:create_or_update!).with({ :name => 'Otis Co'}, { :name => 'Otis Co', :employee_count => 600 })
47
+ @replica.run
48
+ end
49
+ it "should mark all items pending delete" do
50
+ @collection.should_receive(:mark_all_items_pending_deletion!)
51
+ @replica.run
52
+ end
53
+ it "should delete items pending delete" do
54
+ @collection.should_receive(:delete_all_items_pending_deletion!)
55
+ @replica.run
56
+ end
57
+ end
58
+
59
+ context "not requiring a full refresh" do
60
+ before do
61
+ @time = Time.parse("1/1/2011 00:00:00")
62
+ Time.stub!(:now).and_return(@time)
63
+ @replica.stub!(:do_full_refresh?).and_return(false)
64
+
65
+ @deleted_client1 = Client.new(:name => "Widget Co", :employee_count => 500)
66
+ @replica.stub!(:find).with(:deleted, @last_replicated_time).and_return([@deleted_client1])
67
+
68
+ @updated_client1 = Client.new(:name => "ABC Co", :employee_count => 700)
69
+ @replica.stub!(:find).with(:updated, @last_replicated_time).and_return([@updated_client1])
70
+
71
+ @replica.stub!(:find).with(:created, @last_replicated_time).and_return([@created_client1])
72
+
73
+ @collection.stub!(:delete!)
74
+ @collection.stub!(:create_or_update!)
75
+ @collection.stub!(:update!)
76
+ @collection.stub!(:last_replicated_at=)
77
+ end
78
+
79
+ it "should delete the items in the destination database" do
80
+ @collection.should_receive(:delete!).with({ :name => 'Widget Co' })
81
+ @replica.run
82
+ end
83
+
84
+ it "should create the items in the destination database" do
85
+ @collection.should_receive(:create_or_update!).with({ :name => 'Otis Co' }, { :name => 'Otis Co', :employee_count => 600 })
86
+ @replica.run
87
+ end
88
+
89
+ it "should update the items in the destination database" do
90
+ @collection.should_receive(:update!).with({ :name => 'ABC Co' }, { :name => 'ABC Co', :employee_count => 700 })
91
+ @replica.run
92
+ end
93
+
94
+ it "should set the last replicated at time to the time the run started" do
95
+ @collection.should_receive(:last_replicated_at=).with(@time)
96
+ @replica.run
97
+ end
98
+ end
99
+ end
100
+
101
+ describe "retrieving data from source object" do
102
+ before do
103
+ class Client
104
+ attr_accessor :name, :employee_count, :something_else
105
+ def initialize(args)
106
+ args.each do |key, value|
107
+ self.send(:"#{key}=", value)
108
+ end
109
+ end
110
+ end
111
+
112
+ @collection = Mongar::Mongo::Collection.new(:name => "clients")
113
+ @replica = Mongar::Replica.new(:source => Client, :destination => @collection)
114
+ @replica.column :name do
115
+ primary_index
116
+ transform :downcase
117
+ end
118
+ @replica.column :employee_count do
119
+ transform do |value|
120
+ value.nil? ? 0 : value
121
+ end
122
+ end
123
+
124
+ @client1 = Client.new(:name => "Widget Co", :employee_count => 500)
125
+ @client2 = Client.new(:name => "Widget 2 Co", :employee_count => nil)
126
+ end
127
+
128
+ describe "#source_object_to_hash" do
129
+ it "should return a hash of all the columns" do
130
+ @replica.source_object_to_hash(@client1).should == {:name => "widget co", :employee_count => 500}
131
+ end
132
+ it "should run any block transforms on the value" do
133
+ @replica.source_object_to_hash(@client2)[:employee_count].should == 0
134
+ end
135
+ it "should return any symbol transforms on the value" do
136
+ @replica.source_object_to_hash(@client1)[:name].should == 'widget co'
137
+ end
138
+ end
139
+
140
+ describe "#source_object_to_primary_key_hash" do
141
+ it "should return a hash of just the primary key column and value" do
142
+ @replica.source_object_to_primary_key_hash(@client1).should == {:name => "widget co"}
143
+ end
144
+ it "should return any symbol transforms on the value" do
145
+ @replica.source_object_to_primary_key_hash(@client1)[:name].should == 'widget co'
146
+ end
147
+ end
148
+ end
149
+
150
+ describe "#new" do
151
+ before do
152
+ @collection = Mongar::Mongo::Collection.new(:name => 'clients')
153
+ @replica = Mongar::Replica.new(:destination => @collection)
154
+ end
155
+ it "should set the parent replica of the new collection" do
156
+ @replica.destination.replica.should == @replica
157
+ end
158
+ end
159
+
160
+ describe "#find" do
161
+ before do
162
+ class Client; end
163
+ @replica.source = Client
164
+ @date_time = Time.parse("1/1/2011 00:00:00")
165
+ @replica.stub!(:source).and_return(Client)
166
+ end
167
+
168
+ context "default finders" do
169
+ it "should execute the deleted_finder against the object given" do
170
+ Client.should_receive(:find_every_with_deleted).with(:conditions => ["deleted_at > ?", @date_time])
171
+ @replica.find(:deleted, @date_time)
172
+ end
173
+ it "should execute the created_finder against the object given" do
174
+ Client.should_receive(:find).with(:all, :conditions => ["created_at > ? AND deleted_at IS NULL", @date_time])
175
+ @replica.find(:created, @date_time)
176
+ end
177
+ it "should execute the updated_finder against the object given" do
178
+ Client.should_receive(:find).with(:all, :conditions => ["updated_at > ? AND deleted_at IS NULL", @date_time])
179
+ @replica.find(:updated, @date_time)
180
+ end
181
+ end
182
+
183
+ context "custom finder blocks" do
184
+ before do
185
+ @replica.set_deleted_finder do |last_replicated_at|
186
+ deleted_items_since(last_replicated_at)
187
+ end
188
+
189
+ @replica.set_created_finder do |last_replicated_at|
190
+ created_items_since(last_replicated_at)
191
+ end
192
+
193
+ @replica.set_updated_finder do |last_replicated_at|
194
+ updated_items_since(last_replicated_at)
195
+ end
196
+ end
197
+
198
+ it "should call the custom deleted finder" do
199
+ Client.should_receive(:deleted_items_since).with(@date_time)
200
+ @replica.find(:deleted, @date_time)
201
+ end
202
+
203
+ it "should call the custom created finder" do
204
+ Client.should_receive(:created_items_since).with(@date_time)
205
+ @replica.find(:created, @date_time)
206
+ end
207
+
208
+ it "should call the custom updated finder" do
209
+ Client.should_receive(:updated_items_since).with(@date_time)
210
+ @replica.find(:updated, @date_time)
211
+ end
212
+ end
213
+
214
+ context "null finders" do
215
+ before do
216
+ @replica.no_deleted_finder
217
+ end
218
+
219
+ it "should not try to find anything" do
220
+ Client.should_not_receive(:find_every_with_deleted)
221
+ Client.should_not_receive(:find)
222
+ @replica.find(:deleted, @date_time)
223
+ end
224
+
225
+ it "should return an empty array" do
226
+ @replica.find(:deleted, @date_time).should == []
227
+ end
228
+ end
229
+ end
230
+
231
+ describe "#no_deleted_finder" do
232
+ it "should set the deleted_finder to nil" do
233
+ @replica.no_deleted_finder
234
+ @replica.deleted_finder.should be_nil
235
+ end
236
+ end
237
+
238
+ describe "#columns" do
239
+ it "should default to an empty array" do
240
+ @replica.columns.should be_empty
241
+ end
242
+ end
243
+
244
+ describe "#column" do
245
+ before do
246
+ @block = lambda {}
247
+ @mock_column = mock(Mongar::Column)
248
+ Mongar::Column.stub!(:new).and_return(@mock_column)
249
+ end
250
+ it "should populate the columns array with the new column" do
251
+ @replica.column :first_name
252
+ @replica.columns.should == [@mock_column]
253
+ end
254
+ it "should create a new column with the name given" do
255
+ Mongar::Column.should_receive(:new).with(:name => :first_name).and_return(@mock_column)
256
+ @replica.column :first_name
257
+ end
258
+ it "should instance_eval the block given" do
259
+ @mock_column.should_receive(:instance_eval).with(&@block)
260
+ @replica.column :first_name, &@block
261
+ end
262
+ end
263
+
264
+ describe "#primary_index" do
265
+ before do
266
+ @column = Mongar::Column.new(:name => :id)
267
+ @column.primary_index
268
+
269
+ @replica.columns = [@column]
270
+ end
271
+ it "should return the primary index column" do
272
+ @replica.primary_index.should == @column
273
+ end
274
+ end
275
+
276
+ describe "#full_refresh" do
277
+ context "given an argument" do
278
+ it "should take :every as an argument and save the time period in seconds" do
279
+ @replica.full_refresh :every => 3601
280
+ @replica.instance_variable_get(:"@full_refresh").should == 3601
281
+ end
282
+
283
+ it "should take :if as an argument and save the block" do
284
+ @proc = Proc.new { |something| }
285
+ @replica.full_refresh :if => @proc
286
+ @replica.instance_variable_get(:"@full_refresh").should == @proc
287
+ end
288
+ end
289
+
290
+ context "not given an argument" do
291
+ before do
292
+ @replica.instance_variable_set(:"@full_refresh", 3602)
293
+ end
294
+ it "should return the current full_refresh setting" do
295
+ @replica.full_refresh.should == 3602
296
+ end
297
+ end
298
+ end
299
+
300
+ describe "#do_full_refresh?" do
301
+ before do
302
+ @time = Time.now
303
+ @mongo = Mongar::Mongo.new
304
+ @collection = Mongar::Mongo::Collection.new(:name => 'clients')
305
+ @collection.stub!(:last_replicated_at).and_return(@time)
306
+ @replica.stub!(:mongodb).and_return(@mongo)
307
+ @replica.destination = @collection
308
+ end
309
+
310
+ context "given the full_refresh condition is a time period" do
311
+ before do
312
+ @replica.full_refresh :every => 3600
313
+ end
314
+
315
+ it "should return false if the time since the last refresh is less than 60 minutes" do
316
+ @replica.do_full_refresh?.should be_false
317
+ end
318
+ it "should return true if the time since the last refresh is 60 minutes" do
319
+ @collection.stub!(:last_replicated_at).and_return(@time - 3601)
320
+ @replica.do_full_refresh?.should be_true
321
+ end
322
+ it "should return true if the last refresh time is nil" do
323
+ @collection.stub!(:last_replicated_at).and_return(nil)
324
+ @replica.do_full_refresh?.should be_true
325
+ end
326
+ end
327
+
328
+ context "given the full_refresh condition is a proc" do
329
+ before do
330
+ @mock_source = mock(Object, :something_changed? => false)
331
+ @replica.source = @mock_source
332
+ @replica.full_refresh :if => Proc.new { |last_replicated_date|
333
+ something_changed?(last_replicated_date)
334
+ }
335
+ end
336
+
337
+ it "should return true if the proc evaluated in the source context is true" do
338
+ @mock_source.should_receive(:something_changed?).with(@time).and_return(true)
339
+ @replica.do_full_refresh?.should be_true
340
+ end
341
+ it "should return false if the proc evaluated in the source context is false" do
342
+ @replica.do_full_refresh?.should be_false
343
+ end
344
+ end
345
+ end
346
+
347
+ describe "#mongodb_name" do
348
+ it "should return the name given with #use_mongodb" do
349
+ @replica.use_mongodb :other_mongo
350
+ @replica.mongodb_name.should == :other_mongo
351
+ end
352
+
353
+ it "should default to :default if use_mongodb is never called" do
354
+ @replica.mongodb_name.should == :default
355
+ end
356
+ end
357
+
358
+ describe "#mongodb" do
359
+ before do
360
+ @fake_db = mock(Mongar::Mongo)
361
+ Mongar::Mongo.databases[:default] = @fake_db
362
+ end
363
+
364
+ it "should return nil if it cannot find the mongo db by the name" do
365
+ @replica.use_mongodb :something
366
+ @replica.mongodb.should == nil
367
+ end
368
+
369
+ it "should return the mongo db based on the name" do
370
+ @replica.mongodb.should == @fake_db
371
+ end
372
+ end
373
+ end
@@ -0,0 +1,81 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Mongar" do
4
+ describe ".configure" do
5
+ before do
6
+
7
+ end
8
+ end
9
+
10
+ describe "#replicate" do
11
+ before do
12
+ class Client
13
+ end
14
+
15
+ @mongar = Mongar.new
16
+
17
+ @block = lambda {}
18
+ @mock_replica = mock(Mongar::Replica)
19
+ Mongar::Replica.stub!(:new).and_return(@mock_replica)
20
+ @mock_replica.stub!(:instance_eval)
21
+
22
+ @collection = Mongar::Mongo::Collection.new
23
+ Mongar::Mongo::Collection.stub!(:new).and_return(@collection)
24
+ end
25
+
26
+ it "should make a new Mongar::Replica and pass it the block" do
27
+ @mock_replica.should_receive(:instance_eval).with(&@block)
28
+ @mongar.replicate({ Client => 'clients' }, &@block)
29
+ end
30
+
31
+ it "should populate Mongar.replicas with one Replica instance for Clients" do
32
+ @mongar.replicate({ Client => 'clients' }, &@block)
33
+ @mongar.replicas.should == [@mock_replica]
34
+ end
35
+
36
+ it "should initialize a new replica with the source and destination objects" do
37
+ Mongar::Replica.should_receive(:new).with(:source => Client, :destination => @collection, :log_level => nil)
38
+ @mongar.replicate({ Client => 'clients' }, &@block)
39
+ end
40
+ end
41
+
42
+ describe "#mongo" do
43
+ before do
44
+ @mongar = Mongar.new
45
+
46
+ @block = lambda {}
47
+
48
+ @mock_mongo = mock(Mongar::Mongo)
49
+ @mock_mongo.stub!(:instance_eval)
50
+ Mongar::Mongo.stub!(:new).and_return(@mock_mongo)
51
+ end
52
+
53
+ it "should make a new Mongar::Mongo" do
54
+ Mongar::Mongo.should_receive(:new).with(:name => :newdb).and_return(@mock_mongo)
55
+ @mongar.mongo :newdb, &@block
56
+ end
57
+
58
+ it "should pass the block to the new Mongo" do
59
+ @mock_mongo.should_receive(:instance_eval).with(&@block)
60
+ @mongar.mongo :newdb, &@block
61
+ end
62
+
63
+ it "should assign the new Mongo to the Mongar::Mongo.databases hash" do
64
+ @mongar.mongo :newdb, &@block
65
+ Mongar::Mongo.databases[:newdb].should == @mock_mongo
66
+ end
67
+ end
68
+
69
+ describe "#run" do
70
+ before do
71
+ @mongar = Mongar.new
72
+ @replica = Mongar::Replica.new
73
+ @mongar.replicas = [@replica]
74
+ end
75
+
76
+ it "should call run on each replica" do
77
+ @replica.should_receive(:run)
78
+ @mongar.run
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'mongar'
5
+ require 'time'
6
+
7
+ # Requires supporting files with custom matchers and macros, etc,
8
+ # in ./support/ and its subdirectories.
9
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
10
+
11
+ RSpec.configure do |config|
12
+
13
+ end
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongar
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 4
10
+ version: 0.0.4
11
+ platform: ruby
12
+ authors:
13
+ - Philippe Green
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-12-14 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ requirement: &id001 !ruby/object:Gem::Requirement
22
+ none: false
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ hash: 3
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ version_requirements: *id001
31
+ name: linguistics
32
+ prerelease: false
33
+ type: :runtime
34
+ - !ruby/object:Gem::Dependency
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ hash: 3
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ version_requirements: *id002
45
+ name: mongo
46
+ prerelease: false
47
+ type: :runtime
48
+ - !ruby/object:Gem::Dependency
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ hash: 3
55
+ segments:
56
+ - 2
57
+ - 3
58
+ - 0
59
+ version: 2.3.0
60
+ version_requirements: *id003
61
+ name: rspec
62
+ prerelease: false
63
+ type: :development
64
+ - !ruby/object:Gem::Dependency
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ hash: 7
71
+ segments:
72
+ - 0
73
+ - 6
74
+ - 0
75
+ version: 0.6.0
76
+ version_requirements: *id004
77
+ name: yard
78
+ prerelease: false
79
+ type: :development
80
+ - !ruby/object:Gem::Dependency
81
+ requirement: &id005 !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ~>
85
+ - !ruby/object:Gem::Version
86
+ hash: 23
87
+ segments:
88
+ - 1
89
+ - 0
90
+ - 0
91
+ version: 1.0.0
92
+ version_requirements: *id005
93
+ name: bundler
94
+ prerelease: false
95
+ type: :development
96
+ - !ruby/object:Gem::Dependency
97
+ requirement: &id006 !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ~>
101
+ - !ruby/object:Gem::Version
102
+ hash: 7
103
+ segments:
104
+ - 1
105
+ - 6
106
+ - 4
107
+ version: 1.6.4
108
+ version_requirements: *id006
109
+ name: jeweler
110
+ prerelease: false
111
+ type: :development
112
+ - !ruby/object:Gem::Dependency
113
+ requirement: &id007 !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 0
121
+ version: "0"
122
+ version_requirements: *id007
123
+ name: rcov
124
+ prerelease: false
125
+ type: :development
126
+ description: Replicates data from ActiveRecord (or other Ruby data mapping class) to MongoDB
127
+ email: phil@greenviewdata.com
128
+ executables: []
129
+
130
+ extensions: []
131
+
132
+ extra_rdoc_files:
133
+ - LICENSE.txt
134
+ - README.rdoc
135
+ files:
136
+ - .document
137
+ - .rspec
138
+ - Gemfile
139
+ - Gemfile.lock
140
+ - LICENSE.txt
141
+ - README.rdoc
142
+ - Rakefile
143
+ - VERSION
144
+ - lib/mongar.rb
145
+ - lib/mongar/column.rb
146
+ - lib/mongar/logger.rb
147
+ - lib/mongar/mongo.rb
148
+ - lib/mongar/mongo/collection.rb
149
+ - lib/mongar/replica.rb
150
+ - mongar.gemspec
151
+ - spec/fixtures/configure.rb
152
+ - spec/fixtures/full_configure.rb
153
+ - spec/fixtures/sources.rb
154
+ - spec/integration_spec.rb
155
+ - spec/mongar/column_spec.rb
156
+ - spec/mongar/mongo/collection_spec.rb
157
+ - spec/mongar/mongo_spec.rb
158
+ - spec/mongar/replica_spec.rb
159
+ - spec/mongar_spec.rb
160
+ - spec/spec_helper.rb
161
+ homepage: http://github.com/gdi/mongar
162
+ licenses:
163
+ - MIT
164
+ post_install_message:
165
+ rdoc_options: []
166
+
167
+ require_paths:
168
+ - lib
169
+ required_ruby_version: !ruby/object:Gem::Requirement
170
+ none: false
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ hash: 3
175
+ segments:
176
+ - 0
177
+ version: "0"
178
+ required_rubygems_version: !ruby/object:Gem::Requirement
179
+ none: false
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ hash: 3
184
+ segments:
185
+ - 0
186
+ version: "0"
187
+ requirements: []
188
+
189
+ rubyforge_project:
190
+ rubygems_version: 1.8.10
191
+ signing_key:
192
+ specification_version: 3
193
+ summary: Replicates data from ActiveRecord (or other Ruby data mapping class) to MongoDB
194
+ test_files: []
195
+