aboutsummaryrefslogtreecommitdiffstats
path: root/railties/doc/guides/source/creating_plugins/migration_generator.txt
blob: f4fc32481c7c81d5a872cd156594b5fb7a0ed253 (plain) (blame)
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
== Create a generator ==

Many plugins ship with generators.  When you created the plugin above, you specified the --with-generator option, so you already have the generator stubs in 'vendor/plugins/yaffle/generators/yaffle'.

Building generators is a complex topic unto itself and this section will cover one small aspect of generators:  creating a generator that adds a time-stamped migration.

To create a generator you must:

 * Add your instructions to the 'manifest' method of the generator
 * Add any necessary template files to the templates directory
 * Test the generator manually by running various combinations of `script/generate` and `script/destroy`
 * Update the USAGE file to add helpful documentation for your generator

=== Testing generators ===

Many rails plugin authors do not test their generators, however testing generators is quite simple.  A typical generator test does the following:

 * Creates a new fake rails root directory that will serve as destination
 * Runs the generator forward and backward, making whatever assertions are necessary
 * Removes the fake rails root

For the generator in this section, the test could look something like this:

*vendor/plugins/yaffle/test/yaffle_generator_test.rb*

[source, ruby]
------------------------------------------------------------------
require File.dirname(__FILE__) + '/test_helper.rb'
require 'rails_generator'
require 'rails_generator/scripts/generate'
require 'rails_generator/scripts/destroy'

class GeneratorTest < Test::Unit::TestCase

  def fake_rails_root
    File.join(File.dirname(__FILE__), 'rails_root')
  end
  
  def file_list
    Dir.glob(File.join(fake_rails_root, "db", "migrate", "*"))
  end

  def setup
    FileUtils.mkdir_p(fake_rails_root)
    @original_files = file_list
  end

  def teardown
    FileUtils.rm_r(fake_rails_root)
  end
  
  def test_generates_correct_file_name
    Rails::Generator::Scripts::Generate.new.run(["yaffle", "bird"], :destination => fake_rails_root)
    new_file = (file_list - @original_files).first
    assert_match /add_yaffle_fields_to_bird/, new_file
  end
  
end
------------------------------------------------------------------

You can run 'rake' from the plugin directory to see this fail.  Unless you are doing more advanced generator commands it typically suffices to just test the Generate script, and trust that rails will handle the Destroy and Update commands for you.

=== Adding to the manifest ===

This example will demonstrate how to use one of the built-in generator methods named 'migration_template' to create a migration file.  To start, update your generator file to look like this:

*vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb*

[source, ruby]
------------------------------------------------------------------
class YaffleGenerator < Rails::Generator::NamedBase
  def manifest
    record do |m|
      m.migration_template 'migration:migration.rb', "db/migrate", {:assigns => yaffle_local_assigns,
        :migration_file_name => "add_yaffle_fields_to_#{custom_file_name}"
      }
    end
  end

  private
    def custom_file_name
      custom_name = class_name.underscore.downcase
      custom_name = custom_name.pluralize if ActiveRecord::Base.pluralize_table_names
    end

    def yaffle_local_assigns
      returning(assigns = {}) do
        assigns[:migration_action] = "add"
        assigns[:class_name] = "add_yaffle_fields_to_#{custom_file_name}"
        assigns[:table_name] = custom_file_name
        assigns[:attributes] = [Rails::Generator::GeneratedAttribute.new("last_squawk", "string")]
      end
    end
end
------------------------------------------------------------------

The generator creates a new file in 'db/migrate' with a timestamp and an 'add_column' statement.  It reuses the built in rails `migration_template` method, and reuses the built-in rails migration template.

It's courteous to check to see if table names are being pluralized whenever you create a generator that needs to be aware of table names.  This way people using your generator won't have to manually change the generated files if they've turned pluralization off.

=== Manually test the generator ===

To run the generator, type the following at the command line:

------------------------------------------------------------------
./script/generate yaffle bird
------------------------------------------------------------------

and you will see a new file:

*db/migrate/20080529225649_add_yaffle_fields_to_birds.rb*

[source, ruby]
------------------------------------------------------------------
class AddYaffleFieldsToBirds < ActiveRecord::Migration
  def self.up
    add_column :birds, :last_squawk, :string
  end

  def self.down
    remove_column :birds, :last_squawk
  end
end
------------------------------------------------------------------


=== The USAGE file ===

Rails ships with several built-in generators.  You can see all of the generators available to you by typing the following at the command line:

------------------------------------------------------------------
script/generate
------------------------------------------------------------------

You should see something like this:

------------------------------------------------------------------
Installed Generators
  Plugins (vendor/plugins): yaffle
  Builtin: controller, integration_test, mailer, migration, model, observer, plugin, resource, scaffold, session_migration
------------------------------------------------------------------

When you run `script/generate yaffle` you should see the contents of your 'vendor/plugins/yaffle/generators/yaffle/USAGE' file.  

For this plugin, update the USAGE file looks like this:

------------------------------------------------------------------
Description:
    Creates a migration that adds yaffle squawk fields to the given model

Example:
    ./script/generate yaffle hickwall

    This will create:
        db/migrate/TIMESTAMP_add_yaffle_fields_to_hickwall
------------------------------------------------------------------