See more posts

Creating Entity Relationship diagrams with Sinatra/ActiveRecord

One of the first ruby gems I add to new Rails projects is Rails ERD.

However in my personal website and blog, I've opted for the simplicity that Sinatra provides. As my project has grown in complexity by adding more models, I've missed the simple visualization that rails-erd provides.

Setting up rails-erd in Sinatra.

This assumes you've already set up ActiveRecord with Sinatra.

First step is to add rails-erd to your gem file:

ruby
# Gemfile
gem 'rails-erd', '~> 1.6' , group: :development

Running the default task $ bundle exec rake erd will fail because the default task is trying to load the rails environment. The solution is to do the loading yourself.

looking at the rails-erd project, you can see the tasks in /lib/rails_erd/tasks.rake . The problem here is that the generate task runs this task as a prerequisite, but it depends on the Rails environment.

ruby
  task :load_models do
    say "Loading application environment..."
    Rake::Task[:environment].invoke

    say "Loading code in search of Active Record models..."
    begin
      Rails.application.eager_load!

      if Rails.application.respond_to?(:config) && !Rails.application.config.nil?
        Rails.application.config.eager_load_namespaces.each(&:eager_load!) if Rails.application.config.respond_to?(:eager_load_namespaces)
      end
    rescue Exception => err
      if Rake.application.options.trace
        raise
      else
        trace = Rails.backtrace_cleaner.clean(err.backtrace)
        error = (["Loading models failed!\nError occurred while loading application: #{err} (#{err.class})"] + trace).join("\n    ")
        raise error
      end
    end

    raise "Active Record was not loaded." unless defined? ActiveRecord
  end

Creating your own ERD task

The solution is to create our own task which loads the files and runs the :erd task.

To load the Sinatra application, create a file called boot.rb . It's pretty simple, it just requires the gems and loads the Sinatra application.

ruby
# boot.rb
require "sinatra/base"
# ... other dependencies ...
require 'bundler'
Bundler.require(:default, ENV.fetch('APP_ENV', 'development').to_sym)

$LOAD_PATH.unshift(File.join(__dir__))

require 'application.rb' # the Sinatra application
# ... local libs ...

Next, create an :environment rake task to load the boot file. This allows other tasks which don't rely on the environment to run without bloat in the same way rails works.

ruby
# Rakefile

task :environment do
  require './boot'

  environment = ENV.fetch('APP_ENV', 'development') # or RACK_ENV, whichever you use

  if environment == 'development'
    load "rails_erd/tasks.rake"
  end
end

Now, we can create the ERD task. This task should mimic the erd:generate task within the rails-erd gem, but do the loading of our environment and models.

I split this into 2 parts:

  1. load the environment
  2. generate the diagram
ruby
desc "Generate ERD"
task :erd => :environment do
  Rake::Task[:erd_generate].invoke
end

task :erd_generate => ["erd:check_dependencies", "erd:options"] do
  Dir['models/*.rb'].each { |m| require m }
  puts "Generating Entity-Relationship Diagram for #{ActiveRecord::Base.descendants.length} models..."

  require "rails_erd/diagram/graphviz"
  file = RailsERD::Diagram::Graphviz.create

  puts "Done! Saved diagram to #{file}."
  `open #{file}` # I found that I almost always open the file after running rake erd. (xdg-open on linux)
end

Now we can bundle exec rake erd to generate and open our entity relationship diagram.

zsh
$ bundle exec rake erd
Generating Entity-Relationship Diagram for 6 models...
Done! Saved diagram to erd.pdf.

Done!

Thanks for reading! See more posts Leave feedback