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
@@ -7,3 +7,5 @@ Example:
7
7
  bin/rails generate rider_kick:clean_arch --setup
8
8
  bin/rails generate rider_kick:structure Models::User actor:owner uploaders:asset,images
9
9
  bin/rails generate rider_kick:scaffold users scope:dashboard
10
+ bin/rails generate rider_kick:factory Models::Article scope:core
11
+ bin/rails generate rider_kick:factory Models::Article scope:core --static
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'active_support/inflector'
5
+ require 'active_support/core_ext/object/blank'
6
+ require 'yaml'
7
+ require_relative 'errors'
8
+ require_relative '../../rider-kick'
9
+
10
+ module RiderKick
11
+ class BaseGenerator < Rails::Generators::Base
12
+ protected
13
+
14
+ def template_path_for(template_name)
15
+ # Check for custom template path first
16
+ custom_path = RiderKick.configuration.template_path
17
+ if custom_path && Dir.exist?(custom_path)
18
+ custom_template_path = File.join(custom_path, template_name)
19
+ return custom_template_path if File.exist?(custom_template_path)
20
+ end
21
+ # Fallback to default template path
22
+ File.join(self.class.source_root, template_name)
23
+ end
24
+
25
+ def configure_engine
26
+ if options[:engine].present?
27
+ RiderKick.configuration.engine_name = options[:engine]
28
+ # Jika --engine dispecify, selalu ada scope engine nya
29
+ engine_prefix = options[:engine].underscore
30
+ domain_part = options[:domain] || ''
31
+ RiderKick.configuration.domain_scope = domain_part.empty? ? engine_prefix + '/' : engine_prefix + '/' + domain_part
32
+ say "Using engine: #{RiderKick.configuration.engine_name}", :green
33
+ say "Using domain scope: #{RiderKick.configuration.domain_scope}", :green
34
+ elsif options[:domain].present?
35
+ # Jika hanya --domain yang dispecify, gunakan konfigurasi existing
36
+ RiderKick.configuration.domain_scope = options[:domain]
37
+ say "Using domain scope: #{RiderKick.configuration.domain_scope}", :blue
38
+ else
39
+ # Jika tidak ada options, pertahankan konfigurasi existing
40
+ # Hanya tampilkan pesan jika belum pernah di-set
41
+ unless @engine_configured
42
+ if RiderKick.configuration.engine_name
43
+ say "Using engine: #{RiderKick.configuration.engine_name}", :green
44
+ say "Using domain scope: #{RiderKick.configuration.domain_scope}", :green
45
+ else
46
+ say 'Using main app (no engine specified)', :blue
47
+ say "Using domain scope: #{RiderKick.configuration.domain_scope}", :blue
48
+ end
49
+ @engine_configured = true
50
+ end
51
+ end
52
+ end
53
+
54
+ def validate_file_exists!(path, context = '')
55
+ unless File.exist?(path)
56
+ error_message = "File not found: #{path}"
57
+ error_message += " (#{context})" if context.present?
58
+ raise FileNotFoundError.new(error_message, file_path: path, context: context)
59
+ end
60
+ end
61
+
62
+ def validate_yaml_format!(file_path)
63
+ validate_file_exists!(file_path, 'YAML validation')
64
+ begin
65
+ YAML.load_file(file_path)
66
+ rescue Psych::SyntaxError => e
67
+ raise YamlFormatError.new(
68
+ "Invalid YAML format in #{file_path}: #{e.message}",
69
+ file_path: file_path,
70
+ yaml_error: e.message
71
+ )
72
+ rescue => e
73
+ raise YamlFormatError.new(
74
+ "Error reading YAML file #{file_path}: #{e.message}",
75
+ file_path: file_path,
76
+ error: e.message
77
+ )
78
+ end
79
+ end
80
+
81
+ def validate_model_exists!(model_name)
82
+ # Try to constantize the model name
83
+ # This will raise NameError if the model doesn't exist
84
+
85
+ model_name.constantize
86
+ rescue NameError => e
87
+ # Try to load the model file if it exists (for test scenarios where file exists but not loaded)
88
+ model_path = find_model_file(model_name)
89
+ if model_path && File.exist?(model_path)
90
+ # Try loading the file
91
+ begin
92
+ load model_path
93
+ model_name.constantize
94
+ rescue => load_error
95
+ # If loading also fails, raise the original error
96
+ raise ModelNotFoundError.new(
97
+ "Model #{model_name} not found. Make sure the model exists and is valid.",
98
+ model_name: model_name,
99
+ original_error: e.message,
100
+ load_error: load_error.message
101
+ )
102
+ end
103
+ else
104
+ # Model file doesn't exist, raise error
105
+ raise ModelNotFoundError.new(
106
+ "Model #{model_name} not found. Make sure the model exists.",
107
+ model_name: model_name,
108
+ original_error: e.message
109
+ )
110
+ end
111
+ end
112
+
113
+ def find_model_file(model_name)
114
+ # Convert model name to file path
115
+ # Models::User -> app/models/models/user.rb or engines/.../app/models/.../user.rb
116
+ parts = model_name.split('::')
117
+ return nil if parts.empty?
118
+
119
+ file_name = parts.last.underscore + '.rb'
120
+
121
+ # Check main app models path first
122
+ main_path = File.join(RiderKick.configuration.models_path, file_name)
123
+ return main_path if File.exist?(main_path)
124
+
125
+ # Check engine models path if engine is set
126
+ if RiderKick.configuration.engine_name
127
+ engine_path = File.join(
128
+ 'engines',
129
+ RiderKick.configuration.engine_name.underscore,
130
+ 'app/models',
131
+ RiderKick.configuration.engine_name.underscore,
132
+ 'models',
133
+ file_name
134
+ )
135
+ return engine_path if File.exist?(engine_path)
136
+ end
137
+
138
+ nil
139
+ end
140
+
141
+ def validate_domains_path!
142
+ unless Dir.exist?(RiderKick.configuration.domains_path)
143
+ raise ValidationError.new(
144
+ 'Clean architecture structure not found. Run: bin/rails generate rider_kick:clean_arch --setup',
145
+ domains_path: RiderKick.configuration.domains_path
146
+ )
147
+ end
148
+ end
149
+
150
+ def detect_engine_name
151
+ # Detect engine dari struktur file system
152
+ # Cek apakah ada lib/<name>/engine.rb
153
+ return nil unless Dir.exist?('lib')
154
+
155
+ engines = []
156
+ Dir.glob('lib/*/engine.rb').each do |engine_file|
157
+ engine_name = File.basename(File.dirname(engine_file))
158
+ engines << engine_name.camelize if File.exist?(engine_file)
159
+ end
160
+
161
+ # Jika hanya ada satu engine, return itu
162
+ return engines.first if engines.length == 1
163
+
164
+ # Jika ada multiple engines, return nil (user harus specify via --engine option)
165
+ # Atau cek dari gem name di gemspec (fallback)
166
+ gemspec_files = Dir.glob('*.gemspec')
167
+ unless gemspec_files.empty?
168
+ gemspec_content = File.read(gemspec_files.first)
169
+ if gemspec_content =~ /\.name\s*=\s*["']([^"']+)["']/
170
+ gem_name = Regexp.last_match(1)
171
+ # Extract engine name dari gem name (e.g., "my_app-core" -> "Core")
172
+ engine_name = gem_name.split(/[-_]/).last.camelize
173
+ return engine_name if Dir.exist?("lib/#{engine_name.underscore}") && engines.include?(engine_name)
174
+ end
175
+ end
176
+
177
+ nil
178
+ end
179
+
180
+ def detect_domains_path
181
+ if RiderKick.configuration.engine_name
182
+ # Engine: engines/<engine_name>/app/domains/<domain_scope>
183
+ File.join('engines', RiderKick.configuration.engine_name.underscore, 'app/domains', RiderKick.configuration.domain_scope)
184
+ else
185
+ # Main app: app/domains/<domain_scope>
186
+ File.join('app/domains', RiderKick.configuration.domain_scope)
187
+ end
188
+ end
189
+ end
190
+ end
@@ -1,64 +1,146 @@
1
+ require_relative 'base_generator'
2
+ require_relative '../../rider-kick'
3
+
1
4
  module RiderKick
2
- class CleanArchGenerator < Rails::Generators::Base
5
+ class CleanArchGenerator < BaseGenerator
3
6
  source_root File.expand_path('templates', __dir__)
4
7
 
5
8
  class_option :setup, type: :boolean, default: false, desc: 'Setup domain structure'
9
+ class_option :engine, type: :string, default: nil, desc: 'Specify engine name (e.g., Core, Admin)'
10
+ class_option :domain, type: :string, default: '', desc: 'Specify domain scope (e.g., core/, admin/, api/v1/)'
6
11
 
7
12
  def validate_setup_option
8
- raise Thor::Error, 'The --setup option must be specified to create the domain structure.' unless options.setup
13
+ # Jika --engine dispecify, maka --setup otomatis dianggap true
14
+ return if options.engine.present?
15
+
16
+ unless options.setup
17
+ raise ValidationError.new(
18
+ 'The --setup option must be specified to create the domain structure.',
19
+ suggestion: 'Run: bin/rails generate rider_kick:clean_arch --setup'
20
+ )
21
+ end
9
22
  end
10
23
 
11
24
  def create_gem_dependencies
12
- append_to_file('Gemfile', gem_dependencies)
13
- say 'Gems added to Gemfile', :green
25
+ if options[:engine].present?
26
+ # Untuk engine, tambahkan ke Gemfile engine
27
+ engine_gemfile_path = "engines/#{options[:engine].downcase}/Gemfile"
28
+ append_to_file(engine_gemfile_path, gem_dependencies)
29
+ say "Gems added to #{engine_gemfile_path}", :green
30
+ else
31
+ # Untuk main app, tambahkan ke Gemfile host
32
+ append_to_file('Gemfile', gem_dependencies)
33
+ say 'Gems added to Gemfile', :green
34
+ end
14
35
  end
15
36
 
16
37
  def setup_configuration
38
+ configure_engine
17
39
  setup_domain_structure
18
40
 
19
- setup_initializers
20
- setup_dotenv
21
- setup_gitignore
22
- setup_rubocop
23
- setup_init_migration
24
- setup_models
25
- setup_active_storage
26
- setup_rspec
27
- setup_readme
41
+ if options[:engine].present?
42
+ # Untuk engine, hanya setup yang relevan
43
+ setup_init_migration
44
+ setup_models
45
+ setup_engine_generators
46
+ else
47
+ # Untuk main app, setup semua
48
+ setup_initializers
49
+ setup_dotenv
50
+ setup_gitignore
51
+ setup_rubocop
52
+ setup_init_migration
53
+ setup_models
54
+ setup_application_config
55
+ # setup_active_storage
56
+ setup_rspec
57
+ setup_readme
58
+ end
28
59
  end
29
60
 
30
61
  private
31
62
 
63
+ def domain_class_name
64
+ # Convert domain scope to class name
65
+ # Engine: "<Engine>" for ApplicationRecord, "<Engine>::<Domain>" for other classes
66
+ # Main app: "" for ApplicationRecord, "<AppName>" for root domain, "<Domain>" for scoped domain
67
+ scope = RiderKick.configuration.domain_scope.chomp('/')
68
+
69
+ if RiderKick.configuration.engine_name.present?
70
+ # Engine context: domain_scope always starts with engine name
71
+ engine_prefix = RiderKick.configuration.engine_name.to_s.camelize
72
+ engine_underscored = RiderKick.configuration.engine_name.to_s.underscore
73
+
74
+ if scope == engine_underscored
75
+ # Default engine domain: engines/my_engine/app/domains/my_engine/
76
+ engine_prefix
77
+ else
78
+ # Engine with sub-domain: engines/my_engine/app/domains/my_engine/admin/
79
+ # Remove engine prefix from scope and create namespace
80
+ sub_scope = scope.sub(/^#{engine_underscored}\//, '')
81
+ if sub_scope.empty?
82
+ engine_prefix
83
+ else
84
+ "#{engine_prefix}::#{sub_scope.split('/').map(&:camelize).join('::')}"
85
+ end
86
+ end
87
+ elsif scope.empty?
88
+ # Main app context
89
+ # Root domain in main app: use application name
90
+ begin
91
+ Rails.application&.class&.module_parent_name || 'MyApp'
92
+ rescue
93
+ 'MyApp' # Fallback for test environment
94
+ end
95
+ else
96
+ # Scoped domain in main app: use domain name only
97
+ scope.split('/').map(&:camelize).join('::')
98
+ end
99
+ end
100
+
101
+ def domain_class_name_for_application_record
102
+ # Special method for ApplicationRecord class names
103
+ # Engine: "<Engine>" (will become EngineApplicationRecord)
104
+ # Main app: "" (will become ApplicationRecord)
105
+ if RiderKick.configuration.engine_name.present?
106
+ # Engine context: use engine name
107
+ RiderKick.configuration.engine_name.to_s.camelize
108
+ else
109
+ # Main app context: empty string for ApplicationRecord
110
+ ''
111
+ end
112
+ end
113
+
32
114
  def setup_active_storage
33
115
  run 'rails active_storage:install'
34
116
  run 'rails db:migrate'
35
117
  end
36
118
 
37
119
  def setup_domain_structure
38
- empty_directory File.join("#{path_app}/domains/core/use_cases/contract")
39
- empty_directory File.join("#{path_app}/domains/core/repositories")
40
- empty_directory File.join("#{path_app}/domains/core/builders")
41
- empty_directory File.join("#{path_app}/domains/core/entities")
42
- empty_directory File.join("#{path_app}/domains/core/utils")
120
+ empty_directory File.join(RiderKick.configuration.domains_path, 'use_cases/contract')
121
+ empty_directory File.join(RiderKick.configuration.domains_path, 'repositories')
122
+ empty_directory File.join(RiderKick.configuration.domains_path, 'builders')
123
+ empty_directory File.join(RiderKick.configuration.domains_path, 'entities')
124
+ empty_directory File.join(RiderKick.configuration.domains_path, 'utils')
43
125
 
44
126
  # then
45
127
  copy_domain_file
46
128
  end
47
129
 
48
130
  def copy_domain_file
49
- template 'domains/core/use_cases/contract/pagination.rb.tt', File.join("#{path_app}/domains/core/use_cases/contract", 'pagination.rb')
50
- template 'domains/core/use_cases/contract/default.rb.tt', File.join("#{path_app}/domains/core/use_cases/contract", 'default.rb')
51
- template 'domains/core/use_cases/get_version.rb.tt', File.join("#{path_app}/domains/core/use_cases", 'get_version.rb')
131
+ template 'domains/core/use_cases/contract/pagination.rb.tt', File.join(RiderKick.configuration.domains_path, 'use_cases/contract', 'pagination.rb')
132
+ template 'domains/core/use_cases/contract/default.rb.tt', File.join(RiderKick.configuration.domains_path, 'use_cases/contract', 'default.rb')
133
+ template 'domains/core/use_cases/get_version.rb.tt', File.join(RiderKick.configuration.domains_path, 'use_cases', 'get_version.rb')
52
134
 
53
- template 'domains/core/builders/error.rb.tt', File.join("#{path_app}/domains/core/builders", 'error.rb')
54
- template 'domains/core/builders/pagination.rb.tt', File.join("#{path_app}/domains/core/builders", 'pagination.rb')
135
+ template 'domains/core/builders/error.rb.tt', File.join(RiderKick.configuration.domains_path, 'builders', 'error.rb')
136
+ template 'domains/core/builders/pagination.rb.tt', File.join(RiderKick.configuration.domains_path, 'builders', 'pagination.rb')
55
137
 
56
- template 'domains/core/entities/error.rb.tt', File.join("#{path_app}/domains/core/entities", 'error.rb')
57
- template 'domains/core/entities/pagination.rb.tt', File.join("#{path_app}/domains/core/entities", 'pagination.rb')
138
+ template 'domains/core/entities/error.rb.tt', File.join(RiderKick.configuration.domains_path, 'entities', 'error.rb')
139
+ template 'domains/core/entities/pagination.rb.tt', File.join(RiderKick.configuration.domains_path, 'entities', 'pagination.rb')
58
140
 
59
- template 'domains/core/repositories/abstract_repository.rb.tt', File.join("#{path_app}/domains/core/repositories", 'abstract_repository.rb')
60
- template 'domains/core/utils/abstract_utils.rb.tt', File.join("#{path_app}/domains/core/utils", 'abstract_utils.rb')
61
- template 'domains/core/utils/request_methods.rb.tt', File.join("#{path_app}/domains/core/utils", 'request_methods.rb')
141
+ template 'domains/core/repositories/abstract_repository.rb.tt', File.join(RiderKick.configuration.domains_path, 'repositories', 'abstract_repository.rb')
142
+ template 'domains/core/utils/abstract_utils.rb.tt', File.join(RiderKick.configuration.domains_path, 'utils', 'abstract_utils.rb')
143
+ template 'domains/core/utils/request_methods.rb.tt', File.join(RiderKick.configuration.domains_path, 'utils', 'request_methods.rb')
62
144
  end
63
145
 
64
146
  def setup_initializers
@@ -89,7 +171,18 @@ module RiderKick
89
171
  end
90
172
 
91
173
  def setup_init_migration
92
- template 'db/migrate/20220613145533_init_database.rb', File.join('db/migrate/20220613145533_init_database.rb')
174
+ if options[:engine].present?
175
+ # Untuk engine, buat migration dan structures directory di engine directory
176
+ engine_migrate_path = "engines/#{options[:engine].downcase}/db/migrate"
177
+ engine_structures_path = "engines/#{options[:engine].downcase}/db/structures"
178
+ empty_directory engine_migrate_path unless Dir.exist?(engine_migrate_path)
179
+ empty_directory engine_structures_path unless Dir.exist?(engine_structures_path)
180
+ template 'db/migrate/20220613145533_init_database.rb', File.join(engine_migrate_path, '20220613145533_init_database.rb')
181
+ else
182
+ # Untuk main app, buat di root db/migrate dan db/structures
183
+ template 'db/migrate/20220613145533_init_database.rb', File.join('db/migrate/20220613145533_init_database.rb')
184
+ empty_directory 'db/structures' unless Dir.exist?('db/structures')
185
+ end
93
186
  end
94
187
 
95
188
  # Helper untuk menyalin initializers
@@ -102,8 +195,21 @@ module RiderKick
102
195
  end
103
196
 
104
197
  def setup_models
105
- template 'models/application_record.rb', File.join('app/models/application_record.rb')
106
- template 'models/models.rb', File.join('app/models/models.rb')
198
+ if options[:engine].present?
199
+ # Untuk engine, buat application_record.rb di engine directory
200
+ engine_models_path = "engines/#{options[:engine].downcase}/app/models/#{options[:engine].downcase}"
201
+ empty_directory engine_models_path unless Dir.exist?(engine_models_path)
202
+ template 'models/application_record.rb', File.join(engine_models_path, 'application_record.rb')
203
+ else
204
+ # Untuk main app, buat di root app/models
205
+ template 'models/application_record.rb', File.join('app/models/application_record.rb')
206
+ end
207
+
208
+ # Untuk engine, models path akan di engines/<engine_name>/app/models/<engine_name>/models
209
+ # Untuk main app, models path akan di app/models/models
210
+ models_dir = RiderKick.configuration.models_path
211
+ empty_directory models_dir unless Dir.exist?(models_dir)
212
+ template 'models/models.rb', File.join(models_dir, 'models.rb')
107
213
  end
108
214
 
109
215
  def copy_env_development
@@ -114,16 +220,13 @@ module RiderKick
114
220
  end
115
221
 
116
222
  def gem_dependencies
117
- inject_into_file 'Gemfile', after: "group :development, :test do\n" do
118
- <<-CONFIG
119
-
120
- gem "rspec-rails"
121
- gem "factory_bot_rails"
122
- gem "shoulda-matchers"
123
- CONFIG
124
- end
125
-
126
223
  <<~RUBY
224
+ group :development, :test do
225
+ gem "rspec-rails"
226
+ gem "factory_bot_rails"
227
+ gem "faker"
228
+ gem "shoulda-matchers"
229
+ end
127
230
 
128
231
  # Env Variables
129
232
  gem 'dotenv-rails'
@@ -132,11 +235,14 @@ module RiderKick
132
235
  gem 'hashie'
133
236
 
134
237
  # uploading
135
- gem 'image_processing', '>= 1.2'
238
+ gem 'image_processing'
136
239
  gem 'ruby-vips'
137
240
 
138
241
  # pagination
139
- gem 'pagy', '~> 9.2'
242
+ gem 'pagy'
243
+
244
+ # models validation
245
+ gem 'schema_validations'
140
246
  RUBY
141
247
  end
142
248
 
@@ -148,12 +254,96 @@ module RiderKick
148
254
  template '.rspec', File.join('.rspec')
149
255
  template 'spec/support/repository_stubber.rb', File.join('spec/support/repository_stubber.rb')
150
256
  template 'spec/support/file_stuber.rb', File.join('spec/support/file_stuber.rb')
151
- say 'Mengonfigurasi FactoryBot...'
257
+ template 'spec/support/class_stubber.rb', File.join('spec/support/class_stubber.rb')
258
+ say 'Mengonfigurasi FactoryBot dan Faker...'
259
+ setup_factory_bot
152
260
  template 'spec/rails_helper.rb', File.join('spec/rails_helper.rb')
153
261
  end
154
262
 
155
- def path_app
156
- 'app'
263
+ def setup_factory_bot
264
+ # Create factories directory structure
265
+ empty_directory 'spec/factories'
266
+
267
+ # Create FactoryBot support file
268
+ template 'spec/support/factory_bot.rb', File.join('spec/support/factory_bot.rb')
269
+
270
+ # Create Faker support file
271
+ template 'spec/support/faker.rb', File.join('spec/support/faker.rb')
272
+
273
+ # Create example factories file
274
+ template 'spec/factories/.gitkeep', File.join('spec/factories/.gitkeep')
275
+ end
276
+
277
+ def setup_application_config
278
+ application_config_path = 'config/application.rb'
279
+
280
+ # Check if file exists
281
+ unless File.exist?(application_config_path)
282
+ say "File #{application_config_path} not found, skipping application config setup", :yellow
283
+ return
284
+ end
285
+
286
+ # Check if config already exists
287
+ application_content = File.read(application_config_path)
288
+ if application_content.include?("config.paths['db/migrate']")
289
+ say "Migration paths config already exists in #{application_config_path}", :green
290
+ return
291
+ end
292
+
293
+ # Find the class Application block and inject config after class definition
294
+ # Look for pattern: class Application < Rails::Application
295
+ if /class\s+Application\s+<\s+Rails::Application/.match?(application_content)
296
+ # Try to inject right after class definition line
297
+ inject_into_file application_config_path, after: /class\s+Application\s+<\s+Rails::Application\s*\n/ do
298
+ <<~RUBY
299
+ # Load migrations from engines
300
+ config.paths['db/migrate'] << './**/db/migrate'
301
+
302
+ RUBY
303
+ end
304
+ say "Added migration paths config to #{application_config_path}", :green
305
+ else
306
+ say "Could not find Application class in #{application_config_path}, skipping", :yellow
307
+ end
308
+ end
309
+
310
+ def setup_engine_generators
311
+ engine_name_underscore = options[:engine].downcase
312
+ options[:engine].camelize
313
+ engine_rb_path = "engines/#{engine_name_underscore}/lib/#{engine_name_underscore}/engine.rb"
314
+
315
+ # Check if file exists
316
+ unless File.exist?(engine_rb_path)
317
+ say "File #{engine_rb_path} not found, skipping engine generators config setup", :yellow
318
+ return
319
+ end
320
+
321
+ # Check if config already exists
322
+ engine_content = File.read(engine_rb_path)
323
+ if engine_content.include?('config.generators do |generate|')
324
+ say "Generator config already exists in #{engine_rb_path}", :green
325
+ return
326
+ end
327
+
328
+ # Find the engine class definition and inject config inside it
329
+ # Look for pattern: class Engine < Rails::Engine (inside module)
330
+ if /class\s+Engine\s+<\s+::Rails::Engine/.match?(engine_content)
331
+ # Try to inject right after the class definition line
332
+ inject_into_file engine_rb_path, after: /class\s+Engine\s+<\s+::Rails::Engine\s*\n/ do
333
+ <<~RUBY
334
+ config.generators do |generate|
335
+ generate.orm :active_record, primary_key_type: :uuid
336
+ generate.assets = false
337
+ generate.helper = false
338
+ generate.test_framework nil
339
+ end
340
+
341
+ RUBY
342
+ end
343
+ say "Added generator config to #{engine_rb_path}", :green
344
+ else
345
+ say "Could not find Engine class in #{engine_rb_path}, skipping", :yellow
346
+ end
157
347
  end
158
348
  end
159
349
  end