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 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