Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 15
- Log:
Attempt to update Typo to a Typo SVN HEAD release from around the
time the prototype installation was set up on the RISC OS Open Limited
web site. Timestamps place this at 04-Jul so a revision from 05-Jul or
earlier was pulled and copied over the 2.6.0 tarball stable code.
- Author:
- adh
- Date:
- Sat Jul 22 23:27:35 +0100 2006
- Size:
- 4536 Bytes
1 | # bare_migration.rb |
2 | # Scott Bronson |
3 | # 26 Jan 2006 |
4 | # |
5 | # This module provides bare ActiveRecord::Base objects for migration |
6 | # scripts. |
7 | # |
8 | # Most migration scripts use model classes to move data. This is |
9 | # fine when the schema you're migrating from is compatible with your |
10 | # current models. It's disastrous when your models have changed |
11 | # significantly. |
12 | # |
13 | # This class allows you to use bare ActiveRecord classes in your migrate |
14 | # scripts instead of your models so there is no chance of compatibility |
15 | # problems. |
16 | # |
17 | # TODO: Make associations work (has_a, etc.) without requiring legacy calls |
18 | # TODO: If a class already exists when defining it then Ruby just adds to |
19 | # the existing class. This is why migration objects need to be |
20 | # named "BareArticle12" instead of just "BareArticle". This stinks |
21 | # and should be fixed. It won't be an easy fix though. |
22 | # TODO: I know that STI (inheritance) won't work, but it should be |
23 | # simple enough to change instantiate_without_callbacks to make |
24 | # it work. |
25 | # TODO: Create a test framework. |
26 | |
27 | |
28 | # To use: |
29 | # |
30 | # - Create bare inner classes instead of models in your migration script. |
31 | # For instance, this would declare a bare object that matches an Article |
32 | # model: |
33 | # |
34 | # class Migration < ActiveRecord::Migration |
35 | # class BareArticle |
36 | # include BareMigration |
37 | # end |
38 | # ... |
39 | # end |
40 | # |
41 | # Don't forget to nest the declaration of the bare model in order to ensure |
42 | # that the class name is unique. Another option is to declare it outside |
43 | # the scope of the migration with a number based on the migration |
44 | # number, say Bare3Article |
45 | # |
46 | # Use set_table_name if the object doesn't calculate its table name |
47 | # properly. |
48 | # |
49 | # - Then, use the bare objects to migrate the data: |
50 | # |
51 | # BareArticle.find(:all).each do |a| |
52 | # a.published = 1 |
53 | # end |
54 | # |
55 | # - Now your Article module can change all it wants and your migration |
56 | # script will still work. |
57 | |
58 | |
59 | module BareMigration |
60 | def self.append_features(base) |
61 | base.extend(ClassMethods) |
62 | |
63 | # Set the table name by eradicating "Bare" from the calculated table name. |
64 | # You can still use set_table_name if this is wrong. |
65 | base.set_table_name(base.table_name.sub(/.*?bare\d*_?/, '')) |
66 | end |
67 | |
68 | module ClassMethods |
69 | # The problem with STI is that it causes AR to instantiate and |
70 | # return classes DIFFERENT from the class that called find() |
71 | # (classes that come from your app/models dir, a no-no when |
72 | # migrating). For now, this routine overrides that behavior |
73 | # so that you will ONLY get the same class as the one that called |
74 | # find. (todo: make STI work again) |
75 | def instantiate_without_callbacks(record) |
76 | object = allocate |
77 | object.instance_variable_set("@attributes", record) |
78 | object |
79 | end |
80 | |
81 | def find_and_update(find_type=:all, *rest, &update_block) |
82 | self.transaction do |
83 | self.find(find_type, *rest).each do |item| |
84 | update_block[item] |
85 | item.save! |
86 | end |
87 | end |
88 | end |
89 | end |
90 | end |
91 | |
92 | class ActiveRecord::Migration |
93 | def self.opposite_of(method) |
94 | method = method.to_s |
95 | method.sub!(/^add_/, 'remove_') || method.sub!(/^remove_/, 'add_') || |
96 | method.sub!(/^create/, 'drop') || method.sub!(/^drop/, 'create') || |
97 | method.sub!(/^rename_column/, 'reverse_columns') |
98 | end |
99 | |
100 | def self.modify_schema(method, *args) |
101 | case method.to_s |
102 | when 'create_table' |
103 | block = args.last.is_a?(Proc) ? args.pop : Proc.new {|t| nil} |
104 | create_table *args, &block |
105 | when 'remove_column' |
106 | remove_column args[0], args[1] |
107 | when 'reverse_columns' |
108 | (table, to_col, from_col) = args |
109 | rename_column table, from_col, to_col |
110 | else |
111 | send(method, *args) |
112 | end |
113 | end |
114 | |
115 | def self.modify_tables_and_update(*colspecs, &block) |
116 | unless colspecs.first.is_a?(Array) |
117 | colspecs = [colspecs] |
118 | end |
119 | begin |
120 | updated_classes = [] |
121 | colspecs.each do |spec| |
122 | if spec[1].is_a?(Class) |
123 | updated_classes << spec[1] |
124 | spec[1] = spec[1].table_name.to_sym |
125 | end |
126 | end |
127 | colspecs.each {|spec| modify_schema(*spec) } |
128 | updated_classes.uniq! |
129 | if updated_classes.size == 1 && block && block.arity == 1 |
130 | say "About to call find_and_update" |
131 | updated_classes.first.find_and_update(:all, &block) |
132 | else |
133 | block.call if block |
134 | end |
135 | rescue Exception => e |
136 | colspecs.reverse.each do |(method, table, column, *rest)| |
137 | modify_schema(opposite_of(method), table, column, *rest) rescue nil |
138 | end |
139 | raise e |
140 | end |
141 | end |
142 | end |