I am building a series of Rails applications for different users and use cases, but they all hang off the same database schema. Using canonical Rails, that means a single, massive rails app with a bunch of controllers, a heap of views and a complex security model.
I prefer small, focussed apps, so I decided to share the model instead. Here’s how it works.
Sharing the model
Lets call the the first rails project Master. Generate it as usual and create a few models and migrations to set up the database. In my case, the Master rails application is used to do nothing more than import, export and allow users to view the data in the model’s database tables.
rails new Master --database postgresql
cd Master
rails generate model model1 ...
...
rake db:create
rake db:migrate
Lets now create a reporting app, called Reporting that will use the same model as Master.
cd ..
rails new Reporting --database postgresql
cd Reporting
Do not create any models or migrations in this project, you do them all in Master.
To copy the models, create a new rake
task in a new file in Reporting called lib/tasks/sync.rake
:
namespace :sync do
desc 'Copy common models and tests from Master'
task :copy do
source_path = '/Users/Hiltmon/Projects/Master'
dest_path = '/Users/Hiltmon/Projects/Reporting'
# Copy all models & tests
%x{cp #{source_path}/app/models/*.rb #{dest_path}/app/models/}
%x{cp #{source_path}/test/models/*_test.rb #{dest_path}/test/models/}
# Fixtures
%x{cp #{source_path}/test/fixtures/*.yml #{dest_path}/test/fixtures/}
# Database YML
%x{cp #{source_path}/config/database.yml #{dest_path}/config/database.yml}
end
end
Change the source_path
to point to the root of the Master project and the dest_path
to point to the root of the Reporting project. The run it:
rake sync:copy
The script copies the app/models
, test/models
, test/fixtures
and database.yml
files from Master to Reporting. As far as Rails is concerned, these were created via genuine rails commands and the Rails engine will make these models available in your reporting views and controllers. The database.yml
file will also make Rails point to the same database as Master.
Two rules need to be followed to make this work:
- All models or migrations are done in Master and only in Master. That includes any changes to model files.
- Run a
rake sync:copy
every once in a while to bring the Reporting project up to date after changes in Master.
Top Tips:
- If you run
rake db:migrate
in the Reporting project, no harm will be done (because there are no migrations in it). But theschema.rb
file will be regenerated. Which is cool. - You can add additional projects that use the same shared model, I have 5 at the moment.
Rejected Options
I could have just symlinked the model and db folders between the projects, and therefore not needed the rake
task. But that only works for a single development system and single developer. I would have to relink for each new computer or developer that needs to work on these projects.
I also could have used git
submodules to provide a common repository for the copied code, but that would mean that I could not use the regular rails
and rake
commands for models and migrations, which defeats the purpose of using Rails.
Result
As a result of sharing (ok, copying) the models this way, I have several Rails applications running off different servers providing different services to different users all running off the same back-end database. Each application is smaller, simpler, easier to manage and easier to code and maintain. As long as discipline is maintained in that the models and migrations are only performed in the Master project, this trick works great.
Follow the author as @hiltmon on Twitter and @hiltmon on App.Net. Mute #xpost
on one.