fin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+