@k2works/claude-code-booster 3.5.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +42 -42
- package/bin/claude-code-booster +90 -90
- package/lib/assets/.claude/README.md +239 -239
- package/lib/assets/.claude/scripts/generate-inception-deck.mjs +911 -911
- package/lib/assets/.claude/settings.json +11 -11
- package/lib/assets/.claude/skills/ai-agent-guidelines/SKILL.md +111 -111
- package/lib/assets/.claude/skills/analyzing-architecture/SKILL.md +83 -83
- package/lib/assets/.claude/skills/analyzing-business/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-data-model/SKILL.md +77 -77
- package/lib/assets/.claude/skills/analyzing-domain-model/SKILL.md +117 -88
- package/lib/assets/.claude/skills/analyzing-inception-deck/SKILL.md +84 -84
- package/lib/assets/.claude/skills/analyzing-non-functional/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-operation/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-requirements/SKILL.md +91 -91
- package/lib/assets/.claude/skills/analyzing-tech-stack/SKILL.md +101 -101
- package/lib/assets/.claude/skills/analyzing-test-strategy/SKILL.md +89 -89
- package/lib/assets/.claude/skills/analyzing-ui-design/SKILL.md +80 -80
- package/lib/assets/.claude/skills/analyzing-usecases/SKILL.md +72 -72
- package/lib/assets/.claude/skills/creating-adr/SKILL.md +113 -113
- package/lib/assets/.claude/skills/developing-backend/SKILL.md +100 -100
- package/lib/assets/.claude/skills/developing-frontend/SKILL.md +93 -93
- package/lib/assets/.claude/skills/developing-release/SKILL.md +120 -120
- package/lib/assets/.claude/skills/generating-slides/SKILL.md +94 -94
- package/lib/assets/.claude/skills/git-commit/SKILL.md +81 -81
- package/lib/assets/.claude/skills/killing-processes/SKILL.md +44 -44
- package/lib/assets/.claude/skills/operating-backup/SKILL.md +59 -59
- package/lib/assets/.claude/skills/operating-cicd/SKILL.md +54 -54
- package/lib/assets/.claude/skills/operating-deploy/SKILL.md +67 -67
- package/lib/assets/.claude/skills/operating-docs/SKILL.md +219 -219
- package/lib/assets/.claude/skills/operating-provision/SKILL.md +77 -77
- package/lib/assets/.claude/skills/operating-setup/SKILL.md +63 -63
- package/lib/assets/.claude/skills/orchestrating-analysis/SKILL.md +104 -104
- package/lib/assets/.claude/skills/orchestrating-development/SKILL.md +162 -161
- package/lib/assets/.claude/skills/orchestrating-operation/SKILL.md +158 -158
- package/lib/assets/.claude/skills/orchestrating-project/SKILL.md +144 -144
- package/lib/assets/.claude/skills/planning-releases/SKILL.md +119 -119
- package/lib/assets/.claude/skills/syncing-github-project/SKILL.md +151 -151
- package/lib/assets/.claude/skills/tracking-progress/SKILL.md +91 -91
- package/lib/assets/.claude/skills/validating-iteration-plan/SKILL.md +215 -215
- package/lib/assets/.devcontainer/devcontainer.json +34 -34
- package/lib/assets/.env.example +17 -17
- package/lib/assets/.gitattributes +4 -4
- package/lib/assets/.github/workflows/docker-publish.yml +77 -77
- package/lib/assets/.github/workflows/mkdocs.yml +39 -39
- package/lib/assets/AGENTS.md +94 -94
- package/lib/assets/CLAUDE.md +183 -183
- package/lib/assets/README.md +254 -254
- package/lib/assets/docker-compose.yml +33 -33
- package/lib/assets/docs/adr/index.md +10 -10
- package/lib/assets/docs/article/functional-desgin-ppp/all/01-immutability-and-data-transformation.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/all/02-function-composition.md +519 -519
- package/lib/assets/docs/article/functional-desgin-ppp/all/03-polymorphism.md +537 -537
- package/lib/assets/docs/article/functional-desgin-ppp/all/04-data-validation.md +300 -300
- package/lib/assets/docs/article/functional-desgin-ppp/all/05-property-based-testing.md +320 -320
- package/lib/assets/docs/article/functional-desgin-ppp/all/06-tdd-and-functional.md +498 -498
- package/lib/assets/docs/article/functional-desgin-ppp/all/07-composite-pattern.md +298 -298
- package/lib/assets/docs/article/functional-desgin-ppp/all/08-decorator-pattern.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/all/09-adapter-pattern.md +336 -336
- package/lib/assets/docs/article/functional-desgin-ppp/all/10-strategy-pattern.md +303 -303
- package/lib/assets/docs/article/functional-desgin-ppp/all/11-command-pattern.md +286 -286
- package/lib/assets/docs/article/functional-desgin-ppp/all/12-visitor-pattern.md +322 -322
- package/lib/assets/docs/article/functional-desgin-ppp/all/13-abstract-factory-pattern.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/all/14-abstract-server-pattern.md +365 -365
- package/lib/assets/docs/article/functional-desgin-ppp/all/15-gossiping-bus-drivers.md +156 -156
- package/lib/assets/docs/article/functional-desgin-ppp/all/16-payroll-system.md +178 -178
- package/lib/assets/docs/article/functional-desgin-ppp/all/17-video-rental-system.md +312 -312
- package/lib/assets/docs/article/functional-desgin-ppp/all/18-concurrency-system.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/all/19-wa-tor-simulation.md +286 -286
- package/lib/assets/docs/article/functional-desgin-ppp/all/20-pattern-interactions.md +274 -274
- package/lib/assets/docs/article/functional-desgin-ppp/all/21-best-practices.md +294 -294
- package/lib/assets/docs/article/functional-desgin-ppp/all/22-oo-to-fp-migration.md +337 -337
- package/lib/assets/docs/article/functional-desgin-ppp/all/index.md +388 -388
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/01-immutability-and-data-transformation.md +273 -273
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/02-function-composition.md +380 -380
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/03-polymorphism.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/04-clojure-spec.md +350 -350
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/05-property-based-testing.md +352 -352
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/06-tdd-in-functional.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/07-composite-pattern.md +529 -529
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/08-decorator-pattern.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/09-adapter-pattern.md +399 -399
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/10-strategy-pattern.md +485 -485
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/11-command-pattern.md +566 -566
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/12-visitor-pattern.md +567 -567
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/13-abstract-factory-pattern.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/14-abstract-server-pattern.md +462 -462
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/15-gossiping-bus-drivers.md +325 -325
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/16-payroll-system.md +401 -401
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/17-video-rental-system.md +450 -450
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/18-concurrency-system.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/19-wator-simulation.md +739 -739
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/20-pattern-interactions.md +567 -567
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/21-best-practices.md +518 -518
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/22-oo-to-fp-migration.md +532 -532
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/index.md +241 -241
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/01-immutability-and-data-transformation.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/02-function-composition.md +374 -374
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/03-polymorphism.md +375 -375
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/04-data-validation.md +195 -195
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/05-property-based-testing.md +268 -268
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/06-tdd-and-fp.md +294 -294
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/07-effects-and-pure-functions.md +164 -164
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/08-error-handling-strategies.md +168 -168
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/09-io-and-external-systems.md +254 -254
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/10-concurrency-patterns.md +269 -269
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/11-command-pattern.md +148 -148
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/12-visitor-pattern.md +176 -176
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/13-abstract-factory-pattern.md +604 -604
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/14-abstract-server-pattern.md +729 -729
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/15-gossiping-bus-drivers.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/16-payroll-system.md +420 -420
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/17-video-rental-system.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/18-concurrency-system.md +466 -466
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/19-wator-simulation.md +523 -523
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/20-pattern-interactions.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/21-best-practices.md +340 -340
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/22-oo-to-fp-migration.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/index.md +248 -248
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/01-immutability-and-data-transformation.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/02-function-composition.md +452 -452
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/03-polymorphism.md +495 -495
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/04-data-validation.md +416 -416
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/05-property-based-testing.md +382 -382
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/06-tdd-functional.md +687 -687
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/07-composite-pattern.md +442 -442
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/08-decorator-pattern.md +479 -479
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/09-adapter-pattern.md +479 -479
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/10-strategy-pattern.md +427 -427
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/11-command-pattern.md +428 -428
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/12-visitor-pattern.md +339 -339
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/13-abstract-factory-pattern.md +309 -309
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/14-abstract-server-pattern.md +596 -596
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/15-gossiping-bus-drivers.md +355 -355
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/16-payroll-system.md +350 -350
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/17-video-rental-system.md +414 -414
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/18-concurrency-system.md +367 -367
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/19-wator-simulation.md +403 -403
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/20-pattern-interactions.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/21-best-practices.md +324 -324
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/22-oo-to-fp-migration.md +332 -332
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/index.md +274 -274
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/01-immutability-and-data-transformation.md +298 -298
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/02-function-composition.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/03-polymorphism.md +362 -362
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/04-data-validation.md +257 -257
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/05-property-based-testing.md +254 -254
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/06-tdd-functional.md +283 -283
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/07-composite-pattern.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/08-decorator-pattern.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/09-adapter-pattern.md +382 -382
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/10-strategy-pattern.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/11-command-pattern.md +303 -303
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/12-visitor-pattern.md +326 -326
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/13-abstract-factory-pattern.md +332 -332
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/14-abstract-server-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/15-gossiping-bus-drivers.md +177 -177
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/16-payroll-system.md +219 -219
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/17-video-rental-system.md +244 -244
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/18-concurrency-system.md +363 -363
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/19-wator-simulation.md +438 -438
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/20-pattern-interactions.md +325 -325
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/21-best-practices.md +403 -403
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/22-oo-to-fp-migration.md +469 -469
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/index.md +174 -174
- package/lib/assets/docs/article/functional-desgin-ppp/index.md +90 -90
- package/lib/assets/docs/article/functional-desgin-ppp/rust/01-immutability-and-data-transformation.md +450 -450
- package/lib/assets/docs/article/functional-desgin-ppp/rust/02-function-composition.md +463 -463
- package/lib/assets/docs/article/functional-desgin-ppp/rust/03-polymorphism.md +425 -425
- package/lib/assets/docs/article/functional-desgin-ppp/rust/04-data-validation.md +273 -273
- package/lib/assets/docs/article/functional-desgin-ppp/rust/05-property-based-testing.md +247 -247
- package/lib/assets/docs/article/functional-desgin-ppp/rust/06-tdd-and-functional.md +841 -841
- package/lib/assets/docs/article/functional-desgin-ppp/rust/07-composite-pattern.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/rust/08-decorator-pattern.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/rust/09-adapter-pattern.md +339 -339
- package/lib/assets/docs/article/functional-desgin-ppp/rust/10-strategy-pattern.md +331 -331
- package/lib/assets/docs/article/functional-desgin-ppp/rust/11-command-pattern.md +356 -356
- package/lib/assets/docs/article/functional-desgin-ppp/rust/12-visitor-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/rust/13-abstract-factory-pattern.md +361 -361
- package/lib/assets/docs/article/functional-desgin-ppp/rust/14-abstract-server-pattern.md +392 -392
- package/lib/assets/docs/article/functional-desgin-ppp/rust/15-gossiping-bus-drivers.md +300 -300
- package/lib/assets/docs/article/functional-desgin-ppp/rust/16-payroll-system.md +297 -297
- package/lib/assets/docs/article/functional-desgin-ppp/rust/17-video-rental-system.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/rust/18-concurrency-system.md +315 -315
- package/lib/assets/docs/article/functional-desgin-ppp/rust/19-wator-simulation.md +311 -311
- package/lib/assets/docs/article/functional-desgin-ppp/rust/20-pattern-interactions.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/rust/21-best-practices.md +336 -336
- package/lib/assets/docs/article/functional-desgin-ppp/rust/22-oo-to-fp-migration.md +349 -349
- package/lib/assets/docs/article/functional-desgin-ppp/rust/index.md +243 -243
- package/lib/assets/docs/article/functional-desgin-ppp/scala/01-immutability-and-data-transformation.md +328 -328
- package/lib/assets/docs/article/functional-desgin-ppp/scala/02-function-composition.md +348 -348
- package/lib/assets/docs/article/functional-desgin-ppp/scala/03-polymorphism.md +357 -357
- package/lib/assets/docs/article/functional-desgin-ppp/scala/04-data-validation.md +364 -364
- package/lib/assets/docs/article/functional-desgin-ppp/scala/05-property-based-testing.md +515 -515
- package/lib/assets/docs/article/functional-desgin-ppp/scala/06-tdd-functional.md +557 -557
- package/lib/assets/docs/article/functional-desgin-ppp/scala/07-composite-pattern.md +363 -363
- package/lib/assets/docs/article/functional-desgin-ppp/scala/08-decorator-pattern.md +327 -327
- package/lib/assets/docs/article/functional-desgin-ppp/scala/09-adapter-pattern.md +517 -517
- package/lib/assets/docs/article/functional-desgin-ppp/scala/10-strategy-pattern.md +441 -441
- package/lib/assets/docs/article/functional-desgin-ppp/scala/11-command-pattern.md +407 -407
- package/lib/assets/docs/article/functional-desgin-ppp/scala/12-visitor-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/scala/13-abstract-factory-pattern.md +398 -398
- package/lib/assets/docs/article/functional-desgin-ppp/scala/14-abstract-server-pattern.md +476 -476
- package/lib/assets/docs/article/functional-desgin-ppp/scala/15-gossiping-bus-drivers.md +391 -391
- package/lib/assets/docs/article/functional-desgin-ppp/scala/16-payroll-system.md +342 -342
- package/lib/assets/docs/article/functional-desgin-ppp/scala/17-video-rental-system.md +324 -324
- package/lib/assets/docs/article/functional-desgin-ppp/scala/18-concurrency-system.md +730 -730
- package/lib/assets/docs/article/functional-desgin-ppp/scala/19-wator-simulation.md +624 -624
- package/lib/assets/docs/article/functional-desgin-ppp/scala/20-pattern-interactions.md +512 -512
- package/lib/assets/docs/article/functional-desgin-ppp/scala/21-best-practices.md +433 -433
- package/lib/assets/docs/article/functional-desgin-ppp/scala/22-oo-to-fp-migration.md +688 -688
- package/lib/assets/docs/article/functional-desgin-ppp/scala/index.md +243 -243
- package/lib/assets/docs/article/getting-start-tdd/clojure/01-todo-list-and-first-test.md +166 -166
- package/lib/assets/docs/article/getting-start-tdd/clojure/02-fake-it-and-triangulation.md +162 -162
- package/lib/assets/docs/article/getting-start-tdd/clojure/03-obvious-implementation-and-refactoring.md +135 -135
- package/lib/assets/docs/article/getting-start-tdd/clojure/04-version-control-and-conventional-commits.md +88 -88
- package/lib/assets/docs/article/getting-start-tdd/clojure/05-package-management-and-static-analysis.md +299 -299
- package/lib/assets/docs/article/getting-start-tdd/clojure/06-task-runner-and-ci-cd.md +241 -241
- package/lib/assets/docs/article/getting-start-tdd/clojure/07-protocols-and-records.md +131 -131
- package/lib/assets/docs/article/getting-start-tdd/clojure/08-multimethods-and-design-patterns.md +130 -130
- package/lib/assets/docs/article/getting-start-tdd/clojure/09-namespaces-and-module-design.md +127 -127
- package/lib/assets/docs/article/getting-start-tdd/clojure/10-higher-order-functions-and-composition.md +114 -114
- package/lib/assets/docs/article/getting-start-tdd/clojure/11-persistent-data-and-pipeline.md +138 -138
- package/lib/assets/docs/article/getting-start-tdd/clojure/12-error-handling-and-spec.md +161 -161
- package/lib/assets/docs/article/getting-start-tdd/clojure/index.md +65 -65
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter01.md +232 -232
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter02.md +244 -244
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter03.md +202 -202
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter04.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter05.md +256 -256
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter06.md +195 -195
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter07.md +214 -214
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter08.md +249 -249
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter09.md +174 -174
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter10.md +166 -166
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter11.md +192 -192
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter12.md +211 -211
- package/lib/assets/docs/article/getting-start-tdd/csharp/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/elixir/01-todo-list-and-first-test.md +87 -87
- package/lib/assets/docs/article/getting-start-tdd/elixir/02-fake-it-and-triangulation.md +95 -95
- package/lib/assets/docs/article/getting-start-tdd/elixir/03-obvious-implementation-and-refactoring.md +109 -109
- package/lib/assets/docs/article/getting-start-tdd/elixir/04-version-control-and-conventional-commits.md +96 -96
- package/lib/assets/docs/article/getting-start-tdd/elixir/05-package-management-and-static-analysis.md +88 -88
- package/lib/assets/docs/article/getting-start-tdd/elixir/06-task-runner-and-ci-cd.md +71 -71
- package/lib/assets/docs/article/getting-start-tdd/elixir/07-structs-and-protocols.md +110 -110
- package/lib/assets/docs/article/getting-start-tdd/elixir/08-pattern-matching-and-guards.md +108 -108
- package/lib/assets/docs/article/getting-start-tdd/elixir/09-module-design-and-behaviours.md +104 -104
- package/lib/assets/docs/article/getting-start-tdd/elixir/10-higher-order-functions-and-pipeline.md +178 -178
- package/lib/assets/docs/article/getting-start-tdd/elixir/11-stream-and-lazy-evaluation.md +142 -142
- package/lib/assets/docs/article/getting-start-tdd/elixir/12-error-handling-and-with.md +145 -145
- package/lib/assets/docs/article/getting-start-tdd/elixir/index.md +35 -35
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter01.md +202 -202
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter02.md +246 -246
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter03.md +218 -218
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter04.md +179 -179
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter05.md +267 -267
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter06.md +190 -190
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter07.md +161 -161
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter08.md +175 -175
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter09.md +222 -222
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter10.md +189 -189
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter11.md +212 -212
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter12.md +215 -215
- package/lib/assets/docs/article/getting-start-tdd/fsharp/index.md +71 -71
- package/lib/assets/docs/article/getting-start-tdd/go/01-todo-list-and-first-test.md +213 -213
- package/lib/assets/docs/article/getting-start-tdd/go/02-fake-it-and-triangulation.md +302 -302
- package/lib/assets/docs/article/getting-start-tdd/go/03-obvious-implementation-and-refactoring.md +339 -339
- package/lib/assets/docs/article/getting-start-tdd/go/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/go/05-package-management-and-static-analysis.md +272 -272
- package/lib/assets/docs/article/getting-start-tdd/go/06-task-runner-and-ci-cd.md +233 -233
- package/lib/assets/docs/article/getting-start-tdd/go/07-encapsulation-and-polymorphism.md +394 -394
- package/lib/assets/docs/article/getting-start-tdd/go/08-design-patterns.md +422 -422
- package/lib/assets/docs/article/getting-start-tdd/go/09-solid-principles-and-module-design.md +400 -400
- package/lib/assets/docs/article/getting-start-tdd/go/10-higher-order-functions-and-composition.md +226 -226
- package/lib/assets/docs/article/getting-start-tdd/go/11-immutable-data-and-pipeline.md +296 -296
- package/lib/assets/docs/article/getting-start-tdd/go/12-error-handling-and-type-safety.md +411 -411
- package/lib/assets/docs/article/getting-start-tdd/go/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/haskell/01-todo-list-and-first-test.md +279 -279
- package/lib/assets/docs/article/getting-start-tdd/haskell/02-fake-it-and-triangulation.md +337 -337
- package/lib/assets/docs/article/getting-start-tdd/haskell/03-obvious-implementation-and-refactoring.md +257 -257
- package/lib/assets/docs/article/getting-start-tdd/haskell/04-version-control-and-conventional-commits.md +182 -182
- package/lib/assets/docs/article/getting-start-tdd/haskell/05-package-management-and-static-analysis.md +313 -313
- package/lib/assets/docs/article/getting-start-tdd/haskell/06-task-runner-and-ci-cd.md +309 -309
- package/lib/assets/docs/article/getting-start-tdd/haskell/07-algebraic-data-types-and-type-classes.md +412 -412
- package/lib/assets/docs/article/getting-start-tdd/haskell/08-pattern-matching-and-guards.md +390 -390
- package/lib/assets/docs/article/getting-start-tdd/haskell/09-module-design-and-smart-constructors.md +461 -461
- package/lib/assets/docs/article/getting-start-tdd/haskell/10-higher-order-functions-and-currying.md +434 -434
- package/lib/assets/docs/article/getting-start-tdd/haskell/11-function-composition-and-point-free.md +392 -392
- package/lib/assets/docs/article/getting-start-tdd/haskell/12-monad-and-error-handling.md +631 -631
- package/lib/assets/docs/article/getting-start-tdd/haskell/index.md +49 -49
- package/lib/assets/docs/article/getting-start-tdd/index.md +93 -93
- package/lib/assets/docs/article/getting-start-tdd/integration/01-language-overview.md +375 -375
- package/lib/assets/docs/article/getting-start-tdd/integration/02-test-framework-comparison.md +349 -349
- package/lib/assets/docs/article/getting-start-tdd/integration/03-tdd-pattern-comparison.md +445 -445
- package/lib/assets/docs/article/getting-start-tdd/integration/04-type-system-comparison.md +409 -409
- package/lib/assets/docs/article/getting-start-tdd/integration/05-dev-environment-comparison.md +330 -330
- package/lib/assets/docs/article/getting-start-tdd/integration/06-learning-roadmap.md +290 -290
- package/lib/assets/docs/article/getting-start-tdd/integration/index.md +69 -69
- package/lib/assets/docs/article/getting-start-tdd/java/01-todo-list-and-first-test.md +234 -234
- package/lib/assets/docs/article/getting-start-tdd/java/02-fake-it-and-triangulation.md +261 -261
- package/lib/assets/docs/article/getting-start-tdd/java/03-obvious-implementation-and-refactoring.md +185 -185
- package/lib/assets/docs/article/getting-start-tdd/java/04-version-control-and-conventional-commits.md +115 -115
- package/lib/assets/docs/article/getting-start-tdd/java/05-package-management-and-static-analysis.md +382 -382
- package/lib/assets/docs/article/getting-start-tdd/java/06-task-runner-and-ci-cd.md +272 -272
- package/lib/assets/docs/article/getting-start-tdd/java/07-encapsulation-and-polymorphism.md +626 -626
- package/lib/assets/docs/article/getting-start-tdd/java/08-design-patterns.md +393 -393
- package/lib/assets/docs/article/getting-start-tdd/java/09-solid-principles-and-module-design.md +310 -310
- package/lib/assets/docs/article/getting-start-tdd/java/10-higher-order-functions-and-composition.md +188 -188
- package/lib/assets/docs/article/getting-start-tdd/java/11-immutable-data-and-pipeline.md +167 -167
- package/lib/assets/docs/article/getting-start-tdd/java/12-error-handling-and-type-safety.md +205 -205
- package/lib/assets/docs/article/getting-start-tdd/java/index.md +61 -61
- package/lib/assets/docs/article/getting-start-tdd/node/01-todo-list-and-first-test.md +244 -244
- package/lib/assets/docs/article/getting-start-tdd/node/02-fake-it-and-triangulation.md +262 -262
- package/lib/assets/docs/article/getting-start-tdd/node/03-obvious-implementation-and-refactoring.md +169 -169
- package/lib/assets/docs/article/getting-start-tdd/node/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/node/05-package-management-and-static-analysis.md +314 -314
- package/lib/assets/docs/article/getting-start-tdd/node/06-task-runner-and-ci-cd.md +235 -235
- package/lib/assets/docs/article/getting-start-tdd/node/07-encapsulation-and-polymorphism.md +327 -327
- package/lib/assets/docs/article/getting-start-tdd/node/08-design-patterns.md +322 -322
- package/lib/assets/docs/article/getting-start-tdd/node/09-solid-principles-and-module-design.md +285 -285
- package/lib/assets/docs/article/getting-start-tdd/node/10-higher-order-functions-and-composition.md +199 -199
- package/lib/assets/docs/article/getting-start-tdd/node/11-immutable-data-and-pipeline.md +207 -207
- package/lib/assets/docs/article/getting-start-tdd/node/12-error-handling-and-type-safety.md +295 -295
- package/lib/assets/docs/article/getting-start-tdd/node/index.md +56 -56
- package/lib/assets/docs/article/getting-start-tdd/php/01-todo-list-and-first-test.md +259 -259
- package/lib/assets/docs/article/getting-start-tdd/php/02-fake-it-and-triangulation.md +200 -200
- package/lib/assets/docs/article/getting-start-tdd/php/03-obvious-implementation-and-refactoring.md +248 -248
- package/lib/assets/docs/article/getting-start-tdd/php/04-version-control-and-conventional-commits.md +141 -141
- package/lib/assets/docs/article/getting-start-tdd/php/05-package-management-and-static-analysis.md +410 -410
- package/lib/assets/docs/article/getting-start-tdd/php/06-task-runner-and-ci-cd.md +321 -321
- package/lib/assets/docs/article/getting-start-tdd/php/07-encapsulation-and-polymorphism.md +372 -372
- package/lib/assets/docs/article/getting-start-tdd/php/08-design-patterns.md +453 -453
- package/lib/assets/docs/article/getting-start-tdd/php/09-solid-principles-and-module-design.md +460 -460
- package/lib/assets/docs/article/getting-start-tdd/php/10-higher-order-functions-and-composition.md +182 -182
- package/lib/assets/docs/article/getting-start-tdd/php/11-immutable-data-and-pipeline.md +266 -266
- package/lib/assets/docs/article/getting-start-tdd/php/12-error-handling-and-type-safety.md +308 -308
- package/lib/assets/docs/article/getting-start-tdd/php/index.md +84 -84
- package/lib/assets/docs/article/getting-start-tdd/python/01-todo-list-and-first-test.md +201 -201
- package/lib/assets/docs/article/getting-start-tdd/python/02-fake-it-and-triangulation.md +247 -247
- package/lib/assets/docs/article/getting-start-tdd/python/03-obvious-implementation-and-refactoring.md +199 -199
- package/lib/assets/docs/article/getting-start-tdd/python/04-version-control-and-conventional-commits.md +87 -87
- package/lib/assets/docs/article/getting-start-tdd/python/05-package-management-and-static-analysis.md +274 -274
- package/lib/assets/docs/article/getting-start-tdd/python/06-task-runner-and-ci-cd.md +190 -190
- package/lib/assets/docs/article/getting-start-tdd/python/07-encapsulation-and-polymorphism.md +208 -208
- package/lib/assets/docs/article/getting-start-tdd/python/08-design-patterns.md +172 -172
- package/lib/assets/docs/article/getting-start-tdd/python/09-solid-principles-and-module-design.md +130 -130
- package/lib/assets/docs/article/getting-start-tdd/python/10-higher-order-functions-and-composition.md +122 -122
- package/lib/assets/docs/article/getting-start-tdd/python/11-immutable-data-and-pipeline.md +116 -116
- package/lib/assets/docs/article/getting-start-tdd/python/12-error-handling-and-type-safety.md +126 -126
- package/lib/assets/docs/article/getting-start-tdd/python/index.md +55 -55
- package/lib/assets/docs/article/getting-start-tdd/ruby/01-todo-list-and-first-test.md +231 -231
- package/lib/assets/docs/article/getting-start-tdd/ruby/02-fake-it-and-triangulation.md +238 -238
- package/lib/assets/docs/article/getting-start-tdd/ruby/03-obvious-implementation-and-refactoring.md +228 -228
- package/lib/assets/docs/article/getting-start-tdd/ruby/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/ruby/05-package-management-and-static-analysis.md +287 -287
- package/lib/assets/docs/article/getting-start-tdd/ruby/06-task-runner-and-ci-cd.md +248 -248
- package/lib/assets/docs/article/getting-start-tdd/ruby/07-encapsulation-and-polymorphism.md +279 -279
- package/lib/assets/docs/article/getting-start-tdd/ruby/08-design-patterns.md +329 -329
- package/lib/assets/docs/article/getting-start-tdd/ruby/09-solid-principles-and-module-design.md +196 -196
- package/lib/assets/docs/article/getting-start-tdd/ruby/10-higher-order-functions-and-composition.md +175 -175
- package/lib/assets/docs/article/getting-start-tdd/ruby/11-immutable-data-and-pipeline.md +237 -237
- package/lib/assets/docs/article/getting-start-tdd/ruby/12-error-handling-and-type-safety.md +398 -398
- package/lib/assets/docs/article/getting-start-tdd/ruby/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/rust/01-todo-list-and-first-test.md +211 -211
- package/lib/assets/docs/article/getting-start-tdd/rust/02-fake-it-and-triangulation.md +264 -264
- package/lib/assets/docs/article/getting-start-tdd/rust/03-obvious-implementation-and-refactoring.md +233 -233
- package/lib/assets/docs/article/getting-start-tdd/rust/04-version-control-and-conventional-commits.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/rust/05-package-management-and-static-analysis.md +212 -212
- package/lib/assets/docs/article/getting-start-tdd/rust/06-task-runner-and-ci-cd.md +164 -164
- package/lib/assets/docs/article/getting-start-tdd/rust/07-encapsulation-and-polymorphism.md +142 -142
- package/lib/assets/docs/article/getting-start-tdd/rust/08-design-patterns.md +145 -145
- package/lib/assets/docs/article/getting-start-tdd/rust/09-solid-principles-and-module-design.md +110 -110
- package/lib/assets/docs/article/getting-start-tdd/rust/10-higher-order-functions-and-composition.md +94 -94
- package/lib/assets/docs/article/getting-start-tdd/rust/11-immutable-data-and-pipeline.md +105 -105
- package/lib/assets/docs/article/getting-start-tdd/rust/12-error-handling-and-type-safety.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/rust/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/scala/01-todo-list-and-first-test.md +111 -111
- package/lib/assets/docs/article/getting-start-tdd/scala/02-fake-it-and-triangulation.md +107 -107
- package/lib/assets/docs/article/getting-start-tdd/scala/03-obvious-implementation-and-refactoring.md +99 -99
- package/lib/assets/docs/article/getting-start-tdd/scala/04-version-control-and-conventional-commits.md +123 -123
- package/lib/assets/docs/article/getting-start-tdd/scala/05-package-management-and-static-analysis.md +196 -196
- package/lib/assets/docs/article/getting-start-tdd/scala/06-task-runner-and-ci-cd.md +186 -186
- package/lib/assets/docs/article/getting-start-tdd/scala/07-case-classes-and-traits.md +139 -139
- package/lib/assets/docs/article/getting-start-tdd/scala/08-pattern-matching-and-sealed-traits.md +106 -106
- package/lib/assets/docs/article/getting-start-tdd/scala/09-packages-and-module-design.md +75 -75
- package/lib/assets/docs/article/getting-start-tdd/scala/10-higher-order-functions-and-composition.md +104 -104
- package/lib/assets/docs/article/getting-start-tdd/scala/11-collections-and-lazy-evaluation.md +94 -94
- package/lib/assets/docs/article/getting-start-tdd/scala/12-error-handling-and-type-safety.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/scala/index.md +65 -65
- package/lib/assets/docs/article/grokking-concurrency/all/index.md +404 -404
- package/lib/assets/docs/article/grokking-concurrency/all/part-1-ch02-sequential.md +554 -554
- package/lib/assets/docs/article/grokking-concurrency/all/part-2-ch04-05-threads.md +469 -469
- package/lib/assets/docs/article/grokking-concurrency/all/part-3-ch06-multitasking.md +520 -520
- package/lib/assets/docs/article/grokking-concurrency/all/part-4-ch07-parallel-patterns.md +420 -420
- package/lib/assets/docs/article/grokking-concurrency/all/part-5-ch08-09-synchronization.md +510 -510
- package/lib/assets/docs/article/grokking-concurrency/all/part-6-ch10-11-nonblocking-io.md +435 -435
- package/lib/assets/docs/article/grokking-concurrency/all/part-7-ch12-async.md +465 -465
- package/lib/assets/docs/article/grokking-concurrency/all/part-8-ch13-mapreduce.md +377 -377
- package/lib/assets/docs/article/grokking-concurrency/clojure/index.md +116 -116
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-1.md +108 -108
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-2.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-3.md +122 -122
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-4.md +123 -123
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-5.md +118 -118
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-6.md +89 -89
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-7.md +100 -100
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-8.md +120 -120
- package/lib/assets/docs/article/grokking-concurrency/csharp/index.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-1.md +97 -97
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-2.md +123 -123
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-3.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-4.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-5.md +99 -99
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-6.md +61 -61
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-7.md +84 -84
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-8.md +92 -92
- package/lib/assets/docs/article/grokking-concurrency/fsharp/index.md +65 -65
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-1.md +80 -80
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-2.md +103 -103
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-3.md +94 -94
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-4.md +110 -110
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-5.md +104 -104
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-6.md +93 -93
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-7.md +121 -121
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-8.md +107 -107
- package/lib/assets/docs/article/grokking-concurrency/haskell/index.md +248 -248
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-1.md +96 -96
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-2.md +96 -96
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-3.md +91 -91
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-4.md +106 -106
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-5.md +99 -99
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-6.md +95 -95
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-7.md +111 -111
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-8.md +118 -118
- package/lib/assets/docs/article/grokking-concurrency/index.md +66 -66
- package/lib/assets/docs/article/grokking-concurrency/java/index.md +102 -102
- package/lib/assets/docs/article/grokking-concurrency/java/part-1.md +308 -308
- package/lib/assets/docs/article/grokking-concurrency/java/part-2.md +334 -334
- package/lib/assets/docs/article/grokking-concurrency/java/part-3.md +221 -221
- package/lib/assets/docs/article/grokking-concurrency/java/part-4.md +213 -213
- package/lib/assets/docs/article/grokking-concurrency/java/part-5.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/java/part-6.md +69 -69
- package/lib/assets/docs/article/grokking-concurrency/java/part-7.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/java/part-8.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/python/index.md +313 -313
- package/lib/assets/docs/article/grokking-concurrency/python/part-1.md +239 -239
- package/lib/assets/docs/article/grokking-concurrency/python/part-2.md +418 -418
- package/lib/assets/docs/article/grokking-concurrency/python/part-3.md +227 -227
- package/lib/assets/docs/article/grokking-concurrency/python/part-4.md +299 -299
- package/lib/assets/docs/article/grokking-concurrency/python/part-5.md +315 -315
- package/lib/assets/docs/article/grokking-concurrency/python/part-6.md +297 -297
- package/lib/assets/docs/article/grokking-concurrency/python/part-7.md +314 -314
- package/lib/assets/docs/article/grokking-concurrency/python/part-8.md +360 -360
- package/lib/assets/docs/article/grokking-concurrency/rust/index.md +270 -270
- package/lib/assets/docs/article/grokking-concurrency/rust/part-1.md +108 -108
- package/lib/assets/docs/article/grokking-concurrency/rust/part-2.md +120 -120
- package/lib/assets/docs/article/grokking-concurrency/rust/part-3.md +126 -126
- package/lib/assets/docs/article/grokking-concurrency/rust/part-4.md +175 -175
- package/lib/assets/docs/article/grokking-concurrency/rust/part-5.md +158 -158
- package/lib/assets/docs/article/grokking-concurrency/rust/part-6.md +94 -94
- package/lib/assets/docs/article/grokking-concurrency/rust/part-7.md +133 -133
- package/lib/assets/docs/article/grokking-concurrency/rust/part-8.md +155 -155
- package/lib/assets/docs/article/grokking-concurrency/scala/index.md +69 -69
- package/lib/assets/docs/article/grokking-concurrency/scala/part-1.md +78 -78
- package/lib/assets/docs/article/grokking-concurrency/scala/part-2.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/scala/part-3.md +93 -93
- package/lib/assets/docs/article/grokking-concurrency/scala/part-4.md +110 -110
- package/lib/assets/docs/article/grokking-concurrency/scala/part-5.md +119 -119
- package/lib/assets/docs/article/grokking-concurrency/scala/part-6.md +83 -83
- package/lib/assets/docs/article/grokking-concurrency/scala/part-7.md +131 -131
- package/lib/assets/docs/article/grokking-concurrency/scala/part-8.md +129 -129
- package/lib/assets/docs/article/grokkingfp/all/index.md +368 -368
- package/lib/assets/docs/article/grokkingfp/all/part-1-ch01-fp-introduction.md +530 -530
- package/lib/assets/docs/article/grokkingfp/all/part-1-ch02-pure-functions.md +923 -923
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch03-immutable-data.md +1128 -1128
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch04-higher-order-functions.md +1104 -1104
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch05-flatmap.md +1026 -1026
- package/lib/assets/docs/article/grokkingfp/all/part-3-ch06-option.md +785 -785
- package/lib/assets/docs/article/grokkingfp/all/part-3-ch07-either-adt.md +871 -871
- package/lib/assets/docs/article/grokkingfp/all/part-4-ch08-io-monad.md +972 -972
- package/lib/assets/docs/article/grokkingfp/all/part-4-ch09-streams.md +926 -926
- package/lib/assets/docs/article/grokkingfp/all/part-5-ch10-concurrency.md +870 -870
- package/lib/assets/docs/article/grokkingfp/all/part-6-ch11-application.md +715 -715
- package/lib/assets/docs/article/grokkingfp/all/part-6-ch12-testing.md +626 -626
- package/lib/assets/docs/article/grokkingfp/all/writing-plan.md +712 -712
- package/lib/assets/docs/article/grokkingfp/clojure/index.md +276 -276
- package/lib/assets/docs/article/grokkingfp/clojure/part-1.md +667 -667
- package/lib/assets/docs/article/grokkingfp/clojure/part-2.md +643 -643
- package/lib/assets/docs/article/grokkingfp/clojure/part-3.md +620 -620
- package/lib/assets/docs/article/grokkingfp/clojure/part-4.md +697 -697
- package/lib/assets/docs/article/grokkingfp/clojure/part-5.md +751 -751
- package/lib/assets/docs/article/grokkingfp/clojure/part-6.md +721 -721
- package/lib/assets/docs/article/grokkingfp/csharp/index.md +246 -246
- package/lib/assets/docs/article/grokkingfp/csharp/part-1.md +811 -811
- package/lib/assets/docs/article/grokkingfp/csharp/part-2.md +971 -971
- package/lib/assets/docs/article/grokkingfp/csharp/part-3.md +981 -981
- package/lib/assets/docs/article/grokkingfp/csharp/part-4.md +949 -949
- package/lib/assets/docs/article/grokkingfp/csharp/part-5.md +947 -947
- package/lib/assets/docs/article/grokkingfp/csharp/part-6.md +739 -739
- package/lib/assets/docs/article/grokkingfp/elixir/index.md +203 -203
- package/lib/assets/docs/article/grokkingfp/elixir/part-1.md +712 -712
- package/lib/assets/docs/article/grokkingfp/elixir/part-2.md +838 -838
- package/lib/assets/docs/article/grokkingfp/elixir/part-3.md +985 -985
- package/lib/assets/docs/article/grokkingfp/elixir/part-4.md +974 -974
- package/lib/assets/docs/article/grokkingfp/elixir/part-5.md +1286 -1286
- package/lib/assets/docs/article/grokkingfp/elixir/part-6.md +1049 -1049
- package/lib/assets/docs/article/grokkingfp/fsharp/index.md +210 -210
- package/lib/assets/docs/article/grokkingfp/fsharp/part-1.md +714 -714
- package/lib/assets/docs/article/grokkingfp/fsharp/part-2.md +961 -961
- package/lib/assets/docs/article/grokkingfp/fsharp/part-3.md +972 -972
- package/lib/assets/docs/article/grokkingfp/fsharp/part-4.md +832 -832
- package/lib/assets/docs/article/grokkingfp/fsharp/part-5.md +911 -911
- package/lib/assets/docs/article/grokkingfp/fsharp/part-6.md +922 -922
- package/lib/assets/docs/article/grokkingfp/haskell/index.md +234 -234
- package/lib/assets/docs/article/grokkingfp/haskell/part-1.md +591 -591
- package/lib/assets/docs/article/grokkingfp/haskell/part-2.md +866 -866
- package/lib/assets/docs/article/grokkingfp/haskell/part-3.md +915 -915
- package/lib/assets/docs/article/grokkingfp/haskell/part-4.md +878 -878
- package/lib/assets/docs/article/grokkingfp/haskell/part-5.md +845 -845
- package/lib/assets/docs/article/grokkingfp/haskell/part-6.md +844 -844
- package/lib/assets/docs/article/grokkingfp/index.md +143 -143
- package/lib/assets/docs/article/grokkingfp/java/index.md +211 -211
- package/lib/assets/docs/article/grokkingfp/java/part-1.md +648 -648
- package/lib/assets/docs/article/grokkingfp/java/part-2.md +675 -675
- package/lib/assets/docs/article/grokkingfp/java/part-3.md +672 -672
- package/lib/assets/docs/article/grokkingfp/java/part-4.md +771 -771
- package/lib/assets/docs/article/grokkingfp/java/part-5.md +959 -959
- package/lib/assets/docs/article/grokkingfp/java/part-6.md +1328 -1328
- package/lib/assets/docs/article/grokkingfp/python/index.md +258 -258
- package/lib/assets/docs/article/grokkingfp/python/part-1.md +443 -443
- package/lib/assets/docs/article/grokkingfp/python/part-2.md +958 -958
- package/lib/assets/docs/article/grokkingfp/python/part-3.md +1004 -1004
- package/lib/assets/docs/article/grokkingfp/python/part-4.md +765 -765
- package/lib/assets/docs/article/grokkingfp/python/part-5.md +747 -747
- package/lib/assets/docs/article/grokkingfp/python/part-6.md +861 -861
- package/lib/assets/docs/article/grokkingfp/ruby/index.md +330 -330
- package/lib/assets/docs/article/grokkingfp/ruby/part-1.md +755 -755
- package/lib/assets/docs/article/grokkingfp/ruby/part-2.md +938 -938
- package/lib/assets/docs/article/grokkingfp/ruby/part-3.md +946 -946
- package/lib/assets/docs/article/grokkingfp/ruby/part-4.md +921 -921
- package/lib/assets/docs/article/grokkingfp/ruby/part-5.md +908 -908
- package/lib/assets/docs/article/grokkingfp/ruby/part-6.md +1412 -1412
- package/lib/assets/docs/article/grokkingfp/rust/index.md +242 -242
- package/lib/assets/docs/article/grokkingfp/rust/part-1.md +634 -634
- package/lib/assets/docs/article/grokkingfp/rust/part-2.md +1060 -1060
- package/lib/assets/docs/article/grokkingfp/rust/part-3.md +994 -994
- package/lib/assets/docs/article/grokkingfp/rust/part-4.md +573 -573
- package/lib/assets/docs/article/grokkingfp/rust/part-5.md +705 -705
- package/lib/assets/docs/article/grokkingfp/rust/part-6.md +508 -508
- package/lib/assets/docs/article/grokkingfp/scala/index.md +171 -171
- package/lib/assets/docs/article/grokkingfp/scala/part-1.md +543 -543
- package/lib/assets/docs/article/grokkingfp/scala/part-2.md +946 -946
- package/lib/assets/docs/article/grokkingfp/scala/part-3.md +919 -919
- package/lib/assets/docs/article/grokkingfp/scala/part-4.md +742 -742
- package/lib/assets/docs/article/grokkingfp/scala/part-5.md +722 -722
- package/lib/assets/docs/article/grokkingfp/scala/part-6.md +867 -867
- package/lib/assets/docs/article/grokkingfp/typescript/index.md +273 -273
- package/lib/assets/docs/article/grokkingfp/typescript/part-1.md +561 -561
- package/lib/assets/docs/article/grokkingfp/typescript/part-2.md +1129 -1129
- package/lib/assets/docs/article/grokkingfp/typescript/part-3.md +842 -842
- package/lib/assets/docs/article/grokkingfp/typescript/part-4.md +1087 -1087
- package/lib/assets/docs/article/grokkingfp/typescript/part-5.md +717 -717
- package/lib/assets/docs/article/grokkingfp/typescript/part-6.md +982 -982
- package/lib/assets/docs/article/practical-database-design/index.md +121 -121
- package/lib/assets/docs/article/practical-database-design/part1/chapter01.md +288 -288
- package/lib/assets/docs/article/practical-database-design/part1/chapter02.md +518 -518
- package/lib/assets/docs/article/practical-database-design/part1/chapter03.md +557 -557
- package/lib/assets/docs/article/practical-database-design/part2/chapter04.md +924 -924
- package/lib/assets/docs/article/practical-database-design/part2/chapter05.md +1627 -1627
- package/lib/assets/docs/article/practical-database-design/part2/chapter06.md +2716 -2716
- package/lib/assets/docs/article/practical-database-design/part2/chapter07.md +2082 -2082
- package/lib/assets/docs/article/practical-database-design/part2/chapter08.md +2105 -2105
- package/lib/assets/docs/article/practical-database-design/part2/chapter09.md +2031 -2031
- package/lib/assets/docs/article/practical-database-design/part2/chapter10.md +1387 -1387
- package/lib/assets/docs/article/practical-database-design/part2/chapter11.md +1677 -1677
- package/lib/assets/docs/article/practical-database-design/part2/chapter12.md +1417 -1417
- package/lib/assets/docs/article/practical-database-design/part2/chapter13.md +1434 -1434
- package/lib/assets/docs/article/practical-database-design/part3/chapter14.md +667 -667
- package/lib/assets/docs/article/practical-database-design/part3/chapter15.md +1625 -1625
- package/lib/assets/docs/article/practical-database-design/part3/chapter16.md +1915 -1915
- package/lib/assets/docs/article/practical-database-design/part3/chapter17.md +1708 -1708
- package/lib/assets/docs/article/practical-database-design/part3/chapter18.md +2095 -2095
- package/lib/assets/docs/article/practical-database-design/part3/chapter19.md +1123 -1123
- package/lib/assets/docs/article/practical-database-design/part3/chapter20.md +1031 -1031
- package/lib/assets/docs/article/practical-database-design/part3/chapter21.md +1382 -1382
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter14-orm.md +991 -991
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter15-orm.md +1300 -1300
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter16-orm.md +1166 -1166
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter17-orm.md +1584 -1584
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter18-orm.md +1183 -1183
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter19-orm.md +1016 -1016
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter20-orm.md +1753 -1753
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter21-orm.md +1447 -1447
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter22-orm.md +1878 -1878
- package/lib/assets/docs/article/practical-database-design/part4/chapter22.md +965 -965
- package/lib/assets/docs/article/practical-database-design/part4/chapter23.md +2069 -2069
- package/lib/assets/docs/article/practical-database-design/part4/chapter24.md +2439 -2439
- package/lib/assets/docs/article/practical-database-design/part4/chapter25.md +3661 -3661
- package/lib/assets/docs/article/practical-database-design/part4/chapter26.md +2916 -2916
- package/lib/assets/docs/article/practical-database-design/part4/chapter27.md +3105 -3105
- package/lib/assets/docs/article/practical-database-design/part4/chapter28.md +2697 -2697
- package/lib/assets/docs/article/practical-database-design/part4/chapter29.md +2544 -2544
- package/lib/assets/docs/article/practical-database-design/part4/chapter30.md +2180 -2180
- package/lib/assets/docs/article/practical-database-design/part4/chapter31.md +1192 -1192
- package/lib/assets/docs/article/practical-database-design/part4/chapter32.md +2101 -2101
- package/lib/assets/docs/article/practical-database-design/part5/chapter33.md +1032 -1032
- package/lib/assets/docs/article/practical-database-design/part5/chapter34.md +1609 -1609
- package/lib/assets/docs/article/practical-database-design/part5/chapter35.md +1453 -1453
- package/lib/assets/docs/article/practical-database-design/part5/chapter36.md +1292 -1292
- package/lib/assets/docs/article/practical-database-design/part5/chapter37.md +1470 -1470
- package/lib/assets/docs/article/practical-database-design/part5/chapter38.md +1698 -1698
- package/lib/assets/docs/article/practical-database-design/part5/chapter39.md +2334 -2334
- package/lib/assets/docs/article/practical-database-design/study/study2-1.md +1693 -1693
- package/lib/assets/docs/article/practical-database-design/study/study2-2.md +1347 -1347
- package/lib/assets/docs/article/practical-database-design/study/study2-3.md +2044 -2044
- package/lib/assets/docs/article/practical-database-design/study/study2-4.md +2229 -2229
- package/lib/assets/docs/article/practical-database-design/study/study2-5.md +2418 -2418
- package/lib/assets/docs/article/practical-database-design/study/study3-1.md +2205 -2205
- package/lib/assets/docs/article/practical-database-design/study/study3-2.md +2221 -2221
- package/lib/assets/docs/article/practical-database-design/study/study3-3.md +2253 -2253
- package/lib/assets/docs/article/practical-database-design/study/study3-4.md +2106 -2106
- package/lib/assets/docs/article/practical-database-design/study/study3-5.md +2507 -2507
- package/lib/assets/docs/article/practical-database-design/study/study4-1.md +2587 -2587
- package/lib/assets/docs/article/practical-database-design/study/study4-2.md +2075 -2075
- package/lib/assets/docs/article/practical-database-design/study/study4-3.md +1805 -1805
- package/lib/assets/docs/article/practical-database-design/study/study4-4.md +1895 -1895
- package/lib/assets/docs/article/practical-database-design/study/study4-5.md +2878 -2878
- package/lib/assets/docs/assets/css/extra.css +29 -29
- package/lib/assets/docs/assets/js/extra.js +44 -44
- package/lib/assets/docs/development/index.md +39 -39
- package/lib/assets/docs/operation/index.md +11 -11
- package/lib/assets/docs/reference/CodexCLIMCP/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/343/203/225/343/203/255/343/203/274.md +532 -532
- package/lib/assets/docs/reference/CodexCLIMCP/343/202/265/343/203/274/343/203/220/343/203/274/350/250/255/345/256/232/346/211/213/351/240/206.md +341 -341
- package/lib/assets/docs/reference/Java/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +581 -580
- package/lib/assets/docs/reference/SonarQube/343/203/255/343/203/274/343/202/253/343/203/253/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +642 -642
- package/lib/assets/docs/reference/TypeScript/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +465 -465
- package/lib/assets/docs/reference/UI/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +450 -450
- package/lib/assets/docs/reference/images/Ansoff.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandBasicStrategy.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandCategorization.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandRecurutementStrategy.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandValue.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BusinessActivitiy.svg +3 -3
- package/lib/assets/docs/reference/images/HRM.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/MarketingStructure.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/OrganizationElemnts.svg +3 -3
- package/lib/assets/docs/reference/images/PPM.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/PositioningMap.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ProductLayer.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ProductMix.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/SWOT.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/TargetMarket.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ThreeGenericStrategies.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/VRIO.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ValueChain.drawio.svg +3 -3
- package/lib/assets/docs/reference/index.md +52 -52
- package/lib/assets/docs/reference//343/202/210/343/201/204/343/202/275/343/203/225/343/203/210/343/202/246/343/202/247/343/202/242/343/201/250/343/201/257.md +250 -242
- package/lib/assets/docs/reference//343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +2216 -2216
- package/lib/assets/docs/reference//343/202/244/343/203/263/343/203/225/343/203/251/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +1878 -1878
- package/lib/assets/docs/reference//343/202/250/343/202/257/343/202/271/343/203/210/343/203/252/343/203/274/343/203/240/343/203/227/343/203/255/343/202/260/343/203/251/343/203/237/343/203/263/343/202/260.md +550 -544
- package/lib/assets/docs/reference//343/202/263/343/203/274/343/203/207/343/202/243/343/203/263/343/202/260/343/201/250/343/203/206/343/202/271/343/203/210/343/202/254/343/202/244/343/203/211.md +705 -705
- package/lib/assets/docs/reference//343/203/206/343/202/271/343/203/210/346/210/246/347/225/245/343/202/254/343/202/244/343/203/211.md +1313 -1313
- package/lib/assets/docs/reference//343/203/207/343/203/274/343/202/277/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +311 -311
- package/lib/assets/docs/reference//343/203/211/343/203/241/343/202/244/343/203/263/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +599 -599
- package/lib/assets/docs/reference//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +528 -528
- package/lib/assets/docs/reference//343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +689 -682
- package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/202/254/343/202/244/343/203/211.md +461 -461
- package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/203/273/343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273/343/202/254/343/202/244/343/203/211.md +580 -560
- package/lib/assets/docs/reference//343/203/255/343/202/270/343/202/253/343/203/253/343/202/267/343/203/263/343/202/255/343/203/263/343/202/260.md +1367 -1367
- package/lib/assets/docs/reference//344/274/201/346/245/255/347/265/214/345/226/266/350/253/226.md +2637 -2636
- package/lib/assets/docs/reference//347/222/260/345/242/203/345/244/211/346/225/260/347/256/241/347/220/206/343/202/254/343/202/244/343/203/211.md +665 -663
- package/lib/assets/docs/reference//350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1248 -1248
- package/lib/assets/docs/reference//350/250/200/350/252/236/345/210/245/351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +28 -0
- package/lib/assets/docs/reference//351/201/213/345/226/266/347/256/241/347/220/206.md +1482 -1482
- package/lib/assets/docs/reference//351/201/213/347/224/250/343/202/271/343/202/257/343/203/252/343/203/227/343/203/210/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +421 -421
- package/lib/assets/docs/reference//351/201/213/347/224/250/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +392 -392
- package/lib/assets/docs/reference//351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +299 -299
- package/lib/assets/docs/reference//351/235/236/346/251/237/350/203/275/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1236 -1236
- package/lib/assets/docs/review/index.md +5 -5
- package/lib/assets/docs/strategy/index.md +1 -1
- package/lib/assets/docs/template/ADR.md +30 -30
- package/lib/assets/docs/template/AWS/343/202/271/343/203/206/343/203/274/343/202/270/343/203/263/343/202/260/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +1366 -1366
- package/lib/assets/docs/template/AWS/343/203/227/343/203/255/343/203/200/343/202/257/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +634 -634
- package/lib/assets/docs/template/README.md +50 -50
- package/lib/assets/docs/template/index.md +23 -23
- package/lib/assets/docs/template//343/201/276/343/201/232/343/201/223/343/202/214/343/202/222/350/252/255/343/202/202/343/201/206/343/203/252/343/202/271/343/203/210.md +12 -12
- package/lib/assets/docs/template//343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +547 -547
- package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +58 -58
- package/lib/assets/docs/template//343/202/244/343/203/263/343/202/273/343/203/227/343/202/267/343/203/247/343/203/263/343/203/207/343/203/203/343/202/255.md +13 -13
- package/lib/assets/docs/template//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243.md +379 -379
- package/lib/assets/docs/template//344/274/201/346/245/255/345/210/206/346/236/220.md +573 -573
- package/lib/assets/docs/template//345/256/214/345/205/250/345/275/242/345/274/217/343/201/256/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271.md +69 -68
- package/lib/assets/docs/template//350/246/201/344/273/266/345/256/232/347/276/251.md +669 -669
- package/lib/assets/docs/template//350/250/255/350/250/210.md +173 -173
- package/lib/assets/docs/template//351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +688 -688
- package/lib/assets/gulpfile.js +25 -25
- package/lib/assets/mkdocs.yml +136 -135
- package/lib/assets/ops/docker/mkdoc/Dockerfile +19 -19
- package/lib/assets/ops/scripts/journal.js +180 -180
- package/lib/assets/ops/scripts/mkdocs.js +82 -82
- package/lib/assets/ops/scripts/release.js +431 -431
- package/lib/assets/ops/scripts/sonar_local.js +726 -726
- package/lib/assets/ops/scripts/ssh.js +190 -190
- package/lib/assets/ops/scripts/vault.js +299 -299
- package/lib/assets/package-lock.json +1653 -1653
- package/lib/assets/package.json +40 -40
- package/lib/gulpfile.js +37 -37
- package/package.json +41 -41
- package/lib/assets/.claude/agent-memory/xp-programmer/MEMORY.md +0 -6
- package/lib/assets/.claude/agent-memory/xp-programmer/project_cargo_tracker.md +0 -11
- package/lib/assets/.claude/agent-memory/xp-programmer/project_ddd_patterns.md +0 -27
- package/lib/assets/.claude/agent-memory/xp-programmer/project_us07_route_assignment.md +0 -19
|
@@ -1,721 +1,721 @@
|
|
|
1
|
-
# Part VI: 実践的なアプリケーション構築とテスト(Clojure 版)
|
|
2
|
-
|
|
3
|
-
本章では、これまで学んだ関数型プログラミングの概念を統合し、実践的なアプリケーションを構築します。また、関数型プログラミングにおけるテスト戦略についても学びます。
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 第12章: 実践的なアプリケーション構築
|
|
8
|
-
|
|
9
|
-
### 12.1 TravelGuide アプリケーション
|
|
10
|
-
|
|
11
|
-
**ソースファイル**: `app/clojure/src/ch12/travel_guide.clj`
|
|
12
|
-
|
|
13
|
-
旅行ガイドアプリケーションを例に、実践的な FP アプリケーションの構築方法を学びます。
|
|
14
|
-
|
|
15
|
-
```plantuml
|
|
16
|
-
@startuml
|
|
17
|
-
!theme plain
|
|
18
|
-
|
|
19
|
-
package "TravelGuide Application" {
|
|
20
|
-
rectangle "Model" {
|
|
21
|
-
class Location {
|
|
22
|
-
id: String
|
|
23
|
-
name: String
|
|
24
|
-
population: Int
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
class Attraction {
|
|
28
|
-
name: String
|
|
29
|
-
description: Option
|
|
30
|
-
location: Location
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
class TravelGuide {
|
|
34
|
-
attraction: Attraction
|
|
35
|
-
subjects: Vector
|
|
36
|
-
search-report: SearchReport
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
rectangle "Data Access" {
|
|
41
|
-
interface DataAccess {
|
|
42
|
-
+find-attractions()
|
|
43
|
-
+find-artists-from-location()
|
|
44
|
-
+find-movies-about-location()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
class TestDataAccess
|
|
48
|
-
class CachedDataAccess
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
DataAccess <|.. TestDataAccess
|
|
52
|
-
DataAccess <|.. CachedDataAccess
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
@enduml
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### 12.2 ドメインモデルの定義
|
|
59
|
-
|
|
60
|
-
```clojure
|
|
61
|
-
;; レコード定義
|
|
62
|
-
(defrecord Location [id name population])
|
|
63
|
-
(defrecord Attraction [name description location])
|
|
64
|
-
(defrecord MusicArtist [name genre origin])
|
|
65
|
-
(defrecord Movie [name year director])
|
|
66
|
-
(defrecord Hotel [name rating location])
|
|
67
|
-
(defrecord SearchReport [attractions-searched errors])
|
|
68
|
-
(defrecord TravelGuide [attraction subjects search-report])
|
|
69
|
-
|
|
70
|
-
;; コンストラクタ関数
|
|
71
|
-
(defn location [id name population]
|
|
72
|
-
(->Location id name population))
|
|
73
|
-
|
|
74
|
-
(defn attraction
|
|
75
|
-
([name location]
|
|
76
|
-
(->Attraction name nil location))
|
|
77
|
-
([name description location]
|
|
78
|
-
(->Attraction name description location)))
|
|
79
|
-
|
|
80
|
-
(defn travel-guide [attraction subjects search-report]
|
|
81
|
-
(->TravelGuide attraction subjects search-report))
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### 12.3 データアクセス層の抽象化
|
|
85
|
-
|
|
86
|
-
プロトコルを使用して外部データソースへのアクセスを抽象化します。
|
|
87
|
-
|
|
88
|
-
```clojure
|
|
89
|
-
(defprotocol DataAccess
|
|
90
|
-
"データアクセス層の抽象化"
|
|
91
|
-
(find-attractions [this name ordering limit]
|
|
92
|
-
"アトラクションを検索")
|
|
93
|
-
(find-artists-from-location [this location-id limit]
|
|
94
|
-
"ロケーションからアーティストを検索")
|
|
95
|
-
(find-movies-about-location [this location-id limit]
|
|
96
|
-
"ロケーションに関する映画を検索")
|
|
97
|
-
(find-hotels-near-location [this location-id limit]
|
|
98
|
-
"ロケーション近くのホテルを検索"))
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
```plantuml
|
|
102
|
-
@startuml
|
|
103
|
-
!theme plain
|
|
104
|
-
|
|
105
|
-
interface DataAccess {
|
|
106
|
-
+find-attractions(): Result
|
|
107
|
-
+find-artists-from-location(): Result
|
|
108
|
-
+find-movies-about-location(): Result
|
|
109
|
-
+find-hotels-near-location(): Result
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
note right of DataAccess
|
|
113
|
-
プロトコルによる抽象化
|
|
114
|
-
すべての操作は Result を返す
|
|
115
|
-
end note
|
|
116
|
-
|
|
117
|
-
class TestDataAccess {
|
|
118
|
-
-attractions: Vector
|
|
119
|
-
-artists: Vector
|
|
120
|
-
-movies: Vector
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
class FailingDataAccess {
|
|
124
|
-
+returns errors for testing
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
class CachedDataAccess {
|
|
128
|
-
-underlying: DataAccess
|
|
129
|
-
-cache: Atom
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
DataAccess <|.. TestDataAccess
|
|
133
|
-
DataAccess <|.. FailingDataAccess
|
|
134
|
-
DataAccess <|.. CachedDataAccess
|
|
135
|
-
|
|
136
|
-
@enduml
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### 12.4 Result 型によるエラー処理
|
|
140
|
-
|
|
141
|
-
```clojure
|
|
142
|
-
;; Result ユーティリティ
|
|
143
|
-
(defn ok? [result]
|
|
144
|
-
(contains? result :ok))
|
|
145
|
-
|
|
146
|
-
(defn error? [result]
|
|
147
|
-
(contains? result :error))
|
|
148
|
-
|
|
149
|
-
(defn get-value [result default]
|
|
150
|
-
(if (ok? result)
|
|
151
|
-
(:ok result)
|
|
152
|
-
default))
|
|
153
|
-
|
|
154
|
-
(defn get-error [result]
|
|
155
|
-
(:error result))
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### 12.5 テスト用スタブ実装
|
|
159
|
-
|
|
160
|
-
```clojure
|
|
161
|
-
(defrecord TestDataAccess [attractions artists movies hotels]
|
|
162
|
-
DataAccess
|
|
163
|
-
(find-attractions [_ name ordering limit]
|
|
164
|
-
{:ok (take limit (filter #(clojure.string/includes?
|
|
165
|
-
(clojure.string/lower-case (:name %))
|
|
166
|
-
(clojure.string/lower-case name))
|
|
167
|
-
attractions))})
|
|
168
|
-
|
|
169
|
-
(find-artists-from-location [_ location-id limit]
|
|
170
|
-
{:ok (take limit artists)})
|
|
171
|
-
|
|
172
|
-
(find-movies-about-location [_ location-id limit]
|
|
173
|
-
{:ok (take limit movies)})
|
|
174
|
-
|
|
175
|
-
(find-hotels-near-location [_ location-id limit]
|
|
176
|
-
{:ok (take limit hotels)}))
|
|
177
|
-
|
|
178
|
-
(defn create-test-data-access []
|
|
179
|
-
(->TestDataAccess
|
|
180
|
-
[test-attraction ...]
|
|
181
|
-
[test-artist ...]
|
|
182
|
-
[test-movie ...]
|
|
183
|
-
[test-hotel ...]))
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### 12.6 エラー処理付きデータアクセス
|
|
187
|
-
|
|
188
|
-
```clojure
|
|
189
|
-
(defrecord FailingDataAccess []
|
|
190
|
-
DataAccess
|
|
191
|
-
(find-attractions [_ name ordering limit]
|
|
192
|
-
{:ok [(attraction "Fallback Attraction" nil test-location)]})
|
|
193
|
-
|
|
194
|
-
(find-artists-from-location [_ location-id limit]
|
|
195
|
-
{:error "Network error: Failed to fetch artists"})
|
|
196
|
-
|
|
197
|
-
(find-movies-about-location [_ location-id limit]
|
|
198
|
-
{:error "Timeout: Failed to fetch movies"})
|
|
199
|
-
|
|
200
|
-
(find-hotels-near-location [_ location-id limit]
|
|
201
|
-
{:error "Service unavailable"}))
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### 12.7 TravelGuide アプリケーションの構築
|
|
205
|
-
|
|
206
|
-
```clojure
|
|
207
|
-
(defn build-travel-guide
|
|
208
|
-
"旅行ガイドを構築"
|
|
209
|
-
[data-access attraction-name]
|
|
210
|
-
(let [attractions-result (find-attractions data-access attraction-name by-population 3)]
|
|
211
|
-
(if (error? attractions-result)
|
|
212
|
-
{:error (get-error attractions-result)}
|
|
213
|
-
(let [attractions (get-value attractions-result [])]
|
|
214
|
-
(map (fn [attr]
|
|
215
|
-
(let [location-id (get-in attr [:location :id])
|
|
216
|
-
artists-result (find-artists-from-location data-access location-id 2)
|
|
217
|
-
movies-result (find-movies-about-location data-access location-id 2)
|
|
218
|
-
|
|
219
|
-
;; エラーを収集
|
|
220
|
-
errors (cond-> []
|
|
221
|
-
(error? artists-result) (conj (get-error artists-result))
|
|
222
|
-
(error? movies-result) (conj (get-error movies-result)))
|
|
223
|
-
|
|
224
|
-
;; 結果を取得(エラー時は空リスト)
|
|
225
|
-
artists (get-value artists-result [])
|
|
226
|
-
movies (get-value movies-result [])
|
|
227
|
-
|
|
228
|
-
;; サブジェクトを構築
|
|
229
|
-
subjects (concat (map :name artists) (map :name movies))]
|
|
230
|
-
(travel-guide
|
|
231
|
-
attr
|
|
232
|
-
(vec subjects)
|
|
233
|
-
(search-report (count attractions) errors))))
|
|
234
|
-
attractions)))))
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
```plantuml
|
|
238
|
-
@startuml
|
|
239
|
-
!theme plain
|
|
240
|
-
|
|
241
|
-
rectangle "build-travel-guide 関数" {
|
|
242
|
-
card "1. アトラクション検索" as step1
|
|
243
|
-
card "2. アーティスト検索" as step2
|
|
244
|
-
card "3. 映画検索" as step3
|
|
245
|
-
card "4. エラー収集" as step4
|
|
246
|
-
card "5. TravelGuide 組み立て" as step5
|
|
247
|
-
|
|
248
|
-
step1 --> step2 : attraction
|
|
249
|
-
step1 --> step3 : location-id
|
|
250
|
-
step2 --> step4 : artists-result
|
|
251
|
-
step3 --> step4 : movies-result
|
|
252
|
-
step4 --> step5 : errors
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
note bottom
|
|
256
|
-
エラーは収集され
|
|
257
|
-
SearchReport に記録される
|
|
258
|
-
end note
|
|
259
|
-
|
|
260
|
-
@enduml
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
### 12.8 キャッシュ付きデータアクセス
|
|
264
|
-
|
|
265
|
-
`atom` を使用したスレッドセーフなキャッシュの実装:
|
|
266
|
-
|
|
267
|
-
```clojure
|
|
268
|
-
(defn create-cached-data-access
|
|
269
|
-
"キャッシュ付きデータアクセスを作成"
|
|
270
|
-
[underlying-data-access]
|
|
271
|
-
(let [attractions-cache (atom {})
|
|
272
|
-
artists-cache (atom {})
|
|
273
|
-
movies-cache (atom {})
|
|
274
|
-
hotels-cache (atom {})]
|
|
275
|
-
(reify DataAccess
|
|
276
|
-
(find-attractions [_ name ordering limit]
|
|
277
|
-
(let [cache-key (str name "-" ordering "-" limit)]
|
|
278
|
-
(if-let [cached (get @attractions-cache cache-key)]
|
|
279
|
-
{:ok cached}
|
|
280
|
-
(let [result (find-attractions underlying-data-access name ordering limit)]
|
|
281
|
-
(when (ok? result)
|
|
282
|
-
(swap! attractions-cache assoc cache-key (:ok result)))
|
|
283
|
-
result))))
|
|
284
|
-
;; 他のメソッドも同様...
|
|
285
|
-
)))
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
```plantuml
|
|
289
|
-
@startuml
|
|
290
|
-
!theme plain
|
|
291
|
-
skinparam activity {
|
|
292
|
-
BackgroundColor White
|
|
293
|
-
BorderColor Black
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
start
|
|
297
|
-
|
|
298
|
-
:キャッシュキー生成;
|
|
299
|
-
|
|
300
|
-
:atom からキャッシュ確認;
|
|
301
|
-
|
|
302
|
-
if (キャッシュにデータあり?) then (yes)
|
|
303
|
-
:キャッシュからデータ返却;
|
|
304
|
-
else (no)
|
|
305
|
-
:underlying を呼び出し;
|
|
306
|
-
:結果をキャッシュに保存;
|
|
307
|
-
:結果を返却;
|
|
308
|
-
endif
|
|
309
|
-
|
|
310
|
-
stop
|
|
311
|
-
|
|
312
|
-
note right
|
|
313
|
-
atom を使用した
|
|
314
|
-
スレッドセーフなキャッシュ
|
|
315
|
-
end note
|
|
316
|
-
|
|
317
|
-
@enduml
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
### 12.9 リソース管理
|
|
321
|
-
|
|
322
|
-
安全なリソース管理パターン:
|
|
323
|
-
|
|
324
|
-
```clojure
|
|
325
|
-
(defn with-resource
|
|
326
|
-
"リソースを安全に使用(try-with-resources パターン)"
|
|
327
|
-
[acquire release use-fn]
|
|
328
|
-
(let [resource (acquire)]
|
|
329
|
-
(try
|
|
330
|
-
(use-fn resource)
|
|
331
|
-
(finally
|
|
332
|
-
(release resource)))))
|
|
333
|
-
|
|
334
|
-
(defn make-resource
|
|
335
|
-
"リソースを作成"
|
|
336
|
-
[acquire release]
|
|
337
|
-
{:acquire acquire
|
|
338
|
-
:release release})
|
|
339
|
-
|
|
340
|
-
;; ファイルリソースの例
|
|
341
|
-
(defn file-resource [path]
|
|
342
|
-
(make-resource
|
|
343
|
-
#(clojure.java.io/reader path)
|
|
344
|
-
#(.close %)))
|
|
345
|
-
|
|
346
|
-
(defn read-lines [path]
|
|
347
|
-
(use-resource
|
|
348
|
-
(file-resource path)
|
|
349
|
-
(fn [reader]
|
|
350
|
-
(vec (line-seq reader)))))
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### 12.10 バリデーション
|
|
354
|
-
|
|
355
|
-
```clojure
|
|
356
|
-
(defn validate-attraction-name [name]
|
|
357
|
-
(cond
|
|
358
|
-
(nil? name)
|
|
359
|
-
{:error "Attraction name cannot be nil"}
|
|
360
|
-
|
|
361
|
-
(clojure.string/blank? name)
|
|
362
|
-
{:error "Attraction name cannot be empty"}
|
|
363
|
-
|
|
364
|
-
(> (count name) 100)
|
|
365
|
-
{:error "Attraction name too long"}
|
|
366
|
-
|
|
367
|
-
:else
|
|
368
|
-
{:ok name}))
|
|
369
|
-
|
|
370
|
-
(defn validate-limit [limit]
|
|
371
|
-
(cond
|
|
372
|
-
(nil? limit)
|
|
373
|
-
{:error "Limit cannot be nil"}
|
|
374
|
-
|
|
375
|
-
(not (integer? limit))
|
|
376
|
-
{:error "Limit must be an integer"}
|
|
377
|
-
|
|
378
|
-
(<= limit 0)
|
|
379
|
-
{:error "Limit must be positive"}
|
|
380
|
-
|
|
381
|
-
(> limit 100)
|
|
382
|
-
{:error "Limit cannot exceed 100"}
|
|
383
|
-
|
|
384
|
-
:else
|
|
385
|
-
{:ok limit}))
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
---
|
|
389
|
-
|
|
390
|
-
## 第13章: テスト戦略
|
|
391
|
-
|
|
392
|
-
### 13.1 関数型プログラミングのテスト
|
|
393
|
-
|
|
394
|
-
関数型プログラミングでは、純粋関数のおかげでテストが非常に簡単になります。
|
|
395
|
-
|
|
396
|
-
```plantuml
|
|
397
|
-
@startuml
|
|
398
|
-
!theme plain
|
|
399
|
-
|
|
400
|
-
rectangle "テストの種類" {
|
|
401
|
-
rectangle "単体テスト" as unit {
|
|
402
|
-
card "純粋関数のテスト"
|
|
403
|
-
card "高速・独立"
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
rectangle "プロパティベーステスト" as property {
|
|
407
|
-
card "ランダム入力"
|
|
408
|
-
card "不変条件の検証"
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
rectangle "統合テスト" as integration {
|
|
412
|
-
card "コンポーネント連携"
|
|
413
|
-
card "スタブ使用"
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
unit --> property : 補完
|
|
418
|
-
property --> integration : 補完
|
|
419
|
-
|
|
420
|
-
@enduml
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### 13.2 純粋関数のテスト
|
|
424
|
-
|
|
425
|
-
**ソースファイル**: `app/clojure/test/ch12/travel_guide_test.clj`
|
|
426
|
-
|
|
427
|
-
純粋関数は入力と出力のみでテストできます。
|
|
428
|
-
|
|
429
|
-
```clojure
|
|
430
|
-
(deftest filter-popular-locations-tests
|
|
431
|
-
(testing "filters by minimum population"
|
|
432
|
-
(let [locations [(location "Q1" "Small" 1000)
|
|
433
|
-
(location "Q2" "Medium" 100000)
|
|
434
|
-
(location "Q3" "Large" 1000000)]
|
|
435
|
-
result (filter-popular-locations locations 50000)]
|
|
436
|
-
(is (= 2 (count result)))
|
|
437
|
-
(is (every? #(>= (:population %) 50000) result)))))
|
|
438
|
-
|
|
439
|
-
(deftest calculate-average-rating-tests
|
|
440
|
-
(testing "calculates average"
|
|
441
|
-
(let [loc (location "Q1" "City" 100000)
|
|
442
|
-
hotels [(hotel "H1" 4.0 loc)
|
|
443
|
-
(hotel "H2" 5.0 loc)
|
|
444
|
-
(hotel "H3" 3.0 loc)]]
|
|
445
|
-
(is (= 4.0 (calculate-average-rating hotels)))))
|
|
446
|
-
|
|
447
|
-
(testing "returns 0 for empty list"
|
|
448
|
-
(is (= 0.0 (calculate-average-rating [])))))
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
### 13.3 スタブを使用したテスト
|
|
452
|
-
|
|
453
|
-
```clojure
|
|
454
|
-
(deftest test-data-access-tests
|
|
455
|
-
(let [data-access (create-test-data-access)]
|
|
456
|
-
|
|
457
|
-
(testing "find-attractions returns matching attractions"
|
|
458
|
-
(let [result (find-attractions data-access "Test" by-name 10)]
|
|
459
|
-
(is (ok? result))
|
|
460
|
-
(is (= 1 (count (:ok result))))))
|
|
461
|
-
|
|
462
|
-
(testing "find-artists-from-location"
|
|
463
|
-
(let [result (find-artists-from-location data-access "Test City" 10)]
|
|
464
|
-
(is (ok? result))))))
|
|
465
|
-
|
|
466
|
-
(deftest failing-data-access-tests
|
|
467
|
-
(let [data-access (create-failing-data-access)]
|
|
468
|
-
|
|
469
|
-
(testing "find-artists-from-location returns error"
|
|
470
|
-
(let [result (find-artists-from-location data-access "Q123" 10)]
|
|
471
|
-
(is (error? result))))))
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
```plantuml
|
|
475
|
-
@startuml
|
|
476
|
-
!theme plain
|
|
477
|
-
|
|
478
|
-
rectangle "テスト構成" {
|
|
479
|
-
rectangle "本番" as prod {
|
|
480
|
-
interface "DataAccess" as da1
|
|
481
|
-
class "WikidataDataAccess" as wda
|
|
482
|
-
da1 <|.. wda
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
rectangle "テスト" as test {
|
|
486
|
-
interface "DataAccess" as da2
|
|
487
|
-
class "TestDataAccess" as tda
|
|
488
|
-
class "FailingDataAccess" as fda
|
|
489
|
-
da2 <|.. tda
|
|
490
|
-
da2 <|.. fda
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
note bottom of prod
|
|
495
|
-
実際のサービスに接続
|
|
496
|
-
end note
|
|
497
|
-
|
|
498
|
-
note bottom of test
|
|
499
|
-
テストデータを返す
|
|
500
|
-
エラーケースをシミュレート
|
|
501
|
-
end note
|
|
502
|
-
|
|
503
|
-
@enduml
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
### 13.4 プロパティベーステスト
|
|
507
|
-
|
|
508
|
-
ランダムな入力で不変条件を検証します。
|
|
509
|
-
|
|
510
|
-
```clojure
|
|
511
|
-
;; ランダムデータ生成
|
|
512
|
-
(defn random-location []
|
|
513
|
-
(location
|
|
514
|
-
(str "Q" (rand-int 10000))
|
|
515
|
-
(random-string 10)
|
|
516
|
-
(rand-int 10000000)))
|
|
517
|
-
|
|
518
|
-
(defn random-attractions [n]
|
|
519
|
-
(repeatedly n random-attraction))
|
|
520
|
-
|
|
521
|
-
;; プロパティテスト
|
|
522
|
-
(deftest property-filter-popular-locations-tests
|
|
523
|
-
(testing "result size is at most input size"
|
|
524
|
-
(dotimes [_ 100]
|
|
525
|
-
(let [n (inc (rand-int 20))
|
|
526
|
-
locations (repeatedly n random-location)
|
|
527
|
-
min-pop (rand-int 10000000)
|
|
528
|
-
result (filter-popular-locations locations min-pop)]
|
|
529
|
-
(is (<= (count result) n)))))
|
|
530
|
-
|
|
531
|
-
(testing "all results meet minimum population"
|
|
532
|
-
(dotimes [_ 100]
|
|
533
|
-
(let [locations (repeatedly 10 random-location)
|
|
534
|
-
min-pop (rand-int 10000000)
|
|
535
|
-
result (filter-popular-locations locations min-pop)]
|
|
536
|
-
(is (every? #(>= (:population %) min-pop) result))))))
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
```plantuml
|
|
540
|
-
@startuml
|
|
541
|
-
!theme plain
|
|
542
|
-
|
|
543
|
-
rectangle "プロパティベーステスト" {
|
|
544
|
-
card "ランダムデータ生成" as gen
|
|
545
|
-
card "関数を実行" as exec
|
|
546
|
-
card "プロパティ検証" as verify
|
|
547
|
-
card "100回以上繰り返し" as repeat
|
|
548
|
-
|
|
549
|
-
gen --> exec
|
|
550
|
-
exec --> verify
|
|
551
|
-
verify --> repeat
|
|
552
|
-
repeat --> gen : 繰り返し
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
note bottom
|
|
556
|
-
ランダムな入力で
|
|
557
|
-
不変条件を検証
|
|
558
|
-
end note
|
|
559
|
-
|
|
560
|
-
@enduml
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
### 13.5 統合テスト
|
|
564
|
-
|
|
565
|
-
```clojure
|
|
566
|
-
(deftest integration-travel-guide-tests
|
|
567
|
-
(testing "end-to-end travel guide workflow"
|
|
568
|
-
(let [data-access (create-cached-data-access (create-test-data-access))
|
|
569
|
-
;; バリデーション
|
|
570
|
-
validation (validate-search-params "Test" 3)]
|
|
571
|
-
(is (ok? validation))
|
|
572
|
-
|
|
573
|
-
;; ガイド取得
|
|
574
|
-
(let [result (get-travel-guide data-access "Test")]
|
|
575
|
-
(is (ok? result))
|
|
576
|
-
(let [guide (:ok result)]
|
|
577
|
-
(is (some? (:attraction guide)))
|
|
578
|
-
(is (vector? (:subjects guide)))
|
|
579
|
-
(is (some? (:search-report guide)))))))
|
|
580
|
-
|
|
581
|
-
(testing "workflow with partial failure"
|
|
582
|
-
(let [data-access (create-failing-data-access)
|
|
583
|
-
result (get-travel-guide data-access "Test")]
|
|
584
|
-
(is (ok? result))
|
|
585
|
-
(let [guide (:ok result)]
|
|
586
|
-
(is (= 2 (count (get-in guide [:search-report :errors]))))))))
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
### 13.6 テストピラミッド
|
|
590
|
-
|
|
591
|
-
```plantuml
|
|
592
|
-
@startuml
|
|
593
|
-
!theme plain
|
|
594
|
-
|
|
595
|
-
rectangle "テストピラミッド" {
|
|
596
|
-
rectangle "E2E テスト\n(少数)" as e2e
|
|
597
|
-
rectangle "統合テスト\n(中程度)" as integration
|
|
598
|
-
rectangle "単体テスト + プロパティテスト\n(多数)" as unit
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
e2e -[hidden]down- integration
|
|
602
|
-
integration -[hidden]down- unit
|
|
603
|
-
|
|
604
|
-
note right of unit
|
|
605
|
-
FP では純粋関数が多いため
|
|
606
|
-
単体テストが非常に効果的
|
|
607
|
-
end note
|
|
608
|
-
|
|
609
|
-
@enduml
|
|
610
|
-
```
|
|
611
|
-
|
|
612
|
-
---
|
|
613
|
-
|
|
614
|
-
## まとめ
|
|
615
|
-
|
|
616
|
-
### Part VI で学んだこと
|
|
617
|
-
|
|
618
|
-
```plantuml
|
|
619
|
-
@startuml
|
|
620
|
-
!theme plain
|
|
621
|
-
|
|
622
|
-
rectangle "Part VI: 実践的なアプリケーション" {
|
|
623
|
-
rectangle "第12章" as ch12 {
|
|
624
|
-
card "ドメインモデル設計"
|
|
625
|
-
card "DataAccess 抽象化"
|
|
626
|
-
card "リソース管理"
|
|
627
|
-
card "キャッシュ実装"
|
|
628
|
-
card "バリデーション"
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
rectangle "第13章" as ch13 {
|
|
632
|
-
card "純粋関数テスト"
|
|
633
|
-
card "スタブ/モック"
|
|
634
|
-
card "プロパティベーステスト"
|
|
635
|
-
card "統合テスト"
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
ch12 --> ch13
|
|
640
|
-
|
|
641
|
-
@enduml
|
|
642
|
-
```
|
|
643
|
-
|
|
644
|
-
### Clojure vs Scala の比較
|
|
645
|
-
|
|
646
|
-
| 概念 | Scala | Clojure |
|
|
647
|
-
|------|-------|---------|
|
|
648
|
-
| ドメインモデル | case class | defrecord |
|
|
649
|
-
| 抽象化 | trait | defprotocol |
|
|
650
|
-
| リソース管理 | Resource | with-resource |
|
|
651
|
-
| キャッシュ | Ref | atom |
|
|
652
|
-
| エラー処理 | Either | {:ok ...} / {:error ...} |
|
|
653
|
-
| テスト | ScalaTest | clojure.test |
|
|
654
|
-
|
|
655
|
-
### キーポイント
|
|
656
|
-
|
|
657
|
-
1. **プロトコルによる抽象化**: DataAccess で外部依存を抽象化
|
|
658
|
-
2. **with-resource でリソース管理**: 安全なリソースの取得と解放
|
|
659
|
-
3. **atom でキャッシュ**: スレッドセーフな状態管理
|
|
660
|
-
4. **Result マップでエラー処理**: 明示的なエラーハンドリング
|
|
661
|
-
5. **SearchReport**: テスト可能性と可観測性の向上
|
|
662
|
-
6. **スタブ**: 外部依存を差し替えてテスト
|
|
663
|
-
7. **プロパティベーステスト**: ランダム入力で不変条件を検証
|
|
664
|
-
|
|
665
|
-
### 学習の総括
|
|
666
|
-
|
|
667
|
-
```plantuml
|
|
668
|
-
@startuml
|
|
669
|
-
!theme plain
|
|
670
|
-
left to right direction
|
|
671
|
-
|
|
672
|
-
rectangle "FP の学習パス" {
|
|
673
|
-
card "Part I\n基礎" as p1
|
|
674
|
-
card "Part II\n関数型スタイル" as p2
|
|
675
|
-
card "Part III\nnil 安全性" as p3
|
|
676
|
-
card "Part IV\n副作用とシーケンス" as p4
|
|
677
|
-
card "Part V\n並行処理" as p5
|
|
678
|
-
card "Part VI\n実践" as p6
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
p1 --> p2
|
|
682
|
-
p2 --> p3
|
|
683
|
-
p3 --> p4
|
|
684
|
-
p4 --> p5
|
|
685
|
-
p5 --> p6
|
|
686
|
-
|
|
687
|
-
@enduml
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
---
|
|
691
|
-
|
|
692
|
-
## シリーズ全体の総括
|
|
693
|
-
|
|
694
|
-
本シリーズでは、「Grokking Functional Programming」の内容に沿って、Clojure で関数型プログラミングの基礎から実践的なアプリケーション構築までを学びました。
|
|
695
|
-
|
|
696
|
-
### 学んだ主な概念
|
|
697
|
-
|
|
698
|
-
| Part | 章 | 主な概念 |
|
|
699
|
-
|------|-----|----------|
|
|
700
|
-
| I | 1-2 | 純粋関数、参照透過性 |
|
|
701
|
-
| II | 3-5 | イミュータブルデータ、高階関数、mapcat |
|
|
702
|
-
| III | 6-7 | nil 安全性、Result パターン、ADT |
|
|
703
|
-
| IV | 8-9 | 状態管理、遅延シーケンス、トランスデューサー |
|
|
704
|
-
| V | 10-11 | atom/ref/agent、core.async |
|
|
705
|
-
| VI | 12-13 | 実践アプリケーション、テスト |
|
|
706
|
-
|
|
707
|
-
### Clojure における関数型プログラミングの利点
|
|
708
|
-
|
|
709
|
-
1. **シンプルさ**: Lisp の S 式による一貫した構文
|
|
710
|
-
2. **イミュータブルがデフォルト**: 永続データ構造
|
|
711
|
-
3. **豊富な並行プリミティブ**: atom, ref, agent, core.async
|
|
712
|
-
4. **遅延評価**: 効率的なシーケンス処理
|
|
713
|
-
5. **REPL 駆動開発**: インタラクティブな開発体験
|
|
714
|
-
6. **Java 相互運用**: 豊富なエコシステムへのアクセス
|
|
715
|
-
|
|
716
|
-
### 次のステップ
|
|
717
|
-
|
|
718
|
-
- spec や malli による仕様記述とバリデーション
|
|
719
|
-
- Component や Integrant によるシステム構成
|
|
720
|
-
- re-frame や Fulcro によるフロントエンド開発
|
|
721
|
-
- 実際のプロジェクトで Clojure を適用する
|
|
1
|
+
# Part VI: 実践的なアプリケーション構築とテスト(Clojure 版)
|
|
2
|
+
|
|
3
|
+
本章では、これまで学んだ関数型プログラミングの概念を統合し、実践的なアプリケーションを構築します。また、関数型プログラミングにおけるテスト戦略についても学びます。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 第12章: 実践的なアプリケーション構築
|
|
8
|
+
|
|
9
|
+
### 12.1 TravelGuide アプリケーション
|
|
10
|
+
|
|
11
|
+
**ソースファイル**: `app/clojure/src/ch12/travel_guide.clj`
|
|
12
|
+
|
|
13
|
+
旅行ガイドアプリケーションを例に、実践的な FP アプリケーションの構築方法を学びます。
|
|
14
|
+
|
|
15
|
+
```plantuml
|
|
16
|
+
@startuml
|
|
17
|
+
!theme plain
|
|
18
|
+
|
|
19
|
+
package "TravelGuide Application" {
|
|
20
|
+
rectangle "Model" {
|
|
21
|
+
class Location {
|
|
22
|
+
id: String
|
|
23
|
+
name: String
|
|
24
|
+
population: Int
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class Attraction {
|
|
28
|
+
name: String
|
|
29
|
+
description: Option
|
|
30
|
+
location: Location
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class TravelGuide {
|
|
34
|
+
attraction: Attraction
|
|
35
|
+
subjects: Vector
|
|
36
|
+
search-report: SearchReport
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
rectangle "Data Access" {
|
|
41
|
+
interface DataAccess {
|
|
42
|
+
+find-attractions()
|
|
43
|
+
+find-artists-from-location()
|
|
44
|
+
+find-movies-about-location()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class TestDataAccess
|
|
48
|
+
class CachedDataAccess
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
DataAccess <|.. TestDataAccess
|
|
52
|
+
DataAccess <|.. CachedDataAccess
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@enduml
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 12.2 ドメインモデルの定義
|
|
59
|
+
|
|
60
|
+
```clojure
|
|
61
|
+
;; レコード定義
|
|
62
|
+
(defrecord Location [id name population])
|
|
63
|
+
(defrecord Attraction [name description location])
|
|
64
|
+
(defrecord MusicArtist [name genre origin])
|
|
65
|
+
(defrecord Movie [name year director])
|
|
66
|
+
(defrecord Hotel [name rating location])
|
|
67
|
+
(defrecord SearchReport [attractions-searched errors])
|
|
68
|
+
(defrecord TravelGuide [attraction subjects search-report])
|
|
69
|
+
|
|
70
|
+
;; コンストラクタ関数
|
|
71
|
+
(defn location [id name population]
|
|
72
|
+
(->Location id name population))
|
|
73
|
+
|
|
74
|
+
(defn attraction
|
|
75
|
+
([name location]
|
|
76
|
+
(->Attraction name nil location))
|
|
77
|
+
([name description location]
|
|
78
|
+
(->Attraction name description location)))
|
|
79
|
+
|
|
80
|
+
(defn travel-guide [attraction subjects search-report]
|
|
81
|
+
(->TravelGuide attraction subjects search-report))
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 12.3 データアクセス層の抽象化
|
|
85
|
+
|
|
86
|
+
プロトコルを使用して外部データソースへのアクセスを抽象化します。
|
|
87
|
+
|
|
88
|
+
```clojure
|
|
89
|
+
(defprotocol DataAccess
|
|
90
|
+
"データアクセス層の抽象化"
|
|
91
|
+
(find-attractions [this name ordering limit]
|
|
92
|
+
"アトラクションを検索")
|
|
93
|
+
(find-artists-from-location [this location-id limit]
|
|
94
|
+
"ロケーションからアーティストを検索")
|
|
95
|
+
(find-movies-about-location [this location-id limit]
|
|
96
|
+
"ロケーションに関する映画を検索")
|
|
97
|
+
(find-hotels-near-location [this location-id limit]
|
|
98
|
+
"ロケーション近くのホテルを検索"))
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```plantuml
|
|
102
|
+
@startuml
|
|
103
|
+
!theme plain
|
|
104
|
+
|
|
105
|
+
interface DataAccess {
|
|
106
|
+
+find-attractions(): Result
|
|
107
|
+
+find-artists-from-location(): Result
|
|
108
|
+
+find-movies-about-location(): Result
|
|
109
|
+
+find-hotels-near-location(): Result
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
note right of DataAccess
|
|
113
|
+
プロトコルによる抽象化
|
|
114
|
+
すべての操作は Result を返す
|
|
115
|
+
end note
|
|
116
|
+
|
|
117
|
+
class TestDataAccess {
|
|
118
|
+
-attractions: Vector
|
|
119
|
+
-artists: Vector
|
|
120
|
+
-movies: Vector
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
class FailingDataAccess {
|
|
124
|
+
+returns errors for testing
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
class CachedDataAccess {
|
|
128
|
+
-underlying: DataAccess
|
|
129
|
+
-cache: Atom
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
DataAccess <|.. TestDataAccess
|
|
133
|
+
DataAccess <|.. FailingDataAccess
|
|
134
|
+
DataAccess <|.. CachedDataAccess
|
|
135
|
+
|
|
136
|
+
@enduml
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 12.4 Result 型によるエラー処理
|
|
140
|
+
|
|
141
|
+
```clojure
|
|
142
|
+
;; Result ユーティリティ
|
|
143
|
+
(defn ok? [result]
|
|
144
|
+
(contains? result :ok))
|
|
145
|
+
|
|
146
|
+
(defn error? [result]
|
|
147
|
+
(contains? result :error))
|
|
148
|
+
|
|
149
|
+
(defn get-value [result default]
|
|
150
|
+
(if (ok? result)
|
|
151
|
+
(:ok result)
|
|
152
|
+
default))
|
|
153
|
+
|
|
154
|
+
(defn get-error [result]
|
|
155
|
+
(:error result))
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 12.5 テスト用スタブ実装
|
|
159
|
+
|
|
160
|
+
```clojure
|
|
161
|
+
(defrecord TestDataAccess [attractions artists movies hotels]
|
|
162
|
+
DataAccess
|
|
163
|
+
(find-attractions [_ name ordering limit]
|
|
164
|
+
{:ok (take limit (filter #(clojure.string/includes?
|
|
165
|
+
(clojure.string/lower-case (:name %))
|
|
166
|
+
(clojure.string/lower-case name))
|
|
167
|
+
attractions))})
|
|
168
|
+
|
|
169
|
+
(find-artists-from-location [_ location-id limit]
|
|
170
|
+
{:ok (take limit artists)})
|
|
171
|
+
|
|
172
|
+
(find-movies-about-location [_ location-id limit]
|
|
173
|
+
{:ok (take limit movies)})
|
|
174
|
+
|
|
175
|
+
(find-hotels-near-location [_ location-id limit]
|
|
176
|
+
{:ok (take limit hotels)}))
|
|
177
|
+
|
|
178
|
+
(defn create-test-data-access []
|
|
179
|
+
(->TestDataAccess
|
|
180
|
+
[test-attraction ...]
|
|
181
|
+
[test-artist ...]
|
|
182
|
+
[test-movie ...]
|
|
183
|
+
[test-hotel ...]))
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 12.6 エラー処理付きデータアクセス
|
|
187
|
+
|
|
188
|
+
```clojure
|
|
189
|
+
(defrecord FailingDataAccess []
|
|
190
|
+
DataAccess
|
|
191
|
+
(find-attractions [_ name ordering limit]
|
|
192
|
+
{:ok [(attraction "Fallback Attraction" nil test-location)]})
|
|
193
|
+
|
|
194
|
+
(find-artists-from-location [_ location-id limit]
|
|
195
|
+
{:error "Network error: Failed to fetch artists"})
|
|
196
|
+
|
|
197
|
+
(find-movies-about-location [_ location-id limit]
|
|
198
|
+
{:error "Timeout: Failed to fetch movies"})
|
|
199
|
+
|
|
200
|
+
(find-hotels-near-location [_ location-id limit]
|
|
201
|
+
{:error "Service unavailable"}))
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 12.7 TravelGuide アプリケーションの構築
|
|
205
|
+
|
|
206
|
+
```clojure
|
|
207
|
+
(defn build-travel-guide
|
|
208
|
+
"旅行ガイドを構築"
|
|
209
|
+
[data-access attraction-name]
|
|
210
|
+
(let [attractions-result (find-attractions data-access attraction-name by-population 3)]
|
|
211
|
+
(if (error? attractions-result)
|
|
212
|
+
{:error (get-error attractions-result)}
|
|
213
|
+
(let [attractions (get-value attractions-result [])]
|
|
214
|
+
(map (fn [attr]
|
|
215
|
+
(let [location-id (get-in attr [:location :id])
|
|
216
|
+
artists-result (find-artists-from-location data-access location-id 2)
|
|
217
|
+
movies-result (find-movies-about-location data-access location-id 2)
|
|
218
|
+
|
|
219
|
+
;; エラーを収集
|
|
220
|
+
errors (cond-> []
|
|
221
|
+
(error? artists-result) (conj (get-error artists-result))
|
|
222
|
+
(error? movies-result) (conj (get-error movies-result)))
|
|
223
|
+
|
|
224
|
+
;; 結果を取得(エラー時は空リスト)
|
|
225
|
+
artists (get-value artists-result [])
|
|
226
|
+
movies (get-value movies-result [])
|
|
227
|
+
|
|
228
|
+
;; サブジェクトを構築
|
|
229
|
+
subjects (concat (map :name artists) (map :name movies))]
|
|
230
|
+
(travel-guide
|
|
231
|
+
attr
|
|
232
|
+
(vec subjects)
|
|
233
|
+
(search-report (count attractions) errors))))
|
|
234
|
+
attractions)))))
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
```plantuml
|
|
238
|
+
@startuml
|
|
239
|
+
!theme plain
|
|
240
|
+
|
|
241
|
+
rectangle "build-travel-guide 関数" {
|
|
242
|
+
card "1. アトラクション検索" as step1
|
|
243
|
+
card "2. アーティスト検索" as step2
|
|
244
|
+
card "3. 映画検索" as step3
|
|
245
|
+
card "4. エラー収集" as step4
|
|
246
|
+
card "5. TravelGuide 組み立て" as step5
|
|
247
|
+
|
|
248
|
+
step1 --> step2 : attraction
|
|
249
|
+
step1 --> step3 : location-id
|
|
250
|
+
step2 --> step4 : artists-result
|
|
251
|
+
step3 --> step4 : movies-result
|
|
252
|
+
step4 --> step5 : errors
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
note bottom
|
|
256
|
+
エラーは収集され
|
|
257
|
+
SearchReport に記録される
|
|
258
|
+
end note
|
|
259
|
+
|
|
260
|
+
@enduml
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### 12.8 キャッシュ付きデータアクセス
|
|
264
|
+
|
|
265
|
+
`atom` を使用したスレッドセーフなキャッシュの実装:
|
|
266
|
+
|
|
267
|
+
```clojure
|
|
268
|
+
(defn create-cached-data-access
|
|
269
|
+
"キャッシュ付きデータアクセスを作成"
|
|
270
|
+
[underlying-data-access]
|
|
271
|
+
(let [attractions-cache (atom {})
|
|
272
|
+
artists-cache (atom {})
|
|
273
|
+
movies-cache (atom {})
|
|
274
|
+
hotels-cache (atom {})]
|
|
275
|
+
(reify DataAccess
|
|
276
|
+
(find-attractions [_ name ordering limit]
|
|
277
|
+
(let [cache-key (str name "-" ordering "-" limit)]
|
|
278
|
+
(if-let [cached (get @attractions-cache cache-key)]
|
|
279
|
+
{:ok cached}
|
|
280
|
+
(let [result (find-attractions underlying-data-access name ordering limit)]
|
|
281
|
+
(when (ok? result)
|
|
282
|
+
(swap! attractions-cache assoc cache-key (:ok result)))
|
|
283
|
+
result))))
|
|
284
|
+
;; 他のメソッドも同様...
|
|
285
|
+
)))
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
```plantuml
|
|
289
|
+
@startuml
|
|
290
|
+
!theme plain
|
|
291
|
+
skinparam activity {
|
|
292
|
+
BackgroundColor White
|
|
293
|
+
BorderColor Black
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
start
|
|
297
|
+
|
|
298
|
+
:キャッシュキー生成;
|
|
299
|
+
|
|
300
|
+
:atom からキャッシュ確認;
|
|
301
|
+
|
|
302
|
+
if (キャッシュにデータあり?) then (yes)
|
|
303
|
+
:キャッシュからデータ返却;
|
|
304
|
+
else (no)
|
|
305
|
+
:underlying を呼び出し;
|
|
306
|
+
:結果をキャッシュに保存;
|
|
307
|
+
:結果を返却;
|
|
308
|
+
endif
|
|
309
|
+
|
|
310
|
+
stop
|
|
311
|
+
|
|
312
|
+
note right
|
|
313
|
+
atom を使用した
|
|
314
|
+
スレッドセーフなキャッシュ
|
|
315
|
+
end note
|
|
316
|
+
|
|
317
|
+
@enduml
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 12.9 リソース管理
|
|
321
|
+
|
|
322
|
+
安全なリソース管理パターン:
|
|
323
|
+
|
|
324
|
+
```clojure
|
|
325
|
+
(defn with-resource
|
|
326
|
+
"リソースを安全に使用(try-with-resources パターン)"
|
|
327
|
+
[acquire release use-fn]
|
|
328
|
+
(let [resource (acquire)]
|
|
329
|
+
(try
|
|
330
|
+
(use-fn resource)
|
|
331
|
+
(finally
|
|
332
|
+
(release resource)))))
|
|
333
|
+
|
|
334
|
+
(defn make-resource
|
|
335
|
+
"リソースを作成"
|
|
336
|
+
[acquire release]
|
|
337
|
+
{:acquire acquire
|
|
338
|
+
:release release})
|
|
339
|
+
|
|
340
|
+
;; ファイルリソースの例
|
|
341
|
+
(defn file-resource [path]
|
|
342
|
+
(make-resource
|
|
343
|
+
#(clojure.java.io/reader path)
|
|
344
|
+
#(.close %)))
|
|
345
|
+
|
|
346
|
+
(defn read-lines [path]
|
|
347
|
+
(use-resource
|
|
348
|
+
(file-resource path)
|
|
349
|
+
(fn [reader]
|
|
350
|
+
(vec (line-seq reader)))))
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 12.10 バリデーション
|
|
354
|
+
|
|
355
|
+
```clojure
|
|
356
|
+
(defn validate-attraction-name [name]
|
|
357
|
+
(cond
|
|
358
|
+
(nil? name)
|
|
359
|
+
{:error "Attraction name cannot be nil"}
|
|
360
|
+
|
|
361
|
+
(clojure.string/blank? name)
|
|
362
|
+
{:error "Attraction name cannot be empty"}
|
|
363
|
+
|
|
364
|
+
(> (count name) 100)
|
|
365
|
+
{:error "Attraction name too long"}
|
|
366
|
+
|
|
367
|
+
:else
|
|
368
|
+
{:ok name}))
|
|
369
|
+
|
|
370
|
+
(defn validate-limit [limit]
|
|
371
|
+
(cond
|
|
372
|
+
(nil? limit)
|
|
373
|
+
{:error "Limit cannot be nil"}
|
|
374
|
+
|
|
375
|
+
(not (integer? limit))
|
|
376
|
+
{:error "Limit must be an integer"}
|
|
377
|
+
|
|
378
|
+
(<= limit 0)
|
|
379
|
+
{:error "Limit must be positive"}
|
|
380
|
+
|
|
381
|
+
(> limit 100)
|
|
382
|
+
{:error "Limit cannot exceed 100"}
|
|
383
|
+
|
|
384
|
+
:else
|
|
385
|
+
{:ok limit}))
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## 第13章: テスト戦略
|
|
391
|
+
|
|
392
|
+
### 13.1 関数型プログラミングのテスト
|
|
393
|
+
|
|
394
|
+
関数型プログラミングでは、純粋関数のおかげでテストが非常に簡単になります。
|
|
395
|
+
|
|
396
|
+
```plantuml
|
|
397
|
+
@startuml
|
|
398
|
+
!theme plain
|
|
399
|
+
|
|
400
|
+
rectangle "テストの種類" {
|
|
401
|
+
rectangle "単体テスト" as unit {
|
|
402
|
+
card "純粋関数のテスト"
|
|
403
|
+
card "高速・独立"
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
rectangle "プロパティベーステスト" as property {
|
|
407
|
+
card "ランダム入力"
|
|
408
|
+
card "不変条件の検証"
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
rectangle "統合テスト" as integration {
|
|
412
|
+
card "コンポーネント連携"
|
|
413
|
+
card "スタブ使用"
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
unit --> property : 補完
|
|
418
|
+
property --> integration : 補完
|
|
419
|
+
|
|
420
|
+
@enduml
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### 13.2 純粋関数のテスト
|
|
424
|
+
|
|
425
|
+
**ソースファイル**: `app/clojure/test/ch12/travel_guide_test.clj`
|
|
426
|
+
|
|
427
|
+
純粋関数は入力と出力のみでテストできます。
|
|
428
|
+
|
|
429
|
+
```clojure
|
|
430
|
+
(deftest filter-popular-locations-tests
|
|
431
|
+
(testing "filters by minimum population"
|
|
432
|
+
(let [locations [(location "Q1" "Small" 1000)
|
|
433
|
+
(location "Q2" "Medium" 100000)
|
|
434
|
+
(location "Q3" "Large" 1000000)]
|
|
435
|
+
result (filter-popular-locations locations 50000)]
|
|
436
|
+
(is (= 2 (count result)))
|
|
437
|
+
(is (every? #(>= (:population %) 50000) result)))))
|
|
438
|
+
|
|
439
|
+
(deftest calculate-average-rating-tests
|
|
440
|
+
(testing "calculates average"
|
|
441
|
+
(let [loc (location "Q1" "City" 100000)
|
|
442
|
+
hotels [(hotel "H1" 4.0 loc)
|
|
443
|
+
(hotel "H2" 5.0 loc)
|
|
444
|
+
(hotel "H3" 3.0 loc)]]
|
|
445
|
+
(is (= 4.0 (calculate-average-rating hotels)))))
|
|
446
|
+
|
|
447
|
+
(testing "returns 0 for empty list"
|
|
448
|
+
(is (= 0.0 (calculate-average-rating [])))))
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### 13.3 スタブを使用したテスト
|
|
452
|
+
|
|
453
|
+
```clojure
|
|
454
|
+
(deftest test-data-access-tests
|
|
455
|
+
(let [data-access (create-test-data-access)]
|
|
456
|
+
|
|
457
|
+
(testing "find-attractions returns matching attractions"
|
|
458
|
+
(let [result (find-attractions data-access "Test" by-name 10)]
|
|
459
|
+
(is (ok? result))
|
|
460
|
+
(is (= 1 (count (:ok result))))))
|
|
461
|
+
|
|
462
|
+
(testing "find-artists-from-location"
|
|
463
|
+
(let [result (find-artists-from-location data-access "Test City" 10)]
|
|
464
|
+
(is (ok? result))))))
|
|
465
|
+
|
|
466
|
+
(deftest failing-data-access-tests
|
|
467
|
+
(let [data-access (create-failing-data-access)]
|
|
468
|
+
|
|
469
|
+
(testing "find-artists-from-location returns error"
|
|
470
|
+
(let [result (find-artists-from-location data-access "Q123" 10)]
|
|
471
|
+
(is (error? result))))))
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
```plantuml
|
|
475
|
+
@startuml
|
|
476
|
+
!theme plain
|
|
477
|
+
|
|
478
|
+
rectangle "テスト構成" {
|
|
479
|
+
rectangle "本番" as prod {
|
|
480
|
+
interface "DataAccess" as da1
|
|
481
|
+
class "WikidataDataAccess" as wda
|
|
482
|
+
da1 <|.. wda
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
rectangle "テスト" as test {
|
|
486
|
+
interface "DataAccess" as da2
|
|
487
|
+
class "TestDataAccess" as tda
|
|
488
|
+
class "FailingDataAccess" as fda
|
|
489
|
+
da2 <|.. tda
|
|
490
|
+
da2 <|.. fda
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
note bottom of prod
|
|
495
|
+
実際のサービスに接続
|
|
496
|
+
end note
|
|
497
|
+
|
|
498
|
+
note bottom of test
|
|
499
|
+
テストデータを返す
|
|
500
|
+
エラーケースをシミュレート
|
|
501
|
+
end note
|
|
502
|
+
|
|
503
|
+
@enduml
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### 13.4 プロパティベーステスト
|
|
507
|
+
|
|
508
|
+
ランダムな入力で不変条件を検証します。
|
|
509
|
+
|
|
510
|
+
```clojure
|
|
511
|
+
;; ランダムデータ生成
|
|
512
|
+
(defn random-location []
|
|
513
|
+
(location
|
|
514
|
+
(str "Q" (rand-int 10000))
|
|
515
|
+
(random-string 10)
|
|
516
|
+
(rand-int 10000000)))
|
|
517
|
+
|
|
518
|
+
(defn random-attractions [n]
|
|
519
|
+
(repeatedly n random-attraction))
|
|
520
|
+
|
|
521
|
+
;; プロパティテスト
|
|
522
|
+
(deftest property-filter-popular-locations-tests
|
|
523
|
+
(testing "result size is at most input size"
|
|
524
|
+
(dotimes [_ 100]
|
|
525
|
+
(let [n (inc (rand-int 20))
|
|
526
|
+
locations (repeatedly n random-location)
|
|
527
|
+
min-pop (rand-int 10000000)
|
|
528
|
+
result (filter-popular-locations locations min-pop)]
|
|
529
|
+
(is (<= (count result) n)))))
|
|
530
|
+
|
|
531
|
+
(testing "all results meet minimum population"
|
|
532
|
+
(dotimes [_ 100]
|
|
533
|
+
(let [locations (repeatedly 10 random-location)
|
|
534
|
+
min-pop (rand-int 10000000)
|
|
535
|
+
result (filter-popular-locations locations min-pop)]
|
|
536
|
+
(is (every? #(>= (:population %) min-pop) result))))))
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
```plantuml
|
|
540
|
+
@startuml
|
|
541
|
+
!theme plain
|
|
542
|
+
|
|
543
|
+
rectangle "プロパティベーステスト" {
|
|
544
|
+
card "ランダムデータ生成" as gen
|
|
545
|
+
card "関数を実行" as exec
|
|
546
|
+
card "プロパティ検証" as verify
|
|
547
|
+
card "100回以上繰り返し" as repeat
|
|
548
|
+
|
|
549
|
+
gen --> exec
|
|
550
|
+
exec --> verify
|
|
551
|
+
verify --> repeat
|
|
552
|
+
repeat --> gen : 繰り返し
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
note bottom
|
|
556
|
+
ランダムな入力で
|
|
557
|
+
不変条件を検証
|
|
558
|
+
end note
|
|
559
|
+
|
|
560
|
+
@enduml
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### 13.5 統合テスト
|
|
564
|
+
|
|
565
|
+
```clojure
|
|
566
|
+
(deftest integration-travel-guide-tests
|
|
567
|
+
(testing "end-to-end travel guide workflow"
|
|
568
|
+
(let [data-access (create-cached-data-access (create-test-data-access))
|
|
569
|
+
;; バリデーション
|
|
570
|
+
validation (validate-search-params "Test" 3)]
|
|
571
|
+
(is (ok? validation))
|
|
572
|
+
|
|
573
|
+
;; ガイド取得
|
|
574
|
+
(let [result (get-travel-guide data-access "Test")]
|
|
575
|
+
(is (ok? result))
|
|
576
|
+
(let [guide (:ok result)]
|
|
577
|
+
(is (some? (:attraction guide)))
|
|
578
|
+
(is (vector? (:subjects guide)))
|
|
579
|
+
(is (some? (:search-report guide)))))))
|
|
580
|
+
|
|
581
|
+
(testing "workflow with partial failure"
|
|
582
|
+
(let [data-access (create-failing-data-access)
|
|
583
|
+
result (get-travel-guide data-access "Test")]
|
|
584
|
+
(is (ok? result))
|
|
585
|
+
(let [guide (:ok result)]
|
|
586
|
+
(is (= 2 (count (get-in guide [:search-report :errors]))))))))
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### 13.6 テストピラミッド
|
|
590
|
+
|
|
591
|
+
```plantuml
|
|
592
|
+
@startuml
|
|
593
|
+
!theme plain
|
|
594
|
+
|
|
595
|
+
rectangle "テストピラミッド" {
|
|
596
|
+
rectangle "E2E テスト\n(少数)" as e2e
|
|
597
|
+
rectangle "統合テスト\n(中程度)" as integration
|
|
598
|
+
rectangle "単体テスト + プロパティテスト\n(多数)" as unit
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
e2e -[hidden]down- integration
|
|
602
|
+
integration -[hidden]down- unit
|
|
603
|
+
|
|
604
|
+
note right of unit
|
|
605
|
+
FP では純粋関数が多いため
|
|
606
|
+
単体テストが非常に効果的
|
|
607
|
+
end note
|
|
608
|
+
|
|
609
|
+
@enduml
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
## まとめ
|
|
615
|
+
|
|
616
|
+
### Part VI で学んだこと
|
|
617
|
+
|
|
618
|
+
```plantuml
|
|
619
|
+
@startuml
|
|
620
|
+
!theme plain
|
|
621
|
+
|
|
622
|
+
rectangle "Part VI: 実践的なアプリケーション" {
|
|
623
|
+
rectangle "第12章" as ch12 {
|
|
624
|
+
card "ドメインモデル設計"
|
|
625
|
+
card "DataAccess 抽象化"
|
|
626
|
+
card "リソース管理"
|
|
627
|
+
card "キャッシュ実装"
|
|
628
|
+
card "バリデーション"
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
rectangle "第13章" as ch13 {
|
|
632
|
+
card "純粋関数テスト"
|
|
633
|
+
card "スタブ/モック"
|
|
634
|
+
card "プロパティベーステスト"
|
|
635
|
+
card "統合テスト"
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
ch12 --> ch13
|
|
640
|
+
|
|
641
|
+
@enduml
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Clojure vs Scala の比較
|
|
645
|
+
|
|
646
|
+
| 概念 | Scala | Clojure |
|
|
647
|
+
|------|-------|---------|
|
|
648
|
+
| ドメインモデル | case class | defrecord |
|
|
649
|
+
| 抽象化 | trait | defprotocol |
|
|
650
|
+
| リソース管理 | Resource | with-resource |
|
|
651
|
+
| キャッシュ | Ref | atom |
|
|
652
|
+
| エラー処理 | Either | {:ok ...} / {:error ...} |
|
|
653
|
+
| テスト | ScalaTest | clojure.test |
|
|
654
|
+
|
|
655
|
+
### キーポイント
|
|
656
|
+
|
|
657
|
+
1. **プロトコルによる抽象化**: DataAccess で外部依存を抽象化
|
|
658
|
+
2. **with-resource でリソース管理**: 安全なリソースの取得と解放
|
|
659
|
+
3. **atom でキャッシュ**: スレッドセーフな状態管理
|
|
660
|
+
4. **Result マップでエラー処理**: 明示的なエラーハンドリング
|
|
661
|
+
5. **SearchReport**: テスト可能性と可観測性の向上
|
|
662
|
+
6. **スタブ**: 外部依存を差し替えてテスト
|
|
663
|
+
7. **プロパティベーステスト**: ランダム入力で不変条件を検証
|
|
664
|
+
|
|
665
|
+
### 学習の総括
|
|
666
|
+
|
|
667
|
+
```plantuml
|
|
668
|
+
@startuml
|
|
669
|
+
!theme plain
|
|
670
|
+
left to right direction
|
|
671
|
+
|
|
672
|
+
rectangle "FP の学習パス" {
|
|
673
|
+
card "Part I\n基礎" as p1
|
|
674
|
+
card "Part II\n関数型スタイル" as p2
|
|
675
|
+
card "Part III\nnil 安全性" as p3
|
|
676
|
+
card "Part IV\n副作用とシーケンス" as p4
|
|
677
|
+
card "Part V\n並行処理" as p5
|
|
678
|
+
card "Part VI\n実践" as p6
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
p1 --> p2
|
|
682
|
+
p2 --> p3
|
|
683
|
+
p3 --> p4
|
|
684
|
+
p4 --> p5
|
|
685
|
+
p5 --> p6
|
|
686
|
+
|
|
687
|
+
@enduml
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
---
|
|
691
|
+
|
|
692
|
+
## シリーズ全体の総括
|
|
693
|
+
|
|
694
|
+
本シリーズでは、「Grokking Functional Programming」の内容に沿って、Clojure で関数型プログラミングの基礎から実践的なアプリケーション構築までを学びました。
|
|
695
|
+
|
|
696
|
+
### 学んだ主な概念
|
|
697
|
+
|
|
698
|
+
| Part | 章 | 主な概念 |
|
|
699
|
+
|------|-----|----------|
|
|
700
|
+
| I | 1-2 | 純粋関数、参照透過性 |
|
|
701
|
+
| II | 3-5 | イミュータブルデータ、高階関数、mapcat |
|
|
702
|
+
| III | 6-7 | nil 安全性、Result パターン、ADT |
|
|
703
|
+
| IV | 8-9 | 状態管理、遅延シーケンス、トランスデューサー |
|
|
704
|
+
| V | 10-11 | atom/ref/agent、core.async |
|
|
705
|
+
| VI | 12-13 | 実践アプリケーション、テスト |
|
|
706
|
+
|
|
707
|
+
### Clojure における関数型プログラミングの利点
|
|
708
|
+
|
|
709
|
+
1. **シンプルさ**: Lisp の S 式による一貫した構文
|
|
710
|
+
2. **イミュータブルがデフォルト**: 永続データ構造
|
|
711
|
+
3. **豊富な並行プリミティブ**: atom, ref, agent, core.async
|
|
712
|
+
4. **遅延評価**: 効率的なシーケンス処理
|
|
713
|
+
5. **REPL 駆動開発**: インタラクティブな開発体験
|
|
714
|
+
6. **Java 相互運用**: 豊富なエコシステムへのアクセス
|
|
715
|
+
|
|
716
|
+
### 次のステップ
|
|
717
|
+
|
|
718
|
+
- spec や malli による仕様記述とバリデーション
|
|
719
|
+
- Component や Integrant によるシステム構成
|
|
720
|
+
- re-frame や Fulcro によるフロントエンド開発
|
|
721
|
+
- 実際のプロジェクトで Clojure を適用する
|