refactor: remove outdated server design documentation
Remove 557-line server-design.md from _go-learning/architecture - content is now covered in updated architecture documentation with real implementation examples and test coverage.
This commit is contained in:
@@ -1,404 +0,0 @@
|
|||||||
# Refactoring #2: Shared Utilities and Validation Layer
|
|
||||||
|
|
||||||
**Date**: 2024-11-20
|
|
||||||
**Type**: Code Quality Improvement, Data Integrity
|
|
||||||
|
|
||||||
## Problem Statement
|
|
||||||
|
|
||||||
After the initial CV/UI model separation (Refactoring #1), code analysis revealed:
|
|
||||||
|
|
||||||
1. **Code Duplication**: The `findDataFile()` function was duplicated in both `cv/loader.go` and `ui/loader.go` (~46 lines of duplicate code)
|
|
||||||
2. **No Data Validation**: CV and UI data loaded from JSON had no validation, risking runtime errors from malformed data
|
|
||||||
3. **Magic Strings**: Language codes ("en", "es") were hardcoded throughout the codebase
|
|
||||||
|
|
||||||
## Solution
|
|
||||||
|
|
||||||
Implemented two complementary improvements:
|
|
||||||
|
|
||||||
### Part 1: Extract Shared Utilities
|
|
||||||
|
|
||||||
Created three new utility packages:
|
|
||||||
|
|
||||||
1. **`internal/fileutil`** - File operations
|
|
||||||
- `FindDataFile()`: Search for data files in directory tree
|
|
||||||
- `LoadJSON()`: Generic JSON loading helper
|
|
||||||
|
|
||||||
2. **`internal/lang`** - Language validation
|
|
||||||
- Constants: `English = "en"`, `Spanish = "es"`
|
|
||||||
- `IsValid()`: Quick validation check
|
|
||||||
- `Validate()`: Detailed validation with error messages
|
|
||||||
- `All()`: List all supported languages
|
|
||||||
|
|
||||||
### Part 2: Add Validation Layer
|
|
||||||
|
|
||||||
Created comprehensive validation in `internal/models/cv/validation.go`:
|
|
||||||
|
|
||||||
- `Validate()` method on `CV` struct
|
|
||||||
- Individual validation for each CV component
|
|
||||||
- Type-safe error handling with `ValidationError` and `ValidationErrors`
|
|
||||||
- Integrated into `LoadCV()` - validation happens automatically on load
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Before
|
|
||||||
|
|
||||||
```
|
|
||||||
cv/loader.go (69 lines)
|
|
||||||
├── findDataFile() [23 lines]
|
|
||||||
├── LoadCV()
|
|
||||||
└── replaceYearPlaceholder()
|
|
||||||
|
|
||||||
ui/loader.go (56 lines)
|
|
||||||
├── findDataFile() [23 lines] ← DUPLICATE!
|
|
||||||
└── LoadUI()
|
|
||||||
```
|
|
||||||
|
|
||||||
### After
|
|
||||||
|
|
||||||
```
|
|
||||||
internal/fileutil/
|
|
||||||
├── fileutil.go (FindDataFile)
|
|
||||||
└── json.go (LoadJSON)
|
|
||||||
|
|
||||||
internal/lang/
|
|
||||||
└── lang.go (validation + constants)
|
|
||||||
|
|
||||||
internal/models/cv/
|
|
||||||
├── cv.go (types)
|
|
||||||
├── loader.go (uses utilities, 36 lines)
|
|
||||||
└── validation.go (comprehensive validation)
|
|
||||||
|
|
||||||
internal/models/ui/
|
|
||||||
├── ui.go (types)
|
|
||||||
└── loader.go (uses utilities, 24 lines)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
|
|
||||||
### 1. DRY (Don't Repeat Yourself)
|
|
||||||
|
|
||||||
**Eliminated ~46 lines of duplication**
|
|
||||||
|
|
||||||
Before:
|
|
||||||
```go
|
|
||||||
// cv/loader.go - 23 lines
|
|
||||||
func findDataFile(filename string) (string, error) {
|
|
||||||
// ... implementation
|
|
||||||
}
|
|
||||||
|
|
||||||
// ui/loader.go - 23 lines
|
|
||||||
func findDataFile(filename string) (string, error) {
|
|
||||||
// ... implementation (IDENTICAL!)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
```go
|
|
||||||
// internal/fileutil/fileutil.go - ONE implementation
|
|
||||||
func FindDataFile(filename string) (string, error) {
|
|
||||||
if filename == "" {
|
|
||||||
return "", fmt.Errorf("filename cannot be empty")
|
|
||||||
}
|
|
||||||
// ... implementation
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Type Safety
|
|
||||||
|
|
||||||
**Language codes are now constants**
|
|
||||||
|
|
||||||
Before:
|
|
||||||
```go
|
|
||||||
if lang != "en" && lang != "es" { // Magic strings, typo-prone
|
|
||||||
return fmt.Errorf("unsupported language: %s", lang)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
```go
|
|
||||||
import "github.com/juanatsap/cv-site/internal/lang"
|
|
||||||
|
|
||||||
if err := lang.Validate(language); err != nil {
|
|
||||||
return nil, err // Clear error: "unsupported language: fr (supported: [en es])"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Data Integrity
|
|
||||||
|
|
||||||
**Validation catches errors at load time**
|
|
||||||
|
|
||||||
```go
|
|
||||||
func LoadCV(language string) (*CV, error) {
|
|
||||||
// ... load JSON
|
|
||||||
|
|
||||||
// Validate loaded data
|
|
||||||
if err := cvData.Validate(); err != nil {
|
|
||||||
return nil, fmt.Errorf("validation failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &cvData, nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Example validation:
|
|
||||||
- Personal.Email must be valid email format
|
|
||||||
- Experience must have position, company, startDate
|
|
||||||
- Skills.Proficiency must be 1-5
|
|
||||||
- Languages.Level must be 1-5
|
|
||||||
- Project.URL must be valid URL
|
|
||||||
- Meta.Version and Meta.Language are required
|
|
||||||
|
|
||||||
### 4. Better Error Messages
|
|
||||||
|
|
||||||
Before (no validation):
|
|
||||||
```
|
|
||||||
Runtime error: nil pointer dereference when accessing cv.Personal.Name
|
|
||||||
```
|
|
||||||
|
|
||||||
After (with validation):
|
|
||||||
```
|
|
||||||
validation failed:
|
|
||||||
- personal.name: name is required
|
|
||||||
- personal.email: invalid email format
|
|
||||||
- experience[0].position: position is required
|
|
||||||
- skills.technical[1].proficiency: proficiency must be between 1 and 5
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Single Source of Truth
|
|
||||||
|
|
||||||
**One place to update each utility**
|
|
||||||
|
|
||||||
- Need to change how files are found? Update `fileutil.FindDataFile()`
|
|
||||||
- Need to add a new language? Update `lang` package
|
|
||||||
- All consumers automatically benefit from improvements
|
|
||||||
|
|
||||||
## Why This Approach?
|
|
||||||
|
|
||||||
### 1. Generic JSON Helper
|
|
||||||
|
|
||||||
```go
|
|
||||||
// LoadJSON works with ANY struct type
|
|
||||||
func LoadJSON(filename string, target interface{}) error {
|
|
||||||
filepath, err := FindDataFile(filename)
|
|
||||||
// ... read and unmarshal into target
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage
|
|
||||||
var cv CV
|
|
||||||
LoadJSON("data/cv-en.json", &cv)
|
|
||||||
|
|
||||||
var ui UI
|
|
||||||
LoadJSON("data/ui-en.json", &ui)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Why generic?** Go doesn't have generics (pre-1.18), so `interface{}` allows reuse across all types.
|
|
||||||
|
|
||||||
### 2. Validation Error Collection
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ValidationErrors []ValidationError
|
|
||||||
|
|
||||||
func (cv *CV) Validate() error {
|
|
||||||
var errors ValidationErrors
|
|
||||||
|
|
||||||
// Collect ALL errors, don't stop at first one
|
|
||||||
if err := cv.Personal.Validate(); err != nil {
|
|
||||||
errors = append(errors, ...)
|
|
||||||
}
|
|
||||||
if err := cv.Skills.Validate(); err != nil {
|
|
||||||
errors = append(errors, ...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errors) > 0 {
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Why collect all errors?** User can fix all issues at once instead of iterative "fix one, run again" cycles.
|
|
||||||
|
|
||||||
### 3. Flexible GitRepoUrl Validation
|
|
||||||
|
|
||||||
```go
|
|
||||||
// GitRepoUrl can be either a URL or a local filesystem path
|
|
||||||
// We don't validate it strictly since it supports both formats
|
|
||||||
```
|
|
||||||
|
|
||||||
**Why flexible?** The field is used for getting git commit dates, which works with both:
|
|
||||||
- Remote URLs: `https://github.com/user/project`
|
|
||||||
- Local paths: `/Users/txeo/Git/yo/cv`
|
|
||||||
|
|
||||||
Being too strict would break legitimate use cases.
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
### Test Coverage
|
|
||||||
|
|
||||||
Created comprehensive tests for all new functionality:
|
|
||||||
|
|
||||||
1. **`internal/fileutil/fileutil_test.go`**
|
|
||||||
- FindDataFile with existing files
|
|
||||||
- FindDataFile with non-existent files
|
|
||||||
- FindDataFile with empty filename
|
|
||||||
- LoadJSON with valid data
|
|
||||||
- LoadJSON with invalid JSON
|
|
||||||
- LoadJSON with nil target
|
|
||||||
|
|
||||||
2. **`internal/lang/lang_test.go`**
|
|
||||||
- IsValid for en, es, fr, empty, uppercase
|
|
||||||
- Validate with valid/invalid languages
|
|
||||||
- All() returns correct list
|
|
||||||
|
|
||||||
3. **`internal/models/cv/validation_test.go`**
|
|
||||||
- Personal validation (name, email, URLs)
|
|
||||||
- Experience validation (required fields, dates)
|
|
||||||
- Education validation
|
|
||||||
- Skills validation (proficiency ranges, categories)
|
|
||||||
- Language validation (levels 1-5)
|
|
||||||
- Project validation (title, URLs)
|
|
||||||
- Meta validation
|
|
||||||
- CV.Validate() - full integration test
|
|
||||||
|
|
||||||
### Test Results
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ go test ./...
|
|
||||||
PASS
|
|
||||||
ok github.com/juanatsap/cv-site/internal/fileutil 0.432s
|
|
||||||
ok github.com/juanatsap/cv-site/internal/lang 0.326s
|
|
||||||
ok github.com/juanatsap/cv-site/internal/models/cv 0.490s
|
|
||||||
ok github.com/juanatsap/cv-site/internal/models/ui 0.315s
|
|
||||||
```
|
|
||||||
|
|
||||||
All tests pass, including validation with real CV data files.
|
|
||||||
|
|
||||||
## Code Metrics
|
|
||||||
|
|
||||||
### Lines of Code
|
|
||||||
|
|
||||||
| Package | Lines | Purpose |
|
|
||||||
|---------|-------|---------|
|
|
||||||
| `internal/fileutil/fileutil.go` | 45 | File finding logic |
|
|
||||||
| `internal/fileutil/json.go` | 25 | Generic JSON loading |
|
|
||||||
| `internal/lang/lang.go` | 35 | Language validation |
|
|
||||||
| `internal/models/cv/validation.go` | 380 | Comprehensive validation |
|
|
||||||
| **Total New Code** | **485** | |
|
|
||||||
| **Code Eliminated** | **-46** | Removed duplication |
|
|
||||||
| **Net Addition** | **439** | Quality & safety infrastructure |
|
|
||||||
|
|
||||||
### Loader Simplification
|
|
||||||
|
|
||||||
| File | Before | After | Reduction |
|
|
||||||
|------|--------|-------|-----------|
|
|
||||||
| `cv/loader.go` | 69 lines | 36 lines | -48% |
|
|
||||||
| `ui/loader.go` | 56 lines | 24 lines | -57% |
|
|
||||||
|
|
||||||
## Interview Talking Points
|
|
||||||
|
|
||||||
### 1. Code Quality Practices
|
|
||||||
|
|
||||||
"I identified code duplication early and extracted shared utilities following the DRY principle. This reduced maintenance burden and created a single source of truth."
|
|
||||||
|
|
||||||
### 2. Error Handling
|
|
||||||
|
|
||||||
"Instead of failing fast, I implemented error collection to report all validation issues at once, improving developer experience and debugging efficiency."
|
|
||||||
|
|
||||||
### 3. API Design
|
|
||||||
|
|
||||||
"The validation API follows Go idioms - methods on types, error wrapping with %w, and clear error messages that guide users toward solutions."
|
|
||||||
|
|
||||||
### 4. Testing
|
|
||||||
|
|
||||||
"I wrote comprehensive table-driven tests covering edge cases and integration with real data. All utilities have 100% test coverage."
|
|
||||||
|
|
||||||
### 5. Type Safety
|
|
||||||
|
|
||||||
"I replaced magic strings with typed constants, making the code self-documenting and preventing typos at compile time."
|
|
||||||
|
|
||||||
### 6. Gradual Improvement
|
|
||||||
|
|
||||||
"Rather than a big-bang refactor, I implemented utilities first, then validation, testing each step independently. This minimizes risk."
|
|
||||||
|
|
||||||
## Performance Considerations
|
|
||||||
|
|
||||||
### Validation Overhead
|
|
||||||
|
|
||||||
Validation happens once per CV load, not on every request:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// LoadCV is called once and cached
|
|
||||||
cv, err := LoadCV("en") // Validation runs here
|
|
||||||
// ... cache cv in memory
|
|
||||||
|
|
||||||
// Later requests use cached cv (no validation)
|
|
||||||
handler(cv) // Fast - no validation overhead
|
|
||||||
```
|
|
||||||
|
|
||||||
**Cost**: ~1ms for complete CV validation
|
|
||||||
**Benefit**: Guaranteed data integrity throughout application lifetime
|
|
||||||
|
|
||||||
### FindDataFile Performance
|
|
||||||
|
|
||||||
```go
|
|
||||||
func FindDataFile(filename string) (string, error) {
|
|
||||||
// Check current dir first (most common case)
|
|
||||||
if _, err := os.Stat(filename); err == nil {
|
|
||||||
return filename, nil // Fast path
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search parent directories only if needed
|
|
||||||
for _, path := range []string{"../"+filename, "../../"+filename, ...} {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Fast path**: O(1) for files in current directory
|
|
||||||
**Worst case**: O(n) where n = directory depth (typically 3-4)
|
|
||||||
|
|
||||||
## Future Improvements
|
|
||||||
|
|
||||||
1. **Validation Levels**: Add `ValidateStrict()` vs `ValidateBasic()` for different use cases
|
|
||||||
2. **Custom Validators**: Allow plugins for domain-specific validation rules
|
|
||||||
3. **Validation Warnings**: Distinguish between errors (block load) and warnings (log but continue)
|
|
||||||
4. **Performance**: Cache validation results if CV data doesn't change
|
|
||||||
5. **Internationalization**: Translate validation error messages based on language
|
|
||||||
|
|
||||||
## Related Documentation
|
|
||||||
|
|
||||||
- [Refactoring #1: CV/UI Model Separation](./001-cv-model-separation.md)
|
|
||||||
- [Server Design: Why Goroutines?](../architecture/server-design.md)
|
|
||||||
|
|
||||||
## Commit Message
|
|
||||||
|
|
||||||
```
|
|
||||||
refactor: Extract shared utilities and add validation layer
|
|
||||||
|
|
||||||
Part 1: Shared Utilities
|
|
||||||
- Create internal/fileutil package (FindDataFile, LoadJSON)
|
|
||||||
- Create internal/lang package (constants + validation)
|
|
||||||
- Eliminate 46 lines of code duplication
|
|
||||||
- Simplify cv/loader.go (69→36 lines, -48%)
|
|
||||||
- Simplify ui/loader.go (56→24 lines, -57%)
|
|
||||||
|
|
||||||
Part 2: Validation Layer
|
|
||||||
- Add comprehensive validation in internal/models/cv/validation.go
|
|
||||||
- Validate Personal, Experience, Education, Skills, Languages, Projects, Meta
|
|
||||||
- Integrate validation into LoadCV (automatic on load)
|
|
||||||
- Create ValidationError and ValidationErrors types
|
|
||||||
- Report all validation errors at once (better UX)
|
|
||||||
|
|
||||||
Testing:
|
|
||||||
- Add tests for fileutil package (FindDataFile, LoadJSON)
|
|
||||||
- Add tests for lang package (IsValid, Validate, All)
|
|
||||||
- Add comprehensive validation tests (280+ test cases)
|
|
||||||
- All tests pass with real CV data
|
|
||||||
|
|
||||||
Benefits:
|
|
||||||
- DRY: Single source of truth for utilities
|
|
||||||
- Type safety: Language constants instead of magic strings
|
|
||||||
- Data integrity: Validation catches errors at load time
|
|
||||||
- Better errors: Clear messages showing all issues
|
|
||||||
- Maintainability: Centralized utilities easier to update
|
|
||||||
```
|
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# 🎓 Personal Go Backend Learning Knowledge Base
|
||||||
|
|
||||||
|
> **PRIVATE DOCUMENTATION** - This directory is gitignored and contains personal learning notes about Go backend development using this CV project as a practical learning ground.
|
||||||
|
|
||||||
|
## 📚 Purpose
|
||||||
|
|
||||||
|
This knowledge base exists to document **WHY** we make specific architectural and implementation decisions in Go backend development. While building this CV application, I'm using it as an opportunity to deeply understand:
|
||||||
|
|
||||||
|
- Go backend architecture patterns
|
||||||
|
- Package organization and dependency management
|
||||||
|
- Concurrency patterns (goroutines, channels)
|
||||||
|
- HTTP server best practices
|
||||||
|
- Code organization and maintainability
|
||||||
|
- Refactoring strategies
|
||||||
|
- Testing approaches
|
||||||
|
|
||||||
|
## 🗂️ Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
_go-learning/
|
||||||
|
├── README.md # This file
|
||||||
|
├── architecture/ # System architecture explanations
|
||||||
|
│ ├── server-design.md # Why goroutines, server lifecycle
|
||||||
|
│ ├── package-structure.md # Package organization philosophy
|
||||||
|
│ └── dependency-graph.md # How components interact
|
||||||
|
├── refactorings/ # Detailed refactoring documentation
|
||||||
|
│ ├── 001-cv-model-separation.md # CV/UI model separation
|
||||||
|
│ └── ... # Future refactorings
|
||||||
|
├── patterns/ # Go patterns and idioms
|
||||||
|
│ ├── error-handling.md # Error wrapping, custom errors
|
||||||
|
│ ├── interfaces.md # When and how to use interfaces
|
||||||
|
│ └── constructors.md # Builder patterns, factory functions
|
||||||
|
├── best-practices/ # Go best practices with examples
|
||||||
|
│ ├── naming-conventions.md # Package, func, var naming
|
||||||
|
│ ├── testing.md # Table-driven tests, mocks
|
||||||
|
│ └── performance.md # Profiling, optimization
|
||||||
|
└── diagrams/ # Visual architecture diagrams
|
||||||
|
├── current-state/ # Before refactorings
|
||||||
|
└── target-state/ # After refactorings
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Learning Goals
|
||||||
|
|
||||||
|
### Short-term (During CV Project)
|
||||||
|
- [ ] Understand Go package organization best practices
|
||||||
|
- [ ] Master goroutine usage and server lifecycle
|
||||||
|
- [ ] Learn proper error handling patterns
|
||||||
|
- [ ] Understand interface design principles
|
||||||
|
- [ ] Practice test-driven development
|
||||||
|
|
||||||
|
### Medium-term (Job Interview Preparation)
|
||||||
|
- [ ] Explain architectural decisions confidently
|
||||||
|
- [ ] Discuss trade-offs between different approaches
|
||||||
|
- [ ] Demonstrate deep understanding of Go idioms
|
||||||
|
- [ ] Show practical experience with production patterns
|
||||||
|
|
||||||
|
### Long-term (Professional Growth)
|
||||||
|
- [ ] Build intuition for clean architecture
|
||||||
|
- [ ] Develop systematic refactoring skills
|
||||||
|
- [ ] Master concurrent programming patterns
|
||||||
|
- [ ] Create reusable knowledge base for future projects
|
||||||
|
|
||||||
|
## 📖 How to Use This
|
||||||
|
|
||||||
|
### When Learning:
|
||||||
|
1. Read the relevant document before making changes
|
||||||
|
2. Understand the **WHY** behind the pattern
|
||||||
|
3. Implement the change
|
||||||
|
4. Document any new insights or questions
|
||||||
|
|
||||||
|
### When Interviewing:
|
||||||
|
1. Review architecture documents
|
||||||
|
2. Practice explaining decisions
|
||||||
|
3. Prepare to discuss trade-offs
|
||||||
|
4. Reference specific examples from this project
|
||||||
|
|
||||||
|
### When Stuck:
|
||||||
|
1. Check if there's a relevant pattern documented
|
||||||
|
2. Look at similar refactorings
|
||||||
|
3. Review best practices
|
||||||
|
4. Add new learnings to prevent future confusion
|
||||||
|
|
||||||
|
## 🔄 Living Document Philosophy
|
||||||
|
|
||||||
|
This knowledge base is **constantly evolving**. Every time we:
|
||||||
|
- Make an architectural decision → Document WHY
|
||||||
|
- Refactor code → Capture lessons learned
|
||||||
|
- Discover a better pattern → Update best practices
|
||||||
|
- Face a challenge → Record solution and reasoning
|
||||||
|
|
||||||
|
## 🚀 Current Focus: CV Model Refactoring
|
||||||
|
|
||||||
|
**Active Learning**: Separating CV domain models from UI presentation models
|
||||||
|
|
||||||
|
**Key Questions Being Answered**:
|
||||||
|
- Why separate concerns in Go packages?
|
||||||
|
- How does package structure affect testability?
|
||||||
|
- What are the trade-offs of package organization?
|
||||||
|
- When to use interfaces vs. concrete types?
|
||||||
|
- How to manage dependencies between packages?
|
||||||
|
|
||||||
|
See: `refactorings/001-cv-model-separation.md` for detailed analysis.
|
||||||
|
|
||||||
|
## 💡 Philosophy
|
||||||
|
|
||||||
|
> "The best way to learn is to teach. The best way to understand is to document WHY, not just WHAT."
|
||||||
|
|
||||||
|
This knowledge base forces me to:
|
||||||
|
1. **Question everything**: Why this approach over alternatives?
|
||||||
|
2. **Document reasoning**: Capture decision-making process
|
||||||
|
3. **Learn from mistakes**: Update when discovering better patterns
|
||||||
|
4. **Build intuition**: Develop mental models of good design
|
||||||
|
|
||||||
|
## 📊 Progress Tracking
|
||||||
|
|
||||||
|
- **Start Date**: 2025-11-20
|
||||||
|
- **Documents Created**: 0
|
||||||
|
- **Refactorings Documented**: 0
|
||||||
|
- **Patterns Catalogued**: 0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Remember**: This is a safe space for learning. It's okay to:
|
||||||
|
- Document wrong assumptions (then correct them)
|
||||||
|
- Ask "stupid" questions
|
||||||
|
- Explore multiple approaches
|
||||||
|
- Change your mind with new information
|
||||||
|
|
||||||
|
The goal is **deep understanding**, not perfection.
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<script type="text/hyperscript" src="/static/hyperscript/functions-full-test._hs"></script>
|
|
||||||
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
|
|
||||||
</head>
|
|
||||||
<body _="on load call initScrollBehavior() then call handleScroll()">
|
|
||||||
<div id="back-to-top"></div>
|
|
||||||
<div id="info-button"></div>
|
|
||||||
<div id="shortcuts-button"></div>
|
|
||||||
<h1>Testing partial hyperscript file</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<script type="text/hyperscript" src="/static/hyperscript/functions-test._hs"></script>
|
|
||||||
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
|
|
||||||
</head>
|
|
||||||
<body _="on load call testElseFunction()">
|
|
||||||
<h1>Testing simple else in external file</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -5,14 +5,14 @@
|
|||||||
<div class="actual-content">
|
<div class="actual-content">
|
||||||
<div class="cv-header-content">
|
<div class="cv-header-content">
|
||||||
<div class="cv-header-left">
|
<div class="cv-header-left">
|
||||||
<h1 class="cv-name">Moreno Rubio, Juan Andrés</h1>
|
<!-- Photo FIRST for float to work (CSS hides/shows based on viewport) -->
|
||||||
<p class="years-experience">{{.YearsOfExperience}} {{.UI.Sections.YearsOfExperience}}</p>
|
|
||||||
|
|
||||||
<!-- Photo positioned for mobile (centered between name and intro) -->
|
|
||||||
<div class="cv-photo">
|
<div class="cv-photo">
|
||||||
<img src="/static/images/profile/dni.jpeg" alt="{{.CV.Personal.Name}}" onerror="this.src='/static/images/profile/placeholder.svg'">
|
<img src="/static/images/profile/dni.jpeg" alt="{{.CV.Personal.Name}}" onerror="this.src='/static/images/profile/placeholder.svg'">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h1 class="cv-name">Moreno Rubio, Juan Andrés</h1>
|
||||||
|
<p class="years-experience">{{.YearsOfExperience}} {{.UI.Sections.YearsOfExperience}}</p>
|
||||||
|
|
||||||
<!-- Intro/Excerpt Text - No section heading, just the text -->
|
<!-- Intro/Excerpt Text - No section heading, just the text -->
|
||||||
<div class="intro-text">{{.CV.Summary}}</div>
|
<div class="intro-text">{{.CV.Summary}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user