-
Notifications
You must be signed in to change notification settings - Fork 0
/
database_persistence.rb
323 lines (274 loc) · 8.6 KB
/
database_persistence.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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
require 'pg'
require 'bcrypt'
class DatabasePersistence
def initialize(logger)
@db = PG.connect(dbname: 'library')
@logger = logger
end
def query(sql, *params)
@logger.info "#{sql}: #{params}"
@db.exec_params(sql, params)
end
def disconnect
@db.close
end
# methods used for login validation
def find_id_num_user(id_num)
sql = 'SELECT id, password FROM users WHERE id = $1'
query(sql, id_num).first
end
def id_num_matches_password?(id_num, password)
if find_id_num_user(id_num)
result = find_id_num_user(id_num)
BCrypt::Password.new(result['password']) == password
else
false
end
end
# finds user based on ID and returns a new User obj
def find_user(id_number)
sql = 'SELECT id, first_name, last_name, admin FROM users WHERE id = $1;'
result = query(sql, id_number).first
return nil if result.nil?
id = result['id']
first_name = result['first_name']
last_name = result['last_name']
admin = result['admin']
User.new(id, first_name, last_name, admin)
end
# finds book based on ID and returns a new Book obj
def find_book(book_id)
sql = <<~SQL
SELECT * FROM books
WHERE id = $1;
SQL
result = query(sql, book_id).first
return nil if result.nil?
data = {
id: result['id'],
title: result['title'],
author: result['author'],
checked_out: result['checked_out'] == 't',
checked_out_user_id: result['checked_out_user_id'],
date_checked_out: result['date_checked_out']
}
Book.new(data)
end
# adding a new book to DB
def add_new_book(title, author)
sql = <<~SQL
INSERT INTO books (title, author)
VALUES ($1, $2);
SQL
query(sql, title, author)
end
# updates book info within DB
def update_book_info(new_book_info)
sql = <<~SQL
UPDATE books SET title = $1, author = $2, checked_out = $3, checked_out_user_id = $4, date_checked_out = $5
WHERE id = $6
SQL
title = new_book_info[:title].split(' ').map(&:capitalize).join(' ')
author = new_book_info[:author].split(' ').map(&:capitalize).join(' ')
checked_out = new_book_info[:checked_out] == 't' || new_book_info[:checked_out] == 'true'
checked_out_user_id = new_book_info[:checked_out_user_id]
date_checked_out = new_book_info[:date_checked_out]
book_id = new_book_info[:book_id]
query(sql, title, author, checked_out, checked_out_user_id, date_checked_out, book_id)
end
# deletes book within DB and returns the deleted book title & author for display
def delete_book(book_id)
info = query('SELECT title, author FROM books WHERE id = $1', book_id).first.values
sql = <<~SQL
DELETE FROM books
WHERE id = $1
SQL
query(sql, book_id)
info
end
# method certifying the new user is a unique user by comparing the names and password.
def new_user_unique?(first_name, last_name, password)
sql = <<~SQL
SELECT * FROM users
WHERE first_name LIKE $1 AND last_name LIKE $2;
SQL
results = query(sql, first_name, last_name)
return true if results.first.nil?
results.all? do |result|
BCrypt::Password.new(result['password']) != password
end
end
def encrypt_password(password)
BCrypt::Password.create(password)
end
# method for adding new user to the DB & returns the new user ID
def create_new_user(first_name, last_name, password)
crypt_password = encrypt_password(password)
sql = <<~SQL
INSERT INTO users(first_name, last_name, password)
VALUES($1, $2, $3)
SQL
query(sql, first_name, last_name, crypt_password)
return_new_user_id(first_name, last_name, crypt_password)
end
# returns the new ID for the newly created user
def return_new_user_id(first_name, last_name, crypt_password)
sql_new_user_id = <<~SQL
SELECT id FROM users
WHERE first_name LIKE $1 AND
last_name LIKE $2 AND password LIKE $3;
SQL
query(sql_new_user_id, first_name, last_name, crypt_password).values.first.first
end
# method for updating user info in DB
def edit_user_info(id, first_name, last_name)
sql = <<~SQL
UPDATE users
set first_name = $1, last_name = $2
Where id = $3;
SQL
query(sql, first_name.capitalize, last_name.capitalize, id.to_i)
end
# deletes user info from the DB
def delete_user(user_id)
sql = <<~SQL
DELETE FROM users WHERE id = $1;
SQL
query(sql, user_id)
end
# finds the desired books from the DB based on the
# current list_type within the 'data' hash passed in & returns an array of new Book objs
def grab_books_from_db(data)
list_type = data[:list_type]
@condition = generate_sql_for_grab_books(list_type)
sql = <<~SQL
#{@condition}
LIMIT $1
OFFSET $2
SQL
results =
if list_type == 'checked_out'
query(sql, data[:limit], data[:offset], data[:user_id])
else
query(sql, data[:limit], data[:offset])
end
results.map do |result|
data = {
id: result['id'],
title: result['title'],
author: result['author'],
checked_out: result['checked_out'],
checked_out_user_id: result['checked_out_user_id'],
date_checked_out: result['date_checked_out']
}
Book.new(data)
end
end
def generate_sql_for_grab_books(list_type)
case list_type
when 'checked_out'
<<~SQL
SELECT id, title, author, checked_out, checked_out_user_id, date_checked_out FROM books
WHERE checked_out_user_id = $3
ORDER BY date_checked_out ASC
SQL
when 'available'
<<~SQL
SELECT id, title, author, checked_out, checked_out_user_id FROM books
WHERE checked_out = 'f'
ORDER BY title
SQL
else
<<~SQL
SELECT id, title, author, checked_out, checked_out_user_id FROM books
ORDER BY title, checked_out
SQL
end
end
# finds the total number of books within a certain list_type and returns it as an integer (used in pagination)
def total_book_count(data)
list_type = data[:list_type]
sql = generate_sql_for_total_book_cout(list_type)
results =
if list_type == 'checked_out'
query(sql, data[:user_id])
else
query(sql)
end
results.field_values('count').first.to_i
end
def generate_sql_for_total_book_cout(list_type)
case list_type
when 'checked_out'
<<~SQL
SELECT count(id) FROM books
WHERE checked_out_user_id = $1
SQL
when 'available'
<<~SQL
SELECT count(id) FROM books
WHERE checked_out = 'f'
SQL
else
<<~SQL
SELECT count(id) FROM books
SQL
end
end
# updates the status of a book to checked_out = false, etc
def return_book(user_id, book_id)
sql = <<~SQL
UPDATE books SET checked_out = false, checked_out_user_id = NULL, date_checked_out = NULL
WHERE checked_out_user_id = $1 AND id = $2;
SQL
query(sql, user_id, book_id)
end
# updated book status checked_out = true, etc
def checkout_book(user_id, book_id)
sql = <<~SQL
UPDATE books SET checked_out = true, checked_out_user_id = $1, date_checked_out = now()::date
WHERE id = $2;
SQL
query(sql, user_id, book_id)
end
# checks to see if a user has any checked out books (used when deleting a user)
def no_checked_out_books?(user_id)
sql = <<~SQL
SELECT count(b.id) FROM books AS b
JOIN users AS u ON u.id = b.checked_out_user_id
WHERE u.id = $1;
SQL
query(sql, user_id).first['count'].to_i.zero?
end
end
# This class is used to store user info from the DB to the app.
class User
attr_reader :first_name, :last_name, :admin
def initialize(id, first_name, last_name, admin)
@id = id,
@first_name = first_name.capitalize,
@last_name = last_name.capitalize,
@admin = admin == 't'
end
def name
"#{first_name} #{last_name}"
end
# this may seem like a strange method,
# but for reasons I could not figure out the the instance variable @id
# is an array [id, first_name, last_name] and so to pull the id from it this method is needed.
def id
@id.first
end
end
# This class is used to store book info from the DB to the app.
class Book
attr_reader :id, :title, :author, :checked_out, :checked_out_user_id, :date_checked_out
def initialize(data)
@id = data[:id]
@title = data[:title]
@author = data[:author]
@checked_out = data[:checked_out]
@checked_out_user_id = data[:checked_out_user_id]# .empty? ? nil : data[:checked_out_user_id]
@date_checked_out = data[:date_checked_out]# .empty? ? nil : data[:checked_out_user_id]
end
end