rider-kick 0.0.13 → 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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +629 -25
  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 -45
  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 +22 -13
  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 +95 -22
  13. data/lib/generators/rider_kick/scaffold_generator.rb +377 -62
  14. data/lib/generators/rider_kick/scaffold_generator_builder_uploaders_spec.rb +119 -14
  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 +37 -10
  17. data/lib/generators/rider_kick/scaffold_generator_contracts_with_scope_spec.rb +40 -11
  18. data/lib/generators/rider_kick/scaffold_generator_engine_spec.rb +221 -0
  19. data/lib/generators/rider_kick/scaffold_generator_idempotent_spec.rb +38 -13
  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 +31 -12
  23. data/lib/generators/rider_kick/scaffold_generator_with_scope_spec.rb +32 -11
  24. data/lib/generators/rider_kick/structure_generator.rb +154 -43
  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 +3 -3
  28. data/lib/generators/rider_kick/structure_generator_success_spec.rb +33 -5
  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 +140 -66
  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 -4
  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 +13 -8
  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 +3 -3
  66. data/lib/generators/rider_kick/templates/domains/core/utils/request_methods.rb.tt +1 -1
  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 +8 -6
  79. data/lib/rider_kick/builders/abstract_active_record_entity_builder_spec.rb +644 -0
  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 +1 -1
  83. data/lib/rider_kick/entities/failure_details_spec.rb +1 -1
  84. data/lib/rider_kick/matchers/use_case_result.rb +1 -1
  85. data/lib/rider_kick/use_cases/abstract_use_case.rb +1 -1
  86. data/lib/rider_kick/version.rb +1 -1
  87. metadata +129 -8
  88. data/CHANGELOG.md +0 -5
@@ -3,7 +3,7 @@
3
3
  require 'rails/generators'
4
4
  require 'tmpdir'
5
5
  require 'active_support/inflector'
6
- require 'ostruct'
6
+
7
7
  require 'fileutils'
8
8
  require 'generators/rider_kick/scaffold_generator'
9
9
 
@@ -13,16 +13,41 @@ RSpec.describe 'rider_kick:scaffold contracts' do
13
13
  it 'menulis kontrak schema yang tepat di use_cases (list/fetch_by_id/create/update/destroy)' do
14
14
  Dir.mktmpdir do |dir|
15
15
  Dir.chdir(dir) do
16
- FileUtils.mkdir_p(%w[
17
- app/domains/core/use_cases
18
- app/domains/core/repositories
19
- app/domains/core/builders
20
- app/domains/core/entities
21
- app/models/models
22
- db/structures
16
+ # Set domain scope to core for this test
17
+ RiderKick.configuration.domain_scope = 'core/'
18
+
19
+ FileUtils.mkdir_p([
20
+ RiderKick.configuration.domains_path + '/core/use_cases',
21
+ RiderKick.configuration.domains_path + '/core/repositories',
22
+ RiderKick.configuration.domains_path + '/core/builders',
23
+ RiderKick.configuration.domains_path + '/core/entities',
24
+ 'app/models/models',
25
+ 'db/structures'
23
26
  ])
24
27
 
25
28
  # Stub model & kolom
29
+ Object.send(:remove_const, :Models) if Object.const_defined?(:Models)
30
+ Object.send(:remove_const, :Column) if Object.const_defined?(:Column)
31
+ module Models; end
32
+
33
+ Column = Struct.new(:name, :type, :sql_type, :null, :default, :precision, :scale, :limit)
34
+ class Models::User
35
+ def self.columns
36
+ [
37
+ Column.new('id', :uuid),
38
+ Column.new('created_at', :datetime),
39
+ Column.new('updated_at', :datetime)
40
+ ]
41
+ end
42
+
43
+ def self.columns_hash
44
+ columns.to_h { |c| [c.name.to_s, Struct.new(:type).new(c.type)] }
45
+ end
46
+
47
+ def self.column_names
48
+ columns.map { |c| c.name.to_s }
49
+ end
50
+ end
26
51
 
27
52
  File.write('app/models/models/user.rb', "class Models::User < ApplicationRecord; end\n")
28
53
 
@@ -31,6 +56,8 @@ RSpec.describe 'rider_kick:scaffold contracts' do
31
56
  model: Models::User
32
57
  resource_name: users
33
58
  actor: owner
59
+ resource_owner_id: account_id
60
+ resource_owner: account
34
61
  uploaders: []
35
62
  search_able: []
36
63
  domains:
@@ -39,7 +66,7 @@ RSpec.describe 'rider_kick:scaffold contracts' do
39
66
  action_create: { use_case: { contract: [] } }
40
67
  action_update: { use_case: { contract: [] } }
41
68
  action_destroy: { use_case: { contract: [] } }
42
- entity: { skipped_fields: [id, created_at, updated_at] }
69
+ entity: { db_attributes: [id, created_at, updated_at] }
43
70
  YAML
44
71
 
45
72
  # Generate
@@ -47,7 +74,7 @@ RSpec.describe 'rider_kick:scaffold contracts' do
47
74
 
48
75
  # Cek isi kontrak
49
76
  readf = ->(path) { File.read(File.join(path)) }
50
- base = 'app/domains/core/use_cases/users'
77
+ base = RiderKick.configuration.domains_path + '/use_cases/users'
51
78
 
52
79
  expect(readf["#{base}/owner_list_user.rb"])
53
80
  .to include('Core::UseCases::Contract::Default', 'Contract::Pagination')
@@ -3,7 +3,7 @@
3
3
  require 'rails/generators'
4
4
  require 'tmpdir'
5
5
  require 'active_support/inflector'
6
- require 'ostruct'
6
+
7
7
  require 'fileutils'
8
8
  require 'generators/rider_kick/scaffold_generator'
9
9
 
@@ -13,21 +13,50 @@ RSpec.describe 'rider_kick:scaffold contracts (with scope)' do
13
13
  it 'menaruh use_cases di folder scope yang benar & menulis kontrak sesuai' do
14
14
  Dir.mktmpdir do |dir|
15
15
  Dir.chdir(dir) do
16
- FileUtils.mkdir_p(%w[
17
- app/domains/core/use_cases
18
- app/domains/core/repositories
19
- app/domains/core/builders
20
- app/domains/core/entities
21
- app/models/models
22
- db/structures
16
+ # Set domain scope to core for this test
17
+ RiderKick.configuration.domain_scope = 'core/'
18
+
19
+ FileUtils.mkdir_p([
20
+ RiderKick.configuration.domains_path + '/core/use_cases',
21
+ RiderKick.configuration.domains_path + '/core/repositories',
22
+ RiderKick.configuration.domains_path + '/core/builders',
23
+ RiderKick.configuration.domains_path + '/core/entities',
24
+ 'app/models/models',
25
+ 'db/structures'
23
26
  ])
24
27
 
28
+ # Stub model & kolom
29
+ Object.send(:remove_const, :Models) if Object.const_defined?(:Models)
30
+ Object.send(:remove_const, :Column) if Object.const_defined?(:Column)
31
+ module Models; end
32
+
33
+ Column = Struct.new(:name, :type, :sql_type, :null, :default, :precision, :scale, :limit)
34
+ class Models::User
35
+ def self.columns
36
+ [
37
+ Column.new('id', :uuid),
38
+ Column.new('created_at', :datetime),
39
+ Column.new('updated_at', :datetime)
40
+ ]
41
+ end
42
+
43
+ def self.columns_hash
44
+ columns.to_h { |c| [c.name.to_s, Struct.new(:type).new(c.type)] }
45
+ end
46
+
47
+ def self.column_names
48
+ columns.map { |c| c.name.to_s }
49
+ end
50
+ end
51
+
25
52
  File.write('app/models/models/user.rb', "class Models::User < ApplicationRecord; end\n")
26
53
 
27
54
  File.write('db/structures/users_structure.yaml', <<~YAML)
28
55
  model: Models::User
29
56
  resource_name: users
30
57
  actor: owner
58
+ resource_owner_id: account_id
59
+ resource_owner: account
31
60
  uploaders: []
32
61
  search_able: []
33
62
  domains:
@@ -36,13 +65,13 @@ RSpec.describe 'rider_kick:scaffold contracts (with scope)' do
36
65
  action_create: { use_case: { contract: [] } }
37
66
  action_update: { use_case: { contract: [] } }
38
67
  action_destroy: { use_case: { contract: [] } }
39
- entity: { skipped_fields: [id, created_at, updated_at] }
68
+ entity: { db_attributes: [id, created_at, updated_at] }
40
69
  YAML
41
70
 
42
71
  klass.new(['users', 'scope:dashboard']).generate_use_case
43
72
 
44
- base = 'app/domains/core/use_cases/dashboard/users'
45
- %w[owner_list_user owner_fetch_user_by_id owner_create_user owner_update_user owner_destroy_user].each do |uc|
73
+ base = RiderKick.configuration.domains_path + '/use_cases/dashboard/users'
74
+ ['owner_list_user', 'owner_fetch_user_by_id', 'owner_create_user', 'owner_update_user', 'owner_destroy_user'].each do |uc|
46
75
  expect(File).to exist("#{base}/#{uc}.rb")
47
76
  end
48
77
 
@@ -0,0 +1,221 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'tmpdir'
5
+ require 'fileutils'
6
+ require 'generators/rider_kick/scaffold_generator'
7
+
8
+ RSpec.describe 'rider_kick:scaffold generator with engine option' do
9
+ let(:klass) { RiderKick::ScaffoldGenerator }
10
+
11
+ context 'without --engine option (main app)' do
12
+ it 'uses main app models path (app/models/models)' do
13
+ Dir.mktmpdir do |dir|
14
+ Dir.chdir(dir) do
15
+ FileUtils.mkdir_p([
16
+ RiderKick.configuration.domains_path + '/core/use_cases',
17
+ RiderKick.configuration.domains_path + '/core/repositories',
18
+ RiderKick.configuration.domains_path + '/core/builders',
19
+ RiderKick.configuration.domains_path + '/core/entities',
20
+ 'app/models/models',
21
+ 'db/structures'
22
+ ])
23
+
24
+ # Stub model & kolom
25
+ Object.send(:remove_const, :Models) if Object.const_defined?(:Models)
26
+ Object.send(:remove_const, :Column) if Object.const_defined?(:Column)
27
+ module Models; end
28
+
29
+ Column = Struct.new(:name, :type, :sql_type, :null, :default, :precision, :scale, :limit)
30
+ class Models::User
31
+ def self.columns
32
+ [
33
+ Column.new('id', :uuid),
34
+ Column.new('created_at', :datetime),
35
+ Column.new('updated_at', :datetime)
36
+ ]
37
+ end
38
+
39
+ def self.columns_hash
40
+ columns.to_h { |c| [c.name.to_s, Struct.new(:type).new(c.type)] }
41
+ end
42
+
43
+ def self.column_names
44
+ columns.map { |c| c.name.to_s }
45
+ end
46
+ end
47
+
48
+ File.write('app/models/models/user.rb', "class Models::User < ApplicationRecord; end\n")
49
+ File.write('db/structures/users_structure.yaml', <<~YAML)
50
+ model: Models::User
51
+ resource_name: users
52
+ actor: owner
53
+ resource_owner_id: account_id
54
+ resource_owner: account
55
+ uploaders: []
56
+ search_able: []
57
+ domains:
58
+ action_list: { use_case: { contract: [] } }
59
+ action_fetch_by_id: { use_case: { contract: [] } }
60
+ action_create: { use_case: { contract: [] } }
61
+ action_update: { use_case: { contract: [] } }
62
+ action_destroy: { use_case: { contract: [] } }
63
+ entity: { db_attributes: [id, created_at, updated_at] }
64
+ YAML
65
+
66
+ # Reset configuration untuk test
67
+ RiderKick.configuration.engine_name = nil
68
+
69
+ instance = klass.new(['users'])
70
+ allow(instance).to receive(:options).and_return({ engine: nil })
71
+ instance.generate_use_case
72
+
73
+ # Verifikasi models_path menggunakan main app
74
+ expect(RiderKick.configuration.models_path).to eq('app/models/models')
75
+ expect(RiderKick.configuration.engine_name).to be_nil
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ context 'with --engine option' do
82
+ it 'uses engine models path (engines/<engine_name>/app/models/<engine_name>/models)' do
83
+ Dir.mktmpdir do |dir|
84
+ Dir.chdir(dir) do
85
+ # Set configuration for test first
86
+ RiderKick.configuration.engine_name = 'Core'
87
+ RiderKick.configuration.domain_scope = 'core/'
88
+
89
+ FileUtils.mkdir_p([
90
+ RiderKick.configuration.domains_path,
91
+ RiderKick.configuration.models_path,
92
+ 'engines/core/app/models/core',
93
+ 'engines/core/db/structures'
94
+ ])
95
+
96
+ # Stub model classes
97
+ Object.send(:remove_const, :Models) if Object.const_defined?(:Models)
98
+ Object.send(:remove_const, :Column) if Object.const_defined?(:Column)
99
+ module Models; end
100
+
101
+ module Models::Core; end
102
+
103
+ Column = Struct.new(:name, :type, :sql_type, :null, :default, :precision, :scale, :limit)
104
+ class Models::Core::User
105
+ def self.columns
106
+ [
107
+ Column.new('id', :uuid),
108
+ Column.new('created_at', :datetime),
109
+ Column.new('updated_at', :datetime)
110
+ ]
111
+ end
112
+
113
+ def self.columns_hash
114
+ columns.to_h { |c| [c.name.to_s, Struct.new(:type).new(c.type)] }
115
+ end
116
+
117
+ def self.column_names
118
+ columns.map { |c| c.name.to_s }
119
+ end
120
+ end
121
+
122
+ File.write('engines/core/app/models/core/models/user.rb', "class Models::Core::User < ApplicationRecord; end\n")
123
+ File.write('engines/core/db/structures/users_structure.yaml', <<~YAML)
124
+ model: Models::Core::User
125
+ resource_name: users
126
+ actor: owner
127
+ resource_owner_id: account_id
128
+ resource_owner: account
129
+ uploaders: []
130
+ search_able: []
131
+ domains:
132
+ action_list: { use_case: { contract: [] } }
133
+ action_fetch_by_id: { use_case: { contract: [] } }
134
+ action_create: { use_case: { contract: [] } }
135
+ action_update: { use_case: { contract: [] } }
136
+ action_destroy: { use_case: { contract: [] } }
137
+ entity: { db_attributes: [id, created_at, updated_at] }
138
+ YAML
139
+
140
+ instance = klass.new(['users'])
141
+ instance.generate_use_case
142
+
143
+ # Verifikasi models_path menggunakan engine
144
+ expect(RiderKick.configuration.models_path).to eq('engines/core/app/models/core/models')
145
+ expect(RiderKick.configuration.engine_name).to eq('Core')
146
+ end
147
+ end
148
+ end
149
+
150
+ it 'generates files with correct engine configuration' do
151
+ Dir.mktmpdir do |dir|
152
+ Dir.chdir(dir) do
153
+ # Set configuration for test first
154
+ RiderKick.configuration.engine_name = 'Admin'
155
+ RiderKick.configuration.domain_scope = 'admin/core/' # Engine-prefixed
156
+
157
+ FileUtils.mkdir_p([
158
+ RiderKick.configuration.domains_path,
159
+ 'app/models/admin',
160
+ 'engines/admin/db/structures'
161
+ ])
162
+
163
+ # Stub model classes
164
+ Object.send(:remove_const, :Models) if Object.const_defined?(:Models)
165
+ Object.send(:remove_const, :Column) if Object.const_defined?(:Column)
166
+ module Models; end
167
+
168
+ module Models::Admin; end
169
+
170
+ Column = Struct.new(:name, :type, :sql_type, :null, :default, :precision, :scale, :limit)
171
+ class Models::Admin::Product
172
+ def self.columns
173
+ [
174
+ Column.new('id', :uuid),
175
+ Column.new('created_at', :datetime),
176
+ Column.new('updated_at', :datetime)
177
+ ]
178
+ end
179
+
180
+ def self.columns_hash
181
+ columns.to_h { |c| [c.name.to_s, Struct.new(:type).new(c.type)] }
182
+ end
183
+
184
+ def self.column_names
185
+ columns.map { |c| c.name.to_s }
186
+ end
187
+ end
188
+
189
+ File.write('app/models/admin/product.rb', "class Models::Admin::Product < ApplicationRecord; end\n")
190
+ File.write('engines/admin/db/structures/products_structure.yaml', <<~YAML)
191
+ model: Models::Admin::Product
192
+ resource_name: products
193
+ actor: admin
194
+ resource_owner_id: account_id
195
+ resource_owner: account
196
+ uploaders: []
197
+ search_able: []
198
+ domains:
199
+ action_list: { use_case: { contract: [] } }
200
+ action_fetch_by_id: { use_case: { contract: [] } }
201
+ action_create: { use_case: { contract: [] } }
202
+ action_update: { use_case: { contract: [] } }
203
+ action_destroy: { use_case: { contract: [] } }
204
+ entity: { db_attributes: [id, created_at, updated_at] }
205
+ YAML
206
+
207
+ instance = klass.new(['products'])
208
+ instance.generate_use_case
209
+
210
+ # Verifikasi engine name sudah di-set
211
+ expect(RiderKick.configuration.engine_name).to eq('Admin')
212
+ expect(RiderKick.configuration.models_path).to eq('engines/admin/app/models/admin/models')
213
+
214
+ # Verifikasi files ter-generate
215
+ expect(File).to exist(RiderKick.configuration.domains_path + '/builders/product.rb')
216
+ expect(File).to exist(RiderKick.configuration.domains_path + '/entities/product.rb')
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
@@ -4,7 +4,7 @@
4
4
  require 'rails/generators'
5
5
  require 'tmpdir'
6
6
  require 'fileutils'
7
- require 'ostruct'
7
+
8
8
  require 'generators/rider_kick/scaffold_generator'
9
9
 
10
10
  RSpec.describe 'rider_kick:scaffold generator (idempotent)' do
@@ -13,15 +13,40 @@ RSpec.describe 'rider_kick:scaffold generator (idempotent)' do
13
13
  it 'tidak menduplikasi konten ketika dijalankan ulang' do
14
14
  Dir.mktmpdir do |dir|
15
15
  Dir.chdir(dir) do
16
- FileUtils.mkdir_p %w[
17
- app/domains/core/use_cases
18
- app/domains/core/repositories
19
- app/domains/core/builders
20
- app/domains/core/entities
21
- app/models/models
22
- db/structures
16
+ # Reset configuration for this test
17
+ RiderKick.configuration.engine_name = nil
18
+ RiderKick.configuration.domain_scope = 'core/'
19
+
20
+ FileUtils.mkdir_p [
21
+ RiderKick.configuration.domains_path,
22
+ 'app/models/models',
23
+ 'db/structures'
23
24
  ]
24
25
 
26
+ # Stub model classes
27
+ Object.send(:remove_const, :Models) if Object.const_defined?(:Models)
28
+ Object.send(:remove_const, :Column) if Object.const_defined?(:Column)
29
+ module Models; end
30
+
31
+ Column = Struct.new(:name, :type, :sql_type, :null, :default, :precision, :scale, :limit)
32
+ class Models::User
33
+ def self.columns
34
+ [
35
+ Column.new('id', :uuid),
36
+ Column.new('created_at', :datetime),
37
+ Column.new('updated_at', :datetime)
38
+ ]
39
+ end
40
+
41
+ def self.columns_hash
42
+ columns.to_h { |c| [c.name.to_s, Struct.new(:type).new(c.type)] }
43
+ end
44
+
45
+ def self.column_names
46
+ columns.map { |c| c.name.to_s }
47
+ end
48
+ end
49
+
25
50
  # model fisik & YAML minimal
26
51
  File.write('app/models/models/user.rb', "class Models::User < ApplicationRecord; end\n")
27
52
  File.write('db/structures/users_structure.yaml', <<~YAML)
@@ -36,23 +61,23 @@ RSpec.describe 'rider_kick:scaffold generator (idempotent)' do
36
61
  action_create: { use_case: { contract: [] } }
37
62
  action_update: { use_case: { contract: [] } }
38
63
  action_destroy: { use_case: { contract: [] } }
39
- entity: { skipped_fields: [id, created_at, updated_at] }
64
+ entity: { db_attributes: [id, created_at, updated_at] }
40
65
  YAML
41
66
 
42
67
  # run pertama
43
68
  klass.new(['users']).generate_use_case
44
- builder_v1 = File.read('app/domains/core/builders/user.rb')
69
+ builder_v1 = File.read(RiderKick.configuration.domains_path + '/builders/user.rb')
45
70
 
46
71
  # run kedua (seharusnya idempotent)
47
72
  klass.new(['users']).generate_use_case
48
- builder_v2 = File.read('app/domains/core/builders/user.rb')
73
+ builder_v2 = File.read(RiderKick.configuration.domains_path + '/builders/user.rb')
49
74
 
50
75
  # konten tidak berubah
51
76
  expect(builder_v2).to eq(builder_v1)
52
77
 
53
78
  # entity & repositori tetap 1 file masing-masing
54
- expect(Dir['app/domains/core/entities/user.rb'].size).to eq(1)
55
- expect(Dir['app/domains/core/repositories/users/*.rb'].size).to be >= 5
79
+ expect(Dir[RiderKick.configuration.domains_path + '/entities/user.rb'].size).to eq(1)
80
+ expect(Dir[RiderKick.configuration.domains_path + '/repositories/users/*.rb'].size).to be >= 5
56
81
  end
57
82
  end
58
83
  end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'tmpdir'
5
+ require 'generators/rider_kick/scaffold_generator'
6
+
7
+ RSpec.describe 'rider_kick:scaffold list spec generation' do
8
+ let(:klass) { RiderKick::ScaffoldGenerator }
9
+
10
+ it 'generates list spec with resource_owner_id' do
11
+ stub_const('Models', Module.new)
12
+ stub_const('Models::Article', Class.new do
13
+ def self.columns
14
+ [
15
+ Struct.new(:name, :type).new('id', :integer),
16
+ Struct.new(:name, :type).new('title', :string),
17
+ Struct.new(:name, :type).new('content', :text),
18
+ Struct.new(:name, :type).new('account_id', :string),
19
+ Struct.new(:name, :type).new('user_id', :integer),
20
+ Struct.new(:name, :type).new('created_at', :datetime),
21
+ Struct.new(:name, :type).new('updated_at', :datetime)
22
+ ]
23
+ end
24
+
25
+ def self.column_names
26
+ ['id', 'title', 'content', 'account_id', 'user_id', 'created_at', 'updated_at']
27
+ end
28
+
29
+ def self.columns_hash
30
+ {
31
+ 'title' => Struct.new(:type).new(:string),
32
+ 'content' => Struct.new(:type).new(:text),
33
+ 'account_id' => Struct.new(:type).new(:string),
34
+ 'user_id' => Struct.new(:type).new(:integer)
35
+ }
36
+ end
37
+ end)
38
+
39
+ Dir.mktmpdir do |dir|
40
+ Dir.chdir(dir) do
41
+ FileUtils.mkdir_p('app/domains')
42
+ FileUtils.mkdir_p('db/structures')
43
+
44
+ # Create structure YAML
45
+ structure_yaml = <<~YAML
46
+ model: Models::Article
47
+ resource_name: article
48
+ actor: owner
49
+ resource_owner_id: account_id
50
+ domains:
51
+ action_list:
52
+ use_case:
53
+ contract: []
54
+ repository:
55
+ filters:
56
+ - "{ field: 'title', type: 'search' }"
57
+ YAML
58
+
59
+ File.write('db/structures/articles_structure.yaml', structure_yaml)
60
+
61
+ instance = klass.new(['articles', { 'scope' => 'dashboard' }])
62
+
63
+ # Mock methods
64
+ allow(instance).to receive(:say)
65
+ allow(instance).to receive(:empty_directory)
66
+ allow(RiderKick.configuration).to receive(:domains_path).and_return('app/domains')
67
+
68
+ instance.generate_use_case
69
+
70
+ list_spec_file = 'app/domains/repositories/articles/list_article_spec.rb'
71
+ expect(File.exist?(list_spec_file)).to be true
72
+
73
+ content = File.read(list_spec_file)
74
+
75
+ # Verify format matches user's requirements
76
+ expect(content).to include('let(:repository) { described_class }')
77
+ expect(content).to include('repository.new(params: params).call')
78
+ expect(content).to include('account_id: SecureRandom.uuid')
79
+ expect(content).to include('create_list(:article, 3, account_id: params[:account_id])')
80
+ expect(content).to include('context \'with search filters\'')
81
+ expect(content).to include('context \'with resource owner filter\'')
82
+ expect(content).to include('context \'with sorting\'')
83
+ end
84
+ end
85
+ end
86
+
87
+ it 'generates list spec without resource_owner_id' do
88
+ stub_const('Models', Module.new)
89
+ stub_const('Models::Product', Class.new do
90
+ def self.columns
91
+ [
92
+ Struct.new(:name, :type).new('id', :integer),
93
+ Struct.new(:name, :type).new('name', :string),
94
+ Struct.new(:name, :type).new('price', :decimal),
95
+ Struct.new(:name, :type).new('created_at', :datetime),
96
+ Struct.new(:name, :type).new('updated_at', :datetime)
97
+ ]
98
+ end
99
+
100
+ def self.column_names
101
+ ['id', 'name', 'price', 'created_at', 'updated_at']
102
+ end
103
+
104
+ def self.columns_hash
105
+ {
106
+ 'name' => double(type: :string),
107
+ 'price' => double(type: :decimal)
108
+ }
109
+ end
110
+ end)
111
+
112
+ Dir.mktmpdir do |dir|
113
+ Dir.chdir(dir) do
114
+ FileUtils.mkdir_p('app/domains')
115
+ FileUtils.mkdir_p('db/structures')
116
+
117
+ structure_yaml = <<~YAML
118
+ model: Models::Product
119
+ resource_name: product
120
+ actor: admin
121
+ domains:
122
+ action_list:
123
+ use_case:
124
+ contract: []
125
+ repository:
126
+ filters: []
127
+ YAML
128
+
129
+ File.write('db/structures/products_structure.yaml', structure_yaml)
130
+
131
+ instance = klass.new(['products', { 'scope' => 'admin' }])
132
+
133
+ allow(instance).to receive(:say)
134
+ allow(instance).to receive(:empty_directory)
135
+ allow(RiderKick.configuration).to receive(:domains_path).and_return('app/domains')
136
+
137
+ instance.generate_use_case
138
+
139
+ list_spec_file = 'app/domains/repositories/products/list_product_spec.rb'
140
+ expect(File.exist?(list_spec_file)).to be true
141
+
142
+ content = File.read(list_spec_file)
143
+
144
+ # Verify format without resource_owner_id
145
+ expect(content).to include('let(:repository) { described_class }')
146
+ expect(content).to include('repository.new(params: params).call')
147
+ expect(content).to include('create_list(:product, 3)')
148
+ expect(content).not_to include('account_id')
149
+ expect(content).not_to include('user_id')
150
+ end
151
+ end
152
+ end
153
+ end