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> |