orders 0.0.4 → 0.0.5
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/HISTORY +4 -0
- data/VERSION +1 -1
- data/lib/orders/indexed_list.rb +31 -10
- data/lib/orders/order_book.rb +16 -11
- data/lib/orders/order_book_item.rb +8 -1
- data/lib/orders/order_list.rb +15 -9
- data/spec/orders/indexed_list_spec.rb +4 -0
- data/spec/orders/order_book_item_spec.rb +11 -1
- data/spec/orders/order_book_spec.rb +21 -0
- data/spec/orders/order_list_spec.rb +8 -0
- data/spec/orders/shared_examples.rb +166 -116
- metadata +1 -1
data/HISTORY
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.5
|
data/lib/orders/indexed_list.rb
CHANGED
@@ -3,22 +3,43 @@ module Orders
|
|
3
3
|
# ������� ����� "������������� ������"
|
4
4
|
class IndexedList < Hash
|
5
5
|
|
6
|
+
def initialize
|
7
|
+
@iteration_mutex = Mutex.new
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
6
11
|
# Returns default list index for items
|
7
12
|
def index item
|
8
13
|
item.object_id
|
9
14
|
end
|
10
15
|
|
11
|
-
#
|
16
|
+
# Determines if item is worthy to be included in the list
|
17
|
+
def check item
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
# Adds new item to the list if it passes check
|
22
|
+
# (replaces item with the same index)
|
23
|
+
def add? item
|
24
|
+
@iteration_mutex.synchronize { self[index item] = item } if check item
|
25
|
+
end
|
26
|
+
|
27
|
+
# Adds new item to the list, returning self for easy chaining
|
12
28
|
def add item
|
13
|
-
|
29
|
+
add? item
|
14
30
|
self
|
15
31
|
end
|
16
32
|
|
17
33
|
alias << add
|
18
34
|
|
35
|
+
# Removes item from the list, returns nil if nothing removed
|
36
|
+
def remove? item
|
37
|
+
@iteration_mutex.synchronize { delete index item }
|
38
|
+
end
|
39
|
+
|
19
40
|
# Removes item from the list
|
20
41
|
def remove item
|
21
|
-
|
42
|
+
remove? item
|
22
43
|
self
|
23
44
|
end
|
24
45
|
|
@@ -30,20 +51,20 @@ module Orders
|
|
30
51
|
# Removes either all items, or items for which given block returns trueish value
|
31
52
|
def clear
|
32
53
|
if block_given?
|
33
|
-
each {|item| remove item if yield item}
|
54
|
+
each { |item| remove item if yield item }
|
34
55
|
else
|
35
|
-
|
56
|
+
each { |item| remove item }
|
36
57
|
end
|
37
58
|
end
|
38
59
|
|
39
|
-
# Yields list items in order of their index
|
60
|
+
# Yields list items (but NOT keys!) in order of their index
|
40
61
|
def each
|
62
|
+
keys_dup = []
|
63
|
+
@iteration_mutex.synchronize { keys_dup = keys.dup }
|
41
64
|
if block_given?
|
42
|
-
|
65
|
+
keys_dup.sort.each { |key| yield self[key] }
|
43
66
|
else
|
44
|
-
|
45
|
-
keys.sort.each { |key| ary << self[key] }
|
46
|
-
ary
|
67
|
+
keys_dup.sort.map { |key| self[key] }
|
47
68
|
end
|
48
69
|
end
|
49
70
|
|
data/lib/orders/order_book.rb
CHANGED
@@ -3,7 +3,9 @@ module Orders
|
|
3
3
|
# ������ ������� �� ����
|
4
4
|
class OrderBook < IndexedList
|
5
5
|
|
6
|
-
attr_accessor :
|
6
|
+
attr_accessor :changed
|
7
|
+
attr_reader :isin_id
|
8
|
+
alias isin isin_id
|
7
9
|
|
8
10
|
def initialize isin_id
|
9
11
|
@isin_id = isin_id
|
@@ -15,21 +17,24 @@ module Orders
|
|
15
17
|
item.price
|
16
18
|
end
|
17
19
|
|
18
|
-
def
|
19
|
-
|
20
|
+
def check item
|
21
|
+
item.price > 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def add? item
|
25
|
+
if super
|
20
26
|
@changed = true # Marking DOM as changed
|
21
27
|
item.order_book = self
|
22
|
-
|
23
|
-
else
|
24
|
-
self
|
28
|
+
item
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
32
|
+
def remove? item
|
33
|
+
if super
|
34
|
+
@changed = true # Marking DOM as changed
|
35
|
+
item.order_book = nil
|
36
|
+
item
|
37
|
+
end
|
33
38
|
end
|
34
39
|
end
|
35
40
|
end
|
@@ -11,9 +11,16 @@ module Orders
|
|
11
11
|
# order_book : tOrderBook;
|
12
12
|
class OrderBookItem
|
13
13
|
attr_accessor :isin_id, :id, :rev, :price, :volume, :buysell, :order_book
|
14
|
+
alias isin isin_id
|
15
|
+
alias isin= isin_id=
|
14
16
|
|
15
17
|
def initialize opts = {}
|
16
|
-
opts.each {|key, value| send "#{key}=", value}
|
18
|
+
opts.each { |key, value| send "#{key}=", value }
|
19
|
+
end
|
20
|
+
|
21
|
+
def price= val
|
22
|
+
val = val.to_f
|
23
|
+
@price = val.round == val ? val.to_i : val
|
17
24
|
end
|
18
25
|
|
19
26
|
def inspect
|
data/lib/orders/order_list.rb
CHANGED
@@ -7,7 +7,10 @@ module Orders
|
|
7
7
|
attr_accessor :order_books
|
8
8
|
|
9
9
|
def initialize
|
10
|
-
@order_books =
|
10
|
+
@order_books = Hash.new do |hash, key|
|
11
|
+
hash[key] = Orders::OrderBook.new(key)
|
12
|
+
hash[key]
|
13
|
+
end
|
11
14
|
super
|
12
15
|
end
|
13
16
|
|
@@ -15,18 +18,21 @@ module Orders
|
|
15
18
|
item.id
|
16
19
|
end
|
17
20
|
|
18
|
-
def add item
|
19
|
-
order_book = @order_books[item.isin_id] ||= Orders::OrderBook.new(item.isin_id)
|
21
|
+
def add? item
|
20
22
|
old_item = self[index item]
|
21
23
|
remove old_item if old_item # Remove old item with the same index(id)
|
22
|
-
|
23
|
-
|
24
|
+
if super
|
25
|
+
@order_books[item.isin_id].add item # Add item to appropriate order book
|
26
|
+
item
|
27
|
+
end
|
24
28
|
end
|
25
29
|
|
26
|
-
def remove item
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
def remove? item
|
31
|
+
if super
|
32
|
+
# Removing item from appropriate order book when it's deleted from order list
|
33
|
+
@order_books[item.isin_id].remove item
|
34
|
+
item
|
35
|
+
end
|
30
36
|
end
|
31
37
|
end
|
32
38
|
end
|
@@ -5,6 +5,7 @@ describe Orders::OrderBookItem do
|
|
5
5
|
subject { Orders::OrderBookItem.new }
|
6
6
|
|
7
7
|
its (:isin_id) {should == nil}
|
8
|
+
its (:isin) {should == nil}
|
8
9
|
its (:id) {should == nil}
|
9
10
|
its (:rev) {should == nil}
|
10
11
|
its (:price) {should == nil}
|
@@ -14,7 +15,7 @@ describe Orders::OrderBookItem do
|
|
14
15
|
end
|
15
16
|
|
16
17
|
describe '#new with opts' do
|
17
|
-
subject { Orders::OrderBookItem.new :
|
18
|
+
subject { Orders::OrderBookItem.new :isin => 1234567,
|
18
19
|
:id => 12,
|
19
20
|
:rev => 123,
|
20
21
|
:price => 1234,
|
@@ -24,6 +25,7 @@ describe Orders::OrderBookItem do
|
|
24
25
|
}
|
25
26
|
|
26
27
|
its (:isin_id) {should == 1234567}
|
28
|
+
its (:isin) {should == 1234567}
|
27
29
|
its (:id) {should == 12}
|
28
30
|
its (:rev) {should == 123}
|
29
31
|
its (:price) {should == 1234}
|
@@ -37,6 +39,14 @@ describe Orders::OrderBookItem do
|
|
37
39
|
subject.inspect.should == "12:1234>12345+"
|
38
40
|
end
|
39
41
|
end
|
42
|
+
|
43
|
+
describe '#price=' do
|
44
|
+
it 'converts given price to Integer if it is integer' do
|
45
|
+
subject.price = 1313.0
|
46
|
+
subject.price.should == 1313
|
47
|
+
subject.price.should be_an Integer
|
48
|
+
end
|
49
|
+
end
|
40
50
|
end
|
41
51
|
end
|
42
52
|
|
@@ -15,8 +15,29 @@ describe Orders::OrderBook do
|
|
15
15
|
it_behaves_like 'index_list'
|
16
16
|
|
17
17
|
its (:isin_id) {should == 123456}
|
18
|
+
its (:isin) {should == 123456}
|
18
19
|
its (:changed) {should == true}
|
19
20
|
|
21
|
+
it 'is possible to set its #changed attribute' do
|
22
|
+
subject.changed = false
|
23
|
+
subject.changed.should == false
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'but #isin(_id) attribute is not settable' do
|
27
|
+
expect { subject.isin_id = 1313 }.to raise_error NoMethodError
|
28
|
+
expect { subject.isin = 1313 }.to raise_error NoMethodError
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#check' do
|
32
|
+
it 'fails if item.price <= 0' do
|
33
|
+
subject.check(@zero_price_item).should == false
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns true otherwise' do
|
37
|
+
subject.check(@item).should == true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
20
41
|
describe 'adding item' do
|
21
42
|
before(:each) do
|
22
43
|
subject.add(@item).size.should == 1
|
@@ -63,6 +63,13 @@ describe Orders::OrderList do
|
|
63
63
|
|
64
64
|
specify { subject.order_books.should be_empty }
|
65
65
|
|
66
|
+
it 'returns order_book for any isin_id, even if it was not initialized' do
|
67
|
+
order_book = subject.order_books[1313]
|
68
|
+
order_book.should_not be_nil
|
69
|
+
order_book.should be_an Orders::OrderBook
|
70
|
+
order_book.should be_empty
|
71
|
+
end
|
72
|
+
|
66
73
|
describe 'adding item' do
|
67
74
|
let(:expected_number_of_books) { 1 }
|
68
75
|
|
@@ -129,6 +136,7 @@ describe Orders::OrderList do
|
|
129
136
|
subject.add new_item
|
130
137
|
order_book = subject.order_books[@item.isin_id]
|
131
138
|
order_book.should_not have_key @item.price
|
139
|
+
order_book.size.should == 0
|
132
140
|
end
|
133
141
|
end
|
134
142
|
end
|
@@ -1,5 +1,102 @@
|
|
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
|
+
|
20
|
+
it 'is synchronized to allow other threads iterations' do
|
21
|
+
expect_sync 3
|
22
|
+
remove_operation @item
|
23
|
+
remove_operation @item1
|
24
|
+
remove_operation @item2
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'deleting non-included items' do
|
29
|
+
it 'returns to indicate failure' do
|
30
|
+
remove_operation(@item2).should == returns_if_remove_failed #nil
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'removes nothing from the list' do
|
34
|
+
remove_operation(@item2)
|
35
|
+
subject.values.should include @item, @item1
|
36
|
+
subject.size.should == 2
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
shared_examples_for 'adding items' do
|
42
|
+
it 'runs check on item' do
|
43
|
+
subject.should_receive(:check).with(@item)
|
44
|
+
add_operation(@item)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'adds item only if it passes check' do
|
48
|
+
subject.should_receive(:check).with(@item).and_return(false)
|
49
|
+
add_operation(@item).should == returns_if_add_failed
|
50
|
+
subject.values.should_not include @item
|
51
|
+
subject.should_not have_key item_index
|
52
|
+
subject.size.should == 0
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'returns either self or item in case of success' do
|
56
|
+
add_operation(@item).should == returns_if_add_success
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'adds items to indexed list' do
|
60
|
+
add_operation(@item)
|
61
|
+
subject.values.should include @item
|
62
|
+
subject.should have_key item_index
|
63
|
+
subject[item_index].should == @item
|
64
|
+
subject.size.should == 1
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not add same items twice' do
|
68
|
+
subject.add(@item)
|
69
|
+
add_operation(@item)
|
70
|
+
subject.values.should include @item
|
71
|
+
subject.should have_key item_index
|
72
|
+
subject[item_index].should == @item
|
73
|
+
subject.size.should == 1
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'even adds item using << as alias to #add' do
|
77
|
+
subject << @item
|
78
|
+
subject.values.should include @item
|
79
|
+
subject.should have_key item_index
|
80
|
+
subject[item_index].should == @item
|
81
|
+
subject.size.should == 1
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'is synchronized to allow other threads iterations' do
|
85
|
+
expect_sync 3
|
86
|
+
add_operation @item
|
87
|
+
add_operation @item1
|
88
|
+
subject << @item2
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
1
92
|
shared_examples_for 'index_list' do
|
2
93
|
|
94
|
+
def expect_sync times = 1
|
95
|
+
mutex = subject.instance_variable_get(:@iteration_mutex)
|
96
|
+
mutex.should be_a Mutex
|
97
|
+
mutex.should_receive(:synchronize).exactly(times).times
|
98
|
+
end
|
99
|
+
|
3
100
|
describe 'items get/set' do
|
4
101
|
it 'returns item from list by their index' do
|
5
102
|
subject.add(@item)[item_index].should == @item
|
@@ -20,119 +117,91 @@ shared_examples_for 'index_list' do
|
|
20
117
|
end
|
21
118
|
end
|
22
119
|
|
23
|
-
describe '#add' do
|
24
|
-
|
25
|
-
subject.add
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'adds items to indexed list' do
|
29
|
-
subject.add(@item)
|
30
|
-
subject.values.should include @item
|
31
|
-
subject[item_index].should == @item
|
32
|
-
subject.size.should == 1
|
120
|
+
describe '#add?' do
|
121
|
+
def add_operation item
|
122
|
+
subject.add? item
|
33
123
|
end
|
34
124
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
subject[item_index].should == @item
|
39
|
-
subject.size.should == 1
|
40
|
-
end
|
125
|
+
let(:returns_if_add_success) { @item }
|
126
|
+
let(:returns_if_add_failed) { nil }
|
127
|
+
it_behaves_like 'adding items'
|
41
128
|
end
|
42
129
|
|
43
|
-
describe '#
|
44
|
-
|
45
|
-
|
46
|
-
context 'removing existing item' do
|
47
|
-
it 'returns self for easy method chaining' do
|
48
|
-
subject.remove(@item).should == subject
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'removes item from indexed list' do
|
52
|
-
subject.remove(@item)
|
53
|
-
subject.values.should_not include @item
|
54
|
-
subject[item_index].should == nil
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'does not remove other items' do
|
58
|
-
subject.remove(@item)
|
59
|
-
subject.values.should include @item1
|
60
|
-
subject.size.should == 1
|
61
|
-
end
|
130
|
+
describe '#add' do
|
131
|
+
def add_operation item
|
132
|
+
subject.add item
|
62
133
|
end
|
63
134
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'removes nothing from the list' do
|
70
|
-
subject.remove(@item2)
|
71
|
-
subject.values.should include @item, @item1
|
72
|
-
subject.size.should == 2
|
73
|
-
end
|
74
|
-
end
|
135
|
+
let(:returns_if_add_success) { subject }
|
136
|
+
let(:returns_if_add_failed) { subject }
|
137
|
+
it_behaves_like 'adding items'
|
75
138
|
end
|
76
139
|
|
77
|
-
|
78
|
-
before(:each) { subject.add(@item).add(@item1) }
|
79
|
-
context 'deleting existing item by index' do
|
80
|
-
|
81
|
-
it 'returns self' do
|
82
|
-
subject.delete_by_index(item_index).should == subject
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'deletes item with given index from the list' do
|
86
|
-
subject.delete_by_index(item_index)
|
87
|
-
subject.values.should_not include @item
|
88
|
-
subject[item_index].should == nil
|
89
|
-
end
|
140
|
+
context 'with couple of items in the list' do
|
141
|
+
before(:each) { subject.add(@item).add(@item1).size.should == 2 }
|
90
142
|
|
91
|
-
|
92
|
-
|
93
|
-
subject.
|
143
|
+
describe '#remove?' do
|
144
|
+
def remove_operation item
|
145
|
+
subject.remove? item
|
94
146
|
end
|
95
147
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
subject.size.should == 1
|
100
|
-
end
|
148
|
+
let(:returns_if_remove_success) { @item }
|
149
|
+
let(:returns_if_remove_failed) { nil }
|
150
|
+
it_behaves_like 'removing items'
|
101
151
|
end
|
102
152
|
|
103
|
-
|
104
|
-
|
105
|
-
subject.
|
153
|
+
describe '#remove' do
|
154
|
+
def remove_operation item
|
155
|
+
subject.remove item
|
106
156
|
end
|
107
157
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
subject.size.should == 2
|
112
|
-
end
|
158
|
+
let(:returns_if_remove_success) { subject }
|
159
|
+
let(:returns_if_remove_failed) { subject }
|
160
|
+
it_behaves_like 'removing items'
|
113
161
|
end
|
114
|
-
end
|
115
162
|
|
116
|
-
|
117
|
-
|
163
|
+
describe '#clear' do
|
164
|
+
context 'without block' do
|
165
|
+
it 'removes all items from list' do
|
166
|
+
subject.clear
|
167
|
+
subject.should be_empty
|
168
|
+
end
|
118
169
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
170
|
+
it 'ensures that #remove method is called once for each item in list' do
|
171
|
+
subject.should_receive(:remove).twice
|
172
|
+
subject.clear
|
173
|
+
end
|
123
174
|
end
|
124
175
|
|
125
|
-
|
126
|
-
|
127
|
-
|
176
|
+
context 'with block given' do
|
177
|
+
it 'yields items from list in index order' do
|
178
|
+
@count = 0
|
179
|
+
@items = []
|
180
|
+
subject.clear do |item|
|
181
|
+
@count += 1
|
182
|
+
@items << item
|
183
|
+
end
|
184
|
+
@count.should == 2
|
185
|
+
if subject.index(@item) < subject.index(@item1)
|
186
|
+
@items.should == [@item, @item1]
|
187
|
+
else
|
188
|
+
@items.should == [@item1, @item]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'only removes items for which given block returns true' do
|
193
|
+
subject.should_receive(:remove).once
|
194
|
+
subject.clear { |item| true if item == @item1 }
|
195
|
+
subject.should have_key item_index
|
196
|
+
end
|
128
197
|
end
|
129
198
|
end
|
130
199
|
|
131
|
-
|
200
|
+
describe '#each' do
|
132
201
|
it 'yields items from list in index order' do
|
133
202
|
@count = 0
|
134
203
|
@items = []
|
135
|
-
subject.
|
204
|
+
subject.each do |item|
|
136
205
|
@count += 1
|
137
206
|
@items << item
|
138
207
|
end
|
@@ -144,37 +213,18 @@ shared_examples_for 'index_list' do
|
|
144
213
|
end
|
145
214
|
end
|
146
215
|
|
147
|
-
it '
|
148
|
-
subject.
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
end
|
154
|
-
|
155
|
-
describe '#each' do
|
156
|
-
before(:each) { subject.add(@item).add(@item1) }
|
157
|
-
|
158
|
-
it 'yields items from list in index order' do
|
159
|
-
@count = 0
|
160
|
-
@items = []
|
161
|
-
subject.each do |item|
|
162
|
-
@count += 1
|
163
|
-
@items << item
|
164
|
-
end
|
165
|
-
@count.should == 2
|
166
|
-
if subject.index(@item) < subject.index(@item1)
|
167
|
-
@items.should == [@item, @item1]
|
168
|
-
else
|
169
|
-
@items.should == [@item1, @item]
|
216
|
+
it 'returns list item in array, sorted by their index, if no block given' do
|
217
|
+
if subject.index(@item) < subject.index(@item1)
|
218
|
+
subject.each.should == [@item, @item1]
|
219
|
+
else
|
220
|
+
subject.each.should == [@item1, @item]
|
221
|
+
end
|
170
222
|
end
|
171
|
-
end
|
172
223
|
|
173
|
-
|
174
|
-
|
175
|
-
subject.each
|
176
|
-
|
177
|
-
subject.each.should == [@item1, @item]
|
224
|
+
it 'is synchronized to allow other threads add items while we`re iterating' do
|
225
|
+
expect_sync 2
|
226
|
+
subject.each { |item|}
|
227
|
+
subject.each
|
178
228
|
end
|
179
229
|
end
|
180
230
|
end
|