I’m currently working on a new gem that should be able to run seeds in your Rails app efficiently.
While creating the gem, I ran into an interesting problem.
I wanted to generate a seedie.yml file that generates seeds based on its config. A simple seedie.yml file looks like this:
We can write this
seedie.yml config from scratch and manually.
However, I wanted to generate this config automatically from the Rails app.
It should be as simple as
bin/rails g seedie:install.
So I wrote an
install_generator that helps generate this seed file based on your
There is a problem here!
Post both have a
belongs_to association with
So, it cannot be created before
ActiveRecord does not know about the order of models,
It doesn’t know that
User needs to be created before
It just generates the config based on the order of the models in the
So I need to find a way to be able to sort these models based on their associations, particularly
First, I need to decide a priority for each model.
- Models with no associations will have the highest priority.
- Models with 1 BelongsTo association will have a higher priority than models with 2 BelongsTo associations.
- Models’ Associations needs to have a higher priority that the model itself
But how do I find associations for the model?
Reflection is the answer.
ActiveRecord has a very cool module called
ActiveRecord::Reflection that helps us find associations for a model.
Let’s take a look at the following models:
We have these four models with different dependencies. (Taken from an opensource project of mine called SprintSpades)
Now, let’s try and sort these models based on their dependencies.
To satisfy all the dependencies, the order should be:
For this I’m creating a new class called
This is the first iteration so there are a lot of things that can be improved here.
What am I doing here?
sorted_models array will contain the models sorted by their dependencies.
remaining_models will contain the models that are yet to be sorted.
- I’m looping through the
remaining_models and sorting its dependencies by checking if the association exists in the
- I’m breaking the loop if there are no more models to sort.
- Also checking for circular dependencies if any and adding a warning so that the user can fix it.
There are few problems with this:
- Polymorphic association do not support (&:klass) method.
- Associations shows circular dependency even if they are not.
- The order is still wrong
After many iterations, I came up with this:
ModelSorter now does the following:
- It maps the models with its dependencies.
- It loops through the models and searches for its dependencies recursively using depth_first_search method.
- Sets the associations first before the model itself.
- Needed to reject optional associations to avoid circular dependencies and also it doesn’t matter right now.
- Earlier, I used stack to store the resolved models but the order was wrong so used a queue instead.
- Also, I’m using a
@unresolved array to store the models that are yet to be resolved.
Using this now if I run
rails g seedie:install , it will generate the following
It is not perfect yet, but it is a good start.
I’m still working on this gem and will be releasing it soon.
I’ll be writing more about it in the future as well.
Follow me on Twitter for more updates.
You can also follow me on Github to see what I’m working on.