fin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +26 -0
  2. data/HISTORY +27 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +36 -0
  5. data/Rakefile +25 -0
  6. data/VERSION +1 -0
  7. data/features/order_book.feature +9 -0
  8. data/features/step_definitions/order_book_steps.rb +0 -0
  9. data/features/support/env.rb +10 -0
  10. data/features/support/world.rb +12 -0
  11. data/lib/fin.rb +13 -0
  12. data/lib/fin/book.rb +50 -0
  13. data/lib/fin/book_manager.rb +42 -0
  14. data/lib/fin/changed_list.rb +49 -0
  15. data/lib/fin/container_list.rb +33 -0
  16. data/lib/fin/deal_list.rb +18 -0
  17. data/lib/fin/indexed_list.rb +74 -0
  18. data/lib/fin/models/deal.rb +75 -0
  19. data/lib/fin/models/instrument.rb +78 -0
  20. data/lib/fin/models/model.rb +39 -0
  21. data/lib/fin/models/money_limit.rb +81 -0
  22. data/lib/fin/models/order.rb +45 -0
  23. data/lib/fin/models/position.rb +57 -0
  24. data/lib/fin/order_list.rb +17 -0
  25. data/lib/legacy.rb +443 -0
  26. data/lib/version.rb +8 -0
  27. data/spec/fin/book_spec.rb +215 -0
  28. data/spec/fin/changed_list_spec.rb +16 -0
  29. data/spec/fin/container_list_spec.rb +63 -0
  30. data/spec/fin/deal_list_spec.rb +102 -0
  31. data/spec/fin/indexed_list_spec.rb +20 -0
  32. data/spec/fin/models/deal_spec.rb +140 -0
  33. data/spec/fin/models/instrument_spec.rb +54 -0
  34. data/spec/fin/models/model_spec.rb +109 -0
  35. data/spec/fin/models/money_limit_spec.rb +143 -0
  36. data/spec/fin/models/order_spec.rb +67 -0
  37. data/spec/fin/models/position_spec.rb +74 -0
  38. data/spec/fin/models/shared_examples.rb +5 -0
  39. data/spec/fin/order_list_spec.rb +140 -0
  40. data/spec/fin/shared_examples.rb +355 -0
  41. data/spec/spec_helper.rb +17 -0
  42. data/tasks/common.rake +18 -0
  43. data/tasks/doc.rake +14 -0
  44. data/tasks/gem.rake +40 -0
  45. data/tasks/git.rake +34 -0
  46. data/tasks/spec.rake +16 -0
  47. data/tasks/version.rake +71 -0
  48. metadata +155 -0
@@ -0,0 +1,5 @@
1
+ shared_examples_for 'model' do
2
+ it 'has index method' do
3
+ expect {subject.index }.to_not raise_error
4
+ end
5
+ end
@@ -0,0 +1,140 @@
1
+ require 'spec_helper'
2
+ require 'fin/shared_examples.rb'
3
+
4
+ describe Fin::OrderList do
5
+ subject { Fin::OrderList.new }
6
+ let(:item_index) { @item.id }
7
+ let (:new_item_book_index) {new_item.price}
8
+
9
+ before(:each) do
10
+ @item = Fin::Order.new :isin_id => 1234, :id => 0, :price => 20
11
+ @item1 = Fin::Order.new :isin_id => 1234, :id => 1, :price => 10
12
+ @same_isin_item = @item1
13
+ @item2 = Fin::Order.new :isin_id => 5678, :id => 2, :price => 10
14
+ @diff_isin_item = @item2
15
+ @zero_price_item = Fin::Order.new :isin_id => 1234, :id => 3, :price => 0
16
+ @repeat_item = Fin::Order.new :isin_id => 1234, :id => 0, :price => 13
17
+ @repeat_zero_price_item = Fin::Order.new :isin_id => 1234, :id => 0, :price => 0
18
+ end
19
+
20
+ it_behaves_like 'changed_list'
21
+
22
+ specify { subject.books.should be_empty }
23
+
24
+ it 'returns order_book for any isin_id, even if it was not initialized' do
25
+ order_book = subject.books[1313]
26
+ order_book.should_not be_nil
27
+ order_book.should be_an Fin::Book
28
+ order_book.should be_empty
29
+ end
30
+
31
+ describe 'adding item' do
32
+ let(:expected_number_of_books) { 1 }
33
+
34
+ context 'to empty OrderList' do
35
+ let(:new_item) { @item }
36
+
37
+ it_behaves_like 'adding_item_to_books'
38
+
39
+ context 'with zero price' do
40
+ let(:new_item) { @zero_price_item }
41
+
42
+ it_behaves_like 'not_adding_item_to_books'
43
+ end
44
+ end
45
+
46
+ context 'to non-empty OrderList' do
47
+ before(:each) do
48
+ subject.add(@item).size.should == 1
49
+ end
50
+
51
+ context 'with existing isin' do
52
+ let(:new_item) { @same_isin_item }
53
+
54
+ it_behaves_like 'adding_item_to_books'
55
+ end
56
+
57
+ context 'with different isin' do
58
+ let(:new_item) { @diff_isin_item }
59
+ let(:expected_number_of_books) { 2 }
60
+
61
+ it_behaves_like 'adding_item_to_books'
62
+ end
63
+
64
+ context 'with zero price' do
65
+ let(:new_item) { @zero_price_item }
66
+
67
+ it_behaves_like 'not_adding_item_to_books'
68
+ end
69
+
70
+ context 'with repeat isin/id and non-zero price' do
71
+ let(:new_item) { @repeat_item }
72
+
73
+ it_behaves_like 'adding_item_to_books'
74
+
75
+ it 'changes price of item in list' do
76
+ subject.add new_item
77
+ subject[item_index].price.should_not == @item.price
78
+ subject[item_index].price.should == @repeat_item.price
79
+ end
80
+
81
+ it 'removes old item from appropriate order book' do
82
+ subject.add new_item
83
+ order_book = subject.books[@item.isin_id]
84
+ order_book.should_not have_key @item.price
85
+ end
86
+ end
87
+
88
+ context 'with repeat isin/id and zero price' do
89
+ let(:new_item) { @repeat_zero_price_item }
90
+
91
+ it_behaves_like 'not_adding_item_to_books'
92
+
93
+ it 'removes old item from appropriate order book' do
94
+ subject.add new_item
95
+ order_book = subject.books[@item.isin_id]
96
+ order_book.should_not have_key @item.price
97
+ order_book.size.should == 0
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ describe 'removing item' do
104
+ before(:each) do
105
+ subject.add(@item).size.should == 1
106
+ end
107
+
108
+ context 'with existing item' do
109
+ let(:unwanted_item) { @item }
110
+ let(:expected_size) { 0 }
111
+
112
+ it 'deletes item from the list' do
113
+ subject.remove(unwanted_item)
114
+ subject[unwanted_item.id].should == nil
115
+ subject.size.should == expected_size
116
+ end
117
+
118
+ it 'removes item from its related order book' do
119
+ subject.remove(unwanted_item)
120
+ subject.books[unwanted_item.isin_id].should_not have_key unwanted_item.price
121
+ end
122
+ end
123
+
124
+ context 'with non-existing item' do
125
+ let(:unwanted_item) { @item1 }
126
+ let(:expected_size) { 1 }
127
+
128
+ it 'still returns the list itself' do
129
+ subject.remove(unwanted_item).should == subject
130
+ end
131
+
132
+ it 'deletes nothing from the list' do
133
+ subject.remove(unwanted_item)
134
+ subject[item_index].should == @item
135
+ subject.size.should == expected_size
136
+ end
137
+ end
138
+ end
139
+ end
140
+
@@ -0,0 +1,355 @@
1
+ shared_examples_for 'removing items' do
2
+ context 'removing existing item' do
3
+ it 'returns to indicate success' do
4
+ remove_operation(@item).should == returns_if_remove_success #@item
5
+ end
6
+
7
+ it 'removes item from indexed list' do
8
+ remove_operation(@item)
9
+ subject.values.should_not include @item
10
+ subject.should_not have_key item_index
11
+ subject[item_index].should == nil
12
+ end
13
+
14
+ it 'does not remove other items' do
15
+ remove_operation(@item)
16
+ subject.values.should include @item1
17
+ subject.size.should == 1
18
+ end
19
+ end
20
+
21
+ context 'deleting non-included items' do
22
+ it 'returns to indicate failure' do
23
+ remove_operation(@item2).should == returns_if_remove_failed #nil
24
+ end
25
+
26
+ it 'removes nothing from the list' do
27
+ remove_operation(@item2)
28
+ subject.values.should include @item, @item1
29
+ subject.size.should == 2
30
+ end
31
+
32
+ it 'does not raise if given nonsense instead of proper item to delete' do
33
+ [nil, 0, 123, 'nonsense', [1, 2, 3], {:this => 'sucks'}].each do |non_item|
34
+ remove_operation(non_item)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ shared_examples_for 'adding items' do
41
+ it 'runs check on item' do
42
+ subject.should_receive(:check).with(@item)
43
+ add_operation(@item)
44
+ end
45
+
46
+ it 'adds item only if it passes check' do
47
+ subject.should_receive(:check).with(@item).and_return(false)
48
+ add_operation(@item).should == returns_if_add_failed
49
+ subject.values.should_not include @item
50
+ subject.should_not have_key item_index
51
+ subject.size.should == 0
52
+ end
53
+
54
+ it 'returns either self or item in case of success' do
55
+ add_operation(@item).should == returns_if_add_success
56
+ end
57
+
58
+ it 'adds items to indexed list' do
59
+ add_operation(@item)
60
+ subject.values.should include @item
61
+ subject.should have_key item_index
62
+ subject[item_index].should == @item
63
+ subject.size.should == 1
64
+ end
65
+
66
+ it 'does not add same items twice' do
67
+ subject.add(@item)
68
+ add_operation(@item)
69
+ subject.values.should include @item
70
+ subject.should have_key item_index
71
+ subject[item_index].should == @item
72
+ subject.size.should == 1
73
+ end
74
+
75
+ it 'even adds item using << as alias to #add' do
76
+ subject << @item
77
+ subject.values.should include @item
78
+ subject.should have_key item_index
79
+ subject[item_index].should == @item
80
+ subject.size.should == 1
81
+ end
82
+ end
83
+
84
+
85
+ shared_examples_for 'changed_list' do
86
+ it_behaves_like 'index_list'
87
+
88
+ it 'has changed and updated attributes, true at creation' do
89
+ subject.changed.should == true
90
+ subject.updated.should == true
91
+ end
92
+
93
+ it 'has change_count attribute, 0 at creation' do
94
+ subject.change_count.should == 0
95
+ end
96
+
97
+ it 'allows setting of updated attribute externally' do
98
+ subject.updated = false
99
+ subject.updated.should == false
100
+ subject.updated = true
101
+ subject.updated.should == true
102
+ end
103
+
104
+ context 'when item is being added to list' do
105
+ before(:each) { subject.changed = false; subject.updated = false }
106
+
107
+ context 'successfully' do
108
+ it 'auto-sets changed attribute, but not updated attribute' do
109
+ subject.add @item
110
+ subject.changed.should == true
111
+ subject.updated.should == false
112
+ end
113
+
114
+ it 'increases changed_count' do
115
+ subject.add @item
116
+ subject.change_count.should == 1
117
+ end
118
+ end
119
+
120
+ unless described_class == Fin::ChangedList # it can add ANYTHING...
121
+ context 'unsuccessfully' do
122
+ [nil, "none", 1313, [1, 2, 3]].each do |non_item|
123
+ it 'doesn`t set changed or updated attributes' do
124
+ subject.add non_item
125
+ subject.changed.should == false
126
+ subject.updated.should == false
127
+ end
128
+
129
+ it 'doesn`t increase changed_count' do
130
+ subject.add non_item
131
+ subject.change_count.should == 0
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ context 'when item is being removed from list' do
139
+ before(:each) do
140
+ subject.add(@item)
141
+ subject.changed = false
142
+ subject.updated = false
143
+ end
144
+
145
+ context 'successfully' do
146
+ it 'auto-sets changed attribute, but not updated attribute' do
147
+ subject.remove @item
148
+ subject.changed.should == true
149
+ subject.updated.should == false
150
+ end
151
+
152
+ it 'increases changed_count' do
153
+ subject.remove @item
154
+ subject.change_count.should == 2 # added, then removed
155
+ end
156
+ end
157
+
158
+ context 'unsuccessfully' do
159
+ [nil, "none", 1313, [1, 2, 3], @item1].each do |non_item|
160
+ it ' doesn`t set changed or updated attributes' do
161
+ subject.remove non_item
162
+ subject.changed.should == false
163
+ subject.updated.should == false
164
+ end
165
+
166
+ it 'doesn`t increase changed_count' do
167
+ subject.remove non_item
168
+ subject.change_count.should == 1 # initial addition, but not removal
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+
176
+ shared_examples_for 'index_list' do
177
+
178
+ describe 'items get/set' do
179
+ it 'returns item from list by their index' do
180
+ p subject.add(@item)
181
+ p item_index
182
+ subject[item_index].should == @item
183
+ end
184
+
185
+ it 'returns nil when item with requested index not in collection' do
186
+ subject[item_index].should == nil
187
+ end
188
+
189
+ it 'is not possible to add item into collection directly' do
190
+ expect { subject[item_index] = @item }.to raise_error NoMethodError
191
+ end
192
+ end
193
+
194
+ describe '#index' do
195
+ it 'indexes arbitrary items by their index (redefined in subclasses)' do
196
+ subject.index(@item).should == item_index
197
+ end
198
+ end
199
+
200
+ describe '#add?' do
201
+ def add_operation item
202
+ subject.add? item
203
+ end
204
+
205
+ let(:returns_if_add_success) { @item }
206
+ let(:returns_if_add_failed) { nil }
207
+ it_behaves_like 'adding items'
208
+ end
209
+
210
+ describe '#add' do
211
+ def add_operation item
212
+ subject.add item
213
+ end
214
+
215
+ let(:returns_if_add_success) { subject }
216
+ let(:returns_if_add_failed) { subject }
217
+ it_behaves_like 'adding items'
218
+ end
219
+
220
+ context 'with couple of items in the list' do
221
+ before(:each) { subject.add(@item).add(@item1).size.should == 2 }
222
+
223
+ describe '#remove?' do
224
+ def remove_operation item
225
+ subject.remove? item
226
+ end
227
+
228
+ let(:returns_if_remove_success) { @item }
229
+ let(:returns_if_remove_failed) { nil }
230
+ it_behaves_like 'removing items'
231
+ end
232
+
233
+ describe '#remove' do
234
+ def remove_operation item
235
+ subject.remove item
236
+ end
237
+
238
+ let(:returns_if_remove_success) { subject }
239
+ let(:returns_if_remove_failed) { subject }
240
+ it_behaves_like 'removing items'
241
+ end
242
+
243
+ describe '#clear' do
244
+ context 'without block' do
245
+ it 'removes all items from list' do
246
+ subject.clear
247
+ subject.should be_empty
248
+ end
249
+
250
+ it 'ensures that #remove method is called once for each item in list' do
251
+ subject.should_receive(:remove).twice
252
+ subject.clear
253
+ end
254
+ end
255
+
256
+ context 'with block given' do
257
+ it 'yields items from list in index order' do
258
+ @count = 0
259
+ @items = []
260
+ subject.clear do |item|
261
+ @count += 1
262
+ @items << item
263
+ end
264
+ @count.should == 2
265
+ if subject.index(@item) < subject.index(@item1)
266
+ @items.should == [@item, @item1]
267
+ else
268
+ @items.should == [@item1, @item]
269
+ end
270
+ end
271
+
272
+ it 'only removes items for which given block returns true' do
273
+ subject.should_receive(:remove).once
274
+ subject.clear { |item| true if item == @item1 }
275
+ subject.should have_key item_index
276
+ end
277
+ end
278
+ end
279
+
280
+ describe '#each' do
281
+ it 'yields items from list in index order' do
282
+ @count = 0
283
+ @items = []
284
+ subject.each do |item|
285
+ @count += 1
286
+ @items << item
287
+ end
288
+ @count.should == 2
289
+ if subject.index(@item) < subject.index(@item1)
290
+ @items.should == [@item, @item1]
291
+ else
292
+ @items.should == [@item1, @item]
293
+ end
294
+ end
295
+
296
+ it 'returns list item in array, sorted by their index, if no block given' do
297
+ if subject.index(@item) < subject.index(@item1)
298
+ subject.each.should == [@item, @item1]
299
+ else
300
+ subject.each.should == [@item1, @item]
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end
306
+
307
+ shared_examples_for 'adding_item_to_books' do
308
+ it_behaves_like 'creating_books'
309
+
310
+ it 'adds this item to appropriate book' do
311
+ subject.add new_item
312
+ book = subject.books[new_item.isin_id]
313
+ book.should have_key new_item_book_index
314
+ book[new_item_book_index].should == new_item
315
+ end
316
+ end
317
+
318
+ shared_examples_for 'not_adding_item_to_books' do
319
+ it_behaves_like 'creating_books'
320
+
321
+ it 'does not add this item to list' do
322
+ subject.add new_item
323
+ book = subject.books[new_item.isin_id]
324
+ book.should_not have_key new_item_book_index
325
+ book[new_item_book_index].should == nil
326
+ end
327
+ end
328
+
329
+ shared_examples_for 'creating_books' do
330
+ it 'creates appropriate order book' do
331
+ subject.add(new_item)
332
+ subject.books.should have(expected_number_of_books).books
333
+ book = subject.books[new_item.isin_id]
334
+ book.should be_an Fin::Book
335
+ book.isin_id.should == new_item.isin_id
336
+ end
337
+
338
+ it 'sets item`s book property correctly' do
339
+ subject.add(new_item)
340
+ book = subject.books[new_item.isin_id]
341
+ if new_item_book_index == 0
342
+ case subject
343
+ when Fin::OrderList
344
+ new_item.book.should == nil
345
+ else
346
+ new_item.book.should == book
347
+ end
348
+ else
349
+ new_item.book.should == book
350
+ end
351
+ end
352
+ end
353
+
354
+
355
+