diff options
author | Carlhuda <carlhuda@engineyard.com> | 2010-04-30 13:08:28 -0700 |
---|---|---|
committer | Carlhuda <carlhuda@engineyard.com> | 2010-04-30 13:48:06 -0700 |
commit | 785493ffed41abcca0686bf05b0a0157e0844d47 (patch) | |
tree | 07ede62e2e5634814e161671d20cf18915ef1141 /railties | |
parent | 0761d1b0f7e89d49fe8cd6ab7eed3f084bd56fbd (diff) | |
download | rails-785493ffed41abcca0686bf05b0a0157e0844d47.tar.gz rails-785493ffed41abcca0686bf05b0a0157e0844d47.tar.bz2 rails-785493ffed41abcca0686bf05b0a0157e0844d47.zip |
Provide a mechanism for overriding the entire generator via a class that handles the individual elements of generating a new Rails application
Diffstat (limited to 'railties')
-rw-r--r-- | railties/lib/rails/generators/rails/app/app_generator.rb | 142 | ||||
-rw-r--r-- | railties/test/generators/app_generator_test.rb | 134 |
2 files changed, 181 insertions, 95 deletions
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 29e78a898c..380057a10e 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -2,14 +2,44 @@ require 'digest/md5' require 'active_support/secure_random' require 'rails/version' unless defined?(Rails::VERSION) require 'rbconfig' +require 'open-uri' +require 'uri' module Rails - class AppBuilder + module ActionMethods + attr_reader :options + def initialize(generator) @generator = generator @options = generator.options end + private + %w(template copy_file directory empty_directory inside + empty_directory_with_gitkeep create_file chmod shebang).each do |method| + class_eval <<-RUBY + def #{method}(*args, &block) + @generator.send(:#{method}, *args, &block) + end + RUBY + end + + # TODO: Remove once this is fully in place + def method_missing(meth, *args, &block) + STDERR.puts "Calling #{meth} with #{args.inspect} with #{block}" + @generator.send(meth, *args, &block) + end + end + + class AppBuilder + def rakefile + template "Rakefile" + end + + def readme + copy_file "README" + end + def gemfile template "Gemfile" end @@ -81,7 +111,11 @@ module Rails end def javascripts - directory "public/javascripts" + unless options[:skip_prototype] + directory "public/javascripts" + else + empty_directory_with_gitkeep "public/javascripts" + end end def script @@ -108,23 +142,6 @@ module Rails def vendor_plugins empty_directory_with_gitkeep "vendor/plugins" end - - - private - %w(template copy_file directory empty_directory inside - empty_directory_with_gitkeep create_file chmod shebang).each do |method| - class_eval <<-RUBY - def #{method}(*args, &block) - @generator.send(:#{method}, *args, &block) - end - RUBY - end - - # TODO: Remove once this is fully in place - def method_missing(meth, *args, &block) - STDERR.puts "Calling #{meth} with #{args.inspect} with #{block}" - @generator.send(meth, *args, &block) - end end module Generators @@ -142,52 +159,49 @@ module Rails attr_accessor :rails_template add_shebang_option! - argument :app_path, :type => :string + argument :app_path, :type => :string - class_option :database, :type => :string, :aliases => "-d", :default => "sqlite3", - :desc => "Preconfigure for selected database (options: #{DATABASES.join('/')})" + class_option :database, :type => :string, :aliases => "-d", :default => "sqlite3", + :desc => "Preconfigure for selected database (options: #{DATABASES.join('/')})" - class_option :template, :type => :string, :aliases => "-m", - :desc => "Path to an application template (can be a filesystem path or URL)." + class_option :builder, :type => :string, :aliases => "-b", + :desc => "Path to an application builder (can be a filesystem path or URL)" - class_option :dev, :type => :boolean, :default => false, - :desc => "Setup the application with Gemfile pointing to your Rails checkout" + class_option :template, :type => :string, :aliases => "-m", + :desc => "Path to an application template (can be a filesystem path or URL)." - class_option :edge, :type => :boolean, :default => false, - :desc => "Setup the application with Gemfile pointing to Rails repository" + class_option :dev, :type => :boolean, :default => false, + :desc => "Setup the application with Gemfile pointing to your Rails checkout" - class_option :skip_gemfile, :type => :boolean, :default => false, - :desc => "Don't create a Gemfile" + class_option :edge, :type => :boolean, :default => false, + :desc => "Setup the application with Gemfile pointing to Rails repository" - class_option :skip_activerecord, :type => :boolean, :aliases => "-O", :default => false, - :desc => "Skip ActiveRecord files" + class_option :skip_gemfile, :type => :boolean, :default => false, + :desc => "Don't create a Gemfile" - class_option :skip_testunit, :type => :boolean, :aliases => "-T", :default => false, - :desc => "Skip TestUnit files" + class_option :skip_activerecord, :type => :boolean, :aliases => "-O", :default => false, + :desc => "Skip ActiveRecord files" - class_option :skip_prototype, :type => :boolean, :aliases => "-J", :default => false, - :desc => "Skip Prototype files" + class_option :skip_testunit, :type => :boolean, :aliases => "-T", :default => false, + :desc => "Skip TestUnit files" - class_option :skip_git, :type => :boolean, :aliases => "-G", :default => false, - :desc => "Skip Git ignores and keeps" + class_option :skip_prototype, :type => :boolean, :aliases => "-J", :default => false, + :desc => "Skip Prototype files" + + class_option :skip_git, :type => :boolean, :aliases => "-G", :default => false, + :desc => "Skip Git ignores and keeps" # Add bin/rails options - class_option :version, :type => :boolean, :aliases => "-v", :group => :rails, - :desc => "Show Rails version number and quit" + class_option :version, :type => :boolean, :aliases => "-v", :group => :rails, + :desc => "Show Rails version number and quit" - class_option :help, :type => :boolean, :aliases => "-h", :group => :rails, - :desc => "Show this help message and quit" + class_option :help, :type => :boolean, :aliases => "-h", :group => :rails, + :desc => "Show this help message and quit" def initialize(*args) raise Error, "Options should be given after the application name. For details run: rails --help" if args[0].blank? super - if builder = options[:builder] - apply builder - end - - @builder = AppBuilder.new(self) - if !options[:skip_activerecord] && !DATABASES.include?(options[:database]) raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}." end @@ -203,8 +217,8 @@ module Rails end def create_root_files - copy_file "README" - template "Rakefile" + build(:readme) + build(:rakefile) build(:configru) build(:gitignore) unless options[:skip_git] build(:gemfile) unless options[:skip_gemfile] @@ -253,15 +267,10 @@ module Rails def create_public_stylesheets_files build(:stylesheets) - empty_directory_with_gitkeep "public/stylesheets" end def create_prototype_files - unless options[:skip_prototype] - build(:javascripts) - else - empty_directory_with_gitkeep "public/javascripts" - end + build(:javascripts) end def create_script_files @@ -301,8 +310,27 @@ module Rails "rails #{self.arguments.map(&:usage).join(' ')} [options]" end + def builder + @builder ||= begin + if path = options[:builder] + if URI(path).is_a?(URI::HTTP) + contents = open(path, "Accept" => "application/x-thor-template") {|io| io.read } + else + contents = open(path) {|io| io.read } + end + + prok = eval("proc { #{contents} }", TOPLEVEL_BINDING, path, 1) + instance_eval(&prok) + end + + builder_class = defined?(::AppBuilder) ? ::AppBuilder : Rails::AppBuilder + builder_class.send(:include, ActionMethods) + builder_class.new(self) + end + end + def build(meth, *args) - @builder.send(meth, *args) if @builder.respond_to?(meth) + builder.send(meth, *args) if builder.respond_to?(meth) end def set_default_accessors! diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 1e5aa156ce..1a93867013 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -2,6 +2,40 @@ require 'abstract_unit' require 'generators/generators_test_helper' require 'rails/generators/rails/app/app_generator' +DEFAULT_APP_FILES = %w( + .gitignore + Gemfile + Rakefile + config.ru + app/controllers + app/helpers + app/models + app/views/layouts + config/environments + config/initializers + config/locales + db + doc + lib + lib/tasks + log + public/images + public/javascripts + public/stylesheets + script/rails + test/fixtures + test/functional + test/integration + test/performance + test/unit + vendor + vendor/plugins + tmp/sessions + tmp/sockets + tmp/cache + tmp/pids +) + class AppGeneratorTest < Rails::Generators::TestCase include GeneratorsTestHelper arguments [destination_root] @@ -20,39 +54,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_application_skeleton_is_created run_generator - %w( - .gitignore - Gemfile - Rakefile - config.ru - app/controllers - app/helpers - app/models - app/views/layouts - config/environments - config/initializers - config/locales - db - doc - lib - lib/tasks - log - public/images - public/javascripts - public/stylesheets - script/rails - test/fixtures - test/functional - test/integration - test/performance - test/unit - vendor - vendor/plugins - tmp/sessions - tmp/sockets - tmp/cache - tmp/pids - ).each{ |path| assert_file path } + DEFAULT_APP_FILES.each{ |path| assert_file path } end def test_application_controller_and_layout_files @@ -66,7 +68,7 @@ class AppGeneratorTest < Rails::Generators::TestCase content = capture(:stderr){ run_generator(["--skip-activerecord", destination_root]) } assert_equal "Options should be given after the application name. For details run: rails --help\n", content end - + def test_name_collision_raises_an_error content = capture(:stderr){ run_generator [File.join(destination_root, "generate")] } assert_equal "Invalid application name generate. Please give a name which does not match one of the reserved rails words.\n", content @@ -197,10 +199,66 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file 'Gemfile', /^gem\s+["']rails["'],\s+:git\s+=>\s+["']#{Regexp.escape("git://github.com/rails/rails.git")}["']$/ end - protected +protected - def action(*args, &block) - silence(:stdout){ generator.send(*args, &block) } - end + def action(*args, &block) + silence(:stdout){ generator.send(*args, &block) } + end end + +class CustomAppGeneratorTest < Rails::Generators::TestCase + include GeneratorsTestHelper + tests Rails::Generators::AppGenerator + + arguments [destination_root] + + def setup + super + Rails::Generators::AppGenerator.instance_variable_set('@desc', nil) + @bundle_command = File.basename(Thor::Util.ruby_command).sub(/ruby/, 'bundle') + end + + def teardown + super + Rails::Generators::AppGenerator.instance_variable_set('@desc', nil) + Object.class_eval { remove_const :AppBuilder if const_defined?(:AppBuilder) } + end + + def test_builder_option_with_empty_app_builder + FileUtils.cd(Rails.root) + run_generator([destination_root, "-b", "#{Rails.root}/lib/empty_builder.rb"]) + DEFAULT_APP_FILES.each{ |path| assert_no_file path } + end + + def test_builder_option_with_simple_app_builder + FileUtils.cd(Rails.root) + run_generator([destination_root, "-b", "#{Rails.root}/lib/simple_builder.rb"]) + (DEFAULT_APP_FILES - ['config.ru']).each{ |path| assert_no_file path } + assert_file "config.ru", %[run proc { |env| [200, { "Content-Type" => "text/html" }, ["Hello World"]] }] + end + + def test_builder_option_with_tweak_app_builder + FileUtils.cd(Rails.root) + run_generator([destination_root, "-b", "#{Rails.root}/lib/tweak_builder.rb"]) + DEFAULT_APP_FILES.each{ |path| assert_file path } + assert_file "config.ru", %[run proc { |env| [200, { "Content-Type" => "text/html" }, ["Hello World"]] }] + end + + def test_builder_option_with_http + path = "http://gist.github.com/103208.txt" + template = "class AppBuilder; end" + template.instance_eval "def read; self; end" # Make the string respond to read + + generator([destination_root], :builder => path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template) + capture(:stdout) { generator.invoke } + + DEFAULT_APP_FILES.each{ |path| assert_no_file path } + end + +protected + + def action(*args, &block) + silence(:stdout){ generator.send(*args, &block) } + end +end
\ No newline at end of file |