@@ -166,33 +166,51 @@ def self.content(app_name, db_name)
166
166
db = #{ app_name } .raw_db_connection
167
167
model_file_name = args[:model_file_name]&.to_s
168
168
169
- models_dir = #{ app_name } .root
169
+ app_root_dir = TestApp.root
170
+ app_dir = File.join(TestApp.root, "app")
170
171
171
- Dir.glob("app/models/ **/*.rb").each do |model_file|
172
+ Dir.glob("app/**/*.rb").each do |model_file|
172
173
next if !model_file_name.nil? && model_file == model_file_name
173
174
174
- model_path = File.expand_path(model_file, models_dir)
175
- modules = model_file.gsub("app/models/", "").gsub(".rb", "").split("/").map { |mod| Zeitwerk::Inflector.new.camelize(mod, model_path) }
176
- const_name = modules.join("::")
177
- model_klass = Object.const_get(const_name)
178
- next unless model_klass.ancestors.include?(Kirei::Model)
175
+ model_path = File.expand_path(model_file, app_root_dir)
176
+ loader = Zeitwerk::Registry.loaders.find { |l| l.tag == "app" }
177
+
178
+ full_path = File.expand_path(model_file, app_root_dir)
179
+ klass_constant_name = loader.inflector.camelize(File.basename(model_file, ".rb"), full_path)
180
+
181
+ #
182
+ # root namespaces in Zeitwerk are flattend, e.g. if "app/models" is a root namespace
183
+ # then a file "app/models/airport.rb" is loaded as "::Airport".
184
+ # if it weren't a root namespace, it would be "::Models::Airport".
185
+ #
186
+ root_dir_namespaces = loader.dirs.filter_map { |dir| dir == app_dir ? nil : Pathname.new(dir).relative_path_from(Pathname.new(app_dir)).to_s }
187
+ relative_path = Pathname.new(full_path).relative_path_from(Pathname.new(app_dir)).to_s
188
+ root_dir_of_model = root_dir_namespaces.find { |root_dir| relative_path.start_with?(root_dir) }
189
+ relative_path.sub!("\# {root_dir_of_model}/", "") unless root_dir_of_model.nil? || root_dir_of_model.empty?
190
+
191
+ namespace_parts = relative_path.split("/")
192
+ namespace_parts.pop
193
+ namespace_parts.map! { |part| loader.inflector.camelize(part, full_path) }
194
+
195
+ constant_name = "\# {namespace_parts.join('::')}::\# {klass_constant_name}"
196
+
197
+ model_klass = Object.const_get(constant_name)
198
+ next unless model_klass.respond_to?(:table_name)
179
199
180
200
table_name = model_klass.table_name
181
201
schema = db.schema(table_name)
182
202
183
203
schema_comments = format_schema_comments(table_name, schema)
204
+ file_content = File.read(model_path)
184
205
185
- file_contents = File.read(model_path)
186
-
187
- # Remove existing schema info comments if present
188
- updated_contents = file_contents.sub(/# == Schema Info\\ n(.*?)(\\ n#\\n)?\\n(?=\\s*(?:class|module))/m, "")
206
+ file_content_without_schema_info = file_content.sub(/# == Schema Info\\ n(.*?)(\\ n#\\n)?\\n(?=\\s*(?:class|module))/m, "")
189
207
190
208
# Insert the new schema comments before the module/class definition
191
- first_const = modules .first
192
- first_module_or_class = modules.count == 1 ? "class #{ first_const } " : "module #{ first_const } "
193
- modified_contents = updated_contents .sub(/(A|\n )(#{ first_module_or_class } )/m, "\\ 1 #{ schema_comments } \n \n \\ 2")
209
+ first_module = namespace_parts .first
210
+ first_module_or_class = first_module.nil? ? "class \# {klass_constant_name }" : "module \# {first_module }"
211
+ modified_content = file_content_without_schema_info .sub(/(A|\\ n)(\ # {first_module_or_class})/m, "\\ \\ 1 \ # {schema_comments}\\ n \\ n \\ \\ 2")
194
212
195
- File.write(model_path, modified_contents )
213
+ File.write(model_path, modified_content )
196
214
end
197
215
end
198
216
end
@@ -215,7 +233,7 @@ def format_schema_comments(table_name, schema)
215
233
type ||= info[:db_type]
216
234
null = info[:allow_null] ? 'null' : 'not null'
217
235
primary_key = info[:primary_key] ? ', primary key' : ''
218
- lines << "# \# {name.to_s.ljust(20)}:\# {type} \# {null}\# {primary_key}"
236
+ lines << "# \# {name.to_s.ljust(20)}:\# {type.to_s.ljust(20)} \# {null}\# {primary_key}"
219
237
end
220
238
lines.join("\\ n") + "\\ n#"
221
239
end
0 commit comments