rider-kick 0.0.13 → 0.0.15

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 (91) 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 +236 -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/init_generator.rb +15 -0
  13. data/lib/generators/rider_kick/repositories_contract_spec.rb +95 -22
  14. data/lib/generators/rider_kick/scaffold_generator.rb +381 -64
  15. data/lib/generators/rider_kick/scaffold_generator_builder_uploaders_spec.rb +119 -14
  16. data/lib/generators/rider_kick/scaffold_generator_conditional_filtering_spec.rb +820 -0
  17. data/lib/generators/rider_kick/scaffold_generator_contracts_spec.rb +37 -10
  18. data/lib/generators/rider_kick/scaffold_generator_contracts_with_scope_spec.rb +42 -14
  19. data/lib/generators/rider_kick/scaffold_generator_engine_spec.rb +221 -0
  20. data/lib/generators/rider_kick/scaffold_generator_idempotent_spec.rb +38 -13
  21. data/lib/generators/rider_kick/scaffold_generator_list_spec_format_spec.rb +153 -0
  22. data/lib/generators/rider_kick/scaffold_generator_rspec_spec.rb +347 -0
  23. data/lib/generators/rider_kick/scaffold_generator_success_spec.rb +31 -12
  24. data/lib/generators/rider_kick/scaffold_generator_with_scope_spec.rb +33 -12
  25. data/lib/generators/rider_kick/structure_generator.rb +156 -43
  26. data/lib/generators/rider_kick/structure_generator_comprehensive_spec.rb +598 -0
  27. data/lib/generators/rider_kick/structure_generator_engine_spec.rb +279 -0
  28. data/lib/generators/rider_kick/structure_generator_spec.rb +3 -3
  29. data/lib/generators/rider_kick/structure_generator_success_spec.rb +33 -5
  30. data/lib/generators/rider_kick/structure_generator_unit_spec.rb +2202 -0
  31. data/lib/generators/rider_kick/templates/.rubocop.yml +5 -4
  32. data/lib/generators/rider_kick/templates/config/initializers/rider_kick.rb.tt +29 -0
  33. data/lib/generators/rider_kick/templates/config/initializers/version.rb.tt +1 -1
  34. data/lib/generators/rider_kick/templates/db/migrate/20220613145533_init_database.rb +1 -1
  35. data/lib/generators/rider_kick/templates/db/structures/example.yaml.tt +142 -66
  36. data/lib/generators/rider_kick/templates/domains/core/builders/builder.rb.tt +36 -10
  37. data/lib/generators/rider_kick/templates/domains/core/builders/builder_spec.rb.tt +219 -0
  38. data/lib/generators/rider_kick/templates/domains/core/builders/error.rb.tt +2 -2
  39. data/lib/generators/rider_kick/templates/domains/core/builders/pagination.rb.tt +2 -2
  40. data/lib/generators/rider_kick/templates/domains/core/entities/entity.rb.tt +32 -14
  41. data/lib/generators/rider_kick/templates/domains/core/entities/error.rb.tt +1 -1
  42. data/lib/generators/rider_kick/templates/domains/core/entities/pagination.rb.tt +1 -1
  43. data/lib/generators/rider_kick/templates/domains/core/repositories/abstract_repository.rb.tt +4 -4
  44. data/lib/generators/rider_kick/templates/domains/core/repositories/create.rb.tt +2 -2
  45. data/lib/generators/rider_kick/templates/domains/core/repositories/create_spec.rb.tt +78 -0
  46. data/lib/generators/rider_kick/templates/domains/core/repositories/destroy.rb.tt +2 -2
  47. data/lib/generators/rider_kick/templates/domains/core/repositories/destroy_spec.rb.tt +88 -0
  48. data/lib/generators/rider_kick/templates/domains/core/repositories/fetch_by_id.rb.tt +3 -3
  49. data/lib/generators/rider_kick/templates/domains/core/repositories/fetch_by_id_spec.rb.tt +62 -0
  50. data/lib/generators/rider_kick/templates/domains/core/repositories/list.rb.tt +13 -8
  51. data/lib/generators/rider_kick/templates/domains/core/repositories/list_spec.rb.tt +190 -0
  52. data/lib/generators/rider_kick/templates/domains/core/repositories/update.rb.tt +4 -4
  53. data/lib/generators/rider_kick/templates/domains/core/repositories/update_spec.rb.tt +119 -0
  54. data/lib/generators/rider_kick/templates/domains/core/use_cases/contract/default.rb.tt +1 -1
  55. data/lib/generators/rider_kick/templates/domains/core/use_cases/contract/pagination.rb.tt +1 -1
  56. data/lib/generators/rider_kick/templates/domains/core/use_cases/create.rb.tt +3 -7
  57. data/lib/generators/rider_kick/templates/domains/core/use_cases/create_spec.rb.tt +71 -0
  58. data/lib/generators/rider_kick/templates/domains/core/use_cases/destroy.rb.tt +3 -7
  59. data/lib/generators/rider_kick/templates/domains/core/use_cases/destroy_spec.rb.tt +62 -0
  60. data/lib/generators/rider_kick/templates/domains/core/use_cases/fetch_by_id.rb.tt +3 -7
  61. data/lib/generators/rider_kick/templates/domains/core/use_cases/fetch_by_id_spec.rb.tt +62 -0
  62. data/lib/generators/rider_kick/templates/domains/core/use_cases/get_version.rb.tt +2 -2
  63. data/lib/generators/rider_kick/templates/domains/core/use_cases/list.rb.tt +3 -7
  64. data/lib/generators/rider_kick/templates/domains/core/use_cases/list_spec.rb.tt +64 -0
  65. data/lib/generators/rider_kick/templates/domains/core/use_cases/update.rb.tt +3 -7
  66. data/lib/generators/rider_kick/templates/domains/core/use_cases/update_spec.rb.tt +73 -0
  67. data/lib/generators/rider_kick/templates/domains/core/utils/abstract_utils.rb.tt +3 -3
  68. data/lib/generators/rider_kick/templates/domains/core/utils/request_methods.rb.tt +1 -1
  69. data/lib/generators/rider_kick/templates/env.development +1 -1
  70. data/lib/generators/rider_kick/templates/env.production +1 -1
  71. data/lib/generators/rider_kick/templates/env.test +1 -1
  72. data/lib/generators/rider_kick/templates/models/{application_record.rb → application_record.rb.tt} +3 -1
  73. data/lib/generators/rider_kick/templates/models/model_spec.rb.tt +68 -0
  74. data/lib/generators/rider_kick/templates/spec/factories/.gitkeep +19 -0
  75. data/lib/generators/rider_kick/templates/spec/factories/factory.rb.tt +8 -0
  76. data/lib/generators/rider_kick/templates/spec/rails_helper.rb +3 -0
  77. data/lib/generators/rider_kick/templates/spec/support/class_stubber.rb +148 -0
  78. data/lib/generators/rider_kick/templates/spec/support/factory_bot.rb +34 -0
  79. data/lib/generators/rider_kick/templates/spec/support/faker.rb +61 -0
  80. data/lib/generators/rider_kick/templates/spec/support/use_case_stubber.rb +14 -0
  81. data/lib/rider-kick.rb +8 -6
  82. data/lib/rider_kick/builders/abstract_active_record_entity_builder_spec.rb +644 -0
  83. data/lib/rider_kick/configuration.rb +258 -0
  84. data/lib/rider_kick/configuration_engine_spec.rb +377 -0
  85. data/lib/rider_kick/entities/failure_details.rb +1 -1
  86. data/lib/rider_kick/entities/failure_details_spec.rb +1 -1
  87. data/lib/rider_kick/matchers/use_case_result.rb +1 -1
  88. data/lib/rider_kick/use_cases/abstract_use_case.rb +1 -1
  89. data/lib/rider_kick/version.rb +1 -1
  90. metadata +132 -8
  91. data/CHANGELOG.md +0 -5
@@ -0,0 +1,258 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/inflector'
4
+
5
+ module RiderKick
6
+ DEFAULT_TYPE_MAPPING = {
7
+ 'uuid' => ':string',
8
+ 'string' => ':string',
9
+ 'text' => ':string',
10
+ 'integer' => ':integer',
11
+ 'boolean' => ':bool',
12
+ 'float' => ':float',
13
+ 'decimal' => ':decimal',
14
+ 'date' => ':date',
15
+ 'upload' => 'Types::File',
16
+ 'datetime' => ':time'
17
+ }.freeze
18
+ public_constant :DEFAULT_TYPE_MAPPING
19
+
20
+ DEFAULT_ENTITY_TYPE_MAPPING = {
21
+ 'uuid' => 'Types::Strict::String',
22
+ 'string' => 'Types::Strict::String',
23
+ 'text' => 'Types::Strict::String',
24
+ 'integer' => 'Types::Strict::Integer',
25
+ 'boolean' => 'Types::Strict::Bool',
26
+ 'float' => 'Types::Strict::Float',
27
+ 'decimal' => 'Types::Strict::Decimal',
28
+ 'date' => 'Types::Strict::Date',
29
+ 'datetime' => 'Types::Strict::Time'
30
+ }.freeze
31
+ public_constant :DEFAULT_ENTITY_TYPE_MAPPING
32
+ # Backward compatibility constants
33
+ TYPE_MAPPING = DEFAULT_TYPE_MAPPING
34
+ ENTITY_TYPE_MAPPING = DEFAULT_ENTITY_TYPE_MAPPING
35
+ public_constant :TYPE_MAPPING
36
+ public_constant :ENTITY_TYPE_MAPPING
37
+
38
+ # Faker mapping registry for factory generator customization
39
+ class FakerMapping
40
+ @mappings = {}
41
+
42
+ class << self
43
+ attr_reader :mappings
44
+
45
+ def register(expression, &block)
46
+ @mappings[expression] = block
47
+ end
48
+
49
+ def get(expression)
50
+ @mappings[expression]
51
+ end
52
+
53
+ def clear
54
+ @mappings = {}
55
+ end
56
+ end
57
+ end
58
+
59
+ class Configuration
60
+ attr_reader :domains_path, :models_path, :engine_name, :domain_scope, :template_path
61
+ attr_accessor :entities_path, :adapters_path, :use_cases_dir, :entities_dir, :adapters_dir, :repositories_dir, :builders_dir, :utils_dir
62
+
63
+ def initialize
64
+ @engine_name = detect_engine_name
65
+ @domain_scope = ''
66
+ @use_cases_dir = 'use_cases'
67
+ @entities_dir = 'entities'
68
+ @adapters_dir = 'adapters'
69
+ @repositories_dir = 'repositories'
70
+ @builders_dir = 'builders'
71
+ @utils_dir = 'utils'
72
+ @domains_path = detect_domains_path
73
+ @entities_path = File.join(@domains_path, @entities_dir)
74
+ @adapters_path = File.join(@domains_path, @adapters_dir)
75
+ @models_path = detect_models_path
76
+ @template_path = nil
77
+ @type_mapping = DEFAULT_TYPE_MAPPING.dup
78
+ @entity_type_mapping = DEFAULT_ENTITY_TYPE_MAPPING.dup
79
+
80
+ @domains_path_overridden = false
81
+ @models_path_overridden = false
82
+ end
83
+
84
+ attr_reader :type_mapping
85
+
86
+ attr_reader :entity_type_mapping
87
+
88
+ def register_type_mapping(db_type, dry_type)
89
+ @type_mapping[db_type.to_s] = dry_type.to_s
90
+ end
91
+
92
+ def register_entity_type_mapping(db_type, dry_type)
93
+ @entity_type_mapping[db_type.to_s] = dry_type.to_s
94
+ end
95
+
96
+ def domains_path=(path)
97
+ validate_path_format!(path, 'domains_path')
98
+ @domains_path = File.expand_path(path)
99
+ @entities_path = File.join(@domains_path, @entities_dir)
100
+ @adapters_path = File.join(@domains_path, @adapters_dir)
101
+ @domains_path_overridden = true
102
+ end
103
+
104
+ def models_path=(path)
105
+ validate_path_format!(path, 'models_path')
106
+ @models_path = File.expand_path(path)
107
+ @models_path_overridden = true
108
+ end
109
+
110
+ def template_path=(path)
111
+ if path.nil? || path.to_s.strip.empty?
112
+ @template_path = nil
113
+ else
114
+ validate_path_format!(path, 'template_path')
115
+ @template_path = File.expand_path(path)
116
+ end
117
+ end
118
+
119
+ def engine_name=(name)
120
+ if name.nil? || name.to_s.strip.empty?
121
+ @engine_name = nil
122
+ else
123
+ validate_engine_name_format!(name)
124
+ @engine_name = name.to_s
125
+ end
126
+
127
+ unless @models_path_overridden
128
+ @models_path = detect_models_path
129
+ end
130
+
131
+ unless @domains_path_overridden
132
+ @domains_path = detect_domains_path
133
+ @entities_path = File.join(@domains_path, 'entities')
134
+ @adapters_path = File.join(@domains_path, 'adapters')
135
+ end
136
+ end
137
+
138
+ def domain_scope=(scope)
139
+ if scope.nil? || scope.to_s.strip.empty?
140
+ @domain_scope = ''
141
+ else
142
+ validate_domain_scope_format!(scope)
143
+ @domain_scope = scope.to_s
144
+ end
145
+
146
+ unless @domains_path_overridden
147
+ @domains_path = detect_domains_path
148
+ @entities_path = File.join(@domains_path, 'entities')
149
+ @adapters_path = File.join(@domains_path, 'adapters')
150
+ end
151
+ end
152
+
153
+ private
154
+
155
+ def validate_path_format!(path, attribute_name)
156
+ path_str = path.to_s.strip
157
+ return if path_str.empty?
158
+
159
+ # Basic path validation - should not contain invalid characters
160
+ if path_str.match?(/[<>:"|?*\x00-\x1f]/)
161
+ raise RiderKick::ConfigurationError.new(
162
+ "Invalid #{attribute_name} format: contains invalid characters",
163
+ attribute: attribute_name,
164
+ value: path_str
165
+ )
166
+ end
167
+ end
168
+
169
+ def validate_engine_name_format!(name)
170
+ name_str = name.to_s.strip
171
+ return if name_str.empty?
172
+
173
+ # Engine name can be CamelCase (e.g., 'Core', 'Admin') or underscore_case (e.g., 'order_engine')
174
+ # Both formats are acceptable and will be converted as needed
175
+ unless name_str.match?(/^[A-Z][a-zA-Z0-9]*$/) || name_str.match?(/^[a-z][a-z0-9_]*$/)
176
+ raise RiderKick::ConfigurationError.new(
177
+ "Invalid engine_name format: must be CamelCase (e.g., 'Core', 'Admin') or underscore_case (e.g., 'order_engine')",
178
+ attribute: 'engine_name',
179
+ value: name_str,
180
+ suggestion: "Use CamelCase format like 'Core' or 'Admin', or underscore_case like 'order_engine'"
181
+ )
182
+ end
183
+ end
184
+
185
+ def validate_domain_scope_format!(scope)
186
+ scope_str = scope.to_s.strip
187
+ return if scope_str.empty?
188
+
189
+ # Domain scope should contain only alphanumeric, slash, underscore, hyphen
190
+ unless scope_str.match?(/^[a-zA-Z0-9\/_-]+$/)
191
+ raise RiderKick::ConfigurationError.new(
192
+ 'Invalid domain_scope format: must contain only alphanumeric characters, slashes, underscores, and hyphens',
193
+ attribute: 'domain_scope',
194
+ value: scope_str,
195
+ suggestion: "Use format like 'core/', 'admin/', 'api/v1/'"
196
+ )
197
+ end
198
+ end
199
+
200
+ def detect_engine_name
201
+ # Detect engine dari struktur file system
202
+ # Cek apakah ada lib/<name>/engine.rb
203
+ return nil unless Dir.exist?('lib')
204
+
205
+ engines = []
206
+ Dir.glob('lib/*/engine.rb').each do |engine_file|
207
+ engine_name = File.basename(File.dirname(engine_file))
208
+ engines << engine_name.camelize if File.exist?(engine_file)
209
+ end
210
+
211
+ # Jika hanya ada satu engine, return itu
212
+ return engines.first if engines.length == 1
213
+
214
+ # Jika ada multiple engines, return nil (user harus specify via --engine option)
215
+ # Atau cek dari gem name di gemspec (fallback)
216
+ gemspec_files = Dir.glob('*.gemspec')
217
+ unless gemspec_files.empty?
218
+ gemspec_content = File.read(gemspec_files.first)
219
+ if gemspec_content =~ /\.name\s*=\s*["']([^"']+)["']/
220
+ gem_name = Regexp.last_match(1)
221
+ # Extract engine name dari gem name (e.g., "my_app-core" -> "Core")
222
+ engine_name = gem_name.split(/[-_]/).last.camelize
223
+ return engine_name if Dir.exist?("lib/#{engine_name.underscore}") && engines.include?(engine_name)
224
+ end
225
+ end
226
+
227
+ nil
228
+ end
229
+
230
+ def detect_models_path
231
+ if @engine_name
232
+ # Engine: engines/<engine_name>/app/models/<engine_name>/models
233
+ File.join('engines', @engine_name.underscore, 'app/models', @engine_name.underscore, 'models')
234
+ else
235
+ # Main app: app/models/models
236
+ 'app/models/models'
237
+ end
238
+ end
239
+
240
+ def detect_domains_path
241
+ if @engine_name
242
+ # Engine: engines/<engine_name>/app/domains/<domain_scope>
243
+ File.join('engines', @engine_name.underscore, 'app/domains', @domain_scope)
244
+ else
245
+ # Main app: app/domains/<domain_scope>
246
+ File.join('app/domains', @domain_scope)
247
+ end
248
+ end
249
+ end
250
+
251
+ def self.configuration
252
+ @configuration ||= Configuration.new
253
+ end
254
+
255
+ def self.configure
256
+ yield(configuration)
257
+ end
258
+ end
@@ -0,0 +1,377 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tmpdir'
4
+ require 'fileutils'
5
+ require 'rider_kick/configuration'
6
+
7
+ RSpec.describe RiderKick::Configuration do
8
+ describe '#engine_name detection' do
9
+ it 'returns nil when no engine found' do
10
+ Dir.mktmpdir do |dir|
11
+ Dir.chdir(dir) do
12
+ FileUtils.mkdir_p('lib')
13
+ config = RiderKick::Configuration.new
14
+ expect(config.engine_name).to be_nil
15
+ end
16
+ end
17
+ end
18
+
19
+ it 'returns engine name when single engine found' do
20
+ Dir.mktmpdir do |dir|
21
+ Dir.chdir(dir) do
22
+ FileUtils.mkdir_p('lib/core')
23
+ File.write('lib/core/engine.rb', 'module Core; class Engine < ::Rails::Engine; end; end')
24
+
25
+ config = RiderKick::Configuration.new
26
+ expect(config.engine_name).to eq('Core')
27
+ end
28
+ end
29
+ end
30
+
31
+ it 'returns nil when multiple engines found (requires explicit specification)' do
32
+ Dir.mktmpdir do |dir|
33
+ Dir.chdir(dir) do
34
+ FileUtils.mkdir_p(['lib/core', 'lib/admin'])
35
+ File.write('lib/core/engine.rb', 'module Core; class Engine < ::Rails::Engine; end; end')
36
+ File.write('lib/admin/engine.rb', 'module Admin; class Engine < ::Rails::Engine; end; end')
37
+
38
+ config = RiderKick::Configuration.new
39
+ expect(config.engine_name).to be_nil
40
+ end
41
+ end
42
+ end
43
+
44
+ it 'can be set explicitly via engine_name=' do
45
+ Dir.mktmpdir do |dir|
46
+ Dir.chdir(dir) do
47
+ config = RiderKick::Configuration.new
48
+ config.engine_name = 'Core'
49
+ expect(config.engine_name).to eq('Core')
50
+ end
51
+ end
52
+ end
53
+
54
+ it 'sets engine_name to nil when given nil' do
55
+ Dir.mktmpdir do |dir|
56
+ Dir.chdir(dir) do
57
+ config = RiderKick::Configuration.new
58
+ config.engine_name = 'Core'
59
+ config.engine_name = nil
60
+ expect(config.engine_name).to be_nil
61
+ end
62
+ end
63
+ end
64
+
65
+ it 'sets engine_name to nil when given empty string' do
66
+ Dir.mktmpdir do |dir|
67
+ Dir.chdir(dir) do
68
+ config = RiderKick::Configuration.new
69
+ config.engine_name = ''
70
+ expect(config.engine_name).to be_nil
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ describe '#models_path' do
77
+ it 'returns app/models/models when no engine' do
78
+ Dir.mktmpdir do |dir|
79
+ Dir.chdir(dir) do
80
+ config = RiderKick::Configuration.new
81
+ config.engine_name = nil
82
+ expect(config.models_path).to eq('app/models/models')
83
+ end
84
+ end
85
+ end
86
+
87
+ it 'returns engines/<engine_name>/app/models/<engine_name>/models when engine is set' do
88
+ Dir.mktmpdir do |dir|
89
+ Dir.chdir(dir) do
90
+ config = RiderKick::Configuration.new
91
+ config.engine_name = 'Core'
92
+ expect(config.models_path).to eq('engines/core/app/models/core/models')
93
+ end
94
+ end
95
+ end
96
+
97
+ it 'updates models_path when engine_name changes' do
98
+ Dir.mktmpdir do |dir|
99
+ Dir.chdir(dir) do
100
+ config = RiderKick::Configuration.new
101
+
102
+ config.engine_name = nil
103
+ expect(config.models_path).to eq('app/models/models')
104
+
105
+ config.engine_name = 'Admin'
106
+ expect(config.models_path).to eq('engines/admin/app/models/admin/models')
107
+
108
+ config.engine_name = nil
109
+ expect(config.models_path).to eq('app/models/models')
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '#detect_engine_name' do
116
+ it 'returns nil when lib directory does not exist' do
117
+ Dir.mktmpdir do |dir|
118
+ Dir.chdir(dir) do
119
+ config = RiderKick::Configuration.new
120
+ expect(config.engine_name).to be_nil
121
+ end
122
+ end
123
+ end
124
+
125
+ it 'detects engine from gemspec fallback when multiple engines exist' do
126
+ Dir.mktmpdir do |dir|
127
+ Dir.chdir(dir) do
128
+ FileUtils.mkdir_p(['lib/core', 'lib/admin'])
129
+ File.write('lib/core/engine.rb', 'module Core; class Engine < ::Rails::Engine; end; end')
130
+ File.write('lib/admin/engine.rb', 'module Admin; class Engine < ::Rails::Engine; end; end')
131
+ File.write('test-admin.gemspec', "Gem::Specification.new do |s|\n s.name = 'test-admin'\nend")
132
+
133
+ config = RiderKick::Configuration.new
134
+ # Should return Admin because gemspec name matches Admin engine
135
+ # The logic checks if gemspec name matches an existing engine
136
+ expect(config.engine_name).to eq('Admin')
137
+ end
138
+ end
139
+ end
140
+
141
+ it 'detects engine from gemspec fallback when single matching engine' do
142
+ Dir.mktmpdir do |dir|
143
+ Dir.chdir(dir) do
144
+ FileUtils.mkdir_p('lib/core')
145
+ File.write('lib/core/engine.rb', 'module Core; class Engine < ::Rails::Engine; end; end')
146
+ File.write('test-core.gemspec', "Gem::Specification.new do |s|\n s.name = 'test-core'\nend")
147
+
148
+ config = RiderKick::Configuration.new
149
+ # Should return Core because it matches the gemspec name
150
+ expect(config.engine_name).to eq('Core')
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ describe '#detect_domains_path' do
157
+ it 'returns app/domains/<domain_scope> when no engine' do
158
+ Dir.mktmpdir do |dir|
159
+ Dir.chdir(dir) do
160
+ config = RiderKick::Configuration.new
161
+ config.engine_name = nil
162
+ config.domain_scope = 'admin/'
163
+ expect(config.domains_path).to eq('app/domains/admin/')
164
+ end
165
+ end
166
+ end
167
+
168
+ it 'returns engines/<engine_name>/app/domains/<domain_scope> when engine is set' do
169
+ Dir.mktmpdir do |dir|
170
+ Dir.chdir(dir) do
171
+ config = RiderKick::Configuration.new
172
+ config.engine_name = 'Core'
173
+ config.domain_scope = 'admin/'
174
+ expect(config.domains_path).to eq('engines/core/app/domains/admin/')
175
+ end
176
+ end
177
+ end
178
+
179
+ it 'handles empty domain_scope' do
180
+ Dir.mktmpdir do |dir|
181
+ Dir.chdir(dir) do
182
+ config = RiderKick::Configuration.new
183
+ config.engine_name = nil
184
+ config.domain_scope = ''
185
+ expect(config.domains_path).to eq('app/domains/')
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ describe '#domains_path=' do
192
+ it 'updates @domains_path with expanded path' do
193
+ Dir.mktmpdir do |dir|
194
+ Dir.chdir(dir) do
195
+ config = RiderKick::Configuration.new
196
+ config.domains_path = 'custom/path'
197
+ expect(config.domains_path).to eq(File.expand_path('custom/path'))
198
+ end
199
+ end
200
+ end
201
+
202
+ it 'updates @entities_path' do
203
+ Dir.mktmpdir do |dir|
204
+ Dir.chdir(dir) do
205
+ config = RiderKick::Configuration.new
206
+ config.domains_path = 'custom/path'
207
+ expect(config.entities_path).to eq(File.join(File.expand_path('custom/path'), 'entities'))
208
+ end
209
+ end
210
+ end
211
+
212
+ it 'updates @adapters_path' do
213
+ Dir.mktmpdir do |dir|
214
+ Dir.chdir(dir) do
215
+ config = RiderKick::Configuration.new
216
+ config.domains_path = 'custom/path'
217
+ expect(config.adapters_path).to eq(File.join(File.expand_path('custom/path'), 'adapters'))
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ describe '#models_path=' do
224
+ it 'updates @models_path with expanded path' do
225
+ Dir.mktmpdir do |dir|
226
+ Dir.chdir(dir) do
227
+ config = RiderKick::Configuration.new
228
+ config.models_path = 'custom/models'
229
+ expect(config.models_path).to eq(File.expand_path('custom/models'))
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ describe '#engine_name=' do
236
+ it 'updates paths when engine_name is set' do
237
+ Dir.mktmpdir do |dir|
238
+ Dir.chdir(dir) do
239
+ config = RiderKick::Configuration.new
240
+ config.engine_name = 'Core'
241
+ expect(config.models_path).to eq('engines/core/app/models/core/models')
242
+ expect(config.domains_path).to eq('engines/core/app/domains/')
243
+ end
244
+ end
245
+ end
246
+
247
+ it 'updates entities_path and adapters_path when engine_name is set' do
248
+ Dir.mktmpdir do |dir|
249
+ Dir.chdir(dir) do
250
+ config = RiderKick::Configuration.new
251
+ config.engine_name = 'Core'
252
+ expect(config.entities_path).to eq('engines/core/app/domains/entities')
253
+ expect(config.adapters_path).to eq('engines/core/app/domains/adapters')
254
+ end
255
+ end
256
+ end
257
+
258
+ it 'handles nil' do
259
+ Dir.mktmpdir do |dir|
260
+ Dir.chdir(dir) do
261
+ config = RiderKick::Configuration.new
262
+ config.engine_name = 'Core'
263
+ config.engine_name = nil
264
+ expect(config.engine_name).to be_nil
265
+ expect(config.models_path).to eq('app/models/models')
266
+ end
267
+ end
268
+ end
269
+
270
+ it 'handles empty string' do
271
+ Dir.mktmpdir do |dir|
272
+ Dir.chdir(dir) do
273
+ config = RiderKick::Configuration.new
274
+ config.engine_name = 'Core'
275
+ config.engine_name = ''
276
+ expect(config.engine_name).to be_nil
277
+ end
278
+ end
279
+ end
280
+
281
+ it 'handles whitespace-only string' do
282
+ Dir.mktmpdir do |dir|
283
+ Dir.chdir(dir) do
284
+ config = RiderKick::Configuration.new
285
+ config.engine_name = ' '
286
+ expect(config.engine_name).to be_nil
287
+ end
288
+ end
289
+ end
290
+ end
291
+
292
+ describe '#domain_scope=' do
293
+ it 'updates domains_path when domain_scope is set' do
294
+ Dir.mktmpdir do |dir|
295
+ Dir.chdir(dir) do
296
+ config = RiderKick::Configuration.new
297
+ config.domain_scope = 'admin/'
298
+ expect(config.domains_path).to eq('app/domains/admin/')
299
+ end
300
+ end
301
+ end
302
+
303
+ it 'updates entities_path and adapters_path when domain_scope is set' do
304
+ Dir.mktmpdir do |dir|
305
+ Dir.chdir(dir) do
306
+ config = RiderKick::Configuration.new
307
+ config.domain_scope = 'admin/'
308
+ expect(config.entities_path).to eq('app/domains/admin/entities')
309
+ expect(config.adapters_path).to eq('app/domains/admin/adapters')
310
+ end
311
+ end
312
+ end
313
+
314
+ it 'handles nil' do
315
+ Dir.mktmpdir do |dir|
316
+ Dir.chdir(dir) do
317
+ config = RiderKick::Configuration.new
318
+ config.domain_scope = 'admin/'
319
+ config.domain_scope = nil
320
+ expect(config.domain_scope).to eq('')
321
+ end
322
+ end
323
+ end
324
+
325
+ it 'handles empty string' do
326
+ Dir.mktmpdir do |dir|
327
+ Dir.chdir(dir) do
328
+ config = RiderKick::Configuration.new
329
+ config.domain_scope = 'admin/'
330
+ config.domain_scope = ''
331
+ expect(config.domain_scope).to eq('')
332
+ end
333
+ end
334
+ end
335
+
336
+ it 'handles whitespace-only string' do
337
+ Dir.mktmpdir do |dir|
338
+ Dir.chdir(dir) do
339
+ config = RiderKick::Configuration.new
340
+ config.domain_scope = ' '
341
+ expect(config.domain_scope).to eq('')
342
+ end
343
+ end
344
+ end
345
+
346
+ it 'updates paths correctly with engine and domain_scope' do
347
+ Dir.mktmpdir do |dir|
348
+ Dir.chdir(dir) do
349
+ config = RiderKick::Configuration.new
350
+ config.engine_name = 'Core'
351
+ config.domain_scope = 'admin/'
352
+ expect(config.domains_path).to eq('engines/core/app/domains/admin/')
353
+ expect(config.entities_path).to eq('engines/core/app/domains/admin/entities')
354
+ expect(config.adapters_path).to eq('engines/core/app/domains/admin/adapters')
355
+ end
356
+ end
357
+ end
358
+ end
359
+
360
+ describe 'RiderKick.configuration' do
361
+ it 'returns singleton Configuration instance' do
362
+ config1 = RiderKick.configuration
363
+ config2 = RiderKick.configuration
364
+ expect(config1.object_id).to eq(config2.object_id)
365
+ end
366
+ end
367
+
368
+ describe 'RiderKick.configure' do
369
+ it 'yields configuration instance' do
370
+ RiderKick.configure do |config|
371
+ expect(config).to be_a(RiderKick::Configuration)
372
+ config.engine_name = 'TestEngine'
373
+ expect(config.engine_name).to eq('TestEngine')
374
+ end
375
+ end
376
+ end
377
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rider_kick/types'
3
+ require_relative '../types'
4
4
  require 'dry/struct'
5
5
 
6
6
  module RiderKick
@@ -2,7 +2,7 @@
2
2
 
3
3
  RSpec.describe RiderKick::Entities::FailureDetails do
4
4
  it 'from_array menggabungkan pesan secara dinamis' do
5
- fd = described_class.from_array(%w[alpha beta gamma])
5
+ fd = described_class.from_array(['alpha', 'beta', 'gamma'])
6
6
  expect(fd.message).to eq('alpha, beta, gamma')
7
7
  expect(fd.type).to eq(:error)
8
8
  expect(fd.other_properties).to eq({})
@@ -1,7 +1,7 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'rider_kick/entities/failure_details'
4
+ require_relative '../entities/failure_details'
5
5
  require 'dry-matcher'
6
6
  require 'dry/monads/all'
7
7
 
@@ -4,7 +4,7 @@
4
4
  require 'dry/monads/result'
5
5
  require 'dry/validation'
6
6
  require 'dry/matcher/result_matcher'
7
- require 'rider_kick/use_cases/contract'
7
+ require_relative 'contract'
8
8
 
9
9
  module RiderKick
10
10
  module UseCases
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RiderKick
4
- VERSION = '0.0.13'
4
+ VERSION = '0.0.15'
5
5
  public_constant :VERSION
6
6
  end