diff --git a/railties/Rakefile b/railties/Rakefile
index 92a1bfc765..5a286bcd1e 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -21,7 +21,8 @@ TEST_DIRS = %w( fixtures unit functional mocks mocks/development mocks/testing
LOG_FILES = %w( apache.log development.log test.log production.log )
HTML_FILES = %w( 404.html 500.html index.html )
-SCRIPT_FILES = %w( new_controller new_model new_mailer new_crud )
+SCRIPT_FILES = %w( generate )
+GENERATORS = %w( controller mailer model scaffold )
VENDOR_LIBS = %w( actionpack activerecord actionmailer railties )
@@ -136,11 +137,16 @@ task :copy_configs do
end
task :copy_generators do
+ mkdir_p File.join(PKG_DESTINATION, 'script')
SCRIPT_FILES.each do |file|
dest_file = File.join(PKG_DESTINATION, 'script', file)
cp File.join('generators', "#{file}.rb"), dest_file
chmod 0755, dest_file
end
+
+ GENERATORS.each do |dir|
+ cp_r File.join('generators', dir), File.join(PKG_DESTINATION, 'script', dir)
+ end
end
task :copy_rootfiles do
diff --git a/railties/generators/new_controller.rb b/railties/generators/controller/USAGE
old mode 100755
new mode 100644
similarity index 81%
rename from railties/generators/new_controller.rb
rename to railties/generators/controller/USAGE
index 3060c06382..0259b3d027
--- a/railties/generators/new_controller.rb
+++ b/railties/generators/controller/USAGE
@@ -1,15 +1,3 @@
-#!/usr/local/bin/ruby
-require File.dirname(__FILE__) + '/../config/environment'
-require 'generator'
-
-unless ARGV.empty?
- rails_root = File.dirname(__FILE__) + '/..'
- name = ARGV.shift
- actions = ARGV
- Generator::Controller.new(rails_root, name, actions).generate
-else
- puts <<-END_HELP
-
NAME
new_controller - create controller and view stub files
@@ -39,5 +27,3 @@ EXAMPLE
The BlogController class will have the following methods: list, display, new, edit.
Each will default to render the associated template file.
-END_HELP
-end
diff --git a/railties/generators/controller/controller_generator.rb b/railties/generators/controller/controller_generator.rb
new file mode 100644
index 0000000000..4b53741565
--- /dev/null
+++ b/railties/generators/controller/controller_generator.rb
@@ -0,0 +1,22 @@
+require 'rails_generator'
+
+class ControllerGenerator < Rails::Generator::Base
+ attr_reader :actions
+
+ def generate
+ @actions = args
+
+ # Controller class, functional test, and helper class.
+ template "controller.rb", "app/controllers/#{file_name}_controller.rb"
+ template "functional_test.rb", "test/functional/#{file_name}_controller_test.rb"
+ template "helper.rb", "app/helpers/#{file_name}_helper.rb"
+
+ # Create the views directory even if there are no actions.
+ FileUtils.mkdir_p "app/views/#{file_name}"
+
+ # Create a view for each action.
+ actions.each do |action|
+ template "view.rhtml", "app/views/#{file_name}/#{action}.rhtml", binding
+ end
+ end
+end
diff --git a/railties/generators/controller/templates/controller.rb b/railties/generators/controller/templates/controller.rb
new file mode 100644
index 0000000000..f9800ab556
--- /dev/null
+++ b/railties/generators/controller/templates/controller.rb
@@ -0,0 +1,10 @@
+class <%= class_name %>Controller < AbstractApplicationController
+<% if options[:scaffold] -%>
+ scaffold :<%= singular_name %>
+<% end -%>
+<% for action in actions -%>
+
+ def <%= action %>
+ end
+<% end -%>
+end
diff --git a/railties/generators/templates/controller_test.erb b/railties/generators/controller/templates/functional_test.rb
similarity index 92%
rename from railties/generators/templates/controller_test.erb
rename to railties/generators/controller/templates/functional_test.rb
index 5577379c62..c975cb3ce3 100644
--- a/railties/generators/templates/controller_test.erb
+++ b/railties/generators/controller/templates/functional_test.rb
@@ -10,7 +10,7 @@ class <%= class_name %>ControllerTest < Test::Unit::TestCase
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
end
- # Replace this with your real tests
+ # Replace this with your real tests.
def test_truth
assert true
end
diff --git a/railties/generators/templates/helper.erb b/railties/generators/controller/templates/helper.rb
similarity index 100%
rename from railties/generators/templates/helper.erb
rename to railties/generators/controller/templates/helper.rb
diff --git a/railties/generators/controller/templates/view.rhtml b/railties/generators/controller/templates/view.rhtml
new file mode 100644
index 0000000000..7e7a7d53ce
--- /dev/null
+++ b/railties/generators/controller/templates/view.rhtml
@@ -0,0 +1,2 @@
+
<%= class_name %>#<%= action %>
+Find me in app/views/<%= file_name %>/<%= action %>.rhtml
diff --git a/railties/generators/generate.rb b/railties/generators/generate.rb
new file mode 100755
index 0000000000..8f2f1497d6
--- /dev/null
+++ b/railties/generators/generate.rb
@@ -0,0 +1,41 @@
+#!/usr/local/bin/ruby
+require File.dirname(__FILE__) + '/../config/environment'
+require 'rails_generator'
+
+unless ARGV.empty?
+ begin
+ name = ARGV.shift
+ Rails::Generator.instance(name, ARGV).generate
+ rescue Rails::Generator::UsageError => e
+ puts e.message
+ end
+else
+ builtin_generators = Rails::Generator.builtin_generators.join(', ')
+ contrib_generators = Rails::Generator.contrib_generators.join(', ')
+
+ $stderr.puts < < ActionMailer::Base
-
<% for action in actions -%>
+
def <%= action %>(sent_on = Time.now)
@recipients = ''
@from = ''
@@ -10,6 +10,5 @@ class <%= class_name %> < ActionMailer::Base
@body = {}
@sent_on = sent_on
end
-
<% end -%>
end
diff --git a/railties/generators/templates/mailer_test.erb b/railties/generators/mailer/templates/unit_test.rb
similarity index 94%
rename from railties/generators/templates/mailer_test.erb
rename to railties/generators/mailer/templates/unit_test.rb
index f17d614195..3bf460907f 100644
--- a/railties/generators/templates/mailer_test.erb
+++ b/railties/generators/mailer/templates/unit_test.rb
@@ -12,11 +12,11 @@ class <%= class_name %>Test < Test::Unit::TestCase
@expected = TMail::Mail.new
@expected.to = 'test@localhost'
@expected.from = 'test@localhost'
- @expected.subject = '<%= class_name %> test mail'
end
<% for action in actions -%>
def test_<%= action %>
+ @expected.subject = '<%= class_name %>#<%= action %> test mail'
@expected.body = read_fixture('<%= action %>')
@expected.date = Time.now
diff --git a/railties/generators/templates/mailer_action.rhtml b/railties/generators/mailer/templates/view.rhtml
similarity index 100%
rename from railties/generators/templates/mailer_action.rhtml
rename to railties/generators/mailer/templates/view.rhtml
diff --git a/railties/generators/new_model.rb b/railties/generators/model/USAGE
old mode 100755
new mode 100644
similarity index 65%
rename from railties/generators/new_model.rb
rename to railties/generators/model/USAGE
index f6fbf5f002..debf185bea
--- a/railties/generators/new_model.rb
+++ b/railties/generators/model/USAGE
@@ -1,14 +1,3 @@
-#!/usr/local/bin/ruby
-require File.dirname(__FILE__) + '/../config/environment'
-require 'generator'
-
-if ARGV.size == 1
- rails_root = File.dirname(__FILE__) + '/..'
- name = ARGV.shift
- Generator::Model.new(rails_root, name).generate
-else
- puts <<-HELP
-
NAME
new_model - create model stub files
@@ -26,6 +15,3 @@ EXAMPLE
This will generate an Account class in app/models/account.rb, an
AccountTest in test/unit/account_test.rb, and the directory
test/fixtures/account.
-
-HELP
-end
diff --git a/railties/generators/model/model_generator.rb b/railties/generators/model/model_generator.rb
new file mode 100644
index 0000000000..04540d6e40
--- /dev/null
+++ b/railties/generators/model/model_generator.rb
@@ -0,0 +1,10 @@
+require 'rails_generator'
+
+class ModelGenerator < Rails::Generator::Base
+ def generate
+ # Model class, unit test, and fixtures.
+ template "model.rb", "app/models/#{file_name}.rb"
+ template "unit_test.rb", "test/unit/#{file_name}_test.rb"
+ template "fixtures.yml", "test/fixtures/#{table_name}.yml"
+ end
+end
diff --git a/railties/generators/model/templates/fixtures.yml b/railties/generators/model/templates/fixtures.yml
new file mode 100644
index 0000000000..a56c16462a
--- /dev/null
+++ b/railties/generators/model/templates/fixtures.yml
@@ -0,0 +1 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
diff --git a/railties/generators/templates/model.erb b/railties/generators/model/templates/model.rb
similarity index 100%
rename from railties/generators/templates/model.erb
rename to railties/generators/model/templates/model.rb
diff --git a/railties/generators/templates/model_test.erb b/railties/generators/model/templates/unit_test.rb
similarity index 82%
rename from railties/generators/templates/model_test.erb
rename to railties/generators/model/templates/unit_test.rb
index a3ad2b72fb..6162d4cf96 100644
--- a/railties/generators/templates/model_test.erb
+++ b/railties/generators/model/templates/unit_test.rb
@@ -4,8 +4,8 @@ require '<%= file_name %>'
class <%= class_name %>Test < Test::Unit::TestCase
fixtures :<%= table_name %>
- # Replace this with your real tests
+ # Replace this with your real tests.
def test_truth
assert true
end
-end
\ No newline at end of file
+end
diff --git a/railties/generators/new_crud.rb b/railties/generators/new_crud.rb
deleted file mode 100755
index 4eaa1cb1f3..0000000000
--- a/railties/generators/new_crud.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/local/bin/ruby
-require File.dirname(__FILE__) + '/../config/environment'
-require 'generator'
-
-unless ARGV.empty?
- rails_root = File.dirname(__FILE__) + '/..'
- name = ARGV.shift
- actions = ARGV
- Generator::Model.new(rails_root, name).generate
- Generator::Controller.new(rails_root, name, actions, :scaffold => true).generate
-else
- puts <<-END_HELP
-
-NAME
- new_crud - create a model and a controller scaffold
-
-SYNOPSIS
- new_crud ModelName [action ...]
-
-DESCRIPTION
- The new_crud generator takes the name of the new model as the
- first argument and an optional list of controller actions as the
- subsequent arguments. All actions may be omitted since the controller
- will have scaffolding automatically set up for this model.
-
-EXAMPLE
- new_crud Account
-
- This will generate an Account model and controller with scaffolding.
- Now create the accounts table in your database and browse to
- http://localhost/account/ -- voila, you're on Rails!
-
-END_HELP
-end
diff --git a/railties/generators/scaffold/USAGE b/railties/generators/scaffold/USAGE
new file mode 100644
index 0000000000..f299ee6f06
--- /dev/null
+++ b/railties/generators/scaffold/USAGE
@@ -0,0 +1,18 @@
+NAME
+ new_scaffold - create a model and a skeleton controller
+
+SYNOPSIS
+ new_scaffold ModelName [action ...]
+
+DESCRIPTION
+ The new_scaffold generator takes the name of the new model as the
+ first argument and an optional list of controller actions as the
+ subsequent arguments. Any actions with scaffolding code available
+ will be generated in your controller; others will be left as stubs.
+
+EXAMPLE
+ new_scaffold Account
+
+ This will generate an Account model and controller.
+ Now create the accounts table in your database and browse to
+ http://localhost/account/ -- voila, you're on Rails!
diff --git a/railties/generators/scaffold/scaffold_generator.rb b/railties/generators/scaffold/scaffold_generator.rb
new file mode 100644
index 0000000000..ee556e8a03
--- /dev/null
+++ b/railties/generators/scaffold/scaffold_generator.rb
@@ -0,0 +1,53 @@
+require 'rails_generator'
+
+class ScaffoldGenerator < Rails::Generator::Base
+ def generate
+ # Model.
+ generator('model').generate
+
+ # Fixtures.
+ template "fixtures.yml", "test/fixtures/#{table_name}.yml"
+
+ # Controller class, functional test, helper, and views.
+ template "controller.rb", "app/controllers/#{file_name}_controller.rb"
+ template "functional_test.rb", "test/functional/#{file_name}_controller_test.rb"
+ template "controller/helper.rb", "app/helpers/#{file_name}_helper.rb"
+
+ # Layout and stylesheet.
+ unless File.file?("app/views/layouts/scaffold.rhtml")
+ template "layout.rhtml", "app/views/layouts/scaffold.rhtml"
+ end
+ unless File.file?("public/stylesheets/scaffold.css")
+ template "style.css", "public/stylesheets/scaffold.css"
+ end
+
+ # Scaffolded views.
+ scaffold_views.each do |action|
+ template "view_#{action}.rhtml", "app/views/#{file_name}/#{action}.rhtml"
+ end
+
+ # Unscaffolded views.
+ unscaffolded_actions.each do |action|
+ template "controller/view.rhtml",
+ "app/views/#{file_name}/#{action}.rhtml",
+ binding
+ end
+ end
+
+ protected
+ def scaffold_views
+ %w(list show new edit)
+ end
+
+ def scaffold_actions
+ scaffold_views + %w(index create update destroy)
+ end
+
+ def unscaffolded_actions
+ args - scaffold_actions
+ end
+
+ def suffix
+ "_#{singular_name}" if options[:suffix]
+ end
+end
diff --git a/railties/generators/scaffold/templates/controller.rb b/railties/generators/scaffold/templates/controller.rb
new file mode 100644
index 0000000000..c8706098da
--- /dev/null
+++ b/railties/generators/scaffold/templates/controller.rb
@@ -0,0 +1,58 @@
+class <%= class_name %>Controller < AbstractApplicationController
+ model :<%= singular_name %>
+ layout 'scaffold'
+
+<% unless suffix -%>
+ def index
+ list
+ render_action 'list'
+ end
+<% end -%>
+
+<% for action in unscaffolded_actions -%>
+ def <%= action %><%= suffix %>
+ end
+
+<% end -%>
+ def list<%= suffix %>
+ @<%= plural_name %> = <%= class_name %>.find_all
+ end
+
+ def show<%= suffix %>
+ @<%= singular_name %> = <%= class_name %>.find(@params['id'])
+ end
+
+ def new<%= suffix %>
+ @<%= singular_name %> = <%= class_name %>.new
+ end
+
+ def create<%= suffix %>
+ @<%= singular_name %> = <%= class_name %>.new(@params['<%= singular_name %>'])
+ if @<%= singular_name %>.save
+ flash['notice'] = '<%= class_name %> was successfully created.'
+ redirect_to :action => 'list<%= suffix %>'
+ else
+ render_action 'new<%= suffix %>'
+ end
+ end
+
+ def edit<%= suffix %>
+ @<%= singular_name %> = <%= class_name %>.find(@params['id'])
+ end
+
+ def update
+ @<%= singular_name %> = <%= class_name %>.find(@params['<%= singular_name %>']['id'])
+ @<%= singular_name %>.attributes = @params['<%= singular_name %>']
+ if @<%= singular_name %>.save
+ flash['notice'] = '<%= class_name %> was successfully updated.'
+ redirect_to :action => 'show<%= suffix %>', :id => @<%= singular_name %>.id
+ else
+ render_action 'edit<%= suffix %>'
+ end
+ end
+
+ def destroy<%= suffix %>
+ <%= class_name %>.find(@params['id']).destroy
+ redirect_to :action => 'list<%= suffix %>'
+ end
+end
diff --git a/railties/generators/scaffold/templates/fixtures.yml b/railties/generators/scaffold/templates/fixtures.yml
new file mode 100644
index 0000000000..ea67f36ad8
--- /dev/null
+++ b/railties/generators/scaffold/templates/fixtures.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+first_<%= singular_name %>:
+ id: 1
+
+another_<%= singular_name %>:
+ id: 2
diff --git a/railties/generators/scaffold/templates/functional_test.rb b/railties/generators/scaffold/templates/functional_test.rb
new file mode 100644
index 0000000000..e82349375b
--- /dev/null
+++ b/railties/generators/scaffold/templates/functional_test.rb
@@ -0,0 +1,109 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require '<%= file_name %>_controller'
+
+# Re-raise errors caught by the controller.
+class <%= class_name %>Controller; def rescue_action(e) raise e end; end
+
+class <%= class_name %>ControllerTest < Test::Unit::TestCase
+ fixtures :<%= table_name %>
+
+ def setup
+ @controller = <%= class_name %>Controller.new
+ @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
+ end
+
+<% for action in unscaffolded_actions -%>
+ def test_<%= action %>
+ process :<%= action %>
+ assert_success
+ assert_rendered_file '<%= action %>'
+ end
+
+<% end -%>
+<% unless suffix -%>
+ def test_index
+ process :index
+ assert_success
+ assert_rendered_file 'list'
+ end
+
+<% end -%>
+ def test_list<%= suffix %>
+ process :list<%= suffix %>
+ assert_success
+ assert_rendered_file 'list<%= suffix %>'
+ assert_template_has '<%= plural_name %>'
+ end
+
+ def test_show<%= suffix %>
+ process :show<%= suffix %>, 'id' => 1
+ assert_success
+ assert_rendered_file 'show'
+ assert_template_has '<%= singular_name %>'
+ assert_valid_record '<%= singular_name %>'
+ end
+
+ def test_show_missing_<%= suffix || 'record' %>
+ process :show<%= suffix %>
+ assert_success
+ assert_rendered_file 'error'
+ end
+
+ def test_new<%= suffix %>
+ process :new<%= suffix %>
+ assert_success
+ assert_rendered_file 'new<%= suffix %>'
+ assert_template_has '<%= singular_name %>'
+ end
+
+ def test_create
+ num_<%= plural_name %> = <%= class_name %>.find_all.size
+
+ process :create<%= suffix %>, '<%= singular_name %>' => { }
+ assert_redirected_to :action => 'list<%= suffix %>'
+
+ assert_equal num_<%= plural_name %> + 1, <%= class_name %>.find_all.size
+ end
+
+ def test_edit<%= suffix %>
+ process :edit<%= suffix %>, 'id' => 1
+ assert_success
+ assert_rendered_file 'edit<%= suffix %>'
+ assert_template_has '<%= singular_name %>'
+ assert_valid_record '<%= singular_name %>'
+ end
+
+ def test_edit_missing_<%= suffix || 'record' %>
+ process :edit<%= suffix %>
+ assert_success
+ assert_rendered_file 'error'
+ end
+
+ def test_update<%= suffix %>
+ process :update<%= suffix %>, 'id' => 1
+ assert_redirected_to :action => 'show<%= suffix %>', :id => 1
+ end
+
+ def test_update_missing_<%= suffix || 'record' %>
+ process :update<%= suffix %>, '<%= singular_name %>' => {}
+ assert_success
+ assert_rendered_file 'error'
+ end
+
+ def test_destroy<%= suffix %>
+ assert_not_nil <%= class_name %>.find(1)
+
+ process :destroy, 'id' => 1
+ assert_redirected_to :action => 'list<%= suffix %>'
+
+ assert_raise(ActiveRecord::RecordNotFound) {
+ <%= singular_name %> = <%= class_name %>.find(1)
+ }
+ end
+
+ def test_destroy_missing_<%= suffix || 'record' %>
+ process :destroy<%= suffix %>
+ assert_success
+ assert_rendered_file 'error'
+ end
+end
diff --git a/railties/generators/scaffold/templates/layout.rhtml b/railties/generators/scaffold/templates/layout.rhtml
new file mode 100644
index 0000000000..59d5585b51
--- /dev/null
+++ b/railties/generators/scaffold/templates/layout.rhtml
@@ -0,0 +1,11 @@
+
+
+ Scaffolding: <%%= controller.controller_name %>#<%%= controller.action_name %>
+
+
+
+
+<%%= @content_for_layout %>
+
+
+
diff --git a/railties/generators/scaffold/templates/style.css b/railties/generators/scaffold/templates/style.css
new file mode 100644
index 0000000000..2db43b7fa1
--- /dev/null
+++ b/railties/generators/scaffold/templates/style.css
@@ -0,0 +1,17 @@
+body { background-color: #fff; color: #333; }
+
+body, p, ol, ul, td {
+ font-family: verdana, arial, helvetica, sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+}
+
+pre {
+ background-color: #eee;
+ padding: 10px;
+ font-size: 11px;
+}
+
+a { color: #000; }
+a:visited { color: #666; }
+a:hover { color: #fff; background-color:#000; }
diff --git a/railties/generators/scaffold/templates/view_edit.rhtml b/railties/generators/scaffold/templates/view_edit.rhtml
new file mode 100644
index 0000000000..fec09e2cbe
--- /dev/null
+++ b/railties/generators/scaffold/templates/view_edit.rhtml
@@ -0,0 +1,6 @@
+Editing <%= singular_name %>
+
+<%%= form '<%= singular_name %>', :action => 'update<%= suffix %>' %>
+
+<%%= link_to 'Show', :action => 'show<%= suffix %>', :id => @<%= singular_name %>.id %> |
+<%%= link_to 'Back', :action => 'list<%= suffix %>' %>
diff --git a/railties/generators/scaffold/templates/view_list.rhtml b/railties/generators/scaffold/templates/view_list.rhtml
new file mode 100644
index 0000000000..068fd67472
--- /dev/null
+++ b/railties/generators/scaffold/templates/view_list.rhtml
@@ -0,0 +1,24 @@
+Listing <%= plural_name %>
+
+
+
+<%% for column in <%= class_name %>.content_columns %>
+ <%%= column.human_name %> |
+<%% end %>
+
+
+<%% for <%= singular_name %> in @<%= plural_name %> %>
+
+ <%% for column in <%= class_name %>.content_columns %>
+ <%%=h <%= singular_name %>[column.name] %> |
+ <%% end %>
+ <%%= link_to 'Show', :action => 'show<%= suffix %>', :id => <%= singular_name %>.id %> |
+ <%%= link_to 'Edit', :action => 'edit<%= suffix %>', :id => <%= singular_name %>.id %> |
+ <%%= link_to 'Destroy', :action => 'destroy<%= suffix %>', :id => <%= singular_name %>.id %> |
+
+<%% end %>
+
+
+
+
+<%%= link_to 'New <%= singular_name %>', :action => 'new<%= suffix %>' %>
diff --git a/railties/generators/scaffold/templates/view_new.rhtml b/railties/generators/scaffold/templates/view_new.rhtml
new file mode 100644
index 0000000000..840bb02775
--- /dev/null
+++ b/railties/generators/scaffold/templates/view_new.rhtml
@@ -0,0 +1,5 @@
+New <%= @singular_name %>
+
+<%%= form '<%= singular_name %>', :action => 'create<%= suffix %>' %>
+
+<%%= link_to 'Back', :action => 'list<%= suffix %>' %>
diff --git a/railties/generators/scaffold/templates/view_show.rhtml b/railties/generators/scaffold/templates/view_show.rhtml
new file mode 100644
index 0000000000..30a3242f14
--- /dev/null
+++ b/railties/generators/scaffold/templates/view_show.rhtml
@@ -0,0 +1,8 @@
+<%% for column in <%= class_name %>.content_columns %>
+
+ <%%= column.human_name %>: <%%= @<%= singular_name %>[column.name] %>
+
+<%% end %>
+
+<%%= link_to 'Edit', :action => 'edit<%= suffix %>', :id => @<%= singular_name %>.id %> |
+<%%= link_to 'Back', :action => 'list<%= suffix %>' %>
diff --git a/railties/generators/templates/controller.erb b/railties/generators/templates/controller.erb
deleted file mode 100644
index 600f5d2c59..0000000000
--- a/railties/generators/templates/controller.erb
+++ /dev/null
@@ -1,19 +0,0 @@
-class <%= class_name %>Controller < AbstractApplicationController
- helper :<%= file_name %>
-<% if options[:scaffold] -%>
- model :<%= file_name %>
- scaffold :<%= options[:scaffold] %>
-
- <%- for action in actions -%>
- #def <%= action %>
- #end
-
- <%- end -%>
-<% else -%>
- <%- for action in actions -%>
- def <%= action %>
- end
-
- <%- end -%>
-<% end -%>
-end
diff --git a/railties/generators/templates/controller_view.rhtml b/railties/generators/templates/controller_view.rhtml
deleted file mode 100644
index d8a310df50..0000000000
--- a/railties/generators/templates/controller_view.rhtml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
- <%= class_name %>#<%= action %>
-
-
-<%= class_name %>#<%= action %>
-Find me in app/views/<%= file_name %>/<%= action %>.rhtml
-
-
diff --git a/railties/lib/rails_generator.rb b/railties/lib/rails_generator.rb
new file mode 100644
index 0000000000..9e64f5f4c5
--- /dev/null
+++ b/railties/lib/rails_generator.rb
@@ -0,0 +1,175 @@
+require 'fileutils'
+
+module Rails
+ module Generator
+ class GeneratorError < StandardError; end
+ class UsageError < GeneratorError; end
+
+ CONTRIB_ROOT = "#{RAILS_ROOT}/script/generators"
+ BUILTIN_ROOT = "#{File.dirname(__FILE__)}/../generators"
+ DEFAULT_SEARCH_PATHS = [CONTRIB_ROOT, BUILTIN_ROOT]
+
+ class << self
+ def instance(name, args = [], search_paths = DEFAULT_SEARCH_PATHS)
+ # RAILS_ROOT constant must be set.
+ unless Object.const_get(:RAILS_ROOT)
+ raise GeneratorError, "RAILS_ROOT must be set. Did you require 'config/environment'?"
+ end
+
+ # Force canonical name.
+ name = Inflector.underscore(name.downcase)
+
+ # Search for filesystem path to requested generator.
+ unless path = find_generator_path(name, search_paths)
+ raise GeneratorError, "#{name} generator not found."
+ end
+
+ # Check for templates directory.
+ template_root = "#{path}/templates"
+ unless File.directory?(template_root)
+ raise GeneratorError, "missing template directory #{template_root}"
+ end
+
+ # Require class file according to naming convention.
+ require "#{path}/#{name}_generator.rb"
+
+ # Find class according to naming convention. Allow Nesting::In::Modules.
+ class_name = Inflector.classify("#{name}_generator")
+ unless klass = find_generator_class(name)
+ raise GeneratorError, "no #{class_name} class defined in #{path}/#{name}_generator.rb"
+ end
+
+ # Instantiate and return generator.
+ klass.new(template_root, RAILS_ROOT, search_paths, args)
+ end
+
+
+ def builtin_generators
+ generators([BUILTIN_ROOT])
+ end
+
+ def contrib_generators
+ generators([CONTRIB_ROOT])
+ end
+
+ def generators(search_paths)
+ generator_paths(search_paths).keys.uniq.sort
+ end
+
+ # Find all generator paths.
+ def generator_paths(search_paths)
+ @paths ||= {}
+ unless @paths[search_paths]
+ paths = Hash.new { |h,k| h[k] = [] }
+ search_paths.each do |path|
+ Dir["#{path}/[a-z]*"].each do |dir|
+ paths[File.basename(dir)] << dir if File.directory?(dir)
+ end
+ end
+ @paths[search_paths] = paths
+ end
+ @paths[search_paths]
+ end
+
+ def find_generator_path(name, search_paths)
+ generator_paths(search_paths)[name].first
+ end
+
+ # Find all generator classes.
+ def generator_classes
+ classes = Hash.new { |h,k| h[k] = [] }
+ class_re = /([^:]+)Generator$/
+ ObjectSpace.each_object(Class) do |object|
+ if md = class_re.match(object.name) and object < Rails::Generator::Base
+ classes[Inflector.underscore(md.captures.first)] << object
+ end
+ end
+ classes
+ end
+
+ def find_generator_class(name)
+ generator_classes[name].first
+ end
+ end
+
+
+ # Talk about generators.
+ class Base
+ attr_reader :template_root, :destination_root, :args, :options,
+ :class_name, :singular_name, :plural_name
+
+ alias_method :file_name, :singular_name
+ alias_method :table_name, :plural_name
+
+ def self.generator_name
+ Inflector.underscore(name.gsub('Generator', ''))
+ end
+
+ def initialize(template_root, destination_root, search_paths, args)
+ @template_root, @destination_root = template_root, destination_root
+ usage if args.empty?
+ @search_paths, @original_args = search_paths, args.dup
+ @class_name, @singular_name, @plural_name = inflect_names(args.shift)
+ @options = extract_options!(args)
+ @args = args
+ end
+
+ protected
+ # Look up another generator with the same arguments.
+ def generator(name)
+ Rails::Generator.instance(name, @original_args, @search_paths)
+ end
+
+ # Generate a file for a Rails application using an ERuby template.
+ # Looks up and evalutes a template by name and writes the result
+ # to a file relative to +destination_root+. The template
+ # is evaluated in the context of the optional eval_binding argument.
+ #
+ # The ERB template uses explicit trim mode to best control the
+ # proliferation of whitespace in generated code. <%- trims leading
+ # whitespace; -%> trims trailing whitespace including one newline.
+ def template(template_name, destination_path, eval_binding = nil)
+ # Determine full paths for source and destination files.
+ template_path = find_template_path(template_name)
+ destination_path = File.join(destination_root, destination_path)
+
+ # Create destination directories.
+ FileUtils.mkdir_p(File.dirname(destination_path))
+
+ # Render template and write result.
+ eval_binding ||= binding
+ contents = ERB.new(File.read(template_path), nil, '-').result(eval_binding)
+ File.open(destination_path, 'w') { |file| file.write(contents) }
+ end
+
+ def usage
+ raise UsageError.new, File.read(usage_path)
+ end
+
+ private
+ def find_template_path(template_name)
+ name, path = template_name.split('/', 2)
+ if path.nil?
+ File.join(template_root, name)
+ elsif generator_path = Rails::Generator.find_generator_path(name, @search_paths)
+ File.join(generator_path, 'templates', path)
+ end
+ end
+
+ def inflect_names(name)
+ camel = Inflector.camelize(Inflector.underscore(name))
+ under = Inflector.underscore(camel)
+ plural = Inflector.pluralize(under)
+ [camel, under, plural]
+ end
+
+ def extract_options!(args)
+ if args.last.is_a?(Hash) then args.pop else {} end
+ end
+
+ def usage_path
+ "#{template_root}/../USAGE"
+ end
+ end
+ end
+end
diff --git a/railties/test/generators/missing_class/missing_class_generator.rb b/railties/test/generators/missing_class/missing_class_generator.rb
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/railties/test/rails_generator_test.rb b/railties/test/rails_generator_test.rb
new file mode 100644
index 0000000000..b63840d338
--- /dev/null
+++ b/railties/test/rails_generator_test.rb
@@ -0,0 +1,95 @@
+$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
+RAILS_ROOT = File.dirname(__FILE__)
+
+require File.dirname(__FILE__) + '/../../activerecord/lib/active_record/support/inflector'
+require 'rails_generator'
+require 'test/unit'
+
+
+class RailsGeneratorTest < Test::Unit::TestCase
+ BUILTINS = %w(controller mailer model scaffold)
+
+ def test_instance_builtins
+ BUILTINS.each do |name|
+ object = nil
+ assert_nothing_raised { object = Rails::Generator.instance(name, ['foo']) }
+ assert_not_nil object
+ assert_match /#{name.capitalize}Generator/, object.class.name
+ assert_respond_to object, :generate
+ end
+ end
+
+ def test_instance_without_rails_root
+ old_verbose, $VERBOSE = $VERBOSE, nil
+ old_rails_root = Object.const_get(:RAILS_ROOT)
+ begin
+ Object.const_set(:RAILS_ROOT, nil)
+ assert_raise(Rails::Generator::GeneratorError) {
+ Rails::Generator.instance('model', ['name'])
+ }
+ ensure
+ Object.const_set(:RAILS_ROOT, old_rails_root)
+ $VERBOSE = old_verbose
+ end
+ end
+
+ def test_instance_not_found
+ assert_raise(Rails::Generator::GeneratorError) {
+ Rails::Generator.instance('foobar')
+ }
+ end
+
+ def test_instance_missing_templates
+ assert_raise(Rails::Generator::GeneratorError) {
+ Rails::Generator.instance('missing_templates')
+ }
+ end
+
+ def test_instance_missing_generator
+ assert_raise(LoadError) {
+ Rails::Generator.instance('missing_generator')
+ }
+ end
+
+ def test_instance_missing_class
+ assert_raise(Rails::Generator::GeneratorError) {
+ Rails::Generator.instance('missing_class')
+ }
+ end
+
+ def test_builtin_generators
+ assert_nothing_raised {
+ assert_equal [], Rails::Generator.builtin_generators - BUILTINS
+ }
+ end
+
+ def test_generator_name
+ assert_equal 'model', Rails::Generator.instance('model', ['name']).class.generator_name
+ end
+
+ def test_generator_usage
+ assert_raise(Rails::Generator::UsageError) {
+ assert_equal 'model', Rails::Generator.instance('model')
+ }
+ end
+
+ def test_generator_vars
+ model = Rails::Generator.instance('model', ['model'])
+ assert_equal "#{Rails::Generator::BUILTIN_ROOT}/model/templates", model.template_root
+ assert_equal RAILS_ROOT, model.destination_root
+ assert_equal 'Model', model.class_name
+ assert_equal 'model', model.singular_name
+ assert_equal 'models', model.plural_name
+ assert_equal model.singular_name, model.file_name
+ assert_equal model.plural_name, model.table_name
+ assert_equal [], model.args
+ end
+
+ def test_generator_generator
+ assert_nothing_raised {
+ model = Rails::Generator.instance('model', ['name'])
+ mailer = model.send(:generator, 'mailer')
+ assert_equal 'mailer', mailer.class.generator_name
+ }
+ end
+end