Class: DatabaseModel::Generator::Base
- Inherits:
-
Object
- Object
- DatabaseModel::Generator::Base
- Defined in:
- lib/database_model_generator.rb
Direct Known Subclasses
Instance Attribute Summary collapse
-
#belongs_to ⇒ Object
readonly
Returns the value of attribute belongs_to.
-
#column_info ⇒ Object
readonly
Returns the value of attribute column_info.
-
#connection ⇒ Object
readonly
Returns the value of attribute connection.
-
#constraints ⇒ Object
readonly
Returns the value of attribute constraints.
-
#dependencies ⇒ Object
readonly
Returns the value of attribute dependencies.
-
#enum_columns ⇒ Object
readonly
Returns the value of attribute enum_columns.
-
#foreign_keys ⇒ Object
readonly
Returns the value of attribute foreign_keys.
-
#model ⇒ Object
readonly
Returns the value of attribute model.
-
#polymorphic_associations ⇒ Object
readonly
Returns the value of attribute polymorphic_associations.
-
#primary_keys ⇒ Object
readonly
Returns the value of attribute primary_keys.
-
#table ⇒ Object
readonly
Returns the value of attribute table.
-
#view ⇒ Object
readonly
Returns the value of attribute view.
Instance Method Summary collapse
- #build_composite_recommendations(recommendations) ⇒ Object
- #build_date_recommendations(recommendations) ⇒ Object
- #build_foreign_key_recommendations(recommendations) ⇒ Object
- #build_full_text_index_sql(column) ⇒ Object
- #build_full_text_recommendations(recommendations) ⇒ Object
- #build_status_recommendations(recommendations) ⇒ Object
- #build_unique_constraint_recommendations(recommendations) ⇒ Object
- #column_names ⇒ Object
- #constraint_summary ⇒ Object
- #detect_polymorphic_associations ⇒ Object
- #disconnect ⇒ Object
- #enum_column_names ⇒ Object
- #enum_definitions ⇒ Object
- #enum_validation_suggestions ⇒ Object
- #find_fk_table(fk) ⇒ Object
- #generate(table, view = false) ⇒ Object
- #generated? ⇒ Boolean
- #get_column_size(column) ⇒ Object
- #get_full_text_index_type ⇒ Object
- #has_enum_columns? ⇒ Boolean
- #has_polymorphic_associations? ⇒ Boolean
- #index_recommendations ⇒ Object
-
#initialize(connection) ⇒ Base
constructor
A new instance of Base.
- #is_date_type?(column) ⇒ Boolean
-
#is_string_type?(column) ⇒ Boolean
Abstract helper methods for database-specific type checking.
- #is_text_type?(column) ⇒ Boolean
- #polymorphic_association_names ⇒ Object
- #polymorphic_has_many_suggestions ⇒ Object
- #table_exists? ⇒ Boolean
Constructor Details
#initialize(connection) ⇒ Base
Returns a new instance of Base.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/database_model_generator.rb', line 59 def initialize(connection) raise ArgumentError, "Connection cannot be nil" if connection.nil? validate_connection(connection) @connection = connection @constraints = [] @primary_keys = [] @foreign_keys = [] @dependencies = [] @belongs_to = [] @polymorphic_associations = [] @enum_columns = [] @column_info = [] @table = nil @model = nil end |
Instance Attribute Details
#belongs_to ⇒ Object (readonly)
Returns the value of attribute belongs_to.
55 56 57 |
# File 'lib/database_model_generator.rb', line 55 def belongs_to @belongs_to end |
#column_info ⇒ Object (readonly)
Returns the value of attribute column_info.
56 57 58 |
# File 'lib/database_model_generator.rb', line 56 def column_info @column_info end |
#connection ⇒ Object (readonly)
Returns the value of attribute connection.
55 56 57 |
# File 'lib/database_model_generator.rb', line 55 def connection @connection end |
#constraints ⇒ Object (readonly)
Returns the value of attribute constraints.
55 56 57 |
# File 'lib/database_model_generator.rb', line 55 def constraints @constraints end |
#dependencies ⇒ Object (readonly)
Returns the value of attribute dependencies.
56 57 58 |
# File 'lib/database_model_generator.rb', line 56 def dependencies @dependencies end |
#enum_columns ⇒ Object (readonly)
Returns the value of attribute enum_columns.
57 58 59 |
# File 'lib/database_model_generator.rb', line 57 def enum_columns @enum_columns end |
#foreign_keys ⇒ Object (readonly)
Returns the value of attribute foreign_keys.
55 56 57 |
# File 'lib/database_model_generator.rb', line 55 def foreign_keys @foreign_keys end |
#model ⇒ Object (readonly)
Returns the value of attribute model.
56 57 58 |
# File 'lib/database_model_generator.rb', line 56 def model @model end |
#polymorphic_associations ⇒ Object (readonly)
Returns the value of attribute polymorphic_associations.
57 58 59 |
# File 'lib/database_model_generator.rb', line 57 def polymorphic_associations @polymorphic_associations end |
#primary_keys ⇒ Object (readonly)
Returns the value of attribute primary_keys.
56 57 58 |
# File 'lib/database_model_generator.rb', line 56 def primary_keys @primary_keys end |
#table ⇒ Object (readonly)
Returns the value of attribute table.
56 57 58 |
# File 'lib/database_model_generator.rb', line 56 def table @table end |
#view ⇒ Object (readonly)
Returns the value of attribute view.
56 57 58 |
# File 'lib/database_model_generator.rb', line 56 def view @view end |
Instance Method Details
#build_composite_recommendations(recommendations) ⇒ Object
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 |
# File 'lib/database_model_generator.rb', line 583 def build_composite_recommendations(recommendations) foreign_key_columns = recommendations[:foreign_keys].map { |fk| fk[:column] } date_column_names = recommendations[:date_queries].map { |d| d[:column] } status_column_names = recommendations[:status_enum].map { |s| s[:column] } if foreign_key_columns.any? && date_column_names.any? fk_col = foreign_key_columns.first date_col = date_column_names.find { |col| col =~ /created/ } || date_column_names.first recommendations[:composite] << { columns: [fk_col, date_col], sql: "add_index :#{@table.downcase}, [:#{fk_col}, :#{date_col}]", reason: "Composite index for filtering by #{fk_col} and #{date_col}" } end if status_column_names.any? && date_column_names.any? status_col = status_column_names.first date_col = date_column_names.find { |col| col =~ /created/ } || date_column_names.first recommendations[:composite] << { columns: [status_col, date_col], sql: "add_index :#{@table.downcase}, [:#{status_col}, :#{date_col}]", reason: "Composite index for filtering by #{status_col} and #{date_col}" } end end |
#build_date_recommendations(recommendations) ⇒ Object
553 554 555 556 557 558 559 560 561 562 563 564 565 566 |
# File 'lib/database_model_generator.rb', line 553 def build_date_recommendations(recommendations) date_columns = @column_info.select do |col| is_date_type?(col) || col.name.downcase =~ /(created_at|updated_at|modified_date|start_date|end_date|due_date)/ end date_columns.each do |col| recommendations[:date_queries] << { column: col.name.downcase, sql: "add_index :#{@table.downcase}, :#{col.name.downcase}", reason: "Date queries for #{col.name}" } end end |
#build_foreign_key_recommendations(recommendations) ⇒ Object
524 525 526 527 528 529 530 531 532 533 534 535 536 |
# File 'lib/database_model_generator.rb', line 524 def build_foreign_key_recommendations(recommendations) @belongs_to.each do |table_ref| fk_column = "#{table_ref.downcase.gsub(/s$/, '')}_id" col = @column_info.find { |c| c.name.downcase == fk_column } if col recommendations[:foreign_keys] << { column: col.name.downcase, sql: "add_index :#{@table.downcase}, :#{col.name.downcase}", reason: "Foreign key index for #{col.name}" } end end end |
#build_full_text_index_sql(column) ⇒ Object
643 644 645 |
# File 'lib/database_model_generator.rb', line 643 def build_full_text_index_sql(column) raise NotImplementedError, "Subclasses must implement build_full_text_index_sql" end |
#build_full_text_recommendations(recommendations) ⇒ Object
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 |
# File 'lib/database_model_generator.rb', line 609 def build_full_text_recommendations(recommendations) text_search_columns = @column_info.select do |col| is_text_type?(col) && col.name.downcase =~ /(name|title|description|content|text|search)/ && (get_column_size(col).nil? || get_column_size(col) > 50) end text_search_columns.each do |col| recommendations[:full_text] << { column: col.name.downcase, sql: build_full_text_index_sql(col), reason: "Full-text search for #{col.name}", type: get_full_text_index_type } end end |
#build_status_recommendations(recommendations) ⇒ Object
568 569 570 571 572 573 574 575 576 577 578 579 580 581 |
# File 'lib/database_model_generator.rb', line 568 def build_status_recommendations(recommendations) status_columns = @column_info.select do |col| col.name.downcase =~ /(status|state|type|role|priority|level|category)$/ && is_string_type?(col) end status_columns.each do |col| recommendations[:status_enum] << { column: col.name.downcase, sql: "add_index :#{@table.downcase}, :#{col.name.downcase}", reason: "Status/enum queries for #{col.name}" } end end |
#build_unique_constraint_recommendations(recommendations) ⇒ Object
538 539 540 541 542 543 544 545 546 547 548 549 550 551 |
# File 'lib/database_model_generator.rb', line 538 def build_unique_constraint_recommendations(recommendations) unique_columns = @column_info.select do |col| col.name.downcase =~ /(email|username|code|slug|uuid|token)/ || (!col.nullable? && col.name.downcase =~ /(name|title)$/ && is_string_type?(col)) end unique_columns.each do |col| recommendations[:unique_constraints] << { column: col.name.downcase, sql: "add_index :#{@table.downcase}, :#{col.name.downcase}, unique: true", reason: "Unique constraint for #{col.name}" } end end |
#column_names ⇒ Object
100 101 102 103 |
# File 'lib/database_model_generator.rb', line 100 def column_names return [] unless generated? @column_info.map(&:name) end |
#constraint_summary ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/database_model_generator.rb', line 115 def constraint_summary return {} unless generated? summary = Hash.new { |h, k| h[k] = [] } @constraints.each do |constraint| type = format_constraint_type(constraint) column_name = get_constraint_column_name(constraint) summary[column_name.downcase] << type end summary end |
#detect_polymorphic_associations ⇒ Object
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
# File 'lib/database_model_generator.rb', line 399 def detect_polymorphic_associations polymorphic_assocs = [] column_names = @column_info.map { |col| col.name.downcase } # Look for patterns like: commentable_type + commentable_id # or imageable_type + imageable_id, etc. type_columns = column_names.select { |name| name.end_with?('_type') } type_columns.each do |type_col| base_name = type_col.gsub(/_type$/, '') id_col = "#{base_name}_id" if column_names.include?(id_col) # Check if this isn't already a regular foreign key unless @foreign_keys.map(&:downcase).include?(id_col) polymorphic_assocs << { name: base_name, foreign_key: id_col, foreign_type: type_col, association_name: base_name } end end end polymorphic_assocs end |
#disconnect ⇒ Object
110 111 112 113 |
# File 'lib/database_model_generator.rb', line 110 def disconnect # Default implementation - subclasses should override @connection = nil end |
#enum_column_names ⇒ Object
438 439 440 |
# File 'lib/database_model_generator.rb', line 438 def enum_column_names @enum_columns.map { |enum_col| enum_col[:name] } end |
#enum_definitions ⇒ Object
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
# File 'lib/database_model_generator.rb', line 442 def enum_definitions # Generate Rails enum definitions definitions = [] @enum_columns.each do |enum_col| if enum_col[:type] == :integer # Integer enum: { draft: 0, published: 1, archived: 2 } values = enum_col[:values].each_with_index.map { |val, idx| "#{val}: #{idx}" }.join(', ') definitions << "enum #{enum_col[:column_name]}: { #{values} }" else # String enum: { low: 'low', medium: 'medium', high: 'high' } values = enum_col[:values].map { |val| "#{val}: '#{val}'" }.join(', ') definitions << "enum #{enum_col[:column_name]}: { #{values} }" end end definitions end |
#enum_validation_suggestions ⇒ Object
459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'lib/database_model_generator.rb', line 459 def enum_validation_suggestions # Suggest validations for enum columns suggestions = [] @enum_columns.each do |enum_col| suggestions << { column: enum_col[:column_name], validation: "validates :#{enum_col[:column_name]}, inclusion: { in: #{enum_col[:column_name].pluralize}.keys }", description: "Validates #{enum_col[:column_name]} is a valid enum value" } end suggestions end |
#find_fk_table(fk) ⇒ Object
491 492 493 494 |
# File 'lib/database_model_generator.rb', line 491 def find_fk_table(fk) # Default implementation - may be overridden by subclasses fk.gsub(/_id$/i, '').pluralize rescue "#{fk.gsub(/_id$/i, '')}s" end |
#generate(table, view = false) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/database_model_generator.rb', line 76 def generate(table, view = false) raise ArgumentError, "Table name cannot be nil or empty" if table.nil? || table.strip.empty? @table = normalize_table_name(table) @model = generate_model_name(table) @view = view reset_state get_column_info get_primary_keys get_foreign_keys unless view get_belongs_to get_constraints unless view get_polymorphic_associations unless view get_enum_columns unless view get_dependencies unless view self end |
#generated? ⇒ Boolean
96 97 98 |
# File 'lib/database_model_generator.rb', line 96 def generated? !@table.nil? && !@column_info.empty? end |
#get_column_size(column) ⇒ Object
639 640 641 |
# File 'lib/database_model_generator.rb', line 639 def get_column_size(column) raise NotImplementedError, "Subclasses must implement get_column_size" end |
#get_full_text_index_type ⇒ Object
647 648 649 |
# File 'lib/database_model_generator.rb', line 647 def get_full_text_index_type raise NotImplementedError, "Subclasses must implement get_full_text_index_type" end |
#has_enum_columns? ⇒ Boolean
434 435 436 |
# File 'lib/database_model_generator.rb', line 434 def has_enum_columns? !@enum_columns.empty? end |
#has_polymorphic_associations? ⇒ Boolean
430 431 432 |
# File 'lib/database_model_generator.rb', line 430 def has_polymorphic_associations? !@polymorphic_associations.empty? end |
#index_recommendations ⇒ Object
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/database_model_generator.rb', line 127 def index_recommendations return {} unless generated? recommendations = { foreign_keys: [], unique_constraints: [], date_queries: [], status_enum: [], composite: [], full_text: [] } build_foreign_key_recommendations(recommendations) build_unique_constraint_recommendations(recommendations) build_date_recommendations(recommendations) build_status_recommendations(recommendations) build_composite_recommendations(recommendations) build_full_text_recommendations(recommendations) recommendations end |
#is_date_type?(column) ⇒ Boolean
631 632 633 |
# File 'lib/database_model_generator.rb', line 631 def is_date_type?(column) raise NotImplementedError, "Subclasses must implement is_date_type?" end |
#is_string_type?(column) ⇒ Boolean
Abstract helper methods for database-specific type checking
627 628 629 |
# File 'lib/database_model_generator.rb', line 627 def is_string_type?(column) raise NotImplementedError, "Subclasses must implement is_string_type?" end |
#is_text_type?(column) ⇒ Boolean
635 636 637 |
# File 'lib/database_model_generator.rb', line 635 def is_text_type?(column) raise NotImplementedError, "Subclasses must implement is_text_type?" end |
#polymorphic_association_names ⇒ Object
472 473 474 |
# File 'lib/database_model_generator.rb', line 472 def polymorphic_association_names @polymorphic_associations.map { |assoc| assoc[:name] } end |
#polymorphic_has_many_suggestions ⇒ Object
476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
# File 'lib/database_model_generator.rb', line 476 def polymorphic_has_many_suggestions # Suggest has_many associations for models that could be polymorphic parents suggestions = [] @polymorphic_associations.each do |assoc| # For a 'commentable' polymorphic association, suggest: # has_many :comments, as: :commentable, dependent: :destroy child_model = pluralize_for_has_many(assoc[:name]) suggestions << { association: "has_many :#{child_model}, as: :#{assoc[:name]}, dependent: :destroy", description: "For models that can have #{child_model} (polymorphic)" } end suggestions end |
#table_exists? ⇒ Boolean
105 106 107 108 |
# File 'lib/database_model_generator.rb', line 105 def table_exists? return false unless @table check_table_exists(@table) end |