GitHub link: activeRecordMigrate
On my current project, in true Agile fashion, we had avoided using a database because we didn’t need one. Eventually, we had requirements for storing data that was coming from an external source, and at this point we had to start making decisions on how we would move forward. We had kept our project “lean” and the biggest concern I had was not introducing a build step that would include installing MySQL or something else that would drastically depart from our build environment.
I had been reading off and on the book Rails for Java Developers by Stuart Halloway and Justin Gehtland. I recommend it for all developers, not for either a Java or Rails reference, but for interesting comparative analysis on web development technologies. One of the things mentioned in that book is using ActiveRecord for schema versioning even in applications not using Rails. After playing around with ActiveRecord in their sample code for awhile, I thought it would clearly provide two big wins:
- No writing a ton of SQL!
- Support any DB that AR supports.
#2 was especially important in our case as we were still not certain what RDBMS we would end up using, and that decision may not be up to us in the end anyway. This is also relevant for #1 because there was going to be DDL rework later down the road which would not provide much value. So plug in ActiveRecord and “Hasta Lasagna, don’t get any on ya” right? There was still one major hurdle in all of this though: I work in a Java shop. However, we already use Groovy and have started to move from Maven to Gradle for our builds. Therefore, if I could have ActiveRecord work in that ecosystem, it would still be viable. Naturally, the choice to do this was JRuby.
I was able to get started using the information in Robert Fischer’s blog post on running Cucumber and JRuby from Gradle. However, I encountered some problems early on with getting the gems necessary to run ActiveRecord. Basically, I wanted to make sure that the gems would always be installed in the same place, and also not to require developers to install JRuby to use the build. I solved that problem with two configuration options. First, I made a gemrc.yml file in the project that set the gem options to always be the same. Secondly, I had to make sure that the gems were going to be on the classpath so that they would be available. I found out how to do this based on Nick Sieger’s blog post on putting Gems-in-a-jar. At that point, everything needed to run ActiveRecord from Gradle was available. Here are the relevant sections of the build.gradle file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
|
The key parts here are gems, migrate, and runJRuby. With gems as part of the resources, it will be on the runtime classpath, meaning that the gems will be visible when I call org.jruby.Main to run the commands I’m passing it. To make sure that the gems path is already set correctly and not require any configuration by the developers, here is the gemrc.yml file that exists in the source directory:
1 2 3 4 |
|
This makes sure that when installing gems, it always puts them into the build directory, so that all the rest of the tasks will see them on the runtime classpath. My Rakefile is fairly simple, but I’m including it for informative purposes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Next, create your database.yml file containing your environments:
1 2 3 4 5 |
|
Naturally, the process is to run the Gradle script with “migrate” and it will automatically run any migrate scripts you have under db/migrate. You can create a simple migration script to test out the configuration:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Running this build from the command line you should see something like the following:
Obviously, not everyone can start with something as easy as the example above for the 001_migration.rb file. Luckily ActiveRecord solves this problem by being able to dump an existing schema out to a Ruby file that can be your “version 0” migration. I have the task in my Rakefile, adding the Gradle task is pretty trivial but I left it out to save space.
That’s about all there is to it. Now I have project that any developer using Gradle can create a database with. To support different databases we just need to add adapter gems and the appropriate configuration in the database.yml file. Also, did I mention it is much more fun to write Ruby than SQL?
Notes: I’m not sure why, but I had errors using JRuby-1.5.6’s org.jruby.Main class to execute the gems task. I ended up sticking with the old jruby-embed which was in Fischer’s original blog post and that works fine, but I would like to know what the deal is with that.
My sample project is now available on GitHub: activeRecordMigrate