syncer 0.0.1 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/syncer.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "syncer/version"
2
+ require "syncer/sync_record"
2
3
  require "syncer/syncer"
3
4
  require "syncer/syncable_record"
4
5
 
@@ -0,0 +1,6 @@
1
+ require 'active_record'
2
+
3
+ class SyncRecord < ActiveRecord::Base
4
+ validates_presence_of :version
5
+ validates_numericality_of :version
6
+ end
@@ -46,8 +46,10 @@ class SyncableRecord < ActiveRecord::Base
46
46
  # if the key is of the form xxx_tmp_id
47
47
  next unless /(?<foreign_key_prefix>.*)_tmp_id$/ =~ key
48
48
  # then find the xxx class
49
+ # TODO assumption here is that model name are single words!!
49
50
  next unless model_class = foreign_key_prefix.capitalize.to_class
50
51
  # find the class model that matches the tmp_id
52
+ # TODO note that if it does not find it it will try to assign it and get a "Can't mass-assign protected attributes" error
51
53
  next unless model = model_class.find_by_tmp_id(remote_record["#{foreign_key_prefix}_tmp_id"])
52
54
  # save the actual model id
53
55
  remote_record["#{foreign_key_prefix}_id"] = model.id
@@ -63,6 +65,8 @@ class SyncableRecord < ActiveRecord::Base
63
65
  # TODO we do not currently check for id's per user
64
66
  # TODO check both when creating and when accessing
65
67
  # TODO we should at some point delete the tmp_ids
68
+
69
+ state_changed = false
66
70
 
67
71
  remote_saved = remote_state['saved'] || []
68
72
  remote_created = remote_state['created'] || []
@@ -110,6 +114,7 @@ class SyncableRecord < ActiveRecord::Base
110
114
  if rec.persisted?
111
115
  response = rec.to_rjson
112
116
  created << response
117
+ state_changed = true
113
118
  end
114
119
  end
115
120
  end
@@ -127,7 +132,10 @@ class SyncableRecord < ActiveRecord::Base
127
132
  attrs = remote_record.dup
128
133
  attrs.delete("id") # can't update id
129
134
  attrs.delete("version") # can't update version
130
- updated << rec if rec.update_attributes(attrs)
135
+ if rec.update_attributes(attrs)
136
+ updated << rec
137
+ state_changed = true
138
+ end
131
139
  end
132
140
  end
133
141
  end
@@ -143,7 +151,10 @@ class SyncableRecord < ActiveRecord::Base
143
151
  # destroy the record only if found and if it has not been updated on the server already
144
152
  if rec && !rec.updated?(remote_record)
145
153
  rec.destroy
146
- deleted << { :id => remote_record['id'] } if remote_record['id']
154
+ if remote_record['id']
155
+ deleted << { :id => remote_record['id'] }
156
+ state_changed = true
157
+ end
147
158
  end
148
159
  end
149
160
  end
@@ -152,6 +163,9 @@ class SyncableRecord < ActiveRecord::Base
152
163
  response[:create] = created unless created.empty?
153
164
  response[:update] = updated unless updated.empty?
154
165
  response[:delete] = deleted unless deleted.empty?
166
+
167
+ # mark here if db state was actually changed requiring version increment
168
+ response[:state_changed] = true if state_changed
155
169
 
156
170
  response
157
171
 
data/lib/syncer/syncer.rb CHANGED
@@ -1,21 +1,50 @@
1
1
  require 'xstring'
2
2
 
3
3
  class Syncer
4
+ SYNC_VERSION_KEY = '_sync_version'
4
5
  SYNC_ORDER_KEY = '_sync_order'
5
6
 
6
7
  # request keys should be strings and not symbols
7
8
  def self.sync(request)
8
- request = request.to_rjson
9
- response = {};
10
- all_sync_request = request
11
- sync_order = all_sync_request[SYNC_ORDER_KEY];
9
+
10
+ # TODO probably check the model actually exists and responds to sync
11
+ # TODO We need to deal with security issues such as User associated models + same for relationships
12
+
13
+ state_changed = false
14
+
15
+ response = {}
16
+ all_sync_request = request.to_rjson
17
+ sync_order = all_sync_request[SYNC_ORDER_KEY]
12
18
  # TODO if nil raise an exception
13
19
  sync_order.each do |model|
14
20
  model_sync_data = all_sync_request[model]
15
21
  next unless model_sync_data # it is ok not to have data for each model in the order
16
22
  sync_response = model.to_class.sync(model_sync_data)
17
23
  response[model] = sync_response
24
+ state_changed = true if sync_response[:state_changed]
18
25
  end
26
+
27
+ # handle version
28
+ increment_sync_state_version if state_changed
29
+ response[SYNC_VERSION_KEY] = sync_state.version
30
+
19
31
  response.to_rjson
20
32
  end
33
+
34
+ def self.version
35
+ { SYNC_VERSION_KEY => sync_state.version }
36
+ end
37
+
38
+ private
39
+
40
+ def self.sync_state
41
+ # TODO this currently supports only one sync account - add multi user support
42
+
43
+ # return the first sync and create it if doesn't exit
44
+ SyncRecord.first || SyncRecord.create
45
+ end
46
+
47
+ def self.increment_sync_state_version
48
+ sync_state.increment!(:version)
49
+ end
21
50
  end
@@ -1,3 +1,3 @@
1
1
  class Syncer
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -42,6 +42,12 @@ ActiveRecord::Migration.create_table "trades", :force => true do |t|
42
42
  t.datetime "updated_at"
43
43
  end
44
44
 
45
+ ActiveRecord::Migration.create_table "sync_records", :force => true do |t|
46
+ t.integer "version", :default => 0
47
+ t.datetime "created_at"
48
+ t.datetime "updated_at"
49
+ end
50
+
45
51
  #setup transactions
46
52
  RSpec.configure do |config|
47
53
  config.around do |example|
@@ -18,7 +18,7 @@ describe "SyncableRecord" do
18
18
  @stock2 = { :market => 'us', :ticker => 'AAPL', :name => 'Apple', :tmp_id => '13' }
19
19
  end
20
20
 
21
- it "it should create the records and return create response for them" do
21
+ it "it should create the records, return create response for them, and mark state changed" do
22
22
  remote_state = { 'created' => [@stock1, @stock2] }
23
23
 
24
24
  response = Stock.sync(HashWithIndifferentAccess.new(remote_state))
@@ -40,6 +40,8 @@ describe "SyncableRecord" do
40
40
  response[:create][1]['tmp_id'].should == @stock2[:tmp_id]
41
41
  response[:delete].should be_nil
42
42
  response[:update].should be_nil
43
+
44
+ response[:state_changed].should be_true
43
45
  end
44
46
  end
45
47
  end
@@ -52,13 +54,15 @@ describe "SyncableRecord" do
52
54
 
53
55
  describe "and no remote records" do
54
56
 
55
- it "it should return create for server records" do
57
+ it "it should return create for server records, and no state change" do
56
58
  response = Stock.sync({})
57
59
  response[:create].length.should == 2
58
60
  response[:create][0].should == @stock1
59
61
  response[:create][1].should == @stock2
60
62
  response[:delete].should be_nil
61
63
  response[:update].should be_nil
64
+
65
+ response[:state_changed].should be_nil
62
66
  end
63
67
  end
64
68
 
@@ -73,13 +77,15 @@ describe "SyncableRecord" do
73
77
 
74
78
  describe "one of the remote records is deleted" do
75
79
 
76
- it "it should delete the deleted record and keep the saved record" do
80
+ it "it should delete the deleted record,keep the saved record, and mark state changed" do
77
81
  remote_state = { 'saved' => [@stock1], 'deleted' => [@stock2] }
78
82
  response = Stock.sync(remote_state)
79
83
  response[:delete].length.should == 1
80
84
  response[:delete][0][:id].should == @stock2.id
81
85
  response[:create].should be_nil
82
86
  response[:update].should be_nil
87
+
88
+ response[:state_changed].should be_true
83
89
  end
84
90
  end
85
91
  end
@@ -92,7 +98,7 @@ describe "SyncableRecord" do
92
98
 
93
99
  describe "and some saved records on remote" do
94
100
 
95
- it "it should return create for new records" do
101
+ it "it should return create for new records and no state change" do
96
102
  remote_state = { 'saved' => [@stock1] }
97
103
 
98
104
  response = Stock.sync(remote_state)
@@ -100,6 +106,8 @@ describe "SyncableRecord" do
100
106
  response[:create][0].should == @new_stock
101
107
  response[:delete].should be_nil
102
108
  response[:update].should be_nil
109
+
110
+ response[:state_changed].should be_nil
103
111
  end
104
112
  end
105
113
 
@@ -109,7 +117,7 @@ describe "SyncableRecord" do
109
117
  @stock4 = { :market => 'us', :ticker => 'AAPL', :name => 'Apple', :tmp_id => '13' }
110
118
  end
111
119
 
112
- it "it should create the new and created records and return create response for them" do
120
+ it "it should create the new and created records and return create response for them and mark state changed" do
113
121
  remote_state = { 'saved' => [@stock1], 'created' => [@stock3, @stock4] }
114
122
 
115
123
  response = Stock.sync(HashWithIndifferentAccess.new(remote_state))
@@ -135,6 +143,8 @@ describe "SyncableRecord" do
135
143
  response[:create][2]['tmp_id'].should == @stock4[:tmp_id]
136
144
  response[:delete].should be_nil
137
145
  response[:update].should be_nil
146
+
147
+ response[:state_changed].should be_true
138
148
  end
139
149
  end
140
150
  end
@@ -150,7 +160,7 @@ describe "SyncableRecord" do
150
160
 
151
161
  describe "and only saved remote records" do
152
162
 
153
- it "it should create the new, update the updated and delete the deleted" do
163
+ it "it should create the new, update the updated and delete the deleted and no state change" do
154
164
  remote_state = { 'saved' => [@stock1, @stock2, @updated_stock.to_rjson, @deleted_stock] }
155
165
 
156
166
  # do server changes: update a stock, delete a stock
@@ -171,13 +181,15 @@ describe "SyncableRecord" do
171
181
  @updated_stock[:version].should == 1
172
182
  @updated_stock[:name].should == "Apple Computers"
173
183
  response[:delete].length.should == 1
174
- response[:delete][0][:id].should == @deleted_stock.id
184
+ response[:delete][0][:id].should == @deleted_stock.id
185
+
186
+ response[:state_changed].should be_nil
175
187
  end
176
188
  end
177
189
 
178
190
  describe "and saved & deleted remote records" do
179
191
 
180
- it "it should create the new, delete the deleted and update the updated" do
192
+ it "it should create the new, delete the deleted and update the updated, and mark state changed" do
181
193
  remote_state = { 'saved' => [@stock2, @updated_stock.to_rjson, @deleted_stock], 'deleted' => [@stock1] }
182
194
 
183
195
  # do server changes: update a stock, delete a stock
@@ -199,12 +211,14 @@ describe "SyncableRecord" do
199
211
  response[:delete].length.should == 2
200
212
  response[:delete][0][:id].should == @deleted_stock.id
201
213
  response[:delete][1][:id].should == @stock1.id
214
+
215
+ response[:state_changed].should be_true
202
216
  end
203
217
  end
204
218
 
205
219
  describe "and saved & updated remote records" do
206
220
 
207
- it "it should create the new, delete the deleted and update the updated" do
221
+ it "it should create the new, delete the deleted and update the updated and mark state changed" do
208
222
  remote_update = @stock1
209
223
  remote_update['name'] = 'This is an updated name'
210
224
  remote_state = { 'saved' => [@stock1, @stock2, @updated_stock, @deleted_stock], 'updated' => [remote_update] }
@@ -233,13 +247,15 @@ describe "SyncableRecord" do
233
247
  response[:update][1]['name'].should == remote_update['name']
234
248
  response[:update][1]['version'].should == remote_update['version']
235
249
  response[:delete].length.should == 1
236
- response[:delete][0][:id].should == @deleted_stock[:id]
250
+ response[:delete][0][:id].should == @deleted_stock[:id]
251
+
252
+ response[:state_changed].should be_true
237
253
  end
238
254
  end
239
255
 
240
256
  describe "and saved, created, updated, and deleted remote records" do
241
257
 
242
- it "it should create the new, delete the deleted and update the updated" do
258
+ it "it should create the new, delete the deleted and update the updated and mark state changed" do
243
259
  remote_update = @stock1
244
260
  remote_update['name'] = 'This is an updated name'
245
261
  remote_create = { :market => 'il', :ticker => 'Razio', :name => 'Razio Oil', :tmp_id => '19' }
@@ -276,7 +292,9 @@ describe "SyncableRecord" do
276
292
  response[:update][1]['version'].should == remote_update['version']
277
293
  response[:delete].length.should == 2
278
294
  response[:delete][0][:id].should == @deleted_stock.id
279
- response[:delete][1][:id].should == @stock2.id
295
+ response[:delete][1][:id].should == @stock2.id
296
+
297
+ response[:state_changed].should be_true
280
298
  end
281
299
  end
282
300
  end
@@ -286,7 +304,7 @@ describe "SyncableRecord" do
286
304
  @stock = FactoryGirl.create(:stock, :market => 'us', :ticker => 'IBM', :name => 'International Business Machines')
287
305
  end
288
306
 
289
- describe "between server deleted record and remote deleted record" do
307
+ describe "between server deleted record and remote deleted record and no state change" do
290
308
 
291
309
  it "it should delete the record" do
292
310
  remote_state = { 'deleted' => [@stock.to_rjson] }
@@ -305,12 +323,14 @@ describe "SyncableRecord" do
305
323
  response[:update].should be_nil
306
324
  response[:delete].length.should == 1
307
325
  response[:delete][0][:id].should == @stock.id
326
+
327
+ response[:state_changed].should be_nil
308
328
  end
309
329
  end
310
330
 
311
331
  describe "between server deleted record and remote updated record" do
312
332
 
313
- it "it should delete the record" do
333
+ it "it should delete the record and no state change" do
314
334
  remote_update = @stock.to_rjson
315
335
  remote_update['name'] = 'Tabulation company'
316
336
 
@@ -330,12 +350,14 @@ describe "SyncableRecord" do
330
350
  response[:update].should be_nil
331
351
  response[:delete].length.should == 1
332
352
  response[:delete][0][:id].should == @stock.id
353
+
354
+ response[:state_changed].should be_nil
333
355
  end
334
356
  end
335
357
 
336
358
  describe "between server updated record and remote deleted record" do
337
359
 
338
- it "it should create the record as if new" do
360
+ it "it should create the record as if new and no state change" do
339
361
  remote_state = { 'deleted' => [@stock.to_rjson] }
340
362
 
341
363
  # do server changes: update the stock
@@ -353,13 +375,15 @@ describe "SyncableRecord" do
353
375
  response[:create][0]['id'].should == @stock[:id]
354
376
  response[:create][0]['name'].should == @stock[:name]
355
377
  response[:update].should be_nil
356
- response[:delete].should be_nil
378
+ response[:delete].should be_nil
379
+
380
+ response[:state_changed].should be_nil
357
381
  end
358
382
  end
359
383
 
360
384
  describe "between server updated record and remote updated record" do
361
385
 
362
- it "it should update the record with the server version" do
386
+ it "it should update the record with the server version and no state change" do
363
387
  remote_update = @stock.to_rjson
364
388
  remote_update['name'] = 'Tabulation company'
365
389
 
@@ -382,7 +406,9 @@ describe "SyncableRecord" do
382
406
  response[:update].length.should == 1
383
407
  response[:update][0]['id'].should == @stock[:id]
384
408
  response[:update][0]['name'].should == @stock[:name]
385
- response[:delete].should be_nil
409
+ response[:delete].should be_nil
410
+
411
+ response[:state_changed].should be_nil
386
412
  end
387
413
  end
388
414
  end
@@ -395,6 +421,8 @@ describe "SyncableRecord" do
395
421
  response = Stock.sync(123)
396
422
 
397
423
  response.should == { :error => "can't convert String into Integer" }
424
+
425
+ response[:state_changed].should be_nil
398
426
  end
399
427
  end
400
428
 
@@ -440,6 +468,7 @@ describe "SyncableRecord" do
440
468
 
441
469
  response = Stock.sync(remote_state)
442
470
  response[:error].should_not be_empty
471
+ response[:state_changed].should be_nil
443
472
  end
444
473
  end
445
474
  end
data/spec/syncer_spec.rb CHANGED
@@ -1,142 +1,266 @@
1
1
  require 'spec_helper.rb'
2
2
 
3
- describe "Sync.sync" do
3
+ describe "Syncer" do
4
4
 
5
+ let(:sync_order_key) { Syncer::SYNC_ORDER_KEY }
6
+ let(:sync_version_key) { Syncer::SYNC_VERSION_KEY }
7
+
5
8
  let(:stock) { FactoryGirl.create(:stock) }
6
- let(:syncOrder) { [:Account, :Stock, :Trade] }
7
- let(:syncOrderKey) { Syncer::SYNC_ORDER_KEY }
8
-
9
- it "should sync a single Model" do
10
- updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
11
- stocks_sync_data = { :updated => [ updated_stock ] }
12
- all_sync_data = { syncOrderKey => syncOrder, :Stock => stocks_sync_data }
13
-
14
- result = Syncer.sync(all_sync_data)
15
-
16
- stock_results = result['Stock']
17
- stock_results.should_not be_nil
18
- update_stock = stock_results['update'][0]
19
- update_stock['id'].should == stock.id
20
- update_stock['version'].should == stock.version + 1
21
- update_stock['market'].should == stock.market
22
- update_stock['ticker'].should == stock.ticker
23
- update_stock['name'].should == updated_stock[:name]
24
- end
9
+ let(:sync_order) { [:Account, :Stock, :Trade] }
10
+
11
+ describe "sync" do
12
+
13
+ it "should sync a single Model" do
14
+ updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
15
+ stocks_sync_data = { :updated => [ updated_stock ] }
16
+ all_sync_data = { sync_order_key => sync_order, :Stock => stocks_sync_data }
17
+
18
+ result = Syncer.sync(all_sync_data)
19
+
20
+ stock_results = result['Stock']
21
+ stock_results.should_not be_nil
22
+ update_stock = stock_results['update'][0]
23
+ update_stock['id'].should == stock.id
24
+ update_stock['version'].should == stock.version + 1
25
+ update_stock['market'].should == stock.market
26
+ update_stock['ticker'].should == stock.ticker
27
+ update_stock['name'].should == updated_stock[:name]
28
+ end
25
29
 
26
- it "should sync multiple independent Models" do
27
- updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
28
- stocks_sync_data = { :updated => [ updated_stock ] }
29
- created_account = FactoryGirl.attributes_for(:account)
30
- created_account['tmp_id'] = "12345abcde"
31
- accounts_sync_data = { :created => [ created_account ] }
32
- all_sync_data = { syncOrderKey => syncOrder, :Stock => stocks_sync_data, :Account => accounts_sync_data }
30
+ it "should sync multiple independent Models" do
31
+ updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
32
+ stocks_sync_data = { :updated => [ updated_stock ] }
33
+ created_account = FactoryGirl.attributes_for(:account)
34
+ created_account['tmp_id'] = "12345abcde"
35
+ accounts_sync_data = { :created => [ created_account ] }
36
+ all_sync_data = { sync_order_key => sync_order, :Stock => stocks_sync_data, :Account => accounts_sync_data }
33
37
 
34
- result = Syncer.sync(all_sync_data)
38
+ result = Syncer.sync(all_sync_data)
35
39
 
36
- stock_results = result['Stock']
37
- stock_results.should_not be_nil
38
- update_stock = stock_results['update'][0]
39
- update_stock['id'].should == stock.id
40
- update_stock['version'].should == stock.version + 1
41
- update_stock['market'].should == stock.market
42
- update_stock['ticker'].should == stock.ticker
43
- update_stock['name'].should == updated_stock[:name]
44
-
45
- account_results = result['Account']
46
- account_results.should_not be_nil
47
- create_account = account_results['create'][0]
48
- create_account['id'].should_not be_nil
49
- create_account['tmp_id'].should == "12345abcde"
50
- create_account['version'].should == 0
51
- create_account['name'].should == created_account[:name]
52
- end
40
+ stock_results = result['Stock']
41
+ stock_results.should_not be_nil
42
+ update_stock = stock_results['update'][0]
43
+ update_stock['id'].should == stock.id
44
+ update_stock['version'].should == stock.version + 1
45
+ update_stock['market'].should == stock.market
46
+ update_stock['ticker'].should == stock.ticker
47
+ update_stock['name'].should == updated_stock[:name]
48
+
49
+ account_results = result['Account']
50
+ account_results.should_not be_nil
51
+ create_account = account_results['create'][0]
52
+ create_account['id'].should_not be_nil
53
+ create_account['tmp_id'].should == "12345abcde"
54
+ create_account['version'].should == 0
55
+ create_account['name'].should == created_account[:name]
56
+ end
53
57
 
54
- it "should sync dependent Models with new model where dependency exists" do
55
- updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
56
- stocks_sync_data = { :updated => [ updated_stock ] }
57
- created_trade = FactoryGirl.attributes_for(:trade, :stock_id => stock.id)
58
- created_trade['tmp_id'] = "12345abcde"
59
- trades_sync_data = { :created => [ created_trade ] }
60
- all_sync_data = { syncOrderKey => syncOrder, :Trade => trades_sync_data, :Stock => stocks_sync_data }
61
-
62
- result = Syncer.sync(all_sync_data)
58
+ it "should sync stock with updated account" do
59
+ created_account = FactoryGirl.attributes_for(:account)
60
+ created_account['tmp_id'] = "12345abcde"
61
+ accounts_sync_data = { :created => [ created_account ] }
62
+ updated_stock = stock.to_rjson
63
+ updated_stock['account_tmp_id'] = created_account['tmp_id']
64
+ stocks_sync_data = { :updated => [ updated_stock ] }
65
+ all_sync_data = { sync_order_key => sync_order, :Stock => stocks_sync_data, :Account => accounts_sync_data }
66
+
67
+ result = Syncer.sync(all_sync_data)
68
+
69
+ account_results = result['Account']
70
+ account_results.should_not be_nil
71
+ create_account = account_results['create'][0]
72
+ create_account['id'].should_not be_nil
73
+ create_account['tmp_id'].should == "12345abcde"
74
+ create_account['version'].should == 0
75
+ create_account['name'].should == created_account[:name]
76
+
77
+ stock_results = result['Stock']
78
+ stock_results.should_not be_nil
79
+ update_stock = stock_results['update'][0]
80
+ update_stock['account_id'].should == create_account['id']
81
+ update_stock['id'].should == stock.id
82
+ update_stock['version'].should == stock.version + 1
83
+ update_stock['market'].should == stock.market
84
+ update_stock['ticker'].should == stock.ticker
85
+ update_stock['name'].should == stock.name
86
+ end
87
+
88
+ it "should sync dependent Models with new model where dependency exists" do
89
+ updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
90
+ stocks_sync_data = { :updated => [ updated_stock ] }
91
+ created_trade = FactoryGirl.attributes_for(:trade, :stock_id => stock.id)
92
+ created_trade['tmp_id'] = "12345abcde"
93
+ trades_sync_data = { :created => [ created_trade ] }
94
+ all_sync_data = { sync_order_key => sync_order, :Trade => trades_sync_data, :Stock => stocks_sync_data }
95
+
96
+ result = Syncer.sync(all_sync_data)
63
97
 
64
- stock_results = result['Stock']
65
- update_stock = stock_results['update'][0]
66
- update_stock['id'].should == stock.id
67
- update_stock['version'].should == stock.version + 1
68
- update_stock['market'].should == stock.market
69
- update_stock['ticker'].should == stock.ticker
70
- update_stock['name'].should == updated_stock[:name]
71
-
72
- trade_results = result['Trade']
73
- create_trade = trade_results['create'][0]
74
- create_trade['id'].should_not be_nil
75
- create_trade['tmp_id'].should == "12345abcde"
76
- create_trade['version'].should == 0
77
- create_trade['stock_id'].should == stock.id
78
- create_trade['amount'].should == created_trade[:amount]
79
- create_trade['buy'].should == created_trade[:buy]
80
- # TODO maybe also test date that seems to return different ...
81
- end
98
+ stock_results = result['Stock']
99
+ update_stock = stock_results['update'][0]
100
+ update_stock['id'].should == stock.id
101
+ update_stock['version'].should == stock.version + 1
102
+ update_stock['market'].should == stock.market
103
+ update_stock['ticker'].should == stock.ticker
104
+ update_stock['name'].should == updated_stock[:name]
105
+
106
+ trade_results = result['Trade']
107
+ create_trade = trade_results['create'][0]
108
+ create_trade['id'].should_not be_nil
109
+ create_trade['tmp_id'].should == "12345abcde"
110
+ create_trade['version'].should == 0
111
+ create_trade['stock_id'].should == stock.id
112
+ create_trade['amount'].should == created_trade[:amount]
113
+ create_trade['buy'].should == created_trade[:buy]
114
+ # TODO maybe also test date that seems to return different ...
115
+ end
82
116
 
83
- it "should sync dependent Models when both models are new" do
84
- created_stock = FactoryGirl.attributes_for(:stock, :name => 'Teva Pharma')
85
- created_stock['tmp_id'] = "abcde12345"
86
- stocks_sync_data = { :saved => [ stock ], :created => [ created_stock ] }
87
- created_trade = FactoryGirl.attributes_for(:trade)
88
- created_trade['tmp_id'] = "12345abcde"
89
- created_trade['stock_tmp_id'] = "abcde12345"
90
- trades_sync_data = { :created => [ created_trade ] }
91
- all_sync_data = { syncOrderKey => syncOrder, :Trade => trades_sync_data, :Stock => stocks_sync_data }
92
-
93
- result = Syncer.sync(all_sync_data)
94
-
95
- stock_results = result['Stock']
96
- create_stock = stock_results['create'][0]
97
- create_stock['id'].should_not be_nil
98
- create_stock['tmp_id'].should == "abcde12345"
99
- create_stock['version'].should == 0
100
- create_stock['market'].should == created_stock[:market]
101
- create_stock['ticker'].should == created_stock[:ticker]
102
- create_stock['name'].should == created_stock[:name]
103
-
104
- trade_results = result['Trade']
105
- create_trade = trade_results['create'][0]
106
- create_trade['id'].should_not be_nil
107
- create_trade['tmp_id'].should == "12345abcde"
108
- create_trade['stock_id'].should == create_stock['id']
109
- create_trade['version'].should == 0
110
- create_trade['amount'].should == created_trade[:amount]
111
- create_trade['buy'].should == created_trade[:buy]
112
- # TODO maybe also test date that seems to return different ...
113
- end
114
-
115
- it "should sync dependent Models with new model where dependent exists and is updated to point to new Model" do
116
- updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version, :account_tmp_id => "12345abcde" }
117
- stocks_sync_data = { :updated => [ updated_stock ] }
118
- created_account = FactoryGirl.attributes_for(:account)
119
- created_account['tmp_id'] = "12345abcde"
120
- accounts_sync_data = { :created => [ created_account ] }
121
- all_sync_data = { syncOrderKey => syncOrder, :Account => accounts_sync_data, :Stock => stocks_sync_data }
122
-
123
- result = Syncer.sync(all_sync_data)
124
-
125
- account_results = result['Account']
126
- account_results.should_not be_nil
127
- create_account = account_results['create'][0]
128
- create_account['id'].should_not be_nil
129
- create_account['tmp_id'].should == "12345abcde"
130
- create_account['version'].should == 0
131
- create_account['name'].should == created_account[:name]
117
+ it "should sync dependent Models when both models are new" do
118
+ created_stock = FactoryGirl.attributes_for(:stock, :name => 'Teva Pharma')
119
+ created_stock['tmp_id'] = "abcde12345"
120
+ stocks_sync_data = { :saved => [ stock ], :created => [ created_stock ] }
121
+ created_trade = FactoryGirl.attributes_for(:trade)
122
+ created_trade['tmp_id'] = "12345abcde"
123
+ created_trade['stock_tmp_id'] = "abcde12345"
124
+ trades_sync_data = { :created => [ created_trade ] }
125
+ all_sync_data = { sync_order_key => sync_order, :Trade => trades_sync_data, :Stock => stocks_sync_data }
126
+
127
+ result = Syncer.sync(all_sync_data)
128
+
129
+ stock_results = result['Stock']
130
+ create_stock = stock_results['create'][0]
131
+ create_stock['id'].should_not be_nil
132
+ create_stock['tmp_id'].should == "abcde12345"
133
+ create_stock['version'].should == 0
134
+ create_stock['market'].should == created_stock[:market]
135
+ create_stock['ticker'].should == created_stock[:ticker]
136
+ create_stock['name'].should == created_stock[:name]
137
+
138
+ trade_results = result['Trade']
139
+ create_trade = trade_results['create'][0]
140
+ create_trade['id'].should_not be_nil
141
+ create_trade['tmp_id'].should == "12345abcde"
142
+ create_trade['stock_id'].should == create_stock['id']
143
+ create_trade['version'].should == 0
144
+ create_trade['amount'].should == created_trade[:amount]
145
+ create_trade['buy'].should == created_trade[:buy]
146
+ # TODO maybe also test date that seems to return different ...
147
+ end
148
+
149
+ it "should sync dependent Models with new model where dependent exists and is updated to point to new Model" do
150
+ updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version, :account_tmp_id => "12345abcde" }
151
+ stocks_sync_data = { :updated => [ updated_stock ] }
152
+ created_account = FactoryGirl.attributes_for(:account)
153
+ created_account['tmp_id'] = "12345abcde"
154
+ accounts_sync_data = { :created => [ created_account ] }
155
+ all_sync_data = { sync_order_key => sync_order, :Account => accounts_sync_data, :Stock => stocks_sync_data }
156
+
157
+ result = Syncer.sync(all_sync_data)
158
+
159
+ account_results = result['Account']
160
+ account_results.should_not be_nil
161
+ create_account = account_results['create'][0]
162
+ create_account['id'].should_not be_nil
163
+ create_account['tmp_id'].should == "12345abcde"
164
+ create_account['version'].should == 0
165
+ create_account['name'].should == created_account[:name]
132
166
 
133
- stock_results = result['Stock']
134
- update_stock = stock_results['update'][0]
135
- update_stock['id'].should == stock.id
136
- update_stock['version'].should == stock.version + 1
137
- update_stock['market'].should == stock.market
138
- update_stock['ticker'].should == stock.ticker
139
- update_stock['name'].should == updated_stock[:name]
140
- update_stock['account_id'].should == create_account['id']
167
+ stock_results = result['Stock']
168
+ update_stock = stock_results['update'][0]
169
+ update_stock['id'].should == stock.id
170
+ update_stock['version'].should == stock.version + 1
171
+ update_stock['market'].should == stock.market
172
+ update_stock['ticker'].should == stock.ticker
173
+ update_stock['name'].should == updated_stock[:name]
174
+ update_stock['account_id'].should == create_account['id']
175
+ end
176
+
177
+ it "returns a database state version number" do
178
+ updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
179
+ stocks_sync_data = { :updated => [ updated_stock ] }
180
+ all_sync_data = { sync_order_key => sync_order, :Stock => stocks_sync_data }
181
+
182
+ result = Syncer.sync(all_sync_data)
183
+
184
+ result[sync_version_key].should_not be_nil
185
+ result[sync_version_key].should be_kind_of(Integer)
186
+ end
187
+
188
+ it "increments the database state version on sync" do
189
+ updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
190
+ stocks_sync_data = { :updated => [ updated_stock ] }
191
+ all_sync_data = { sync_order_key => sync_order, :Stock => stocks_sync_data }
192
+
193
+ result = Syncer.sync(all_sync_data)
194
+
195
+ db_version = result[sync_version_key]
196
+
197
+ updated_stock = { :name => 'Teva Pharma 22', :id => stock.id, :version => stock.version + 1 }
198
+ stocks_sync_data = { :updated => [ updated_stock ] }
199
+ all_sync_data = { sync_order_key => sync_order, :Stock => stocks_sync_data }
200
+
201
+ result = Syncer.sync(all_sync_data)
202
+
203
+ result[sync_version_key].should == db_version + 1
204
+ end
205
+
206
+ it "does NOT increment the database state version if nothing is synced" do
207
+ updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
208
+ stocks_sync_data = { :updated => [ updated_stock ] }
209
+ all_sync_data = { sync_order_key => sync_order, :Stock => stocks_sync_data }
210
+
211
+ result = Syncer.sync(all_sync_data)
212
+
213
+ db_version = result[sync_version_key]
214
+
215
+ # empty sync
216
+ all_sync_data = { sync_order_key => sync_order }
217
+
218
+ result = Syncer.sync(all_sync_data)
219
+
220
+ result[sync_version_key].should == db_version
221
+ end
141
222
  end
142
- end
223
+
224
+ describe "version" do
225
+
226
+ it "returns the database state version" do
227
+ result = Syncer.version
228
+
229
+ result[sync_version_key].should_not be_nil
230
+ result[sync_version_key].should be_kind_of(Integer)
231
+ end
232
+
233
+ it "returns the same version as the last sync" do
234
+ updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
235
+ stocks_sync_data = { :updated => [ updated_stock ] }
236
+ all_sync_data = { sync_order_key => sync_order, :Stock => stocks_sync_data }
237
+
238
+ result = Syncer.sync(all_sync_data)
239
+
240
+ db_version = result[sync_version_key]
241
+
242
+ result = Syncer.version
243
+
244
+ result[sync_version_key].should == db_version
245
+ end
246
+
247
+ it "returns an incremented version after a sync" do
248
+ result = Syncer.version
249
+
250
+ db_version = result[sync_version_key]
251
+
252
+ updated_stock = { :name => 'Teva Pharma', :id => stock.id, :version => stock.version }
253
+ stocks_sync_data = { :updated => [ updated_stock ] }
254
+ all_sync_data = { sync_order_key => sync_order, :Stock => stocks_sync_data }
255
+
256
+ result = Syncer.sync(all_sync_data)
257
+
258
+ result = Syncer.version
259
+
260
+ result[sync_version_key].should == db_version + 1
261
+ end
262
+ end
263
+ end
264
+
265
+
266
+
data/syncer.gemspec CHANGED
@@ -4,8 +4,8 @@ require File.expand_path('../lib/syncer/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Harry Hornreich"]
6
6
  gem.email = ["harryhorn@gmail.com"]
7
- gem.description = %q{ActiveRecord syncer}
8
- gem.summary = %q{ActiveRecord syncer}
7
+ gem.description = %q{ActiveRecord syncer - prerelease}
8
+ gem.summary = %q{ActiveRecord syncer - prerelease}
9
9
  gem.homepage = ""
10
10
 
11
11
  gem.add_runtime_dependency 'activerecord', '>= 2.0'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syncer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
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: 2012-04-12 00:00:00.000000000 Z
12
+ date: 2013-06-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -91,7 +91,7 @@ dependencies:
91
91
  - - ~>
92
92
  - !ruby/object:Gem::Version
93
93
  version: 3.1.0
94
- description: ActiveRecord syncer
94
+ description: ActiveRecord syncer - prerelease
95
95
  email:
96
96
  - harryhorn@gmail.com
97
97
  executables: []
@@ -106,6 +106,7 @@ files:
106
106
  - README.md
107
107
  - Rakefile
108
108
  - lib/syncer.rb
109
+ - lib/syncer/sync_record.rb
109
110
  - lib/syncer/syncable_record.rb
110
111
  - lib/syncer/syncer.rb
111
112
  - lib/syncer/version.rb
@@ -141,7 +142,7 @@ rubyforge_project:
141
142
  rubygems_version: 1.8.21
142
143
  signing_key:
143
144
  specification_version: 3
144
- summary: ActiveRecord syncer
145
+ summary: ActiveRecord syncer - prerelease
145
146
  test_files:
146
147
  - spec/spec_helper.rb
147
148
  - spec/support/account.rb