-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtask_manager.rb
231 lines (185 loc) · 6.69 KB
/
task_manager.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
require 'sinatra'
require 'sinatra/content_for'
require 'tilt/erubis'
require_relative 'database_persistence'
configure do
enable :sessions
set :session_secret, 'secret' # This is used by Sinatra to encrypt session information and to enable state to persist between program restarts.
set :erb, :escape_html => true # escape potentially unsafe user input
end
configure(:development) do # loads the referenced files when working in developement environment
require 'sinatra/reloader'
also_reload "./database_persistence.rb"
end
# This will execute before any pattern is matched with a route.
before do
@storage = DatabasePersistance.new(logger)
end
get '/' do
redirect '/lists'
end
# View all lists
get '/lists' do
@lists = @storage.all_lists
erb :lists, layout: :layout
end
# Render the `new list` form
get '/list/new' do
erb :new_list
end
# Create a new list
post '/lists' do
list_name = params[:list_name].strip
error_message = validate(list_name) { @storage.all_lists.any? { |list| list[:name] == list_name } }
if error_message
session[:error] = error_message
erb :new_list
else
@storage.create_new_list(list_name)
session[:success] = 'The list has been created.'
redirect '/lists'
end
end
# View a single todo list
get '/lists/:id' do |id|
@list_id = id.to_i
@list = load_list(@list_id) # retruns a hash if a list exists otherwise redirects
@todos = @storage.find_todos_for_list(@list_id)
erb :list
end
# Edit an existing todo list
get '/lists/:id/edit' do |id|
@list_id = id.to_i
@list = load_list(@list_id)
erb :edit_list
end
# update an existing todo list
post '/lists/:id' do |id|
list_name = params[:list_name].strip
@list_id = id.to_i
@list = load_list(@list_id)
error_message = validate(list_name) { @storage.all_lists.any? { |list| list[:name] == list_name } }
if error_message
session[:error] = error_message
erb :edit_list
else
@storage.update_list_name(@list_id, list_name)
session[:success] = 'The list has been updated.'
redirect "/lists/#{@list_id}"
end
end
# Delete a todo list
post '/lists/:id/delete' do |id|
list_id = id.to_i
@storage.delete_list(list_id)
session[:success] = "The list has been deleted."
if env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
"/lists"
else
redirect "/lists"
end
end
# add a new todo task to a list
post "/lists/:id/todos" do |id|
list_id = id.to_i
todo_name = params[:todo].strip
@list = load_list(list_id)
@todos = @storage.find_todos_for_list(list_id)
error_message = validate(todo_name) { @todos.any? { |todo| todo[:name] == todo_name } }
if error_message
session[:error] = error_message
erb :list
else
@storage.create_new_todo(list_id, todo_name)
session[:success] = 'Todo item has been added.'
redirect "/lists/#{list_id}"
end
end
# Delete a Todo from a list
post "/lists/:list_id/todos/:todo_id/delete" do |list_id, todo_id|
list_id = list_id.to_i
todo_id = todo_id.to_i
@storage.delete_todo_from_list(list_id, todo_id)
session[:success] = "Todo item has been deleted."
if env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
"/lists/#{list_id}"
else
redirect "/lists/#{list_id}"
end
end
# Update the status of a todo
post "/lists/:list_id/todos/:todo_id" do |list_id, todo_id|
list_id = list_id.to_i
list = load_list(list_id)
todo_id = todo_id.to_i
is_completed = params[:completed] == "true"
@storage.update_todo_status(list_id, todo_id, is_completed)
session[:success] = "Todo item has been updated."
redirect "/lists/#{list_id}"
end
# Mark all todos as complete for a list
post "/lists/:list_id/complete_all" do |list_id|
list_id = list_id.to_i
list = load_list(list_id)
@storage.mark_all_todos_as_completed(list_id)
session[:success] = "All todos have been completed."
redirect "/lists/#{list_id}"
end
not_found do
redirect "/"
end
# returns a list if it exists, otherwise redirects to "/lists" and displays flash error message
def load_list(id)
list = @storage.find_list(id)
return list if list
session[:error] = "The specified list was not found"
redirect '/lists'
end
# returns a String message if the user input is invalid otherwise returns nil if the name is valid.
def validate(user_input)
unless user_input.size.between?(1, 100)
return "Entry name must be between 1 and 100 characters"
end
invalid = yield # yielding to the block for validation, the block will return a boolean, either true or false.
"Entry name must be unique." if invalid
end
after do # closing databse connection
@storage.disconnect
end
helpers do
def list_complete?(list)
list[:todos_count] > 0 && list[:remaining_todos_count] == 0
end
def list_class(list)
"complete" if list_complete?(list)
end
# takes an array of hashes and reorders them while keeping record of their origianl index.
# it then yields each sorted hash with its original index to the explicit block
# this feature is intended exculsievly for the `lists` view template.
def sort_lists(lists, &block)
sorted_list = sort(lists) {|list| list_complete?(list) ? 1 : 0 }
sorted_list.each(&block)
end
def sort_todos(todos, &block)
sorted_list = sort(todos) { |todo| todo[:completed] ? 1 : 0 }
sorted_list.each(&block)
end
# this method expects an implicit block to be passed as an argument when it is invoked.
def sort(list)
list.sort_by {|item| yield(item) }
end
=begin
# Important Notes regarding Line 31 & Line 36
- Ruby doesn't provide any built-in mechanism for comparing boolean values(`true` and `false`).
- I took advantage of the way `Enumerable#sort_by` works in order to sort boolean values based on integer values which do implement comparison(<=>) in their class.
- `Enumerable#sort_by` works in 3 steps:
1. It yields each element of the caller collection to the block, and caches the return value of the block at each iteration temporarily.
2. sorts all of the block's cached returned values from step 1, which correspond to elements in the calling collection
3. returns a new sorted collection object(e.i Array), within which elements are sorted based on the sorted return values of th block from step # 2.
- The above works if the values returned by the block at each initial iteration(step #1) implement a comparison method and are of the same class
- The above could possibly be overidden with custom classes/objects)
# Example:
- suppose at step # 1, you invoke a custom method within the block. if the method returns `nil`,`false`, `true` or raises an `exception`` for any of the passed in elements,
`Enumrable#sort_by` will not work and an exception will be raised.
=end
end