CSCI 4513
Week 16, Lecture 29
Ragnarök tests everything the gods have learned, just as the final exam comprehensively evaluates all the skills you've acquired this semester.
The Tale:
Ragnarök—the twilight of the gods—is the ultimate test. It's not a single challenge but a comprehensive battle where everything the Aesir have learned throughout their existence comes into play. Thor's strength gained from countless battles, Odin's wisdom gathered from his sacrifices, Tyr's courage proven through his trials—all are tested simultaneously.
The gods knew Ragnarök was coming. The Norns prophesied it. Every lesson learned, every skill mastered, every alliance forged was preparation for this moment. They couldn't avoid it—they could only prepare. And though the battle would be fierce, they faced it knowing they had trained for exactly this.
Your final exam is your Ragnarök—a comprehensive test of everything you've learned. HTML fundamentals, JavaScript mastery, React components, backend APIs, database queries, authentication flows. All the "battles" you've fought this semester prepared you for this moment.
<form action="/submit" method="POST">
<label for="email">Email:</label>
<input type="email" id="email" required>
<label for="age">Age:</label>
<input type="number" id="age"
min="18" max="120">
</form>
requiredminlength, maxlengthmin, maxpattern (regex)type (email, number, date).container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto 1fr auto;
gap: 20px;
}
.item {
grid-column: span 2; /* Span 2 columns */
grid-row: 1 / 3; /* From line 1 to 3 */
}
/* Responsive grid */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
/* Define animation */
@keyframes slideIn {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* Apply animation */
.element {
animation: slideIn 0.5s ease-out forwards;
}
/* Transitions */
.button {
transition: all 0.3s ease;
}
/* Mobile-first approach */
.container {
width: 100%;
padding: 10px;
}
/* Tablet */
@media (min-width: 768px) {
.container {
width: 750px;
margin: 0 auto;
}
}
/* Desktop */
@media (min-width: 1024px) {
.container {
width: 960px;
}
}
function Player(name, level) {
this.name = name;
this.level = level;
}
// Add methods to prototype
Player.prototype.greet = function() {
return `Hello, I'm ${this.name}!`;
};
const player1 = new Player('Thor', 100);
console.log(player1.greet()); // "Hello, I'm Thor!"
new keyword! Methods go on prototype, not in constructor.
function createPlayer(name, level) {
let health = 100; // Private variable
return {
name, // Public property
level,
getHealth() { // Public method accessing private var
return health;
},
takeDamage(damage) {
health -= damage;
}
};
}
const player = createPlayer('Odin', 50);
console.log(player.health); // undefined (private!)
console.log(player.getHealth()); // 100
class Player {
#health = 100; // Private field
constructor(name, level) {
this.name = name;
this.level = level;
}
greet() {
return `I'm ${this.name}, level ${this.level}`;
}
get health() {
return this.#health;
}
static compare(p1, p2) {
return p1.level - p2.level;
}
}
class Warrior extends Player {
constructor(name, level, weapon) {
super(name, level);
this.weapon = weapon;
}
}
// Named exports
export function add(a, b) {
return a + b;
}
export const PI = 3.14;
// Default export
export default class Game {
// ...
}
// Named imports
import { add, PI } from './math.js';
// Default import
import Game from './game.js';
// Import all
import * as math from './math.js';
async function getData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
}
}
// Using the function
getData().then(data => console.log(data));
await only works inside async functions!
// Functional Component
function Greeting({ name, age }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old.</p>
</div>
);
}
// Using the component
<Greeting name="Thor" age={1500} />
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// Fetch user data when userId changes
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
// Cleanup function
return () => {
console.log('Cleanup');
};
}, [userId]); // Dependency array
return <div>{user?.name}</div>;
}
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users/:id" element={<UserProfile />} />
</Routes>
</BrowserRouter>
);
}
-- Create table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(100) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Select
SELECT * FROM users WHERE id = 1;
-- Insert
INSERT INTO users (username, email)
VALUES ('thor', 'thor@asgard.com');
-- Update
UPDATE users SET email = 'newemail@asgard.com' WHERE id = 1;
-- Delete
DELETE FROM users WHERE id = 1;
const express = require('express');
const app = express();
// Middleware
app.use(express.json());
// Routes
app.get('/api/users', (req, res) => {
res.json({ users: [] });
});
app.post('/api/users', (req, res) => {
const { username, email } = req.body;
// Create user...
res.status(201).json({ message: 'User created' });
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
// Custom middleware
function logger(req, res, next) {
console.log(`${req.method} ${req.url}`);
next();
}
app.use(logger);
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
// Route-specific middleware
function isAuthenticated(req, res, next) {
if (req.session.userId) {
next();
} else {
res.status(401).json({ error: 'Unauthorized' });
}
}
app.get('/api/protected', isAuthenticated, (req, res) => {
res.json({ data: 'secret' });
});
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
passport.use(
new LocalStrategy(async (username, password, done) => {
try {
const user = await User.findOne({ username });
if (!user) {
return done(null, false, { message: 'Incorrect username' });
}
const match = await bcrypt.compare(password, user.password);
if (!match) {
return done(null, false, { message: 'Incorrect password' });
}
return done(null, user);
} catch(err) {
return done(err);
}
})
);
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
const user = await User.findById(id);
done(null, user);
});
const jwt = require('jsonwebtoken');
// Create token
const token = jwt.sign(
{ userId: user.id, username: user.username },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
// Verify token middleware
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
}
req.user = user;
next();
});
}
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
}
return <button onClick={handleClick}>{count}</button>;
}
// After clicking the button once, what will count be?
Answer: 1
Explanation: All three setCount calls use the same count value (0). To increment multiple times, use the functional form: setCount(c => c + 1)
async function test() {
console.log('A');
await Promise.resolve();
console.log('B');
}
console.log('C');
test();
console.log('D');
// What is the output order?
Answer: C, A, D, B
Explanation: "C" logs first, then test() is called logging "A", then "D" logs (async functions don't block), then "B" logs after await resolves.
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.json({ userId });
});
app.get('/users/special', (req, res) => {
res.json({ message: 'Special route' });
});
// What happens when you visit /users/special?
Answer: Returns {"userId": "special"}
Explanation: The first route matches and treats "special" as an id parameter. Specific routes should be defined BEFORE parameterized routes!
-- Given these tables:
-- users: id, username
-- posts: id, user_id, title
-- What does this query return?
SELECT users.username, posts.title
FROM users
LEFT JOIN posts ON users.id = posts.user_id;
-- vs
SELECT users.username, posts.title
FROM users
INNER JOIN posts ON users.id = posts.user_id;
Answer:
LEFT JOIN: Returns ALL users, even if they have no posts (with NULL for post fields)
INNER JOIN: Returns ONLY users who have posts
function Component({ id }) {
const [data, setData] = useState(null);
useEffect(() => {
fetchData(id).then(setData);
}, []); // Empty dependency array
return <div>{data}</div>;
}
// What's wrong with this code?
Problem: Missing dependency!
The effect uses id but doesn't include it in the dependency array. If id changes, the effect won't re-run. Should be: useEffect(() => { ... }, [id])
function createCounter() {
let count = 0;
return {
increment() {
count++;
},
getCount() {
return count;
}
};
}
const counter1 = createCounter();
const counter2 = createCounter();
counter1.increment();
counter1.increment();
counter2.increment();
// What do these output?
console.log(counter1.getCount());
console.log(counter2.getCount());
Answer: 2 and 1
Explanation: Each factory function call creates a new closure with its own count variable. They don't share state.
Like the gods facing Ragnarök, you've trained all semester for this moment.
You've mastered HTML, CSS, JavaScript, React, Node.js, Express, databases, and authentication.
You've built projects, debugged errors, and solved problems.
Trust your training. You've got this! đź’Ş
đź“… Final Exam Date: TBD
📚 Coverage: All material from Lectures 1-29
🎓 Good luck! May the Norns weave success into your fate!