From aac5f06d46a6a2c3ad991a8a10013d490d342f89 Mon Sep 17 00:00:00 2001 From: Smara9dus Date: Fri, 13 Apr 2018 21:12:29 -0500 Subject: [PATCH 1/6] Part 0 complete part 1 almost complete, problem with UpdateNewEntryField --- elm-todo/Todo.elm | 39 ++++-------------------------- ruby-todo/lib/engine.rb | 5 ++-- ruby-todo/lib/messages.rb | 48 ++++++++++++++++++++++++++++++------- ruby-todo/lib/model.rb | 4 ++-- ruby-todo/test/todo_test.rb | 2 +- 5 files changed, 51 insertions(+), 47 deletions(-) diff --git a/elm-todo/Todo.elm b/elm-todo/Todo.elm index 3a07b24..5e3d72c 100755 --- a/elm-todo/Todo.elm +++ b/elm-todo/Todo.elm @@ -1,13 +1,10 @@ port module Todo exposing (..) {-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering. - This application is broken up into three key parts: - 1. Model - a full definition of the application's state 2. Update - a way to step the application state forward 3. View - a way to visualize our application state with HTML - This clean division of concerns is a core part of Elm. You can read more about this in -} @@ -45,7 +42,6 @@ type alias Model = type alias Entry = { description : String , completed : Bool - , editing : Bool , id : Int } @@ -62,7 +58,6 @@ newEntry : String -> Int -> Entry newEntry desc id = { description = desc , completed = False - , editing = False , id = id } @@ -76,7 +71,6 @@ to them. -} type Msg = UpdateNewEntryField String - | EditingEntry Int Bool | UpdateEntry Int String | Add | Delete Int @@ -102,19 +96,6 @@ update msg model = UpdateNewEntryField str -> { model | newEntryField = str } - EditingEntry id isEditing -> - let - updateEntry t = - if t.id == id then - { t | editing = isEditing } - else - t - - focus = - Dom.focus ("todo-" ++ toString id) - in - { model | entries = List.map updateEntry model.entries } - UpdateEntry id task -> let updateEntry t = @@ -229,7 +210,7 @@ viewKeyedEntry todo = viewEntry : Entry -> Html Msg viewEntry todo = li - [ classList [ ( "completed", todo.completed ), ( "editing", todo.editing ) ] ] + [ classList [ ( "completed", todo.completed ) ] ] [ div [ class "view" ] [ input @@ -240,24 +221,14 @@ viewEntry todo = ] [] , label - [ onDoubleClick (EditingEntry todo.id True) ] + [] [ text todo.description ] , button [ class "destroy" , onClick (Delete todo.id) ] [] - ] - , input - [ class "edit" - , value todo.description - , name "title" - , id ("todo-" ++ toString todo.id) - , onInput (UpdateEntry todo.id) - , onBlur (EditingEntry todo.id False) - , onEnter (EditingEntry todo.id False) - ] - [] + ] ] @@ -313,7 +284,7 @@ viewControlsClear entriesCompleted = infoFooter : Html msg infoFooter = footer [ class "info" ] - [ p [] [ text "Double-click to edit a todo" ] + [ p [] [ text "Double-clicking won't save you now!" ] , p [] [ text "Written by " , a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ] @@ -322,4 +293,4 @@ infoFooter = [ text "Part of " , a [ href "http://todomvc.com" ] [ text "TodoMVC" ] ] - ] + ] \ No newline at end of file diff --git a/ruby-todo/lib/engine.rb b/ruby-todo/lib/engine.rb index a257c20..5667416 100644 --- a/ruby-todo/lib/engine.rb +++ b/ruby-todo/lib/engine.rb @@ -5,8 +5,9 @@ def self.run(*args) def self.run_with_history(model, messages) messages.map do |msg| - msg.apply_to(model) - model + model_copy = model.clone + msg.apply_to(model_copy) + model_copy end end end diff --git a/ruby-todo/lib/messages.rb b/ruby-todo/lib/messages.rb index abe85de..bc200d9 100644 --- a/ruby-todo/lib/messages.rb +++ b/ruby-todo/lib/messages.rb @@ -2,13 +2,19 @@ module Msg class Add def apply_to(model) + new_entries = model.entries + new_id = model.next_id unless model.new_entry_field.blank? - model.entries << Entry.new( + new_entries << Entry.new( description: model.new_entry_field, id: model.next_id) + new_id += 1 end - model.next_id += 1 - model.new_entry_field = "" + new_model = Model.new( + entries: new_entries, + new_entry_field: "", + next_id: new_id) + new_model end end @@ -20,7 +26,11 @@ def initialize(str) attr_reader :str def apply_to(model) - model.new_entry_field = str + new_model = Model.new( + entries: model.entries, + new_entry_field: str, + next_id: model.next_id) + new_model end end @@ -32,11 +42,21 @@ def initialize(id, is_completed) attr_reader :id, :is_completed def apply_to(model) - model.entries.each do |entry| + new_entries = model.entries + new_entries.each do |entry| if entry.id == id - entry.completed = is_completed + updated_entry = Entry.new( + description: entry.description, + completed: is_completed, + id: entry.id) + new_entries[new_entries.index(entry)] = updated_entry end end + new_model = Model.new( + entries: new_entries, + new_entry_field: model.new_entry_field, + next_id: model.next_id) + new_model end end @@ -48,13 +68,25 @@ def initialize(id) attr_reader :id def apply_to(model) - model.entries.reject! { |e| e.id == id } + new_entries = model.entries + new_entries.reject! { |e| e.id == id } + new_model = Model.new( + entries: new_entries, + new_entry_field: model.new_entry_field, + next_id: model.next_id) + new_model end end class DeleteAllCompleted def apply_to(model) - model.entries.reject!(&:completed) + new_entries = model.entries + new_entries.reject!(&:completed) + new_model = Model.new( + entries: new_entries, + new_entry_field: model.new_entry_field, + next_id: model.next_id) + new_model end end diff --git a/ruby-todo/lib/model.rb b/ruby-todo/lib/model.rb index 75a6085..e64ca6a 100644 --- a/ruby-todo/lib/model.rb +++ b/ruby-todo/lib/model.rb @@ -5,7 +5,7 @@ def initialize(entries: [], new_entry_field: "", next_id: nil) (entries.lazy.map(&:id).max || -1) + 1 end - attr_accessor :entries, :new_entry_field, :next_id + attr_reader :entries, :new_entry_field, :next_id def ==(other) !other.nil? && @@ -20,7 +20,7 @@ def initialize(id:, description:, completed: false) @description, @id, @completed = description, id, completed end - attr_accessor :description, :completed, :id + attr_reader :description, :completed, :id def ==(other) !other.nil? && diff --git a/ruby-todo/test/todo_test.rb b/ruby-todo/test/todo_test.rb index f7436c7..d3351dc 100644 --- a/ruby-todo/test/todo_test.rb +++ b/ruby-todo/test/todo_test.rb @@ -91,7 +91,7 @@ end it "supports time travel" do - skip "Mutable model does not support time travel" + # skip "Mutable model does not support time travel" actual_history = Engine.run_with_history(Model.new, [ Msg::UpdateNewEntryField.new("go forward in time"), From 7711c22046e392f36f24a8be8747241579f35b31 Mon Sep 17 00:00:00 2001 From: Smara9dus Date: Sat, 14 Apr 2018 12:02:45 -0500 Subject: [PATCH 2/6] Parts 1 and 2 mostly complete Some tests do not pass --- swift-todo/Sources/Todo/Engine.swift | 4 ++-- swift-todo/Sources/Todo/Message.swift | 25 +++++++++++++++------- swift-todo/Sources/Todo/Model.swift | 4 ++-- swift-todo/Tests/TodoTests/TodoTests.swift | 4 +--- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/swift-todo/Sources/Todo/Engine.swift b/swift-todo/Sources/Todo/Engine.swift index e3b14a7..72b7601 100644 --- a/swift-todo/Sources/Todo/Engine.swift +++ b/swift-todo/Sources/Todo/Engine.swift @@ -14,8 +14,8 @@ struct Engine { static func runWithHistory(on model: Model, applying messages: [Message]) -> [Model] { return messages.map { message in - message.apply(to: model) - return model + let new_model = message.apply(to: model) + return new_model } } } diff --git a/swift-todo/Sources/Todo/Message.swift b/swift-todo/Sources/Todo/Message.swift index 92dd868..356956a 100644 --- a/swift-todo/Sources/Todo/Message.swift +++ b/swift-todo/Sources/Todo/Message.swift @@ -14,30 +14,39 @@ enum Message { case delete(Int) case deleteAllCompleted - func apply(to model: Model) { + func apply(to model: Model) -> Model { switch(self) { case .add: + var newEntries = model.entries + var newNextID = model.nextID if !model.newEntryField.isBlank() { - model.entries.append(Entry(id: model.nextID, description: model.newEntryField)) + newEntries.append(Entry(id: model.nextID, description: model.newEntryField)) + newNextID += 1 } - model.nextID += 1 - model.newEntryField = "" + return Model(nextID: newNextID, newEntryField: "", entries: newEntries) case .updateNewEntryField(let str): - model.newEntryField = str + return Model(nextID: model.nextID, newEntryField: str, entries: model.entries) case .check(let id, let isCompleted): + var newEntries = model.entries for entry in model.entries { if(entry.id == id) { - entry.completed = isCompleted + let i = newEntries.index(of: entry) + newEntries[i!] = Entry(id: id, description: entry.description, completed: isCompleted) } } + return Model(nextID: model.nextID, newEntryField: model.newEntryField, entries: newEntries) case .delete(let id): - model.entries.remove { $0.id == id } + var newEntries = model.entries + newEntries.remove { $0.id == id } + return Model(nextID: model.nextID, newEntryField: model.newEntryField, entries: newEntries) case .deleteAllCompleted: - model.entries.remove { $0.completed } + var newEntries = model.entries + newEntries.remove { $0.completed } + return Model(nextID: model.nextID, newEntryField: model.newEntryField, entries: newEntries) } } } diff --git a/swift-todo/Sources/Todo/Model.swift b/swift-todo/Sources/Todo/Model.swift index 1cca801..d50d863 100644 --- a/swift-todo/Sources/Todo/Model.swift +++ b/swift-todo/Sources/Todo/Model.swift @@ -1,4 +1,4 @@ -class Model { +struct Model { var entries: [Entry] var newEntryField: String var nextID: Int @@ -11,7 +11,7 @@ class Model { } } -class Entry { +struct Entry { var id: Int var description: String var completed: Bool diff --git a/swift-todo/Tests/TodoTests/TodoTests.swift b/swift-todo/Tests/TodoTests/TodoTests.swift index 9220517..e19fddd 100644 --- a/swift-todo/Tests/TodoTests/TodoTests.swift +++ b/swift-todo/Tests/TodoTests/TodoTests.swift @@ -77,7 +77,7 @@ class TodoTests: XCTestCase { XCTAssertEqual([11], newModel.entries.map { $0.id }) } -/* + func testTimeTravel() { let actualHistory = Engine.runWithHistory(on: Model(), applying: [ .updateNewEntryField("go forward in time"), @@ -145,6 +145,4 @@ class TodoTests: XCTestCase { XCTAssertEqual(expected, actual, "History mismatch at step \(index)") } } -*/ - } From bf1e1170d8ad2ee17ee34e9abd42becd25ad2855 Mon Sep 17 00:00:00 2001 From: Smara9dus Date: Tue, 17 Apr 2018 13:20:03 -0500 Subject: [PATCH 3/6] Part 1 done All tests pass --- ruby-todo/lib/engine.rb | 6 ++--- ruby-todo/lib/messages.rb | 32 +++++++++++---------------- swift-todo/Sources/Todo/Engine.swift | 4 ++-- swift-todo/Sources/Todo/Message.swift | 12 +++++----- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/ruby-todo/lib/engine.rb b/ruby-todo/lib/engine.rb index 5667416..a639cc3 100644 --- a/ruby-todo/lib/engine.rb +++ b/ruby-todo/lib/engine.rb @@ -4,10 +4,8 @@ def self.run(*args) end def self.run_with_history(model, messages) - messages.map do |msg| - model_copy = model.clone - msg.apply_to(model_copy) - model_copy + messages.map do |msg| + model = msg.apply_to(model) end end end diff --git a/ruby-todo/lib/messages.rb b/ruby-todo/lib/messages.rb index bc200d9..be64769 100644 --- a/ruby-todo/lib/messages.rb +++ b/ruby-todo/lib/messages.rb @@ -2,19 +2,17 @@ module Msg class Add def apply_to(model) - new_entries = model.entries + new_entries = model.entries.dup new_id = model.next_id unless model.new_entry_field.blank? new_entries << Entry.new( description: model.new_entry_field, id: model.next_id) - new_id += 1 end - new_model = Model.new( - entries: new_entries, - new_entry_field: "", - next_id: new_id) - new_model + return Model.new( + entries: new_entries, + new_entry_field: "", + next_id: new_id += 1) end end @@ -26,11 +24,10 @@ def initialize(str) attr_reader :str def apply_to(model) - new_model = Model.new( - entries: model.entries, + return Model.new( + entries: model.entries.dup, new_entry_field: str, next_id: model.next_id) - new_model end end @@ -42,7 +39,7 @@ def initialize(id, is_completed) attr_reader :id, :is_completed def apply_to(model) - new_entries = model.entries + new_entries = model.entries.dup new_entries.each do |entry| if entry.id == id updated_entry = Entry.new( @@ -52,11 +49,10 @@ def apply_to(model) new_entries[new_entries.index(entry)] = updated_entry end end - new_model = Model.new( + return Model.new( entries: new_entries, new_entry_field: model.new_entry_field, next_id: model.next_id) - new_model end end @@ -68,25 +64,23 @@ def initialize(id) attr_reader :id def apply_to(model) - new_entries = model.entries + new_entries = model.entries.dup new_entries.reject! { |e| e.id == id } - new_model = Model.new( + return Model.new( entries: new_entries, new_entry_field: model.new_entry_field, next_id: model.next_id) - new_model end end class DeleteAllCompleted def apply_to(model) - new_entries = model.entries + new_entries = model.entries.dup new_entries.reject!(&:completed) - new_model = Model.new( + return Model.new( entries: new_entries, new_entry_field: model.new_entry_field, next_id: model.next_id) - new_model end end diff --git a/swift-todo/Sources/Todo/Engine.swift b/swift-todo/Sources/Todo/Engine.swift index 72b7601..6b55e9e 100644 --- a/swift-todo/Sources/Todo/Engine.swift +++ b/swift-todo/Sources/Todo/Engine.swift @@ -14,8 +14,8 @@ struct Engine { static func runWithHistory(on model: Model, applying messages: [Message]) -> [Model] { return messages.map { message in - let new_model = message.apply(to: model) - return new_model + let model = message.apply(to: model) + return model } } } diff --git a/swift-todo/Sources/Todo/Message.swift b/swift-todo/Sources/Todo/Message.swift index 356956a..1363f85 100644 --- a/swift-todo/Sources/Todo/Message.swift +++ b/swift-todo/Sources/Todo/Message.swift @@ -17,26 +17,26 @@ enum Message { func apply(to model: Model) -> Model { switch(self) { case .add: - var newEntries = model.entries - var newNextID = model.nextID if !model.newEntryField.isBlank() { + var newEntries = model.entries newEntries.append(Entry(id: model.nextID, description: model.newEntryField)) - newNextID += 1 + return Model(nextID: (model.nextID + 1), newEntryField: "", entries: newEntries) } - return Model(nextID: newNextID, newEntryField: "", entries: newEntries) + return model case .updateNewEntryField(let str): return Model(nextID: model.nextID, newEntryField: str, entries: model.entries) case .check(let id, let isCompleted): var newEntries = model.entries - for entry in model.entries { + for entry in newEntries { if(entry.id == id) { let i = newEntries.index(of: entry) newEntries[i!] = Entry(id: id, description: entry.description, completed: isCompleted) + return Model(nextID: model.nextID, newEntryField: model.newEntryField, entries: newEntries) } } - return Model(nextID: model.nextID, newEntryField: model.newEntryField, entries: newEntries) + return model case .delete(let id): var newEntries = model.entries From 2accddcaac9472dc0304632ea8302a89acef2d91 Mon Sep 17 00:00:00 2001 From: Smara9dus Date: Wed, 18 Apr 2018 12:09:48 -0500 Subject: [PATCH 4/6] Part 2 update Some tests not working --- swift-todo/Sources/Todo/Message.swift | 67 ++++++----- swift-todo/Tests/TodoTests/TodoTests.swift | 134 ++++++++++----------- 2 files changed, 103 insertions(+), 98 deletions(-) diff --git a/swift-todo/Sources/Todo/Message.swift b/swift-todo/Sources/Todo/Message.swift index 1363f85..d710fae 100644 --- a/swift-todo/Sources/Todo/Message.swift +++ b/swift-todo/Sources/Todo/Message.swift @@ -4,7 +4,6 @@ // // Created by Paul on 2018/4/4. // - import Foundation enum Message { @@ -13,40 +12,46 @@ enum Message { case check(Int, Bool) case delete(Int) case deleteAllCompleted - + func apply(to model: Model) -> Model { switch(self) { - case .add: - if !model.newEntryField.isBlank() { + case .add: + var newModel = model + if !model.newEntryField.isBlank() { + newModel.entries.append(Entry(id: model.nextID, description: model.newEntryField)) + } + newModel.nextID += 1 + newModel.newEntryField = "" + return newModel + + case .updateNewEntryField(let str): + var newModel = model + newModel.newEntryField = str + return newModel + + case .check(let id, let isCompleted): + var newModel = model + for entry in model.entries { + if(entry.id == id) { + var newEntry = entry + newEntry.completed = isCompleted var newEntries = model.entries - newEntries.append(Entry(id: model.nextID, description: model.newEntryField)) - return Model(nextID: (model.nextID + 1), newEntryField: "", entries: newEntries) + let i = newEntries.index(of: entry) + newEntries.insert(newEntry, at: i!) + newModel.entries = newEntries } - return model - - case .updateNewEntryField(let str): - return Model(nextID: model.nextID, newEntryField: str, entries: model.entries) - - case .check(let id, let isCompleted): - var newEntries = model.entries - for entry in newEntries { - if(entry.id == id) { - let i = newEntries.index(of: entry) - newEntries[i!] = Entry(id: id, description: entry.description, completed: isCompleted) - return Model(nextID: model.nextID, newEntryField: model.newEntryField, entries: newEntries) - } - } - return model - - case .delete(let id): - var newEntries = model.entries - newEntries.remove { $0.id == id } - return Model(nextID: model.nextID, newEntryField: model.newEntryField, entries: newEntries) - - case .deleteAllCompleted: - var newEntries = model.entries - newEntries.remove { $0.completed } - return Model(nextID: model.nextID, newEntryField: model.newEntryField, entries: newEntries) + } + return newModel + + case .delete(let id): + var newModel = model + newModel.entries.remove { $0.id == id } + return newModel + + case .deleteAllCompleted: + var newModel = model + newModel.entries.remove { $0.completed } + return newModel } } } diff --git a/swift-todo/Tests/TodoTests/TodoTests.swift b/swift-todo/Tests/TodoTests/TodoTests.swift index e19fddd..342d921 100644 --- a/swift-todo/Tests/TodoTests/TodoTests.swift +++ b/swift-todo/Tests/TodoTests/TodoTests.swift @@ -78,71 +78,71 @@ class TodoTests: XCTestCase { } - func testTimeTravel() { - let actualHistory = Engine.runWithHistory(on: Model(), applying: [ - .updateNewEntryField("go forward in time"), - .add, - .add, // no effect - .updateNewEntryField("delete this item"), - .add, - .delete(2), - .updateNewEntryField("go in time"), - .updateNewEntryField("go backward in time"), - .add, - .check(0, true), - .check(3, true), - .check(3, false), - .deleteAllCompleted - ]) - - let expectedHistory = [ - Model(nextID: 0, newEntryField: "go forward in time", entries: []), - Model(nextID: 1, newEntryField: "", entries: [ - Entry(id: 0, description: "go forward in time", completed: false) - ]), - Model(nextID: 2, newEntryField: "", entries: [ - Entry(id: 0, description: "go forward in time", completed: false) - ]), - Model(nextID: 2, newEntryField: "delete this item", entries: [ - Entry(id: 0, description: "go forward in time", completed: false) - ]), - Model(nextID: 3, newEntryField: "", entries: [ - Entry(id: 0, description: "go forward in time", completed: false), - Entry(id: 2, description: "delete this item", completed: false) - ]), - Model(nextID: 3, newEntryField: "", entries: [ - Entry(id: 0, description: "go forward in time", completed: false) - ]), - Model(nextID: 3, newEntryField: "go in time", entries: [ - Entry(id: 0, description: "go forward in time", completed: false) - ]), - Model(nextID: 3, newEntryField: "go backward in time", entries: [ - Entry(id: 0, description: "go forward in time", completed: false) - ]), - Model(nextID: 4, newEntryField: "", entries: [ - Entry(id: 0, description: "go forward in time", completed: false), - Entry(id: 3, description: "go backward in time", completed: false) - ]), - Model(nextID: 4, newEntryField: "", entries: [ - Entry(id: 0, description: "go forward in time", completed: true), - Entry(id: 3, description: "go backward in time", completed: false) - ]), - Model(nextID: 4, newEntryField: "", entries: [ - Entry(id: 0, description: "go forward in time", completed: true), - Entry(id: 3, description: "go backward in time", completed: true) - ]), - Model(nextID: 4, newEntryField: "", entries: [ - Entry(id: 0, description: "go forward in time", completed: true), - Entry(id: 3, description: "go backward in time", completed: false) - ]), - Model(nextID: 4, newEntryField: "", entries: [ - Entry(id: 3, description: "go backward in time", completed: false) - ]), - ] - - XCTAssertEqual(expectedHistory.count, actualHistory.count) - for (index, (expected, actual)) in zip(expectedHistory, actualHistory).enumerated() { - XCTAssertEqual(expected, actual, "History mismatch at step \(index)") - } - } +// func testTimeTravel() { +// let actualHistory = Engine.runWithHistory(on: Model(), applying: [ +// .updateNewEntryField("go forward in time"), +// .add, +// .add, // no effect +// .updateNewEntryField("delete this item"), +// .add, +// .delete(2), +// .updateNewEntryField("go in time"), +// .updateNewEntryField("go backward in time"), +// .add, +// .check(0, true), +// .check(3, true), +// .check(3, false), +// .deleteAllCompleted +// ]) +// +// let expectedHistory = [ +// Model(nextID: 0, newEntryField: "go forward in time", entries: []), +// Model(nextID: 1, newEntryField: "", entries: [ +// Entry(id: 0, description: "go forward in time", completed: false) +// ]), +// Model(nextID: 2, newEntryField: "", entries: [ +// Entry(id: 0, description: "go forward in time", completed: false) +// ]), +// Model(nextID: 2, newEntryField: "delete this item", entries: [ +// Entry(id: 0, description: "go forward in time", completed: false) +// ]), +// Model(nextID: 3, newEntryField: "", entries: [ +// Entry(id: 0, description: "go forward in time", completed: false), +// Entry(id: 2, description: "delete this item", completed: false) +// ]), +// Model(nextID: 3, newEntryField: "", entries: [ +// Entry(id: 0, description: "go forward in time", completed: false) +// ]), +// Model(nextID: 3, newEntryField: "go in time", entries: [ +// Entry(id: 0, description: "go forward in time", completed: false) +// ]), +// Model(nextID: 3, newEntryField: "go backward in time", entries: [ +// Entry(id: 0, description: "go forward in time", completed: false) +// ]), +// Model(nextID: 4, newEntryField: "", entries: [ +// Entry(id: 0, description: "go forward in time", completed: false), +// Entry(id: 3, description: "go backward in time", completed: false) +// ]), +// Model(nextID: 4, newEntryField: "", entries: [ +// Entry(id: 0, description: "go forward in time", completed: true), +// Entry(id: 3, description: "go backward in time", completed: false) +// ]), +// Model(nextID: 4, newEntryField: "", entries: [ +// Entry(id: 0, description: "go forward in time", completed: true), +// Entry(id: 3, description: "go backward in time", completed: true) +// ]), +// Model(nextID: 4, newEntryField: "", entries: [ +// Entry(id: 0, description: "go forward in time", completed: true), +// Entry(id: 3, description: "go backward in time", completed: false) +// ]), +// Model(nextID: 4, newEntryField: "", entries: [ +// Entry(id: 3, description: "go backward in time", completed: false) +// ]), +// ] +// +// XCTAssertEqual(expectedHistory.count, actualHistory.count) +// for (index, (expected, actual)) in zip(expectedHistory, actualHistory).enumerated() { +// XCTAssertEqual(expected, actual, "History mismatch at step \(index)") +// } +// } } From cc3ff5b4ced61256ddcbe9eaac4dee2222826244 Mon Sep 17 00:00:00 2001 From: Smara9dus Date: Wed, 18 Apr 2018 12:17:16 -0500 Subject: [PATCH 5/6] testAdd now passes Fixed a problem in the engine --- swift-todo/Sources/Todo/Engine.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/swift-todo/Sources/Todo/Engine.swift b/swift-todo/Sources/Todo/Engine.swift index 6b55e9e..c717840 100644 --- a/swift-todo/Sources/Todo/Engine.swift +++ b/swift-todo/Sources/Todo/Engine.swift @@ -13,9 +13,10 @@ struct Engine { } static func runWithHistory(on model: Model, applying messages: [Message]) -> [Model] { + var newModel = model return messages.map { message in - let model = message.apply(to: model) - return model + newModel = message.apply(to: newModel) + return newModel } } } From 37758bfc3167de773d6d7956baae8c86793e7490 Mon Sep 17 00:00:00 2001 From: Smara9dus Date: Wed, 18 Apr 2018 12:44:40 -0500 Subject: [PATCH 6/6] EVERYTHING WORKS. Wow! --- swift-todo/Sources/Todo/Message.swift | 12 +- swift-todo/Tests/TodoTests/TodoTests.swift | 134 ++++++++++----------- 2 files changed, 72 insertions(+), 74 deletions(-) diff --git a/swift-todo/Sources/Todo/Message.swift b/swift-todo/Sources/Todo/Message.swift index d710fae..e100a46 100644 --- a/swift-todo/Sources/Todo/Message.swift +++ b/swift-todo/Sources/Todo/Message.swift @@ -31,14 +31,12 @@ enum Message { case .check(let id, let isCompleted): var newModel = model - for entry in model.entries { - if(entry.id == id) { - var newEntry = entry + for i in model.entries.indices { + if(model.entries[i].id == id) { + var newEntry = model.entries[i] newEntry.completed = isCompleted - var newEntries = model.entries - let i = newEntries.index(of: entry) - newEntries.insert(newEntry, at: i!) - newModel.entries = newEntries + newModel.entries[i] = newEntry + break } } return newModel diff --git a/swift-todo/Tests/TodoTests/TodoTests.swift b/swift-todo/Tests/TodoTests/TodoTests.swift index 342d921..e19fddd 100644 --- a/swift-todo/Tests/TodoTests/TodoTests.swift +++ b/swift-todo/Tests/TodoTests/TodoTests.swift @@ -78,71 +78,71 @@ class TodoTests: XCTestCase { } -// func testTimeTravel() { -// let actualHistory = Engine.runWithHistory(on: Model(), applying: [ -// .updateNewEntryField("go forward in time"), -// .add, -// .add, // no effect -// .updateNewEntryField("delete this item"), -// .add, -// .delete(2), -// .updateNewEntryField("go in time"), -// .updateNewEntryField("go backward in time"), -// .add, -// .check(0, true), -// .check(3, true), -// .check(3, false), -// .deleteAllCompleted -// ]) -// -// let expectedHistory = [ -// Model(nextID: 0, newEntryField: "go forward in time", entries: []), -// Model(nextID: 1, newEntryField: "", entries: [ -// Entry(id: 0, description: "go forward in time", completed: false) -// ]), -// Model(nextID: 2, newEntryField: "", entries: [ -// Entry(id: 0, description: "go forward in time", completed: false) -// ]), -// Model(nextID: 2, newEntryField: "delete this item", entries: [ -// Entry(id: 0, description: "go forward in time", completed: false) -// ]), -// Model(nextID: 3, newEntryField: "", entries: [ -// Entry(id: 0, description: "go forward in time", completed: false), -// Entry(id: 2, description: "delete this item", completed: false) -// ]), -// Model(nextID: 3, newEntryField: "", entries: [ -// Entry(id: 0, description: "go forward in time", completed: false) -// ]), -// Model(nextID: 3, newEntryField: "go in time", entries: [ -// Entry(id: 0, description: "go forward in time", completed: false) -// ]), -// Model(nextID: 3, newEntryField: "go backward in time", entries: [ -// Entry(id: 0, description: "go forward in time", completed: false) -// ]), -// Model(nextID: 4, newEntryField: "", entries: [ -// Entry(id: 0, description: "go forward in time", completed: false), -// Entry(id: 3, description: "go backward in time", completed: false) -// ]), -// Model(nextID: 4, newEntryField: "", entries: [ -// Entry(id: 0, description: "go forward in time", completed: true), -// Entry(id: 3, description: "go backward in time", completed: false) -// ]), -// Model(nextID: 4, newEntryField: "", entries: [ -// Entry(id: 0, description: "go forward in time", completed: true), -// Entry(id: 3, description: "go backward in time", completed: true) -// ]), -// Model(nextID: 4, newEntryField: "", entries: [ -// Entry(id: 0, description: "go forward in time", completed: true), -// Entry(id: 3, description: "go backward in time", completed: false) -// ]), -// Model(nextID: 4, newEntryField: "", entries: [ -// Entry(id: 3, description: "go backward in time", completed: false) -// ]), -// ] -// -// XCTAssertEqual(expectedHistory.count, actualHistory.count) -// for (index, (expected, actual)) in zip(expectedHistory, actualHistory).enumerated() { -// XCTAssertEqual(expected, actual, "History mismatch at step \(index)") -// } -// } + func testTimeTravel() { + let actualHistory = Engine.runWithHistory(on: Model(), applying: [ + .updateNewEntryField("go forward in time"), + .add, + .add, // no effect + .updateNewEntryField("delete this item"), + .add, + .delete(2), + .updateNewEntryField("go in time"), + .updateNewEntryField("go backward in time"), + .add, + .check(0, true), + .check(3, true), + .check(3, false), + .deleteAllCompleted + ]) + + let expectedHistory = [ + Model(nextID: 0, newEntryField: "go forward in time", entries: []), + Model(nextID: 1, newEntryField: "", entries: [ + Entry(id: 0, description: "go forward in time", completed: false) + ]), + Model(nextID: 2, newEntryField: "", entries: [ + Entry(id: 0, description: "go forward in time", completed: false) + ]), + Model(nextID: 2, newEntryField: "delete this item", entries: [ + Entry(id: 0, description: "go forward in time", completed: false) + ]), + Model(nextID: 3, newEntryField: "", entries: [ + Entry(id: 0, description: "go forward in time", completed: false), + Entry(id: 2, description: "delete this item", completed: false) + ]), + Model(nextID: 3, newEntryField: "", entries: [ + Entry(id: 0, description: "go forward in time", completed: false) + ]), + Model(nextID: 3, newEntryField: "go in time", entries: [ + Entry(id: 0, description: "go forward in time", completed: false) + ]), + Model(nextID: 3, newEntryField: "go backward in time", entries: [ + Entry(id: 0, description: "go forward in time", completed: false) + ]), + Model(nextID: 4, newEntryField: "", entries: [ + Entry(id: 0, description: "go forward in time", completed: false), + Entry(id: 3, description: "go backward in time", completed: false) + ]), + Model(nextID: 4, newEntryField: "", entries: [ + Entry(id: 0, description: "go forward in time", completed: true), + Entry(id: 3, description: "go backward in time", completed: false) + ]), + Model(nextID: 4, newEntryField: "", entries: [ + Entry(id: 0, description: "go forward in time", completed: true), + Entry(id: 3, description: "go backward in time", completed: true) + ]), + Model(nextID: 4, newEntryField: "", entries: [ + Entry(id: 0, description: "go forward in time", completed: true), + Entry(id: 3, description: "go backward in time", completed: false) + ]), + Model(nextID: 4, newEntryField: "", entries: [ + Entry(id: 3, description: "go backward in time", completed: false) + ]), + ] + + XCTAssertEqual(expectedHistory.count, actualHistory.count) + for (index, (expected, actual)) in zip(expectedHistory, actualHistory).enumerated() { + XCTAssertEqual(expected, actual, "History mismatch at step \(index)") + } + } }