rider-kick 0.0.12 → 0.0.14

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +668 -27
  3. data/lib/generators/rider_kick/USAGE +2 -0
  4. data/lib/generators/rider_kick/base_generator.rb +190 -0
  5. data/lib/generators/rider_kick/clean_arch_generator.rb +235 -44
  6. data/lib/generators/rider_kick/clean_arch_generator_engine_spec.rb +359 -0
  7. data/lib/generators/rider_kick/clean_arch_generator_factory_bot_spec.rb +131 -0
  8. data/lib/generators/rider_kick/entity_type_mapping_spec.rb +61 -0
  9. data/lib/generators/rider_kick/errors.rb +42 -0
  10. data/lib/generators/rider_kick/factory_generator.rb +238 -0
  11. data/lib/generators/rider_kick/factory_generator_spec.rb +175 -0
  12. data/lib/generators/rider_kick/repositories_contract_spec.rb +135 -0
  13. data/lib/generators/rider_kick/scaffold_generator.rb +377 -62
  14. data/lib/generators/rider_kick/scaffold_generator_builder_uploaders_spec.rb +159 -0
  15. data/lib/generators/rider_kick/scaffold_generator_conditional_filtering_spec.rb +820 -0
  16. data/lib/generators/rider_kick/scaffold_generator_contracts_spec.rb +96 -0
  17. data/lib/generators/rider_kick/scaffold_generator_contracts_with_scope_spec.rb +83 -0
  18. data/lib/generators/rider_kick/scaffold_generator_engine_spec.rb +221 -0
  19. data/lib/generators/rider_kick/scaffold_generator_idempotent_spec.rb +84 -0
  20. data/lib/generators/rider_kick/scaffold_generator_list_spec_format_spec.rb +153 -0
  21. data/lib/generators/rider_kick/scaffold_generator_rspec_spec.rb +347 -0
  22. data/lib/generators/rider_kick/scaffold_generator_success_spec.rb +101 -0
  23. data/lib/generators/rider_kick/scaffold_generator_with_scope_spec.rb +76 -0
  24. data/lib/generators/rider_kick/structure_generator.rb +179 -35
  25. data/lib/generators/rider_kick/structure_generator_comprehensive_spec.rb +598 -0
  26. data/lib/generators/rider_kick/structure_generator_engine_spec.rb +279 -0
  27. data/lib/generators/rider_kick/structure_generator_spec.rb +20 -0
  28. data/lib/generators/rider_kick/structure_generator_success_spec.rb +64 -0
  29. data/lib/generators/rider_kick/structure_generator_unit_spec.rb +2202 -0
  30. data/lib/generators/rider_kick/templates/.rubocop.yml +5 -4
  31. data/lib/generators/rider_kick/templates/config/initializers/version.rb.tt +1 -1
  32. data/lib/generators/rider_kick/templates/db/migrate/20220613145533_init_database.rb +1 -1
  33. data/lib/generators/rider_kick/templates/db/structures/example.yaml.tt +157 -51
  34. data/lib/generators/rider_kick/templates/domains/core/builders/builder.rb.tt +36 -10
  35. data/lib/generators/rider_kick/templates/domains/core/builders/builder_spec.rb.tt +219 -0
  36. data/lib/generators/rider_kick/templates/domains/core/builders/error.rb.tt +2 -2
  37. data/lib/generators/rider_kick/templates/domains/core/builders/pagination.rb.tt +2 -2
  38. data/lib/generators/rider_kick/templates/domains/core/entities/entity.rb.tt +32 -14
  39. data/lib/generators/rider_kick/templates/domains/core/entities/error.rb.tt +1 -1
  40. data/lib/generators/rider_kick/templates/domains/core/entities/pagination.rb.tt +1 -1
  41. data/lib/generators/rider_kick/templates/domains/core/repositories/abstract_repository.rb.tt +4 -16
  42. data/lib/generators/rider_kick/templates/domains/core/repositories/create.rb.tt +2 -2
  43. data/lib/generators/rider_kick/templates/domains/core/repositories/create_spec.rb.tt +78 -0
  44. data/lib/generators/rider_kick/templates/domains/core/repositories/destroy.rb.tt +2 -2
  45. data/lib/generators/rider_kick/templates/domains/core/repositories/destroy_spec.rb.tt +88 -0
  46. data/lib/generators/rider_kick/templates/domains/core/repositories/fetch_by_id.rb.tt +3 -3
  47. data/lib/generators/rider_kick/templates/domains/core/repositories/fetch_by_id_spec.rb.tt +62 -0
  48. data/lib/generators/rider_kick/templates/domains/core/repositories/list.rb.tt +12 -7
  49. data/lib/generators/rider_kick/templates/domains/core/repositories/list_spec.rb.tt +190 -0
  50. data/lib/generators/rider_kick/templates/domains/core/repositories/update.rb.tt +4 -4
  51. data/lib/generators/rider_kick/templates/domains/core/repositories/update_spec.rb.tt +119 -0
  52. data/lib/generators/rider_kick/templates/domains/core/use_cases/contract/default.rb.tt +1 -1
  53. data/lib/generators/rider_kick/templates/domains/core/use_cases/contract/pagination.rb.tt +1 -1
  54. data/lib/generators/rider_kick/templates/domains/core/use_cases/create.rb.tt +3 -7
  55. data/lib/generators/rider_kick/templates/domains/core/use_cases/create_spec.rb.tt +71 -0
  56. data/lib/generators/rider_kick/templates/domains/core/use_cases/destroy.rb.tt +3 -7
  57. data/lib/generators/rider_kick/templates/domains/core/use_cases/destroy_spec.rb.tt +62 -0
  58. data/lib/generators/rider_kick/templates/domains/core/use_cases/fetch_by_id.rb.tt +3 -7
  59. data/lib/generators/rider_kick/templates/domains/core/use_cases/fetch_by_id_spec.rb.tt +62 -0
  60. data/lib/generators/rider_kick/templates/domains/core/use_cases/get_version.rb.tt +2 -2
  61. data/lib/generators/rider_kick/templates/domains/core/use_cases/list.rb.tt +3 -7
  62. data/lib/generators/rider_kick/templates/domains/core/use_cases/list_spec.rb.tt +64 -0
  63. data/lib/generators/rider_kick/templates/domains/core/use_cases/update.rb.tt +3 -7
  64. data/lib/generators/rider_kick/templates/domains/core/use_cases/update_spec.rb.tt +73 -0
  65. data/lib/generators/rider_kick/templates/domains/core/utils/abstract_utils.rb.tt +29 -0
  66. data/lib/generators/rider_kick/templates/domains/core/utils/request_methods.rb.tt +3 -2
  67. data/lib/generators/rider_kick/templates/env.development +1 -1
  68. data/lib/generators/rider_kick/templates/env.production +1 -1
  69. data/lib/generators/rider_kick/templates/env.test +1 -1
  70. data/lib/generators/rider_kick/templates/models/{application_record.rb → application_record.rb.tt} +3 -1
  71. data/lib/generators/rider_kick/templates/models/model_spec.rb.tt +68 -0
  72. data/lib/generators/rider_kick/templates/spec/factories/.gitkeep +19 -0
  73. data/lib/generators/rider_kick/templates/spec/factories/factory.rb.tt +8 -0
  74. data/lib/generators/rider_kick/templates/spec/rails_helper.rb +2 -0
  75. data/lib/generators/rider_kick/templates/spec/support/class_stubber.rb +148 -0
  76. data/lib/generators/rider_kick/templates/spec/support/factory_bot.rb +34 -0
  77. data/lib/generators/rider_kick/templates/spec/support/faker.rb +61 -0
  78. data/lib/rider-kick.rb +9 -6
  79. data/lib/rider_kick/builders/abstract_active_record_entity_builder_spec.rb +596 -68
  80. data/lib/rider_kick/configuration.rb +238 -0
  81. data/lib/rider_kick/configuration_engine_spec.rb +377 -0
  82. data/lib/rider_kick/entities/failure_details.rb +23 -15
  83. data/lib/rider_kick/entities/failure_details_spec.rb +22 -0
  84. data/lib/rider_kick/matchers/use_case_result.rb +1 -1
  85. data/lib/rider_kick/matchers/use_case_result_edge_spec.rb +28 -0
  86. data/lib/rider_kick/use_cases/abstract_use_case.rb +1 -1
  87. data/lib/rider_kick/use_cases/abstract_use_case_spec.rb +57 -0
  88. data/lib/rider_kick/version.rb +1 -1
  89. metadata +345 -52
  90. data/.rspec +0 -3
  91. data/.rubocop.yml +0 -1141
  92. data/CHANGELOG.md +0 -5
  93. data/Rakefile +0 -12
  94. data/lib/rider_kick/matchers/use_case_result_spec.rb +0 -64
data/README.md CHANGED
@@ -1,15 +1,41 @@
1
1
  # RiderKick
2
+ Rails generators for **Clean Architecture** on the backend: use cases, entities, repositories, builders, and utilities — organized for clarity and designed for speed.
3
+
4
+ > **🎉 NEW!** Domain scoping dengan `--domain` option! Organize domain files ke dalam scope yang berbeda (core/, admin/, api/v1/, dll.)
5
+ >
6
+ > **🎉 NEW!** Sekarang dengan automatic RSpec generation! Setiap file yang di-generate otomatis mendapat spec file-nya. [Lihat dokumentasi lengkap](SPEC_GENERATION.md)
7
+ >
8
+ > **🎉 NEW!** FactoryBot factory generator dengan smart Faker generation! [Lihat dokumentasi lengkap](FACTORY_GENERATOR.md)
9
+
2
10
  This gem provides helper interfaces and classes to assist in the construction of application with
3
11
  Clean Architecture, as described in [Robert Martin's seminal book](https://www.amazon.com/gp/product/0134494164).
12
+ ---
13
+ ## Features
4
14
 
15
+ - **Clean Architecture scaffolding**
16
+ Creates `app/domains/<domain>/` with `entities/`, `use_cases/`, `repositories/`, `builders/`, and `utils/`.
17
+ - **Domain scoping dengan --domain option** 🆕
18
+ Organize domain files ke dalam scope yang berbeda: `--domain core/`, `--domain admin/`, `--domain api/v1/`, dll.
19
+ - **Engine support dengan --engine option**
20
+ Generate domain files dalam Rails engines: `--engine MyEngine --domain admin/`
21
+ - **Use-case-first "screaming architecture"**
22
+ Encourages file names like `[role]_[action]_[subject].rb` for immediate intent (e.g., `admin_update_stock.rb`).
23
+ - **Rails-native generators**
24
+ Pragmatic commands for bootstrapping domain structure and scaffolding.
25
+ - **Automatic RSpec generation** 🆕
26
+ Generates comprehensive RSpec files for all generated code (use cases, repositories, builders, entities).
27
+ - **FactoryBot factory generator** 🆕
28
+ Generates smart FactoryBot factories with automatic Faker values and foreign key skipping.
29
+ - **Idempotent, minimal friction**
30
+ Safe to run more than once; prefers appending or no-ops over destructive changes.
31
+ ---
32
+ ## ✅ Compatibility
5
33
 
6
- ## Installation
7
-
8
- Add this line to your application's Gemfile:
34
+ - **Ruby:** ≥ 3.2
35
+ - **Rails:** 7.1, 7.2, 8.0 (up to < 8.1)
9
36
 
10
- ```ruby
11
- gem 'rider-kick'
12
- ```
37
+ ---
38
+ ## Installation
13
39
 
14
40
  And then execute:
15
41
  ```bash
@@ -18,34 +44,593 @@ And then execute:
18
44
  $ bundle add rider-kick
19
45
  $ rails generate rider_kick:clean_arch --setup
20
46
  $ rails db:drop db:create db:migrate db:seed
21
- $ rails g model models/products name price:decimal is_published:boolean
47
+ $ rails g model models/products name price:decimal is_published:boolean
22
48
  $ rails generate rider_kick:structure Models::Product actor:owner
23
- $ rails generate rider_kick:scaffold products scope:dashboard
49
+ $ rails generate rider_kick:scaffold products scope:dashboard
50
+ ```
51
+
52
+ ### Quick Examples with Domain Scoping
53
+
54
+ ```bash
55
+ # Setup dengan domain default (core/)
56
+ $ rails generate rider_kick:clean_arch --setup
57
+
58
+ # Setup untuk admin domain
59
+ $ rails generate rider_kick:clean_arch --setup --domain admin/
60
+
61
+ # Setup untuk API v1 domain
62
+ $ rails generate rider_kick:clean_arch --setup --domain api/v1/
63
+
64
+ # Setup dalam Rails engine
65
+ $ rails generate rider_kick:clean_arch --setup --engine MyEngine --domain mobile/
24
66
  ```
25
67
  ### OPTIONAL
26
68
  ```bash
27
69
  $ bundle add sun-sword
28
70
  ```
71
+ ---
29
72
  ## Usage
73
+
74
+ ### Initial Setup (Required Once)
75
+
76
+ #### Basic Setup (Default Domain)
77
+ ```bash
78
+ # 1. Create new Rails app
79
+ rails new kotaro_minami -d=postgresql -T --skip-javascript --skip-asset-pipeline
80
+
81
+ # 2. Add rider-kick gem
82
+ bundle add rider-kick
83
+
84
+ # 3. Setup Clean Architecture structure (includes RSpec setup & helpers)
85
+ bin/rails generate rider_kick:clean_arch --setup
86
+ ```
87
+
88
+ #### Advanced Setup with Domain Scoping
89
+
90
+ ```bash
91
+ # Setup untuk domain tertentu
92
+ bin/rails generate rider_kick:clean_arch --setup --domain admin/
93
+
94
+ # Setup untuk API domain
95
+ bin/rails generate rider_kick:clean_arch --setup --domain api/v1/
96
+
97
+ # Setup dalam Rails engine
98
+ bin/rails generate rider_kick:clean_arch --setup --engine MyEngine --domain core/
99
+
100
+ # Setup engine dengan domain khusus
101
+ bin/rails generate rider_kick:clean_arch --setup --engine AdminEngine --domain admin/
102
+ ```
103
+
104
+ ### Domain Scoping Explanation
105
+
106
+ **--domain option** memungkinkan Anda mengorganisir domain files ke dalam scope yang berbeda:
107
+
108
+ - **Default**: `--domain core/` → `app/domains/core/`
109
+ - **Admin domain**: `--domain admin/` → `app/domains/admin/`
110
+ - **API domain**: `--domain api/v1/` → `app/domains/api/v1/`
111
+ - **Engine**: `--engine MyEngine --domain mobile/` → `engines/my_engine/app/domains/mobile/`
112
+
113
+ ### Setup Output
114
+
115
+ This setup will create:
116
+ - Domain structure (`app/domains/<domain>/` or `engines/<engine>/app/domains/<domain>/`)
117
+ - RSpec configuration with helpers (`spec/support/class_stubber.rb`, etc.)
118
+ - Database configuration
119
+ - Initializers
120
+
121
+ #### Generated Structure Examples
122
+
123
+ **Main App dengan domain default:**
124
+ ```
125
+ app/
126
+ domains/
127
+ core/ # --domain core/ (default)
128
+ entities/
129
+ builders/
130
+ repositories/
131
+ use_cases/
132
+ utils/
133
+ ```
134
+
135
+ **Main App dengan multiple domains:**
136
+ ```
137
+ app/
138
+ domains/
139
+ core/ # --domain core/
140
+ admin/ # --domain admin/
141
+ api/
142
+ v1/ # --domain api/v1/
143
+ ```
144
+
145
+ **Rails Engine:**
146
+ ```
147
+ engines/
148
+ my_engine/
149
+ app/
150
+ domains/
151
+ core/ # --engine MyEngine --domain core/
152
+ mobile/ # --engine MyEngine --domain mobile/
153
+ ```
154
+
155
+ ### Generate Structure
156
+
157
+ Generator untuk membuat file struktur YAML dari model yang sudah ada. File YAML ini berisi konfigurasi yang akan digunakan oleh generator `scaffold`.
158
+
159
+ ```bash
160
+ rails generate rider_kick:structure MODEL_NAME [SETTINGS] [OPTIONS]
161
+ ```
162
+
163
+ **Required Arguments:**
164
+ - `MODEL_NAME` - Nama model class (e.g., `Models::User`, `Models::Article`)
165
+ - `actor` - Actor/role yang akan menggunakan use case (e.g., `actor:user`, `actor:admin`)
166
+ - `resource_owner` - Nama resource owner untuk authorization (e.g., `resource_owner:account`)
167
+ - `resource_owner_id` - Nama kolom resource owner ID (e.g., `resource_owner_id:account_id`)
168
+
169
+ **Optional Settings:**
170
+ - `uploaders` - Daftar kolom uploader dipisah koma (e.g., `uploaders:avatar,images`)
171
+ - `search_able` - Daftar kolom yang bisa di-search dipisah koma (e.g., `search_able:name,email`)
172
+
173
+ **Options:**
174
+ - `--engine ENGINE_NAME` - Specify engine name (e.g., `Core`, `Admin`)
175
+ - `--domain DOMAIN` - Specify domain scope (e.g., `core/`, `admin/`, `api/v1/`)
176
+
177
+ **Examples:**
178
+ ```bash
179
+ # Basic structure dengan domain default
180
+ rails generate rider_kick:structure Models::User actor:owner resource_owner:account resource_owner_id:account_id
181
+
182
+ # Dengan uploaders dan searchable fields
183
+ rails generate rider_kick:structure Models::Product \
184
+ actor:admin \
185
+ resource_owner:account \
186
+ resource_owner_id:account_id \
187
+ uploaders:image,documents \
188
+ search_able:name,sku \
189
+ --domain admin/
190
+
191
+ # Dalam Rails engine
192
+ rails generate rider_kick:structure Models::Order \
193
+ actor:user \
194
+ resource_owner:account \
195
+ resource_owner_id:account_id \
196
+ --engine OrderEngine \
197
+ --domain fulfillment/
198
+ ```
199
+
200
+ **Output:** File YAML di `db/structures/<model_name>_structure.yaml` yang berisi konfigurasi lengkap untuk scaffold generator.
201
+
202
+ ### Generate Scaffold
203
+
204
+ Generator utama untuk generate use cases, repositories, entities, builders, dan spec files berdasarkan structure YAML yang sudah dibuat.
205
+
206
+ ```bash
207
+ rails generate rider_kick:scaffold STRUCTURE_NAME [SCOPE] [OPTIONS]
208
+ ```
209
+
210
+ **Required Arguments:**
211
+ - `STRUCTURE_NAME` - Nama structure (plural, tanpa `_structure.yaml`). Contoh: `users`, `products`, `orders`
212
+
213
+ **Optional Arguments:**
214
+ - `scope:SCOPE_NAME` - Route scope (e.g., `scope:dashboard`, `scope:admin`)
215
+
216
+ **Options:**
217
+ - `--engine ENGINE_NAME` - Specify engine name (e.g., `Core`, `Admin`)
218
+ - `--domain DOMAIN` - Specify domain scope (e.g., `core/`, `admin/`, `api/v1/`)
219
+
220
+ **Examples:**
221
+ ```bash
222
+ # Basic scaffold dengan domain default
223
+ rails generate rider_kick:scaffold users scope:dashboard
224
+
225
+ # Dengan domain khusus
226
+ rails generate rider_kick:scaffold users scope:admin --domain admin/
227
+
228
+ # Dalam Rails engine
229
+ rails generate rider_kick:scaffold orders --engine OrderEngine --domain fulfillment/
230
+
231
+ # API domain
232
+ rails generate rider_kick:scaffold products --domain api/v1/
233
+ ```
234
+
235
+ **Output:**
236
+ - Use cases: `app/domains/<domain>/use_cases/<scope>/<resource>/`
237
+ - Repositories: `app/domains/<domain>/repositories/<resource>/`
238
+ - Entities: `app/domains/<domain>/entities/`
239
+ - Builders: `app/domains/<domain>/builders/`
240
+ - Spec files untuk semua generated code
241
+
242
+ ### Generate Factory
243
+
244
+ Generator untuk membuat FactoryBot factory files dengan smart Faker generation. Otomatis skip foreign key columns dan generate Faker values berdasarkan tipe kolom.
245
+
246
+ ```bash
247
+ rails generate rider_kick:factory MODEL_NAME [SCOPE] [OPTIONS]
248
+ ```
249
+
250
+ **Required Arguments:**
251
+ - `MODEL_NAME` - Nama model class (e.g., `Models::Article`, `Models::User`)
252
+
253
+ **Optional Arguments:**
254
+ - `scope:SCOPE_NAME` - Scope untuk factory (e.g., `scope:core`)
255
+
256
+ **Options:**
257
+ - `--engine ENGINE_NAME` - Specify engine name (e.g., `Core`, `Admin`)
258
+ - `--static` - Generate static values instead of Faker calls (time fields tetap menggunakan `Time.zone.now`)
259
+
260
+ **Examples:**
261
+ ```bash
262
+ # Factory dengan Faker (default)
263
+ rails generate rider_kick:factory Models::Article scope:core
264
+
265
+ # Factory dengan static values
266
+ rails generate rider_kick:factory Models::Article scope:core --static
267
+
268
+ # Dalam Rails engine
269
+ rails generate rider_kick:factory Models::Order scope:fulfillment --engine OrderEngine
270
+ ```
271
+
272
+ **Smart Faker Mapping:**
273
+ Generator menggunakan smart mapping berdasarkan nama kolom dan tipe:
274
+ - `string` dengan `email` → `Faker::Internet.email`
275
+ - `string` dengan `name` → `Faker::Name.name`
276
+ - `text` dengan `description`/`content` → `Faker::Lorem.paragraph`
277
+ - `integer` dengan `price`/`amount` → `Faker::Number.between(from: 1000, to: 1000000)`
278
+ - `decimal` dengan `price` → `Faker::Commerce.price`
279
+ - `boolean` → `[true, false].sample`
280
+ - `datetime`/`timestamp`/`time` → `Time.zone.now` (selalu)
281
+ - Dan banyak lagi...
282
+
283
+ **Kolom yang Di-skip:**
284
+ - `id`, `created_at`, `updated_at`, `type`
285
+ - Semua foreign key columns (`*_id`)
286
+
287
+ 📖 **[Complete Factory Generator Documentation →](FACTORY_GENERATOR.md)**
288
+ 📖 **[Domain Scoping Guide →](DOMAIN_SCOPING.md)**
289
+ ---
290
+ ## Generated Structure
291
+
292
+ ### Default Structure (Main App)
293
+ ```text
294
+ app/
295
+ domains/
296
+ core/ # Default domain (--domain core/)
297
+ entities/
298
+ builders/
299
+ repositories/
300
+ use_cases/
301
+ utils/
302
+ ```
303
+
304
+ ### Multiple Domains Structure
305
+ ```text
306
+ app/
307
+ domains/
308
+ core/ # Main domain (--domain core/)
309
+ entities/
310
+ builders/
311
+ repositories/
312
+ use_cases/
313
+ utils/
314
+ admin/ # Admin domain (--domain admin/)
315
+ entities/
316
+ builders/
317
+ repositories/
318
+ use_cases/
319
+ utils/
320
+ api/
321
+ v1/ # API domain (--domain api/v1/)
322
+ entities/
323
+ builders/
324
+ repositories/
325
+ use_cases/
326
+ utils/
327
+ ```
328
+
329
+ ### Rails Engine Structure
330
+ ```text
331
+ engines/
332
+ my_engine/
333
+ app/
334
+ domains/
335
+ core/ # Engine domain (--engine MyEngine --domain core/)
336
+ entities/
337
+ builders/
338
+ repositories/
339
+ use_cases/
340
+ utils/
341
+ mobile/ # Mobile domain (--engine MyEngine --domain mobile/)
342
+ entities/
343
+ builders/
344
+ repositories/
345
+ use_cases/
346
+ utils/
347
+ ```
348
+
349
+ ---
350
+
351
+ ## Complete Generator Documentation
352
+
353
+ ### Generator Overview
354
+
355
+ Gem ini menyediakan **4 generator utama**:
356
+
357
+ 1. **`rider_kick:clean_arch`** - Setup Clean Architecture structure
358
+ 2. **`rider_kick:structure`** - Generate structure YAML file dari model
359
+ 3. **`rider_kick:scaffold`** - Generate use cases, repositories, entities, builders
360
+ 4. **`rider_kick:factory`** - Generate FactoryBot factory files
361
+
362
+ ### 1. Generator: `rider_kick:clean_arch`
363
+
364
+ **Deskripsi:**
365
+ Generator untuk setup awal struktur Clean Architecture. Generator ini harus dijalankan pertama kali sebelum menggunakan generator lainnya.
366
+
367
+ **Command:**
30
368
  ```bash
31
- Description:
32
- Clean Architecture generator
33
- rails new kotaro_minami -d=postgresql -T --skip-javascript --skip-asset-pipeline
34
-
35
- Example:
36
- To Generate scaffold:
37
- bin/rails generate rider_kick:clean_arch --setup
38
- bin/rails generate rider_kick:structure Models::User actor:owner
39
- bin/rails generate rider_kick:scaffold users scope:dashboard
369
+ rails generate rider_kick:clean_arch [OPTIONS]
370
+ ```
371
+
372
+ **Options:**
373
+
374
+ | Option | Type | Default | Deskripsi |
375
+ |--------|------|---------|-----------|
376
+ | `--setup` | boolean | `false` | **WAJIB** - Setup domain structure. Harus dispecify untuk membuat struktur domain. |
377
+ | `--engine` | string | `nil` | Specify engine name (e.g., `Core`, `Admin`). Jika dispecify, `--setup` otomatis dianggap true. |
378
+ | `--domain` | string | `''` | Specify domain scope (e.g., `core/`, `admin/`, `api/v1/`). Default: `core/` |
379
+
380
+ **Yang Dihasilkan:**
381
+
382
+ 1. **Domain Structure:**
383
+ - `app/domains/<domain>/use_cases/` (dengan subfolder `contract/`)
384
+ - `app/domains/<domain>/repositories/`
385
+ - `app/domains/<domain>/builders/`
386
+ - `app/domains/<domain>/entities/`
387
+ - `app/domains/<domain>/utils/`
388
+
389
+ 2. **Base Files:**
390
+ - Contract files: `pagination.rb`, `default.rb`
391
+ - Use case: `get_version.rb`
392
+ - Builders: `error.rb`, `pagination.rb`
393
+ - Entities: `error.rb`, `pagination.rb`
394
+ - Repository: `abstract_repository.rb`
395
+ - Utils: `abstract_utils.rb`, `request_methods.rb`
40
396
 
397
+ 3. **Configuration Files (Main App Only):**
398
+ - Initializers: `clean_archithecture.rb`, `generators.rb`, `hashie.rb`, `version.rb`, `zeitwerk.rb`, `pagy.rb`, `route_extensions.rb`
399
+ - Database config: `config/database.yml`
400
+ - Environment files: `.env.development`, `.env.production`, `.env.test`, `env.example`
401
+ - Git ignore: `.gitignore`
402
+ - Rubocop: `.rubocop.yml`
403
+ - README: `README.md`
404
+
405
+ 4. **RSpec Setup (Main App Only):**
406
+ - RSpec configuration
407
+ - Support files: `class_stubber.rb`, `file_stuber.rb`, `repository_stubber.rb`
408
+ - FactoryBot & Faker setup
409
+ - `spec/rails_helper.rb`
410
+
411
+ 5. **Database:**
412
+ - Migration: `db/migrate/20220613145533_init_database.rb`
413
+ - Structures directory: `db/structures/`
414
+
415
+ 6. **Models:**
416
+ - `app/models/application_record.rb` (main app)
417
+ - `app/models/models/models.rb`
418
+
419
+ 7. **Gem Dependencies (ditambahkan ke Gemfile):**
420
+ - `rspec-rails`, `factory_bot_rails`, `faker`, `shoulda-matchers`
421
+ - `dotenv-rails`
422
+ - `hashie`
423
+ - `image_processing`, `ruby-vips`
424
+ - `pagy`
425
+
426
+ **Catatan Penting:**
427
+ - Option `--setup` **WAJIB** dispecify untuk membuat struktur domain
428
+ - Jika `--engine` dispecify, `--setup` otomatis dianggap true
429
+ - Untuk engine, beberapa setup (seperti initializers) tidak dilakukan karena dilakukan di main app
430
+
431
+ ### 2. Generator: `rider_kick:structure`
432
+
433
+ **Deskripsi:**
434
+ Generator untuk membuat file struktur YAML dari model yang sudah ada. File YAML ini berisi konfigurasi yang akan digunakan oleh generator `scaffold` untuk generate use cases, repositories, entities, dan builders.
435
+
436
+ **Command:**
437
+ ```bash
438
+ rails generate rider_kick:structure MODEL_NAME [SETTINGS] [OPTIONS]
41
439
  ```
42
440
 
441
+ **Required Settings:**
442
+
443
+ | Setting | Deskripsi | Contoh |
444
+ |---------|-----------|--------|
445
+ | `actor` | Actor/role yang akan menggunakan use case | `actor:user`, `actor:admin`, `actor:owner` |
446
+ | `resource_owner` | Nama resource owner (untuk authorization) | `resource_owner:account` |
447
+ | `resource_owner_id` | Nama kolom resource owner ID | `resource_owner_id:account_id` |
448
+
449
+ **Optional Settings:**
450
+
451
+ | Setting | Deskripsi | Contoh |
452
+ |---------|-----------|--------|
453
+ | `uploaders` | Daftar kolom uploader (dipisah koma). Otomatis detect single/multiple berdasarkan singular/plural | `uploaders:avatar,images,picture` |
454
+ | `search_able` | Daftar kolom yang bisa di-search (dipisah koma) | `search_able:name,email,title` |
455
+
456
+ **Options:**
457
+
458
+ | Option | Type | Default | Deskripsi |
459
+ |--------|------|---------|-----------|
460
+ | `--engine` | string | `nil` | Specify engine name (e.g., `Core`, `Admin`) |
461
+ | `--domain` | string | `''` | Specify domain scope (e.g., `core/`, `admin/`, `api/v1/`) |
462
+
463
+ **Format YAML yang Dihasilkan:**
464
+
465
+ ```yaml
466
+ model: Models::User
467
+ resource_name: users
468
+ actor: owner
469
+ resource_owner: account
470
+ resource_owner_id: account_id
471
+ uploaders:
472
+ - name: avatar
473
+ type: single
474
+ - name: images
475
+ type: multiple
476
+ search_able: []
477
+ domains:
478
+ action_list:
479
+ use_case:
480
+ contract: [...]
481
+ action_create:
482
+ use_case:
483
+ contract: [...]
484
+ action_update:
485
+ use_case:
486
+ contract: [...]
487
+ action_fetch_by_id:
488
+ use_case:
489
+ contract: [...]
490
+ action_destroy:
491
+ use_case:
492
+ contract: [...]
493
+ entity:
494
+ db_attributes: [...]
495
+ ```
496
+
497
+ **Catatan Penting:**
498
+ - Model harus sudah ada sebelum menjalankan generator ini
499
+ - Generator ini membaca kolom dari model untuk generate contract dan entity attributes
500
+ - Kolom `id`, `created_at`, `updated_at`, `type` otomatis di-exclude dari contract fields
501
+ - Uploader type (single/multiple) otomatis di-detect berdasarkan singular/plural name
502
+
503
+ ### 3. Generator: `rider_kick:scaffold`
504
+
505
+ **Deskripsi:**
506
+ Generator utama untuk generate use cases, repositories, entities, builders, dan spec files berdasarkan structure YAML yang sudah dibuat oleh generator `structure`.
507
+
508
+ **Command:**
509
+ ```bash
510
+ rails generate rider_kick:scaffold STRUCTURE_NAME [SCOPE] [OPTIONS]
511
+ ```
512
+
513
+ **Yang Dihasilkan:**
514
+
515
+ 1. **Use Cases** (di `app/domains/<domain>/use_cases/<scope>/<resource>/`):
516
+ - `{actor}_create_{resource}.rb` - Create use case
517
+ - `{actor}_update_{resource}.rb` - Update use case
518
+ - `{actor}_list_{resource}.rb` - List use case
519
+ - `{actor}_fetch_by_id_{resource}.rb` - Fetch by ID use case
520
+ - `{actor}_destroy_{resource}.rb` - Destroy use case
521
+ - Spec files untuk setiap use case
522
+
523
+ 2. **Repositories** (di `app/domains/<domain>/repositories/<resource>/`):
524
+ - `create_{resource}.rb` - Create repository
525
+ - `update_{resource}.rb` - Update repository
526
+ - `list_{resource}.rb` - List repository
527
+ - `fetch_by_id_{resource}.rb` - Fetch by ID repository
528
+ - `destroy_{resource}.rb` - Destroy repository
529
+ - Spec files untuk setiap repository
530
+
531
+ 3. **Entities** (di `app/domains/<domain>/entities/`):
532
+ - `{resource}.rb` - Entity class dengan attributes dari model
533
+
534
+ 4. **Builders** (di `app/domains/<domain>/builders/`):
535
+ - `{resource}.rb` - Builder class untuk convert ActiveRecord ke Entity
536
+ - Spec file untuk builder
537
+
538
+ 5. **Model Spec** (di `app/models/models/` atau `app/models/<engine>/`):
539
+ - `{resource}_spec.rb` - Model spec file
540
+
541
+ 6. **Model Attachment** (auto-inject ke model file):
542
+ - `has_one_attached` atau `has_many_attached` untuk uploaders
543
+ - Hanya jika model file sudah ada
544
+
545
+ **Catatan Penting:**
546
+ - Structure YAML file harus sudah ada (dibuat dengan generator `structure`)
547
+ - Generator ini membaca konfigurasi dari structure YAML file
548
+ - Validasi dilakukan untuk memastikan filter fields dan entity fields ada di model
549
+ - Uploader attachments otomatis di-inject ke model file jika file sudah ada
550
+ - Semua file yang di-generate otomatis mendapat spec file
551
+
552
+ ### 4. Generator: `rider_kick:factory`
553
+
554
+ **Deskripsi:**
555
+ Generator untuk membuat FactoryBot factory files dengan smart Faker generation. Otomatis skip foreign key columns dan generate Faker values berdasarkan tipe kolom.
556
+
557
+ **Command:**
558
+ ```bash
559
+ rails generate rider_kick:factory MODEL_NAME [SCOPE] [OPTIONS]
560
+ ```
561
+
562
+ **Smart Faker Mapping:**
563
+
564
+ Generator ini menggunakan smart mapping berdasarkan nama kolom dan tipe:
565
+
566
+ | Tipe Kolom | Nama Kolom Contains | Faker Expression |
567
+ |------------|---------------------|-------------------|
568
+ | `string` | `email` | `Faker::Internet.email` |
569
+ | `string` | `name` | `Faker::Name.name` |
570
+ | `string` | `phone` | `Faker::PhoneNumber.phone_number` |
571
+ | `string` | `address` | `Faker::Address.full_address` |
572
+ | `string` | `title` | `Faker::Lorem.sentence(word_count: 3)` |
573
+ | `string` | `code` | `Faker::Alphanumeric.alphanumeric(number: 10)` |
574
+ | `string` | (default) | `Faker::Lorem.word` |
575
+ | `text` | `description`, `content`, `body` | `Faker::Lorem.paragraph(sentence_count: 3)` |
576
+ | `text` | (default) | `Faker::Lorem.sentence` |
577
+ | `integer` | `count`, `quantity` | `Faker::Number.between(from: 1, to: 100)` |
578
+ | `integer` | `age` | `Faker::Number.between(from: 18, to: 80)` |
579
+ | `integer` | `price`, `amount` | `Faker::Number.between(from: 1000, to: 1000000)` |
580
+ | `integer` | (default) | `Faker::Number.number(digits: 5)` |
581
+ | `decimal` | `price`, `amount` | `Faker::Commerce.price` |
582
+ | `decimal` | (default) | `Faker::Number.decimal(l_digits: 4, r_digits: 2)` |
583
+ | `boolean` | - | `[true, false].sample` |
584
+ | `date` | - | `Faker::Date.between(from: 1.year.ago, to: Date.today)` |
585
+ | `datetime`, `timestamp`, `time` | - | `Time.zone.now` (selalu, bahkan dengan `--static`) |
586
+ | `uuid` | - | `SecureRandom.uuid` |
587
+ | `json`, `jsonb` | - | `{ key: Faker::Lorem.word, value: Faker::Lorem.sentence }` |
588
+
589
+ **Kolom yang Di-skip:**
590
+ - `id` - Primary key
591
+ - `created_at` - Timestamp
592
+ - `updated_at` - Timestamp
593
+ - `type` - STI type
594
+ - `*_id` - Semua foreign key columns (ending with `_id`)
595
+
596
+ **Catatan Penting:**
597
+ - Model harus sudah ada sebelum menjalankan generator ini
598
+ - Foreign key columns otomatis di-skip
599
+ - Time-based columns selalu menggunakan `Time.zone.now`, bahkan dengan `--static`
600
+ - Dengan `--static`, Faker expressions akan di-evaluate dan hasilnya dijadikan static values
601
+
602
+ ### Complete Workflow Example
603
+
604
+ ```bash
605
+ # 1. Setup Clean Architecture (sekali di awal)
606
+ rails generate rider_kick:clean_arch --setup --domain core/
607
+
608
+ # 2. Buat model
609
+ rails g model models/products name:string price:decimal is_published:boolean
610
+
611
+ # 3. Generate structure YAML
612
+ rails generate rider_kick:structure Models::Product \
613
+ actor:admin \
614
+ resource_owner:account \
615
+ resource_owner_id:account_id \
616
+ uploaders:image \
617
+ search_able:name,description \
618
+ --domain core/
619
+
620
+ # 4. Generate scaffold (use cases, repositories, entities, builders)
621
+ rails generate rider_kick:scaffold products scope:dashboard --domain core/
622
+
623
+ # 5. Generate factory untuk testing
624
+ rails generate rider_kick:factory Models::Product scope:core
625
+ ```
626
+
627
+ ---
43
628
  ## Philosophy
44
629
 
45
630
  The intention of this gem is to help you build applications that are built from the use case down,
46
631
  and decisions about I/O can be deferred until the last possible moment.
47
632
 
48
- ## Clean Architecture
633
+ ### Clean Architecture
49
634
  This structure provides helper interfaces and classes to assist in the construction of application with Clean Architecture, as described in Robert Martin's seminal book.
50
635
 
51
636
  ```
@@ -53,16 +638,38 @@ This structure provides helper interfaces and classes to assist in the construct
53
638
  - models
54
639
  - models
55
640
  - ...
56
- - domains
57
- - core
58
- ...
59
- - entities (Contract Response)
60
- - builder
61
- - repositories (Business logic)
62
- - use_cases (Just Usecase)
63
- - utils (Class Reusable)
641
+ - domains
642
+ - core # Default domain (--domain core/)
643
+ - entities (Contract Response)
644
+ - builders
645
+ - repositories (Business logic)
646
+ - use_cases (Just Usecase)
647
+ - utils (Class Reusable)
648
+ - admin # Admin domain (--domain admin/)
649
+ - entities
650
+ - builders
651
+ - repositories
652
+ - use_cases
653
+ - utils
654
+ - api/v1 # API domain (--domain api/v1/)
655
+ - entities
656
+ - builders
657
+ - repositories
658
+ - use_cases
659
+ - utils
64
660
  ```
65
- ## Screaming architecture - use cases as an organisational principle
661
+
662
+ ### Domain Scoping untuk Large Applications
663
+ Untuk aplikasi yang besar, Anda dapat menggunakan `--domain` option untuk mengorganisir domain files berdasarkan konteks bisnis:
664
+
665
+ - **`core/`**: Domain utama aplikasi (default)
666
+ - **`admin/`**: Domain untuk fitur admin/pengelolaan
667
+ - **`api/v1/`**: Domain untuk API versioning
668
+ - **`mobile/`**: Domain untuk mobile-specific logic
669
+ - **`reporting/`**: Domain untuk laporan dan analytics
670
+
671
+ Ini membantu menjaga kode tetap terorganisir dan memudahkan maintenance seiring pertumbuhan aplikasi.
672
+ ### Screaming architecture - use cases as an organisational principle
66
673
  Uncle Bob suggests that your source code organisation should allow developers to easily find a listing of all use cases your application provides. Here's an example of how this might look in a this application.
67
674
  ```
68
675
  - app
@@ -77,6 +684,7 @@ Uncle Bob suggests that your source code organisation should allow developers to
77
684
  - retail_customer_makes_deposit.rb
78
685
  - ...
79
686
  ```
687
+
80
688
  Note that the use case name contains:
81
689
 
82
690
  - the user role
@@ -88,4 +696,37 @@ Note that the use case name contains:
88
696
  # admin_fetch_info.rb [specific usecase]
89
697
  # fetch_info.rb [generic usecase] every role can access it
90
698
  ```
699
+ ---
700
+ ---
701
+
702
+ ## Dependencies
703
+
704
+ ### Runtime Dependencies:
705
+ - `activesupport` >= 7.0, < 9.0
706
+ - `dry-matcher` >= 1.0, < 2.0
707
+ - `dry-monads` >= 1.6, < 2.0
708
+ - `dry-struct` >= 1.6, < 2.0
709
+ - `dry-types` >= 1.7, < 2.0
710
+ - `dry-validation` >= 1.9, < 2.0
711
+ - `hashie` >= 5.0, < 6.0
712
+ - `thor` >= 1.2, < 2.0
713
+
714
+ ### Development Dependencies:
715
+ - `bundler` >= 2.4, < 3.0
716
+ - `generator_spec` >= 0.9, < 1.0
717
+ - `rake` >= 13.0, < 14.0
718
+ - `rspec` >= 3.12, < 4.0
719
+ - `rubocop` >= 1.63, < 2.0
720
+ - `rubocop-rspec` >= 3.0, < 4.0
721
+
722
+ ---
723
+
724
+ ## 🤝 Contributing
725
+
726
+ - Fork the repo & bundle install
727
+ - Create a feature branch: git checkout -b feat/your-feature
728
+ - Add tests where it makes sense
729
+ - ``` bundle exec rspec```
730
+ - Open a Pull Request 🎉
91
731
 
732
+ See CONTRIBUTING.md for details.