Skip to content

Commit

Permalink
First attempt at an implementation that detects Ruby on Rails naming …
Browse files Browse the repository at this point in the history
…conventions in references to PKs.
  • Loading branch information
johncurrier committed Sep 25, 2008
1 parent 39a9cd3 commit 5a81a4f
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 1 deletion.
28 changes: 28 additions & 0 deletions src/net/sourceforge/schemaspy/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class Config
private Boolean encodeCommentsEnabled;
private Boolean numRowsEnabled;
private Boolean meterEnabled;
private Boolean railsEnabled;
private Boolean evaluteAll;
private Boolean highQuality;
private Boolean lowQuality;
Expand Down Expand Up @@ -512,6 +513,33 @@ public boolean isRankDirBugEnabled() {
return rankDirBugEnabled;
}

/**
* Look for Ruby on Rails-based naming conventions in
* relationships between logical foreign keys and primary keys.<p>
*
* Basically all tables have a primary key named <code>ID</code>.
* All tables are named plural names.
* The columns that logically reference that <code>ID</code> are the singular
* form of the table name suffixed with <code>_ID</code>.<p>
*
* @param enabled
*/
public void setRailsEnabled(boolean enabled) {
this.railsEnabled = enabled;
}

/**
* @see #setRailsEnabled(boolean)
*
* @return
*/
public boolean isRailsEnabled() {
if (railsEnabled == null)
railsEnabled = options.remove("-rails");

return railsEnabled;
}

/**
* Allow Html In Comments - encode them unless otherwise specified
*/
Expand Down
43 changes: 43 additions & 0 deletions src/net/sourceforge/schemaspy/DbAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import java.util.regex.Pattern;
import net.sourceforge.schemaspy.model.ForeignKeyConstraint;
import net.sourceforge.schemaspy.model.ImpliedForeignKeyConstraint;
import net.sourceforge.schemaspy.model.RailsForeignKeyConstraint;
import net.sourceforge.schemaspy.model.Table;
import net.sourceforge.schemaspy.model.TableColumn;
import net.sourceforge.schemaspy.model.TableIndex;
import net.sourceforge.schemaspy.util.Inflection;

public class DbAnalyzer {
public static List<ImpliedForeignKeyConstraint> getImpliedConstraints(Collection<Table> tables) {
Expand Down Expand Up @@ -81,6 +83,47 @@ public int compare(TableColumn column1, TableColumn column2) {
return impliedConstraints;
}

/**
* Ruby on Rails-based databases typically have no real referential integrity
* constraints. Instead they have a somewhat unusual way of associating
* columns to primary keys.<p>
*
* Basically all tables have a primary key named <code>ID</code>.
* All tables are named plural names.
* The columns that logically reference that <code>ID</code> are the singular
* form of the table name suffixed with <code>_ID</code>.<p>
*
* A side-effect of calling this method is that the returned collection of
* constraints will be "tied into" the associated tables.
*
* @param tables
* @return List of {@link RailsForeignKeyConstraint}s
*/
public static List<RailsForeignKeyConstraint> getRailsConstraints(Map<String, Table> tables) {
List<RailsForeignKeyConstraint> railsConstraints = new ArrayList<RailsForeignKeyConstraint>(tables.size());

// iterate thru each column in each table looking for columns that
// match Rails naming conventions
for (Table table : tables.values()) {
for (TableColumn column : table.getColumns()) {
String columnName = column.getName().toLowerCase();
if (!column.isForeignKey() && column.allowsImpliedParents() && columnName.endsWith("_id")) {
String singular = columnName.substring(0, columnName.length() - 3);
String primaryTableName = Inflection.pluralize(singular);
Table primaryTable = tables.get(primaryTableName);
if (primaryTable != null) {
TableColumn primaryColumn = primaryTable.getColumn("ID");
if (primaryColumn != null) {
railsConstraints.add(new RailsForeignKeyConstraint(primaryColumn, column));
}
}
}
}
}

return railsConstraints;
}

/**
* Returns a <code>List</code> of all of the <code>ForeignKeyConstraint</code>s
* used by the specified tables.
Expand Down
5 changes: 4 additions & 1 deletion src/net/sourceforge/schemaspy/SchemaAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,10 @@ public int analyze(Config config) throws Exception {
// getting implied constraints has a side-effect of associating the parent/child tables, so don't do it
// here unless they want that behavior
List<ImpliedForeignKeyConstraint> impliedConstraints = null;
if (includeImpliedConstraints)
if (config.isRailsEnabled()) {
DbAnalyzer.getRailsConstraints(db.getTablesByName());
impliedConstraints = new ArrayList<ImpliedForeignKeyConstraint>();
} else if (includeImpliedConstraints)
impliedConstraints = DbAnalyzer.getImpliedConstraints(tables);
else
impliedConstraints = new ArrayList<ImpliedForeignKeyConstraint>();
Expand Down
9 changes: 9 additions & 0 deletions src/net/sourceforge/schemaspy/model/Database.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ public String getDescription() {
public Collection<Table> getTables() {
return tables.values();
}

/**
* Return a {@link Map} of all {@link Table}s keyed by their name.
*
* @return
*/
public Map<String, Table> getTablesByName() {
return tables;
}

public Collection<View> getViews() {
return views.values();
Expand Down
30 changes: 30 additions & 0 deletions src/net/sourceforge/schemaspy/model/RailsForeignKeyConstraint.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package net.sourceforge.schemaspy.model;

import net.sourceforge.schemaspy.DbAnalyzer;

/**
* See {@link DbAnalyzer#getRailsConstraints(java.util.Map)} for
* details on Rails naming conventions.
*
* @author John Currier
*/
public class RailsForeignKeyConstraint extends ForeignKeyConstraint {
/**
* @param parentColumn
* @param childColumn
*/
public RailsForeignKeyConstraint(TableColumn parentColumn, TableColumn childColumn) {
super(parentColumn, childColumn);
}

/**
* Normally the name of the constraint, but this one is implied by
* Rails naming conventions.
*
* @return
*/
@Override
public String getName() {
return "ByRailsConventionConstraint";
}
}

0 comments on commit 5a81a4f

Please sign in to comment.