How it Works
A before block initializes an instance of a DatabasePersistence class, using the PG RubyGem to connect to a SQL database and storing the connection in an instance variable. With each user action, a SQL query is executed after being sanitized to prevent malicious injections into the database. Queries return tuple objects representing lists and to-do items. A private method converts the tuples into hashes so they can be used with other methods and displayed to the user. For debugging, queries are reported to the console using a logger object. Finally, An after block terminates the database connection.
- The app uses the Sinatra library/DSL and is hosted on Heroku.
- A Procfile directs the app to use the Puma web server, instead of WEBrick.
- Erubis directly places ruby into the HTML files.
- Helper methods, and Sinatra::Contentfor greatly reduce repitition between HTML files through templating and content sharing.
- A session object simulates statefulness, storing error and success messages.
- To-do items are asynchronously deleted with AJAX requests
- Input validation restricts duplicate list names and invalid lengths. Invalid inputs remain in the form for the user to see the error.
- Rack::Utils.escape_html escapes HTML content to eliminate vulnerabilities and secure data.
- I initially misunderstood the relationship between Heroku and PostresSQL. I thought that Heroku set up a new database each time a different user connected to the app. When I realized that all users share a database, I had to add a method to create tables for each user, using their username. In order to make their username accessbile within the DatabasePersistence class, it's passed in during initialization. The SQL sanitization method, PG::Connection#exec_params, apparently does not work with table creation. So PG::Connection#exec is used instead.
- The lists and to-dos were initially stored in a nested array within the session object. To transition to a SQL database, I extracted all session persistence methods into a seperate document. Next, I changed these session persistence methods into database interaction methods based on SQL statements.
- Inintially, the lists were displayed using Enum#each_with_index. Each index would be placed in the URL to view that list, but this led to a bug. As the lists were sorted, their indices and URLs changed, resulting in the wrong list appearing when you clicked the link. The solution was to assign IDs to the lists, pass a block into the sorting methods, and use the list ID within the block to construct the URL. This turned out to be quite effective, so now each route involving an individual list or to-do item passes list IDs and/or to-do IDs through the route.
- Sometimes "lists" would be duplicated in the URLs of various routes. I have yet to determine the cause, so I ended up creating redirections to handle this bug.