diff --git a/bullet_train-super_scaffolding/lib/bullet_train/super_scaffolding/scaffolders/join_model_scaffolder.rb b/bullet_train-super_scaffolding/lib/bullet_train/super_scaffolding/scaffolders/join_model_scaffolder.rb index db929e6ad..a5012f76c 100644 --- a/bullet_train-super_scaffolding/lib/bullet_train/super_scaffolding/scaffolders/join_model_scaffolder.rb +++ b/bullet_train-super_scaffolding/lib/bullet_train/super_scaffolding/scaffolders/join_model_scaffolder.rb @@ -82,8 +82,11 @@ def run transformer.suppress_could_not_find = false # Add the `has_many ... through:` association in both directions. - transformer.add_has_many_through_associations(has_many_through_transformer) - inverse_transformer.add_has_many_through_associations(inverse_has_many_through_transformer) + # We pass the "opposing" attribute so that both the association name and the + # class name get wired up correctly in cases where they don't match. For instance + # if you want an `assigned_to_membership` relationship to the `memberships` table. + transformer.add_has_many_through_associations(has_many_through_transformer, attributes[1]) + inverse_transformer.add_has_many_through_associations(inverse_has_many_through_transformer, attributes[0]) additional_steps = (transformer.additional_steps + has_many_through_transformer.additional_steps + inverse_transformer.additional_steps + inverse_has_many_through_transformer.additional_steps).uniq diff --git a/bullet_train-super_scaffolding/lib/scaffolding/attribute.rb b/bullet_train-super_scaffolding/lib/scaffolding/attribute.rb index c027c9991..303ec9b99 100644 --- a/bullet_train-super_scaffolding/lib/scaffolding/attribute.rb +++ b/bullet_train-super_scaffolding/lib/scaffolding/attribute.rb @@ -58,6 +58,20 @@ def association_class_name name.split("_id").first end + def plural_association_name + association_class_name.tableize + end + + def class_name_matches? + # if namespaces are involved, just don't... + # TODO: I'm not entirely sure that extracting this conditional was the right thing to do. + # Are there scenarios where we want to assume a match even when namespaces are involved? + if options[:class_name].include?("::") + return false + end + name_without_id.tableize == options[:class_name].tableize.tr("/", "_") + end + def is_association? is_belongs_to? || is_has_many? end diff --git a/bullet_train-super_scaffolding/lib/scaffolding/transformer.rb b/bullet_train-super_scaffolding/lib/scaffolding/transformer.rb index e8c8640c1..96b882cd6 100644 --- a/bullet_train-super_scaffolding/lib/scaffolding/transformer.rb +++ b/bullet_train-super_scaffolding/lib/scaffolding/transformer.rb @@ -603,10 +603,27 @@ def add_has_many_association has_many_string.split(",").first.split(":").last end - def add_has_many_through_associations(has_many_through_transformer) + def add_has_many_through_associations(has_many_through_transformer, attribute_definition) + attribute = Scaffolding::Attribute.new(attribute_definition, :crud_field, 0) has_many_association = add_has_many_association - has_many_through_string = has_many_through_transformer.transform_string("has_many :completely_concrete_tangible_things, through: :$HAS_MANY_ASSOCIATION") + has_many_through_parts = [ + "has_many :completely_concrete_tangible_things", + "through: :$HAS_MANY_ASSOCIATION" + ] + + unless attribute.class_name_matches? + has_many_through_parts << "class_name: \"Scaffolding::CompletelyConcrete::TangibleThing\"" + end + has_many_through_string = has_many_through_transformer.transform_string(has_many_through_parts.join(", ")) has_many_through_string.gsub!("$HAS_MANY_ASSOCIATION", has_many_association) + unless attribute.class_name_matches? + # This handles the case where you're generating a join model where you want association names + # to be different than the class name, so it'll transform something like this: + # has_many :memberships, through: :assignments, class_name: "Membership" + # into something like this: + # has_many :assigned_to_memberships, through: :assignments, class_name: "Membership" + has_many_through_string.gsub!("has_many :#{attribute.plural_association_name}", "has_many :#{attribute.name_without_id.tableize}") + end add_line_to_file(transform_string("./app/models/scaffolding/absolutely_abstract/creative_concept.rb"), has_many_through_string, HAS_MANY_HOOK, prepend: true) end @@ -1142,15 +1159,8 @@ def set_default_#{attribute.name} end - class_name_matches = attribute.name_without_id.tableize == attribute.options[:class_name].tableize.tr("/", "_") - - # but also, if namespaces are involved, just don't... - if attribute.options[:class_name].include?("::") - class_name_matches = false - end - # unless the table name matches the association name. - unless class_name_matches + unless attribute.class_name_matches? if migration_file_name # There are two forms this association creation can take. replace_in_file(migration_file_name, "foreign_key: true", "foreign_key: {to_table: \"#{attribute.options[:class_name].tableize.tr("/", "_")}\"}", /t\.references :#{attribute.name_without_id}/) @@ -1167,7 +1177,7 @@ def set_default_#{attribute.name} # if the `belongs_to` is already there from `rails g model`.. scaffold_replace_line_in_file( "./app/models/scaffolding/completely_concrete/tangible_thing.rb", - class_name_matches ? + attribute.class_name_matches? ? "belongs_to :#{attribute.name_without_id}#{optional_line}" : "belongs_to :#{attribute.name_without_id}, class_name: \"#{attribute.options[:class_name]}\"#{optional_line}", "belongs_to :#{attribute.name_without_id}" @@ -1177,7 +1187,7 @@ def set_default_#{attribute.name} # however, this won't do anything if the association is already there. scaffold_add_line_to_file( "./app/models/scaffolding/completely_concrete/tangible_thing.rb", - class_name_matches ? + attribute.class_name_matches? ? "belongs_to :#{attribute.name_without_id}#{optional_line}" : "belongs_to :#{attribute.name_without_id}, class_name: \"#{attribute.options[:class_name]}\"#{optional_line}", BELONGS_TO_HOOK,