@aicgen/aicgen 1.0.0-beta.1

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.
Files changed (287) hide show
  1. package/.claude/agents/architecture-reviewer.md +88 -0
  2. package/.claude/agents/guideline-checker.md +73 -0
  3. package/.claude/agents/security-auditor.md +108 -0
  4. package/.claude/guidelines/api-design.md +645 -0
  5. package/.claude/guidelines/architecture.md +2503 -0
  6. package/.claude/guidelines/best-practices.md +618 -0
  7. package/.claude/guidelines/code-style.md +304 -0
  8. package/.claude/guidelines/design-patterns.md +573 -0
  9. package/.claude/guidelines/devops.md +226 -0
  10. package/.claude/guidelines/error-handling.md +413 -0
  11. package/.claude/guidelines/language.md +782 -0
  12. package/.claude/guidelines/performance.md +706 -0
  13. package/.claude/guidelines/security.md +583 -0
  14. package/.claude/guidelines/testing.md +568 -0
  15. package/.claude/settings.json +98 -0
  16. package/.claude/settings.local.json +8 -0
  17. package/.env.example +23 -0
  18. package/.eslintrc.json +28 -0
  19. package/.github/workflows/release.yml +180 -0
  20. package/.github/workflows/test.yml +81 -0
  21. package/.gitmodules +3 -0
  22. package/.vs/ProjectSettings.json +3 -0
  23. package/.vs/VSWorkspaceState.json +16 -0
  24. package/.vs/aicgen.slnx/FileContentIndex/5f0ce2a3-fd68-4863-9e23-e428cf1794e3.vsidx +0 -0
  25. package/.vs/aicgen.slnx/v18/.wsuo +0 -0
  26. package/.vs/aicgen.slnx/v18/DocumentLayout.json +54 -0
  27. package/.vs/slnx.sqlite +0 -0
  28. package/AGENTS.md +121 -0
  29. package/CLAUDE.md +36 -0
  30. package/CONTRIBUTING.md +821 -0
  31. package/LICENSE +21 -0
  32. package/README.md +199 -0
  33. package/assets/icon.svg +34 -0
  34. package/assets/logo.svg +41 -0
  35. package/bun.lock +848 -0
  36. package/data/LICENSE +21 -0
  37. package/data/README.md +203 -0
  38. package/data/api/basics.md +292 -0
  39. package/data/api/index.md +8 -0
  40. package/data/api/pagination.md +142 -0
  41. package/data/api/rest.md +137 -0
  42. package/data/api/versioning.md +60 -0
  43. package/data/architecture/clean-architecture/index.md +7 -0
  44. package/data/architecture/clean-architecture/layers.md +111 -0
  45. package/data/architecture/ddd/index.md +8 -0
  46. package/data/architecture/ddd/strategic.md +89 -0
  47. package/data/architecture/ddd/tactical.md +132 -0
  48. package/data/architecture/event-driven/index.md +7 -0
  49. package/data/architecture/event-driven/messaging.md +242 -0
  50. package/data/architecture/event-driven/patterns.md +129 -0
  51. package/data/architecture/feature-toggles/index.md +7 -0
  52. package/data/architecture/feature-toggles/patterns.md +73 -0
  53. package/data/architecture/gui/index.md +7 -0
  54. package/data/architecture/gui/patterns.md +132 -0
  55. package/data/architecture/hexagonal/ports-adapters.md +132 -0
  56. package/data/architecture/index.md +12 -0
  57. package/data/architecture/layered/index.md +7 -0
  58. package/data/architecture/layered/layers.md +100 -0
  59. package/data/architecture/microservices/api-gateway.md +56 -0
  60. package/data/architecture/microservices/boundaries.md +80 -0
  61. package/data/architecture/microservices/communication.md +97 -0
  62. package/data/architecture/microservices/data.md +92 -0
  63. package/data/architecture/microservices/index.md +11 -0
  64. package/data/architecture/microservices/resilience.md +111 -0
  65. package/data/architecture/modular-monolith/boundaries.md +133 -0
  66. package/data/architecture/modular-monolith/structure.md +131 -0
  67. package/data/architecture/serverless/best-practices.md +322 -0
  68. package/data/architecture/serverless/index.md +7 -0
  69. package/data/architecture/serverless/patterns.md +80 -0
  70. package/data/architecture/solid/index.md +7 -0
  71. package/data/architecture/solid/principles.md +187 -0
  72. package/data/database/basics.md +365 -0
  73. package/data/database/design-patterns.md +68 -0
  74. package/data/database/index.md +8 -0
  75. package/data/database/indexing.md +136 -0
  76. package/data/database/nosql.md +223 -0
  77. package/data/database/schema.md +137 -0
  78. package/data/devops/ci-cd.md +66 -0
  79. package/data/devops/index.md +8 -0
  80. package/data/devops/observability.md +73 -0
  81. package/data/devops/practices.md +77 -0
  82. package/data/error-handling/basics.md +222 -0
  83. package/data/error-handling/index.md +7 -0
  84. package/data/error-handling/strategy.md +185 -0
  85. package/data/guideline-mappings.yml +1077 -0
  86. package/data/index.md +3 -0
  87. package/data/language/csharp/basics.md +210 -0
  88. package/data/language/csharp/testing.md +252 -0
  89. package/data/language/go/basics.md +158 -0
  90. package/data/language/go/testing.md +192 -0
  91. package/data/language/index.md +14 -0
  92. package/data/language/java/basics.md +184 -0
  93. package/data/language/java/testing.md +273 -0
  94. package/data/language/javascript/basics.md +217 -0
  95. package/data/language/javascript/testing.md +269 -0
  96. package/data/language/python/async.md +100 -0
  97. package/data/language/python/basics.md +100 -0
  98. package/data/language/python/index.md +10 -0
  99. package/data/language/python/testing.md +125 -0
  100. package/data/language/python/types.md +99 -0
  101. package/data/language/ruby/basics.md +227 -0
  102. package/data/language/ruby/testing.md +267 -0
  103. package/data/language/rust/basics.md +175 -0
  104. package/data/language/rust/testing.md +219 -0
  105. package/data/language/typescript/async.md +103 -0
  106. package/data/language/typescript/basics.md +87 -0
  107. package/data/language/typescript/config.md +95 -0
  108. package/data/language/typescript/error-handling.md +98 -0
  109. package/data/language/typescript/generics.md +85 -0
  110. package/data/language/typescript/index.md +14 -0
  111. package/data/language/typescript/interfaces-types.md +83 -0
  112. package/data/language/typescript/performance.md +103 -0
  113. package/data/language/typescript/testing.md +98 -0
  114. package/data/patterns/base-patterns.md +105 -0
  115. package/data/patterns/concurrency.md +87 -0
  116. package/data/patterns/data-access.md +83 -0
  117. package/data/patterns/distribution.md +86 -0
  118. package/data/patterns/domain-logic.md +81 -0
  119. package/data/patterns/gof.md +109 -0
  120. package/data/patterns/index.md +12 -0
  121. package/data/performance/async.md +148 -0
  122. package/data/performance/basics.md +324 -0
  123. package/data/performance/caching-strategies.md +68 -0
  124. package/data/performance/caching.md +152 -0
  125. package/data/performance/index.md +8 -0
  126. package/data/practices/code-review.md +52 -0
  127. package/data/practices/documentation.md +260 -0
  128. package/data/practices/index.md +11 -0
  129. package/data/practices/planning.md +142 -0
  130. package/data/practices/refactoring.md +91 -0
  131. package/data/practices/version-control.md +55 -0
  132. package/data/security/auth-jwt.md +159 -0
  133. package/data/security/headers.md +143 -0
  134. package/data/security/index.md +10 -0
  135. package/data/security/injection.md +119 -0
  136. package/data/security/secrets.md +148 -0
  137. package/data/style/index.md +8 -0
  138. package/data/style/naming.md +136 -0
  139. package/data/style/organization.md +162 -0
  140. package/data/templates/agents/architecture-reviewer.md +88 -0
  141. package/data/templates/agents/guideline-checker.md +73 -0
  142. package/data/templates/agents/security-auditor.md +108 -0
  143. package/data/templates/antigravity/rules/architecture.md.hbs +5 -0
  144. package/data/templates/antigravity/rules/code-style.md.hbs +5 -0
  145. package/data/templates/antigravity/rules/language.md.hbs +5 -0
  146. package/data/templates/antigravity/rules/performance.md.hbs +5 -0
  147. package/data/templates/antigravity/rules/security.md.hbs +5 -0
  148. package/data/templates/antigravity/rules/testing.md.hbs +5 -0
  149. package/data/templates/antigravity/workflows/add-documentation.md.hbs +23 -0
  150. package/data/templates/antigravity/workflows/generate-integration-tests.md.hbs +17 -0
  151. package/data/templates/antigravity/workflows/generate-unit-tests.md.hbs +20 -0
  152. package/data/templates/antigravity/workflows/performance-audit.md.hbs +24 -0
  153. package/data/templates/antigravity/workflows/refactor-extract-module.md.hbs +17 -0
  154. package/data/templates/antigravity/workflows/security-audit.md.hbs +20 -0
  155. package/data/templates/hooks/formatting.json +26 -0
  156. package/data/templates/hooks/security.json +35 -0
  157. package/data/templates/hooks/testing.json +17 -0
  158. package/data/testing/basics.md +151 -0
  159. package/data/testing/index.md +9 -0
  160. package/data/testing/integration.md +159 -0
  161. package/data/testing/unit-fundamentals.md +128 -0
  162. package/data/testing/unit-mocking.md +116 -0
  163. package/data/version.json +49 -0
  164. package/dist/commands/init.d.ts +8 -0
  165. package/dist/commands/init.d.ts.map +1 -0
  166. package/dist/commands/init.js +46 -0
  167. package/dist/commands/init.js.map +1 -0
  168. package/dist/config/profiles.d.ts +4 -0
  169. package/dist/config/profiles.d.ts.map +1 -0
  170. package/dist/config/profiles.js +30 -0
  171. package/dist/config/profiles.js.map +1 -0
  172. package/dist/config/settings.d.ts +7 -0
  173. package/dist/config/settings.d.ts.map +1 -0
  174. package/dist/config/settings.js +7 -0
  175. package/dist/config/settings.js.map +1 -0
  176. package/dist/index.d.ts +3 -0
  177. package/dist/index.d.ts.map +1 -0
  178. package/dist/index.js +58489 -0
  179. package/dist/index.js.map +1 -0
  180. package/dist/models/guideline.d.ts +15 -0
  181. package/dist/models/guideline.d.ts.map +1 -0
  182. package/dist/models/guideline.js +2 -0
  183. package/dist/models/guideline.js.map +1 -0
  184. package/dist/models/preference.d.ts +9 -0
  185. package/dist/models/preference.d.ts.map +1 -0
  186. package/dist/models/preference.js +2 -0
  187. package/dist/models/preference.js.map +1 -0
  188. package/dist/models/profile.d.ts +9 -0
  189. package/dist/models/profile.d.ts.map +1 -0
  190. package/dist/models/profile.js +2 -0
  191. package/dist/models/profile.js.map +1 -0
  192. package/dist/models/project.d.ts +13 -0
  193. package/dist/models/project.d.ts.map +1 -0
  194. package/dist/models/project.js +2 -0
  195. package/dist/models/project.js.map +1 -0
  196. package/dist/services/ai/anthropic.d.ts +7 -0
  197. package/dist/services/ai/anthropic.d.ts.map +1 -0
  198. package/dist/services/ai/anthropic.js +39 -0
  199. package/dist/services/ai/anthropic.js.map +1 -0
  200. package/dist/services/generator.d.ts +2 -0
  201. package/dist/services/generator.d.ts.map +1 -0
  202. package/dist/services/generator.js +4 -0
  203. package/dist/services/generator.js.map +1 -0
  204. package/dist/services/learner.d.ts +2 -0
  205. package/dist/services/learner.d.ts.map +1 -0
  206. package/dist/services/learner.js +4 -0
  207. package/dist/services/learner.js.map +1 -0
  208. package/dist/services/scanner.d.ts +3 -0
  209. package/dist/services/scanner.d.ts.map +1 -0
  210. package/dist/services/scanner.js +54 -0
  211. package/dist/services/scanner.js.map +1 -0
  212. package/dist/utils/errors.d.ts +15 -0
  213. package/dist/utils/errors.d.ts.map +1 -0
  214. package/dist/utils/errors.js +27 -0
  215. package/dist/utils/errors.js.map +1 -0
  216. package/dist/utils/file.d.ts +7 -0
  217. package/dist/utils/file.d.ts.map +1 -0
  218. package/dist/utils/file.js +32 -0
  219. package/dist/utils/file.js.map +1 -0
  220. package/dist/utils/logger.d.ts +6 -0
  221. package/dist/utils/logger.d.ts.map +1 -0
  222. package/dist/utils/logger.js +17 -0
  223. package/dist/utils/logger.js.map +1 -0
  224. package/dist/utils/path.d.ts +6 -0
  225. package/dist/utils/path.d.ts.map +1 -0
  226. package/dist/utils/path.js +14 -0
  227. package/dist/utils/path.js.map +1 -0
  228. package/docs/planning/memory-lane.md +83 -0
  229. package/package.json +64 -0
  230. package/packaging/linux/aicgen.spec +23 -0
  231. package/packaging/linux/control +9 -0
  232. package/packaging/macos/scripts/postinstall +12 -0
  233. package/packaging/windows/setup.nsi +92 -0
  234. package/planning/BRANDING-SUMMARY.md +194 -0
  235. package/planning/BRANDING.md +174 -0
  236. package/planning/BUILD.md +186 -0
  237. package/planning/CHUNK-IMPLEMENTATION-PLAN.md +87 -0
  238. package/planning/CHUNK-TAXONOMY.md +375 -0
  239. package/planning/CHUNKS-COMPLETE.md +382 -0
  240. package/planning/DESIGN.md +313 -0
  241. package/planning/DYNAMIC-GUIDELINES-DESIGN.md +265 -0
  242. package/planning/ENTERPRISE-UX-COMPLETE.md +281 -0
  243. package/planning/IMPLEMENTATION-PLAN.md +20 -0
  244. package/planning/PHASE1-COMPLETE.md +211 -0
  245. package/planning/PHASE2-COMPLETE.md +350 -0
  246. package/planning/PHASE3-COMPLETE.md +399 -0
  247. package/planning/PHASE4-COMPLETE.md +361 -0
  248. package/planning/PHASE4.5-CHUNKS.md +462 -0
  249. package/planning/STRUCTURE.md +170 -0
  250. package/scripts/add-categories.ts +87 -0
  251. package/scripts/build-binary.ts +46 -0
  252. package/scripts/embed-data.ts +105 -0
  253. package/scripts/generate-version.ts +150 -0
  254. package/scripts/test-decompress.ts +27 -0
  255. package/scripts/test-extract.ts +31 -0
  256. package/src/__tests__/services/assistant-file-writer.test.ts +400 -0
  257. package/src/__tests__/services/guideline-loader.test.ts +281 -0
  258. package/src/__tests__/services/tarball-extraction.test.ts +125 -0
  259. package/src/commands/add-guideline.ts +296 -0
  260. package/src/commands/clear.ts +61 -0
  261. package/src/commands/guideline-selector.ts +123 -0
  262. package/src/commands/init.ts +645 -0
  263. package/src/commands/quick-add.ts +586 -0
  264. package/src/commands/remove-guideline.ts +152 -0
  265. package/src/commands/stats.ts +49 -0
  266. package/src/commands/update.ts +240 -0
  267. package/src/config.ts +82 -0
  268. package/src/embedded-data.ts +1492 -0
  269. package/src/index.ts +67 -0
  270. package/src/models/profile.ts +24 -0
  271. package/src/models/project.ts +43 -0
  272. package/src/services/assistant-file-writer.ts +612 -0
  273. package/src/services/config-generator.ts +150 -0
  274. package/src/services/config-manager.ts +70 -0
  275. package/src/services/data-source.ts +248 -0
  276. package/src/services/first-run-init.ts +148 -0
  277. package/src/services/guideline-loader.ts +311 -0
  278. package/src/services/hook-generator.ts +178 -0
  279. package/src/services/subagent-generator.ts +310 -0
  280. package/src/utils/banner.ts +66 -0
  281. package/src/utils/errors.ts +27 -0
  282. package/src/utils/file.ts +67 -0
  283. package/src/utils/formatting.ts +172 -0
  284. package/src/utils/logger.ts +89 -0
  285. package/src/utils/path.ts +17 -0
  286. package/src/utils/wizard-state.ts +132 -0
  287. package/tsconfig.json +25 -0
@@ -0,0 +1,125 @@
1
+ # Python Testing with pytest
2
+
3
+ ## Basic Test Structure
4
+
5
+ ```python
6
+ import pytest
7
+ from myapp.services import UserService
8
+
9
+ class TestUserService:
10
+ def test_create_user_with_valid_data(self):
11
+ # Arrange
12
+ service = UserService()
13
+ user_data = {"email": "test@example.com", "name": "Test User"}
14
+
15
+ # Act
16
+ user = service.create_user(user_data)
17
+
18
+ # Assert
19
+ assert user.id is not None
20
+ assert user.email == "test@example.com"
21
+
22
+ def test_create_user_with_invalid_email_raises_error(self):
23
+ service = UserService()
24
+
25
+ with pytest.raises(ValidationError, match="Invalid email"):
26
+ service.create_user({"email": "invalid", "name": "Test"})
27
+ ```
28
+
29
+ ## Fixtures
30
+
31
+ ```python
32
+ import pytest
33
+ from sqlalchemy import create_engine
34
+
35
+ @pytest.fixture
36
+ def db_session():
37
+ """Provide a transactional database session."""
38
+ engine = create_engine("sqlite:///:memory:")
39
+ Session = sessionmaker(bind=engine)
40
+ session = Session()
41
+
42
+ yield session
43
+
44
+ session.rollback()
45
+ session.close()
46
+
47
+ @pytest.fixture
48
+ def user_service(db_session):
49
+ """Provide UserService with test database."""
50
+ return UserService(db_session)
51
+
52
+ # Usage - fixtures injected automatically
53
+ def test_find_user(user_service, db_session):
54
+ user = User(email="test@example.com")
55
+ db_session.add(user)
56
+ db_session.commit()
57
+
58
+ found = user_service.find_by_email("test@example.com")
59
+ assert found.id == user.id
60
+ ```
61
+
62
+ ## Parametrized Tests
63
+
64
+ ```python
65
+ @pytest.mark.parametrize("age,expected", [
66
+ (17, False),
67
+ (18, True),
68
+ (21, True),
69
+ (0, False),
70
+ (-1, False),
71
+ ])
72
+ def test_is_adult(age, expected):
73
+ assert is_adult(age) == expected
74
+
75
+ @pytest.mark.parametrize("email", [
76
+ "user@example.com",
77
+ "user.name@example.co.uk",
78
+ "user+tag@example.com",
79
+ ])
80
+ def test_valid_emails(email):
81
+ assert validate_email(email) is True
82
+ ```
83
+
84
+ ## Mocking
85
+
86
+ ```python
87
+ from unittest.mock import Mock, patch, AsyncMock
88
+
89
+ def test_send_notification(mocker):
90
+ # Mock external service
91
+ mock_email = mocker.patch("myapp.services.email_client")
92
+ mock_email.send.return_value = True
93
+
94
+ service = NotificationService()
95
+ result = service.send_welcome_email("user@example.com")
96
+
97
+ assert result is True
98
+ mock_email.send.assert_called_once_with(
99
+ to="user@example.com",
100
+ template="welcome"
101
+ )
102
+
103
+ # Async mocking
104
+ @pytest.mark.asyncio
105
+ async def test_fetch_user(mocker):
106
+ mock_client = mocker.patch("myapp.api.http_client")
107
+ mock_client.get = AsyncMock(return_value={"id": 1, "name": "Test"})
108
+
109
+ user = await fetch_user(1)
110
+ assert user["name"] == "Test"
111
+ ```
112
+
113
+ ## pytest Configuration
114
+
115
+ ```ini
116
+ # pytest.ini
117
+ [pytest]
118
+ testpaths = tests
119
+ python_files = test_*.py
120
+ python_functions = test_*
121
+ addopts = -v --tb=short --strict-markers
122
+ markers =
123
+ slow: marks tests as slow
124
+ integration: marks tests as integration tests
125
+ ```
@@ -0,0 +1,99 @@
1
+ # Python Type Hints
2
+
3
+ ## Basic Type Annotations
4
+
5
+ ```python
6
+ from typing import List, Dict, Optional, Union, Tuple
7
+
8
+ # Variable annotations
9
+ name: str = "Alice"
10
+ age: int = 30
11
+ prices: List[float] = [9.99, 19.99, 29.99]
12
+ user_scores: Dict[str, int] = {"alice": 100, "bob": 85}
13
+
14
+ # Function annotations
15
+ def greet(name: str, times: int = 1) -> str:
16
+ return f"Hello, {name}! " * times
17
+
18
+ def find_user(user_id: int) -> Optional[User]:
19
+ """Returns User or None if not found."""
20
+ return db.get(user_id)
21
+ ```
22
+
23
+ ## Modern Python 3.10+ Syntax
24
+
25
+ ```python
26
+ # ✅ Use built-in types directly (Python 3.9+)
27
+ def process(items: list[str]) -> dict[str, int]:
28
+ return {item: len(item) for item in items}
29
+
30
+ # ✅ Union syntax with | (Python 3.10+)
31
+ def parse(value: str | int | None) -> str:
32
+ if value is None:
33
+ return ""
34
+ return str(value)
35
+
36
+ # Instead of:
37
+ from typing import Union, Optional
38
+ def parse(value: Union[str, int, None]) -> str: ...
39
+ ```
40
+
41
+ ## TypedDict and Protocols
42
+
43
+ ```python
44
+ from typing import TypedDict, Protocol
45
+
46
+ # ✅ TypedDict for structured dictionaries
47
+ class UserDict(TypedDict):
48
+ id: int
49
+ name: str
50
+ email: str
51
+ is_active: bool
52
+
53
+ def create_user(data: UserDict) -> User:
54
+ return User(**data)
55
+
56
+ # ✅ Protocol for structural typing (duck typing)
57
+ class Readable(Protocol):
58
+ def read(self) -> str: ...
59
+
60
+ def process_file(file: Readable) -> None:
61
+ content = file.read()
62
+ # Works with any object that has read() method
63
+ ```
64
+
65
+ ## Generics
66
+
67
+ ```python
68
+ from typing import TypeVar, Generic
69
+
70
+ T = TypeVar('T')
71
+
72
+ class Repository(Generic[T]):
73
+ def __init__(self, model: type[T]) -> None:
74
+ self.model = model
75
+
76
+ def find(self, id: int) -> T | None:
77
+ return self.db.get(self.model, id)
78
+
79
+ def save(self, entity: T) -> T:
80
+ return self.db.save(entity)
81
+
82
+ # Usage
83
+ user_repo = Repository[User](User)
84
+ user = user_repo.find(1) # Returns User | None
85
+ ```
86
+
87
+ ## Type Checking with mypy
88
+
89
+ ```bash
90
+ # Run type checker
91
+ mypy src/
92
+
93
+ # mypy.ini configuration
94
+ [mypy]
95
+ python_version = 3.11
96
+ strict = True
97
+ warn_return_any = True
98
+ warn_unused_ignores = True
99
+ ```
@@ -0,0 +1,227 @@
1
+ # Ruby Fundamentals
2
+
3
+ ## Project Structure (Rails)
4
+
5
+ ```
6
+ myapp/
7
+ ├── app/
8
+ │ ├── controllers/
9
+ │ ├── models/
10
+ │ ├── services/
11
+ │ ├── views/
12
+ │ └── jobs/
13
+ ├── config/
14
+ ├── db/
15
+ │ └── migrate/
16
+ ├── lib/
17
+ ├── spec/ or test/
18
+ ├── Gemfile
19
+ └── Gemfile.lock
20
+ ```
21
+
22
+ ## Naming Conventions
23
+
24
+ ```ruby
25
+ # Classes/Modules: PascalCase
26
+ class UserService
27
+ end
28
+
29
+ module Authentication
30
+ end
31
+
32
+ # Methods/variables: snake_case
33
+ def create_user(email)
34
+ user_name = "test"
35
+ end
36
+
37
+ # Constants: SCREAMING_SNAKE_CASE
38
+ MAX_RETRIES = 3
39
+ DEFAULT_TIMEOUT = 30
40
+
41
+ # Predicate methods: end with ?
42
+ def valid?
43
+ @errors.empty?
44
+ end
45
+
46
+ # Dangerous methods: end with !
47
+ def save!
48
+ raise Error unless save
49
+ end
50
+
51
+ # Private attr: prefix with _
52
+ attr_reader :_internal_state
53
+ ```
54
+
55
+ ## Idiomatic Ruby
56
+
57
+ ```ruby
58
+ # Blocks
59
+ users.each do |user|
60
+ puts user.name
61
+ end
62
+
63
+ # Short blocks with &:method
64
+ emails = users.map(&:email)
65
+ active = users.select(&:active?)
66
+
67
+ # Safe navigation
68
+ user&.profile&.avatar_url
69
+
70
+ # Default values
71
+ def greet(name = "World")
72
+ "Hello, #{name}!"
73
+ end
74
+
75
+ # Keyword arguments
76
+ def create_user(email:, name: nil, role: :user)
77
+ User.new(email: email, name: name, role: role)
78
+ end
79
+
80
+ # Multiple return values
81
+ def parse(input)
82
+ [result, errors]
83
+ end
84
+
85
+ result, errors = parse(input)
86
+ ```
87
+
88
+ ## Error Handling
89
+
90
+ ```ruby
91
+ # Begin/rescue/ensure
92
+ begin
93
+ risky_operation
94
+ rescue NetworkError => e
95
+ logger.error("Network failed: #{e.message}")
96
+ retry if should_retry?
97
+ rescue StandardError => e
98
+ logger.error("Unexpected error: #{e.message}")
99
+ raise
100
+ ensure
101
+ cleanup
102
+ end
103
+
104
+ # Custom errors
105
+ class NotFoundError < StandardError
106
+ attr_reader :id
107
+
108
+ def initialize(id)
109
+ @id = id
110
+ super("Not found: #{id}")
111
+ end
112
+ end
113
+
114
+ # Inline rescue (use sparingly)
115
+ value = risky_call rescue default_value
116
+ ```
117
+
118
+ ## Collections
119
+
120
+ ```ruby
121
+ # Map/Select/Reduce
122
+ emails = users.map { |u| u.email }
123
+ active = users.select { |u| u.active? }
124
+ total = orders.reduce(0) { |sum, o| sum + o.total }
125
+
126
+ # Chaining
127
+ users
128
+ .select(&:active?)
129
+ .map(&:email)
130
+ .uniq
131
+ .sort
132
+
133
+ # Hash operations
134
+ counts = users.group_by(&:department)
135
+ .transform_values(&:count)
136
+
137
+ # Find
138
+ user = users.find { |u| u.id == target_id }
139
+ ```
140
+
141
+ ## Classes and Modules
142
+
143
+ ```ruby
144
+ # Service object pattern
145
+ class CreateUser
146
+ def initialize(repository:, notifier:)
147
+ @repository = repository
148
+ @notifier = notifier
149
+ end
150
+
151
+ def call(email:, name:)
152
+ user = User.new(email: email, name: name)
153
+ @repository.save(user)
154
+ @notifier.welcome(user)
155
+ user
156
+ end
157
+ end
158
+
159
+ # Module for shared behavior
160
+ module Timestampable
161
+ def created_at
162
+ @created_at ||= Time.now
163
+ end
164
+ end
165
+
166
+ class User
167
+ include Timestampable
168
+ end
169
+ ```
170
+
171
+ ## Testing (RSpec)
172
+
173
+ ```ruby
174
+ RSpec.describe UserService do
175
+ let(:repository) { instance_double(UserRepository) }
176
+ let(:service) { described_class.new(repository: repository) }
177
+
178
+ describe "#create" do
179
+ context "with valid email" do
180
+ it "creates a user" do
181
+ allow(repository).to receive(:save)
182
+
183
+ user = service.create(email: "test@example.com")
184
+
185
+ expect(user.email).to eq("test@example.com")
186
+ expect(repository).to have_received(:save).with(user)
187
+ end
188
+ end
189
+
190
+ context "with invalid email" do
191
+ it "raises ValidationError" do
192
+ expect { service.create(email: "invalid") }
193
+ .to raise_error(ValidationError)
194
+ end
195
+ end
196
+ end
197
+ end
198
+ ```
199
+
200
+ ## Rails Conventions
201
+
202
+ ```ruby
203
+ # Controller
204
+ class UsersController < ApplicationController
205
+ def create
206
+ @user = User.new(user_params)
207
+ if @user.save
208
+ redirect_to @user, notice: "User created"
209
+ else
210
+ render :new, status: :unprocessable_entity
211
+ end
212
+ end
213
+
214
+ private
215
+
216
+ def user_params
217
+ params.require(:user).permit(:email, :name)
218
+ end
219
+ end
220
+
221
+ # Model
222
+ class User < ApplicationRecord
223
+ validates :email, presence: true, uniqueness: true
224
+ has_many :posts, dependent: :destroy
225
+ scope :active, -> { where(active: true) }
226
+ end
227
+ ```
@@ -0,0 +1,267 @@
1
+ # Ruby Testing (RSpec)
2
+
3
+ ## Project Structure
4
+
5
+ ```
6
+ myapp/
7
+ ├── app/
8
+ │ └── services/
9
+ │ └── user_service.rb
10
+ └── spec/
11
+ ├── spec_helper.rb
12
+ ├── rails_helper.rb # Rails projects
13
+ └── services/
14
+ └── user_service_spec.rb
15
+ ```
16
+
17
+ ## Basic Specs
18
+
19
+ ```ruby
20
+ RSpec.describe UserService do
21
+ describe "#create" do
22
+ it "creates a user with valid email" do
23
+ service = UserService.new
24
+
25
+ user = service.create(email: "test@example.com")
26
+
27
+ expect(user.email).to eq("test@example.com")
28
+ end
29
+
30
+ it "raises error with invalid email" do
31
+ service = UserService.new
32
+
33
+ expect { service.create(email: "invalid") }
34
+ .to raise_error(ValidationError)
35
+ end
36
+ end
37
+ end
38
+ ```
39
+
40
+ ## Let and Subject
41
+
42
+ ```ruby
43
+ RSpec.describe UserService do
44
+ subject(:service) { described_class.new(repository: repository) }
45
+ let(:repository) { instance_double(UserRepository) }
46
+ let(:user) { User.new(email: "test@example.com") }
47
+
48
+ describe "#find" do
49
+ before do
50
+ allow(repository).to receive(:find).with("1").and_return(user)
51
+ end
52
+
53
+ it "returns the user" do
54
+ result = service.find("1")
55
+ expect(result).to eq(user)
56
+ end
57
+ end
58
+ end
59
+ ```
60
+
61
+ ## Matchers
62
+
63
+ ```ruby
64
+ # Equality
65
+ expect(actual).to eq(expected)
66
+ expect(actual).not_to eq(unexpected)
67
+ expect(actual).to eql(expected) # stricter equality
68
+
69
+ # Boolean
70
+ expect(value).to be true
71
+ expect(value).to be_truthy
72
+ expect(value).to be_falsy
73
+ expect(value).to be_nil
74
+
75
+ # Comparisons
76
+ expect(value).to be > 5
77
+ expect(value).to be_between(1, 10)
78
+
79
+ # Collections
80
+ expect(array).to include(item)
81
+ expect(array).to contain_exactly(1, 2, 3)
82
+ expect(array).to match_array([3, 1, 2])
83
+ expect(array).to be_empty
84
+ expect(hash).to have_key(:name)
85
+
86
+ # Strings
87
+ expect(string).to start_with("Hello")
88
+ expect(string).to end_with("World")
89
+ expect(string).to match(/pattern/)
90
+
91
+ # Types
92
+ expect(object).to be_a(User)
93
+ expect(object).to be_an_instance_of(User)
94
+
95
+ # Predicates (calls object.active?)
96
+ expect(user).to be_active
97
+ expect(user).to have_orders # calls has_orders?
98
+ ```
99
+
100
+ ## Contexts and Shared Examples
101
+
102
+ ```ruby
103
+ RSpec.describe UserService do
104
+ describe "#create" do
105
+ context "with valid email" do
106
+ it "creates the user" do
107
+ # test
108
+ end
109
+
110
+ it "sends welcome email" do
111
+ # test
112
+ end
113
+ end
114
+
115
+ context "with invalid email" do
116
+ it "raises ValidationError" do
117
+ # test
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ # Shared examples
124
+ RSpec.shared_examples "a persisted entity" do
125
+ it "has an id" do
126
+ expect(entity.id).not_to be_nil
127
+ end
128
+
129
+ it "has timestamps" do
130
+ expect(entity.created_at).not_to be_nil
131
+ end
132
+ end
133
+
134
+ RSpec.describe User do
135
+ let(:entity) { User.create(email: "test@example.com") }
136
+
137
+ it_behaves_like "a persisted entity"
138
+ end
139
+ ```
140
+
141
+ ## Mocking and Stubbing
142
+
143
+ ```ruby
144
+ RSpec.describe UserService do
145
+ let(:repository) { instance_double(UserRepository) }
146
+ let(:notifier) { instance_double(EmailNotifier) }
147
+ let(:service) { described_class.new(repository: repository, notifier: notifier) }
148
+
149
+ describe "#create" do
150
+ it "saves user and sends notification" do
151
+ user = User.new(email: "test@example.com")
152
+
153
+ # Stubbing
154
+ allow(repository).to receive(:save).and_return(user)
155
+ allow(notifier).to receive(:welcome)
156
+
157
+ result = service.create(email: "test@example.com")
158
+
159
+ # Verification
160
+ expect(repository).to have_received(:save).with(an_instance_of(User))
161
+ expect(notifier).to have_received(:welcome).with(user)
162
+ end
163
+
164
+ it "raises when repository fails" do
165
+ allow(repository).to receive(:save).and_raise(DatabaseError)
166
+
167
+ expect { service.create(email: "test@example.com") }
168
+ .to raise_error(DatabaseError)
169
+ end
170
+ end
171
+ end
172
+
173
+ # Partial doubles (real objects with stubbed methods)
174
+ RSpec.describe User do
175
+ it "can stub specific methods" do
176
+ user = User.new(email: "test@example.com")
177
+ allow(user).to receive(:premium?).and_return(true)
178
+
179
+ expect(user.premium?).to be true
180
+ end
181
+ end
182
+ ```
183
+
184
+ ## Before/After Hooks
185
+
186
+ ```ruby
187
+ RSpec.describe UserService do
188
+ before(:all) do
189
+ # Run once before all examples
190
+ DatabaseCleaner.strategy = :transaction
191
+ end
192
+
193
+ before(:each) do
194
+ # Run before each example
195
+ DatabaseCleaner.start
196
+ end
197
+
198
+ after(:each) do
199
+ # Run after each example
200
+ DatabaseCleaner.clean
201
+ end
202
+
203
+ around(:each) do |example|
204
+ # Wrap each example
205
+ Timecop.freeze(Time.local(2024)) do
206
+ example.run
207
+ end
208
+ end
209
+ end
210
+ ```
211
+
212
+ ## Testing Rails Controllers
213
+
214
+ ```ruby
215
+ RSpec.describe UsersController, type: :controller do
216
+ describe "POST #create" do
217
+ context "with valid params" do
218
+ it "creates a new user" do
219
+ expect {
220
+ post :create, params: { user: { email: "test@example.com" } }
221
+ }.to change(User, :count).by(1)
222
+ end
223
+
224
+ it "redirects to the user" do
225
+ post :create, params: { user: { email: "test@example.com" } }
226
+ expect(response).to redirect_to(User.last)
227
+ end
228
+ end
229
+ end
230
+ end
231
+ ```
232
+
233
+ ## Request Specs (API Testing)
234
+
235
+ ```ruby
236
+ RSpec.describe "Users API", type: :request do
237
+ describe "GET /api/users/:id" do
238
+ let(:user) { User.create(email: "test@example.com") }
239
+
240
+ it "returns the user" do
241
+ get "/api/users/#{user.id}"
242
+
243
+ expect(response).to have_http_status(:ok)
244
+ expect(JSON.parse(response.body)["email"]).to eq("test@example.com")
245
+ end
246
+ end
247
+ end
248
+ ```
249
+
250
+ ## Running Tests
251
+
252
+ ```bash
253
+ # Run all specs
254
+ bundle exec rspec
255
+
256
+ # Run specific file
257
+ bundle exec rspec spec/services/user_service_spec.rb
258
+
259
+ # Run specific example
260
+ bundle exec rspec spec/services/user_service_spec.rb:15
261
+
262
+ # Run with tag
263
+ bundle exec rspec --tag integration
264
+
265
+ # Run with format
266
+ bundle exec rspec --format documentation
267
+ ```