syncer 0.0.1 → 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.
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