Skip to content

Commit fb05aa0

Browse files
committed
Add Update operation. Fix Insert operation. Add tests.
The Insert operation was previously using the subscript behavior. This meant object being inserted were not inserted but overwrote the value at that index. In order to allow for proper behavior for both subscripting and inserting and extra Operation of .Update was added. Tests were augmented to ensure proper behavior.
1 parent f1f7c48 commit fb05aa0

File tree

4 files changed

+89
-7
lines changed

4 files changed

+89
-7
lines changed

ReactiveArray/Operation.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public enum Operation<T>: CustomDebugStringConvertible {
1212

1313
case Append(value: T)
1414
case Insert(value: T, atIndex: Int)
15+
case Update(value: T, atIndex: Int)
1516
case RemoveElement(atIndex: Int)
1617

1718
public func map<U>(mapper: T -> U) -> Operation<U> {
@@ -21,6 +22,8 @@ public enum Operation<T>: CustomDebugStringConvertible {
2122
result = Operation<U>.Append(value: mapper(value))
2223
case .Insert(let value, let index):
2324
result = Operation<U>.Insert(value: mapper(value), atIndex: index)
25+
case .Update(let value, let index):
26+
result = Operation<U>.Update(value: mapper(value), atIndex: index)
2427
case .RemoveElement(let index):
2528
result = Operation<U>.RemoveElement(atIndex: index)
2629
}
@@ -34,6 +37,8 @@ public enum Operation<T>: CustomDebugStringConvertible {
3437
description = ".Append(value:\(value))"
3538
case .Insert(let value, let index):
3639
description = ".Insert(value: \(value), atIndex:\(index))"
40+
case .Update(let value, let index):
41+
description = ".Update(value: \(value), atIndex:\(index))"
3742
case .RemoveElement(let index):
3843
description = ".RemoveElement(atIndex:\(index))"
3944
}
@@ -46,6 +51,8 @@ public enum Operation<T>: CustomDebugStringConvertible {
4651
return value
4752
case .Insert(let value, _):
4853
return value
54+
case .Update(let value, _):
55+
return value
4956
default:
5057
return Optional.None
5158
}
@@ -59,6 +66,8 @@ public func ==<T: Equatable>(lhs: Operation<T>, rhs: Operation<T>) -> Bool {
5966
return leftValue == rightValue
6067
case (.Insert(let leftValue, let leftIndex), .Insert(let rightValue, let rightIndex)):
6168
return leftIndex == rightIndex && leftValue == rightValue
69+
case (.Update(let leftValue, let leftIndex), .Update(let rightValue, let rightIndex)):
70+
return leftIndex == rightIndex && leftValue == rightValue
6271
case (.RemoveElement(let leftIndex), .RemoveElement(let rightIndex)):
6372
return leftIndex == rightIndex
6473
default:

ReactiveArray/ReactiveArray.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public final class ReactiveArray<T>: CollectionType, MutableCollectionType, Cust
103103
return _elements[index]
104104
}
105105
set(newValue) {
106-
insert(newValue, atIndex: index)
106+
update(newValue, atIndex: index)
107107
}
108108
}
109109

@@ -117,6 +117,11 @@ public final class ReactiveArray<T>: CollectionType, MutableCollectionType, Cust
117117
_sink(Event.Next(operation))
118118
}
119119

120+
public func update(element: T, atIndex index: Int) {
121+
let operation: Operation<T> = .Update(value: element, atIndex: index)
122+
_sink(Event.Next(operation))
123+
}
124+
120125
public func removeAtIndex(index:Int) {
121126
let operation: Operation<T> = .RemoveElement(atIndex: index)
122127
_sink(Event.Next(operation))
@@ -136,6 +141,8 @@ public final class ReactiveArray<T>: CollectionType, MutableCollectionType, Cust
136141
_elements.append(value)
137142
_mutableCount.value = _elements.count
138143
case .Insert(let value, let index):
144+
_elements.insert(value, atIndex: index)
145+
case .Update(let value, let index):
139146
_elements[index] = value
140147
case .RemoveElement(let index):
141148
_elements.removeAtIndex(index)

ReactiveArrayTests/OperationSpec.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,21 @@ class OperationSpec: QuickSpec {
4949

5050
}
5151

52+
context("when the operation is an Update operation") {
53+
54+
beforeEach {
55+
operation = Operation.Update(value: 10, atIndex: 5)
56+
}
57+
58+
it("maps the value to be updated") {
59+
let mappedOperation = operation.map { $0 * 2 }
60+
61+
let areEqual = mappedOperation == Operation.Update(value: 20, atIndex: 5)
62+
expect(areEqual).to(beTrue())
63+
}
64+
65+
}
66+
5267
context("when the operation is a Delete operation") {
5368

5469
beforeEach {
@@ -92,6 +107,18 @@ class OperationSpec: QuickSpec {
92107

93108
}
94109

110+
context("when the operation is an Update operation") {
111+
112+
beforeEach {
113+
operation = Operation.Update(value: 10, atIndex: 5)
114+
}
115+
116+
it("returns the updated value") {
117+
expect(operation.value).to(equal(10))
118+
}
119+
120+
}
121+
95122
context("when the operation is an Remove operation") {
96123

97124
beforeEach {

ReactiveArrayTests/ReactiveArraySpec.swift

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ private func waitForOperation<T>(fromProducer producer: SignalProducer<Operation
1515
when: () -> (),
1616
onAppend: T -> () = { fail("Invalid operation type: .Append(\($0))") },
1717
onInsert: (T, Int) -> () = { fail("Invalid operation type: .Insert(\($0), \($1))") },
18+
onUpdate: (T, Int) -> () = { fail("Invalid operation type: .Update(\($0), \($1))") },
1819
onDelete: Int -> () = { fail("Invalid operation type: .Delete(\($0))") }) {
1920

2021
waitUntil { done in
@@ -24,6 +25,8 @@ private func waitForOperation<T>(fromProducer producer: SignalProducer<Operation
2425
onAppend(value)
2526
case .Insert(let value, let index):
2627
onInsert(value, index)
28+
case .Update(let value, let index):
29+
onUpdate(value, index)
2730
case .RemoveElement(let index):
2831
onDelete(index)
2932
}
@@ -38,19 +41,21 @@ private func waitForOperation<T>(fromSignal signal: Signal<Operation<T>, NoError
3841
when: () -> (),
3942
onAppend: T -> () = { fail("Invalid operation type: .Append(\($0))") },
4043
onInsert: (T, Int) -> () = { fail("Invalid operation type: .Insert(\($0), \($1))") },
44+
onUpdate: (T, Int) -> () = { fail("Invalid operation type: .Update(\($0), \($1))") },
4145
onDelete: Int -> () = { fail("Invalid operation type: .Delete(\($0))") }) {
4246

4347
let producer = SignalProducer<Operation<T>, NoError> { (observer, disposable) in signal.observe(observer) }
44-
waitForOperation(fromProducer: producer, when: when, onAppend: onAppend, onInsert: onInsert, onDelete: onDelete)
48+
waitForOperation(fromProducer: producer, when: when, onAppend: onAppend, onInsert: onInsert, onUpdate: onUpdate, onDelete: onDelete)
4549
}
4650

4751
private func waitForOperation<T>(fromArray array: ReactiveArray<T>,
4852
when: () -> (),
4953
onAppend: T -> () = { fail("Invalid operation type: .Append(\($0))") },
5054
onInsert: (T, Int) -> () = { fail("Invalid operation type: .Insert(\($0), \($1))") },
55+
onUpdate: (T, Int) -> () = { fail("Invalid operation type: .Update(\($0), \($1))") },
5156
onDelete: Int -> () = { fail("Invalid operation type: .Delete(\($0))") }) {
5257

53-
waitForOperation(fromSignal: array.signal, when: when, onAppend: onAppend, onInsert: onInsert, onDelete: onDelete)
58+
waitForOperation(fromSignal: array.signal, when: when, onAppend: onAppend, onInsert: onInsert, onUpdate: onUpdate, onDelete: onDelete)
5459
}
5560

5661
class ReactiveArraySpec: QuickSpec {
@@ -101,6 +106,7 @@ class ReactiveArraySpec: QuickSpec {
101106
array.insert(5, atIndex: 1)
102107

103108
expect(array[1]).to(equal(5))
109+
expect(array.toArray()).to(equal([1,5,2,3,4]))
104110
}
105111

106112
it("signals an insert operation") {
@@ -171,15 +177,16 @@ class ReactiveArraySpec: QuickSpec {
171177
array[1] = 5
172178

173179
expect(array[1]).to(equal(5))
180+
expect(array.toArray()).to(equal([1,5,3,4]))
174181
}
175182

176-
it("signals an insert operation") {
183+
it("signals an update operation") {
177184
waitForOperation(
178185
fromArray: array,
179186
when: {
180187
array[1] = 5
181188
},
182-
onInsert: { (value, index) in
189+
onUpdate: { (value, index) in
183190
expect(value).to(equal(5))
184191
expect(index).to(equal(1))
185192
}
@@ -210,7 +217,7 @@ class ReactiveArraySpec: QuickSpec {
210217
when: {
211218
array[1] = 5
212219
},
213-
onInsert: { (value, index) in
220+
onUpdate: { (value, index) in
214221
expect(value).to(equal(15))
215222
expect(index).to(equal(1))
216223
}
@@ -365,6 +372,23 @@ class ReactiveArraySpec: QuickSpec {
365372

366373
}
367374

375+
context("when an update operation is executed") {
376+
377+
it("signals the operations") {
378+
waitForOperation(
379+
fromSignal: array.signal,
380+
when: {
381+
array[1] = 5
382+
},
383+
onUpdate: { (value, index) in
384+
expect(value).to(equal(5))
385+
expect(index).to(equal(1))
386+
}
387+
)
388+
}
389+
390+
}
391+
368392
context("when a delete operation is executed") {
369393

370394
it("signals the operations") {
@@ -411,7 +435,7 @@ class ReactiveArraySpec: QuickSpec {
411435
.take(1)
412436
.collect()
413437
.startWithNext { counts in
414-
expect(counts).to(equal([countBeforeOperation + 1]))
438+
expect(counts).to(equal([countBeforeOperation + 2]))
415439
done()
416440
}
417441

@@ -422,6 +446,21 @@ class ReactiveArraySpec: QuickSpec {
422446

423447
}
424448

449+
context("when an update operation is executed") {
450+
451+
beforeEach {
452+
producer = producer.skip(1)
453+
}
454+
455+
it("updates the count") {
456+
waitUntil { done in
457+
array[1] = 656
458+
expect(array.count).to(equal(countBeforeOperation))
459+
done()
460+
}
461+
}
462+
463+
}
425464

426465
context("when an append operation is executed") {
427466

0 commit comments

Comments
 (0)