contents/articles/simply-on-rails-4-default-data-migrations.html
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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
-----
title: "Simply On Rails - Part 4: Quick and Easy Default Data Migrations"
content-type: article
timestamp: 1189854600
tags: "rails|ruby|databases"
-----
<p>In the <a href=/articles/simply-on-rails-3-shared-controller">last post</a> of this series I tried to find a <acronym
title="Don't Repeat Yourself"><span class="caps">DRY</span></acronym> solution to deal with tables storing
“ancillary” data, i.e. names of user roles, predefined categories, page state names and other similar
things.<br />
I personally chose to put this kind of data to make my application more dynamic, although I could have decided to
use ENUMs or simply ordinary varchar fields — that would have been easier, but less flexible. For now,
I’m sticking with my original choice.</p>
<p>The data in these tables is kind of a prerequisite for the application to run: I must be able to have a status to
assign to a user when creating it, and the same applies to roles. Sure, I could spend 20 minutes populating these
tables manually, but it would be nice if there was a less tedious way, wouldn’t it?</p>
<p>There is indeed. The inspiration came from a technique described in the book (which I highly recommend) <em>Agile Web
Development With Rails</em>, in which the author outlines how it would be possible to use Rails’ fixtures
and migrations to load data in the database automatically from <span class="caps">YAML</span> files. <br />
All you have to do is create a migration to load the specified <span class="caps">YAML</span> files and you’re
all set.</p>
<p>I wanted to take a little step further, allowing the migration to load data from <em>all <span
class="caps">YAML</span> files in a specific directory</em>, automatically.Let’s start creating the
<span class="caps">YAML</span> files then and place them all in one directory of the application like
<code>/db/migrate/defaults</code>. Here’s the one I used for user roles, for example:
</p>
<div class='yaml'>
<pre><code>visitor:
id: 1
name: Visitor
level: 0
user:
id: 2
name: User
level: 10
contributor:
id: 3
name: Contributor
level: 20
provider:
id: 4
name: Provider
level: 50
operator:
id: 5
name: Operator
level: 100
administrator:
id: 6
name: Administrator
level: 500
webmaster:
id: 7
name: Webmaster
level: 1000</code></pre>
</div>
<p>The important thing to remember is to provide a unique string to identify each record, before specifying each fiels.
The other files look similar, so I won’t bother listing them here.</p>
<p>And here’s the simple code for the migration:</p>
<div class='ruby'>
<pre><code>require 'active_record/fixtures'
class LoadDefaults < ActiveRecord::Migration
def self.up
down
models = self.default_models
models.each do |m|
Fixtures.create_fixtures(self.default_directory, m)
end
end
def self.down
models = self.default_models
models.each do |m|
eval("#{m.singularize.capitalize}.delete_all")
end
end
def self.default_directory
File.join(File.dirname(__FILE__), "defaults" )
end
def self.default_models
files, names = Dir.glob("#{self.default_directory}/*.yml"), []
unless files.blank?
files.each { |f| names << File.basename(f, '.yml') }
names
else
[]
end
end
end</code></pre>
</div>
<p>Basically the migration will look in a directory named “defaults” for some <span class="caps">YAML</span>
files named after a particular database table, and it will attempt to load all the records defined in each one of
them. <br />
The <code>down</code> method of the migration <em>deletes all the data in the specified tables</em>, so use with
care…</p>
|