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:
# 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.
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.
# 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.
# 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:
- load the environment
- generate the diagram
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.
$ bundle exec rake erd
Generating Entity-Relationship Diagram for 6 models...
Done! Saved diagram to erd.pdf.
Done!