While doing some Ruby on Rails code refactoring, I realized I’d like to visualize the database schema of the application.
An easy possibility is to print out db/schema.rb. This seems a bit too linear and little visual to me. OK, I’d better keep thinking.
An enterprise approach would be to take a CASE machinery and feed it with the create DB scripts. That is solid and reliable, but a kind of overkill. What’s more, a Rails app with convention over configuration can do well without foreign keys and so it usually does. This means that relations between tables are mostly implied by column names rather than explicitly declared as DB constraints. Thus, hardly any CASE tool can derive such relations from a DB schema.
OK, let’s try to go the Rails way and put together existing ingredients, add a little cream and see what we can get:
- there are many UML modeling tools out there, although few of the free ones can reverse-engineer a DB schema
- there’s the SchemaDumper in ActiveRecord, which generates the schema.rb based on DB reflection
- there’s this standard XMI format for data interchange between modeling tools
OK, so let’s try and tweak the SchemaDumper to generate XMI, then import it into a UML modeling tool and get the schema diagram from there.
So, I morphed SchemaDumper into UmlDumper, which generates XMI in the widely adopted Rose flavor. E.g. a Typo blog database will result in schema.xml like this:
Then I import this XMI into a UML tool – StarUML in my case – to get the basic picture. Yellow boxes are DB tables, grey boxes are presumed “virtual” tables referenced by columns in other tables.
I play a little with the layout and incorporate the “virtual” tables into the hierarchy. For completeness, I also add a few model classes, which show up only in Rails code, not in the database (manual entries in blue color):
I export the diagram from the tool to print it, share it and enjoy.
Now, that’s it! The approach is quite universal (not yet true for UmlDumper), I can quickly reuse it in my further Rails projects, and so can you.
Note: for one-time use I created a rake task.
namespace :uml do
desc "Generate an XMI db/schema.xml file describing the current DB as seen by AR. Produces XMI 1.1 for UML 1.3 Rose Extended, viewable e.g. by StarUML"
task :schema => :environment do
require 'lib/uml_dumper.rb'
File.open("db/schema.xml", "w") do |file|
ActiveRecord::UmlDumper.dump(ActiveRecord::Base.connection, file)
end
puts "Done. Schema XMI created as db/schema.xml."
end
end
For frequent use (fine-tuning the export), it is better to start the Rails console and call UmlDumper from there to avoid re-loading the whole Rails stack all over again.
This looks similar to the Rails Application Visualizer plugin (which has Graphviz as a dependency instead of UMLDumper). RAV also allows you to visualize your controller structure and the associated methods.
Overall, looks good, though it would be nice if it included all models automatically.
good stuff. But I am kinda clumsy. How do I “tweak the SchemaDumper to generate XMI? ” Can you specify a bit more? Thanks! =)
If you’re using PostgreSQL there are two more ways to visualize the schema –
DBGraffle4 plugin for OmniGraffle Professional (available only on Mac)
postgresql_autodoc (multiplatform, multioutput)
OmniGraffle in combination with DBGraffle produces much more readable diagram than postgresql_autodoc, however, a little ordering of the boxes is needed.
Jacquie: I manually modified the SchemaDumper output statements to print XML instead of Ruby code – you can download the result from http://blog.zmok.net/files/uml_dumper.rb, which is to be placed in #{RAILS_ROOT}/lib
thank Miroslav, good tool, I change this to rails plugin.
easy install version:
./script/plugin install http://cnruby.googlecode.com/svn/trunk/plugins/uml
use this:
rake uml:schema
Thanks, Miroslav, for this useful tool. And thanks (多谢), cnruby, for packaging it as a simple plugin. Everything works (for me at least) exactly as advertised.
Thanks Miroslav. Just what I was looking for. The XMI imports into Visual Paradigm and produces a very useful diagram.
Thank you very much Miroslav. I was using DBDesigner and reverse engineering to generate the database model of my application but your solution is 10x times cooler: UML & XMI are standards, associations are generated …
One thing, I believe you made a small mistake regarding the direction of the association (the arrow)
Take a look at
http://www.agilemodeling.com/artifacts/classDiagram.htm#Associations
if you want to check it.
Therefore, I did the following change:
def print_assoc(stream,asn,e1n,e2n,t1,t2)
stream.puts <
ASSOCS
end
I can also send you a patch if you prefer (just have to learn how to make patch before!)
I’d love to contribute to your code. Would you like to create a rubyforge project? I could do for you if you like. Do you have some tests?
Thanks again for sharing,
Jean-Michel
Really nice stuff. This is a great tutorial for our crossover developers who are building new database driven apps in Ruby and need tools. We’ve cross-tagged it in both so they can find. Let us know if you have more like it for the Ruby section on TekTag.com.
TJ
Hi Miroslav,
This looks a cool tool .. but with Ruby so far I’m a fool …
Ok enough already 🙂
Just getting up to speed with R.O.R., I’ve programmed with a bunch of other languages.
I stuck the uml_dumper.rb in my rails_apps\tasty\lib directory, but I have no clue on how to start it.
I took a stab in the dark and copied script\about to script\uml and changed
the script to :
#require ‘commands/about’
require ‘lib/uml_dumper.rb’
but that doesn’t work.
I get the error messages below.
I’ll keep digging around, but maybe you’ll be bored and have pity on me and have some info for me before I find the answer 🙂
Mike
—————————–
F:\ruby\InstantRails\rails_apps\tasty>ruby script\uml
./lib/uml_dumper.rb:16: undefined method `cattr_accessor’ for ActiveRecord::UmlD
umper:Class (NoMethodError)
from F:/ruby/InstantRails/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_re
quire.rb:27:in `gem_original_require’
from F:/ruby/InstantRails/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_re
quire.rb:27:in `require’
from script/uml:7