orders 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -17,3 +17,7 @@
17
17
  == 0.0.4 / 2011-03-21
18
18
 
19
19
  * Item#to_s added
20
+
21
+ == 0.0.5 / 2011-03-21
22
+
23
+ * Iteration-safety added
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.0.5
@@ -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
- # Adds new item to the list (replaces item with the same index)
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
- self[index item] = item
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
- delete index item
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
- each_value { |item| remove item }
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
- keys.sort.each { |key| yield self[key] }
65
+ keys_dup.sort.each { |key| yield self[key] }
43
66
  else
44
- ary = []
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
 
@@ -3,7 +3,9 @@ module Orders
3
3
  # ������ ������� �� ����
4
4
  class OrderBook < IndexedList
5
5
 
6
- attr_accessor :isin_id, :changed
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 add item
19
- if item.price > 0
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
- super
23
- else
24
- self
28
+ item
25
29
  end
26
30
  end
27
31
 
28
- # Does not call super!
29
- def remove item
30
- @changed = true if delete index item # Marking DOM as changed
31
- item.order_book = nil
32
- self
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
@@ -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
- order_book.add item # Add item to appropriate order book
23
- super
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
- # Removing item from appropriate order book when it's deleted from order list
28
- @order_books[item.isin_id].remove item if delete index item
29
- self
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
@@ -12,5 +12,9 @@ describe Orders::IndexedList do
12
12
  end
13
13
 
14
14
  it_behaves_like 'index_list'
15
+
16
+ it 'checks all items as worthy, by default' do
17
+ subject.check(@item).should == true
18
+ end
15
19
  end
16
20
 
@@ -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 :isin_id => 1234567,
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
- it 'returns self for easy method chaining' do
25
- subject.add(@item).should == subject
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
- it 'adds items to using alias <<' do
36
- subject << @item
37
- subject.values.should include @item
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 '#remove' do
44
- before(:each) { subject.add(@item).add(@item1).size.should == 2 }
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
- context 'deleting non-existing items by index' do
65
- it 'still returns self' do
66
- subject.remove(@item2).should == subject
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
- describe '#delete_by_index' do
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
- it 'calls #remove on this item to properly remove it' do
92
- subject.should_receive(:remove).with(@item)
93
- subject.delete_by_index(item_index)
143
+ describe '#remove?' do
144
+ def remove_operation item
145
+ subject.remove? item
94
146
  end
95
147
 
96
- it 'does not remove other items' do
97
- subject.delete_by_index(item_index)
98
- subject.values.should include @item1
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
- context 'deleting non-existing items by index' do
104
- it 'returns nil' do
105
- subject.delete_by_index(subject.index @item2).should == nil
153
+ describe '#remove' do
154
+ def remove_operation item
155
+ subject.remove item
106
156
  end
107
157
 
108
- it 'removes nothing from the list' do
109
- subject.delete_by_index(subject.index @item2)
110
- subject.values.should include @item, @item1
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
- describe '#clear' do
117
- before(:each) { subject.add(@item).add(@item1) }
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
- context 'without block' do
120
- it 'removes all items from list' do
121
- subject.clear
122
- subject.should be_empty
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
- it 'ensures that #remove method is called once for each item in list' do
126
- subject.should_receive(:remove).twice
127
- subject.clear
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
- context 'with block given' do
200
+ describe '#each' do
132
201
  it 'yields items from list in index order' do
133
202
  @count = 0
134
203
  @items = []
135
- subject.clear do |item|
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 'only removes items for which given block returns true' do
148
- subject.should_receive(:remove).once
149
- subject.clear { |item| true if item == @item1 }
150
- subject.should have_key item_index
151
- end
152
- end
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
- it 'returns list item in array, sorted by their index, if no block given' do
174
- if subject.index(@item) < subject.index(@item1)
175
- subject.each.should == [@item, @item1]
176
- else
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
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: orders
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.4
5
+ version: 0.0.5
6
6
  platform: ruby
7
7
  authors:
8
8
  - arvicco