diff options
302 files changed, 8766 insertions, 4449 deletions
diff --git a/.gitignore b/.gitignore index 5357fbe481..2d879499fa 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ actionmailer/pkg activesupport/pkg railties/pkg railties/test/500.html +railties/test/fixtures/tmp +railties/test/initializer/root/log railties/doc/guides/html/images railties/doc/guides/html/stylesheets benches @@ -38,7 +38,8 @@ Rake::RDocTask.new do |rdoc| rdoc.rdoc_files.include('railties/CHANGELOG') rdoc.rdoc_files.include('railties/MIT-LICENSE') rdoc.rdoc_files.include('railties/README') - rdoc.rdoc_files.include('railties/lib/{*.rb,commands/*.rb,rails/*.rb,rails_generator/*.rb}') + rdoc.rdoc_files.include('railties/lib/{*.rb,commands/*.rb,rails/*.rb,generators/*.rb}') + rdoc.rdoc_files.exclude('railties/lib/vendor/*') rdoc.rdoc_files.include('activerecord/README') rdoc.rdoc_files.include('activerecord/CHANGELOG') diff --git a/activesupport/lib/active_support/mini.rb b/activesupport/lib/active_support/mini.rb index fe7ba48e58..b787650655 100644 --- a/activesupport/lib/active_support/mini.rb +++ b/activesupport/lib/active_support/mini.rb @@ -1,9 +1,9 @@ $LOAD_PATH.unshift File.dirname(__FILE__) -require "core_ext/blank" # whole object.rb pulls up rarely used introspection extensions +require "core_ext/object/blank" require "core_ext/object/metaclass" require 'core_ext/array' require 'core_ext/hash' require 'core_ext/module/attribute_accessors' -require 'core_ext/string/inflections'
\ No newline at end of file +require 'core_ext/string/inflections' diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb index 9a0441206b..b65eeaaae7 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.13/tzinfo/ruby_core_support.rb @@ -1,56 +1,56 @@ -#--
-# Copyright (c) 2008 Philip Ross
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#++
-
-require 'date'
-require 'rational'
-
-module TZInfo
-
- # Methods to support different versions of Ruby.
- module RubyCoreSupport #:nodoc:
-
- # Use Rational.new! for performance reasons in Ruby 1.8.
- # This has been removed from 1.9, but Rational performs better.
- if Rational.respond_to? :new!
- def self.rational_new!(numerator, denominator = 1)
- Rational.new!(numerator, denominator)
- end
- else
- def self.rational_new!(numerator, denominator = 1)
- Rational(numerator, denominator)
- end
- end
-
- # Ruby 1.8.6 introduced new! and deprecated new0.
- # Ruby 1.9.0 removed new0.
- # We still need to support new0 for older versions of Ruby.
- if DateTime.respond_to? :new!
- def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
- DateTime.new!(ajd, of, sg)
- end
- else
- def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
- DateTime.new0(ajd, of, sg)
- end
- end
- end
+#-- +# Copyright (c) 2008 Philip Ross +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +#++ + +require 'date' +require 'rational' + +module TZInfo + + # Methods to support different versions of Ruby. + module RubyCoreSupport #:nodoc: + + # Use Rational.new! for performance reasons in Ruby 1.8. + # This has been removed from 1.9, but Rational performs better. + if Rational.respond_to? :new! + def self.rational_new!(numerator, denominator = 1) + Rational.new!(numerator, denominator) + end + else + def self.rational_new!(numerator, denominator = 1) + Rational(numerator, denominator) + end + end + + # Ruby 1.8.6 introduced new! and deprecated new0. + # Ruby 1.9.0 removed new0. + # We still need to support new0 for older versions of Ruby. + if DateTime.respond_to? :new! + def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY) + DateTime.new!(ajd, of, sg) + end + else + def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY) + DateTime.new0(ajd, of, sg) + end + end + end end
\ No newline at end of file diff --git a/railties/Rakefile b/railties/Rakefile index 61c094150a..3212bf3a4f 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -26,7 +26,8 @@ task :default => :test ## below passes. It's not ideal, but at least ## we can see the failures task :test do - Dir['test/**/*_test.rb'].all? do |file| + dir = ENV["TEST_DIR"] || "**" + Dir["test/#{dir}/*_test.rb"].all? do |file| ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) system(ruby, '-Itest', "-I#{File.dirname(__FILE__)}/../activesupport/lib", file) end or raise "Failures" @@ -40,56 +41,22 @@ Rake::TestTask.new("regular_test") do |t| t.verbose = true end - -BASE_DIRS = %w( - app - config/environments - config/initializers - config/locales - db - doc - log - lib - lib/tasks - public - script - script/performance - test - vendor - vendor/plugins - tmp/sessions - tmp/cache - tmp/sockets - tmp/pids -) - -APP_DIRS = %w( models controllers helpers views views/layouts ) -PUBLIC_DIRS = %w( images javascripts stylesheets ) -TEST_DIRS = %w( fixtures unit functional mocks mocks/development mocks/test ) - -LOG_FILES = %w( server.log development.log test.log production.log ) -HTML_FILES = %w( 422.html 404.html 500.html index.html robots.txt favicon.ico images/rails.png - javascripts/prototype.js javascripts/application.js - javascripts/effects.js javascripts/dragdrop.js javascripts/controls.js ) -BIN_FILES = %w( about console destroy generate performance/benchmarker performance/profiler runner server plugin ) - VENDOR_LIBS = %w( actionpack activerecord actionmailer activesupport activeresource railties ) - desc "Generates a fresh Rails package with documentation" -task :fresh_rails => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_vendor_libraries, :copy_ties_content, :generate_documentation ] +task :fresh_rails => [ :clean, :create_rails, :copy_vendor_libraries, :generate_documentation ] desc "Generates a fresh Rails package using GEMs with documentation" -task :fresh_gem_rails => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_ties_content, :copy_gem_environment ] +task :fresh_gem_rails => [ :clean, :create_rails ] desc "Generates a fresh Rails package without documentation (faster)" -task :fresh_rails_without_docs => [ :clean, :make_dir_structure, :initialize_file_stubs, :copy_vendor_libraries, :copy_ties_content ] +task :fresh_rails_without_docs => [ :clean, :create_rails, :copy_vendor_libraries ] -desc "Generates a fresh Rails package without documentation (faster)" -task :fresh_rails_without_docs_using_links => [ :clean, :make_dir_structure, :initialize_file_stubs, :link_vendor_libraries, :copy_ties_content ] +desc "Generates a fresh Rails package without documentation using links (faster)" +task :fresh_rails_without_docs_using_links => [ :clean, :create_rails, :link_vendor_libraries ] desc "Generates minimal Rails package using symlinks" -task :dev => [ :clean, :make_dir_structure, :initialize_file_stubs, :link_vendor_libraries, :copy_ties_content ] +task :dev => [ :clean, :create_rails, :link_vendor_libraries ] desc "Packages the fresh Rails package with documentation" task :package => [ :clean, :fresh_rails ] do @@ -101,54 +68,31 @@ task :clean do rm_rf PKG_DESTINATION end -# Get external spinoffs ------------------------------------------------------------------- +# Update spinoffs ------------------------------------------------------------------- -desc "Updates railties to the latest version of the javascript spinoffs" -task :update_js do - for js in %w( prototype controls dragdrop effects ) - rm "html/javascripts/#{js}.js" - cp "./../actionpack/lib/action_view/helpers/javascripts/#{js}.js", "html/javascripts" - end +desc "Updates application README to the latest version Railties README" +task :update_readme do + readme = "lib/generators/rails/app/templates/README" + rm readme + cp "./README", readme end -# Make directory structure ---------------------------------------------------------------- - -def make_dest_dirs(dirs, path = '.') - mkdir_p dirs.map { |dir| File.join(PKG_DESTINATION, path.to_s, dir) } -end - -desc "Make the directory structure for the new Rails application" -task :make_dir_structure => [ :make_base_dirs, :make_app_dirs, :make_public_dirs, :make_test_dirs ] - -task(:make_base_dirs) { make_dest_dirs BASE_DIRS } -task(:make_app_dirs) { make_dest_dirs APP_DIRS, 'app' } -task(:make_public_dirs) { make_dest_dirs PUBLIC_DIRS, 'public' } -task(:make_test_dirs) { make_dest_dirs TEST_DIRS, 'test' } - - -# Initialize file stubs ------------------------------------------------------------------- - -desc "Initialize empty file stubs (such as for logging)" -task :initialize_file_stubs => [ :initialize_log_files ] +# Run application generator ------------------------------------------------------------- -task :initialize_log_files do - log_dir = File.join(PKG_DESTINATION, 'log') - chmod 0777, log_dir - LOG_FILES.each do |log_file| - log_path = File.join(log_dir, log_file) - touch log_path - chmod 0666, log_path - end +task :create_rails do + require File.join(File.dirname(__FILE__), 'lib', 'generators') + require 'generators/rails/app/app_generator' + Rails::Generators::AppGenerator.start [ File.basename(PKG_DESTINATION), "--quiet" ], + :destination_root => File.expand_path(File.dirname(PKG_DESTINATION)) end - # Copy Vendors ---------------------------------------------------------------------------- desc "Copy in all the Rails packages to vendor" task :copy_vendor_libraries do mkdir File.join(PKG_DESTINATION, 'vendor', 'rails') VENDOR_LIBS.each { |dir| cp_r File.join('..', dir), File.join(PKG_DESTINATION, 'vendor', 'rails', dir) } - FileUtils.rm_r(Dir.glob(File.join(PKG_DESTINATION, 'vendor', 'rails', "**", ".svn"))) + FileUtils.rm_r(Dir.glob(File.join(PKG_DESTINATION, 'vendor', 'rails', "**", ".git"))) end desc "Link in all the Rails packages to vendor" @@ -158,96 +102,6 @@ task :link_vendor_libraries do end -# Copy Ties Content ----------------------------------------------------------------------- - -desc "Make copies of all the default content of ties" -task :copy_ties_content => [ - :copy_rootfiles, :copy_dispatches, :copy_html_files, :copy_application, - :copy_configs, :copy_binfiles, :copy_test_helpers, :copy_app_doc_readme ] - -task :copy_dispatches do - copy_with_rewritten_ruby_path("dispatches/dispatch.rb", "#{PKG_DESTINATION}/public/dispatch.rb") - chmod 0755, "#{PKG_DESTINATION}/public/dispatch.rb" - - copy_with_rewritten_ruby_path("dispatches/dispatch.rb", "#{PKG_DESTINATION}/public/dispatch.cgi") - chmod 0755, "#{PKG_DESTINATION}/public/dispatch.cgi" - - copy_with_rewritten_ruby_path("dispatches/dispatch.fcgi", "#{PKG_DESTINATION}/public/dispatch.fcgi") - chmod 0755, "#{PKG_DESTINATION}/public/dispatch.fcgi" -end - -task :copy_html_files do - HTML_FILES.each { |file| cp File.join('html', file), File.join(PKG_DESTINATION, 'public', file) } -end - -task :copy_application do - cp "helpers/application_controller.rb", "#{PKG_DESTINATION}/app/controllers/application_controller.rb" - cp "helpers/application_helper.rb", "#{PKG_DESTINATION}/app/helpers/application_helper.rb" -end - -task :copy_configs do - app_name = "rails" - socket = nil - require 'erb' - File.open("#{PKG_DESTINATION}/config/database.yml", 'w') {|f| f.write ERB.new(IO.read("configs/databases/sqlite3.yml"), nil, '-').result(binding)} - - cp "configs/routes.rb", "#{PKG_DESTINATION}/config/routes.rb" - - cp "configs/initializers/backtrace_silencers.rb", "#{PKG_DESTINATION}/config/initializers/backtrace_silencers.rb" - cp "configs/initializers/inflections.rb", "#{PKG_DESTINATION}/config/initializers/inflections.rb" - cp "configs/initializers/mime_types.rb", "#{PKG_DESTINATION}/config/initializers/mime_types.rb" - cp "configs/initializers/new_rails_defaults.rb", "#{PKG_DESTINATION}/config/initializers/new_rails_defaults.rb" - - cp "configs/locales/en.yml", "#{PKG_DESTINATION}/config/locales/en.yml" - - cp "configs/seeds.rb", "#{PKG_DESTINATION}/db/seeds.rb" - - cp "environments/boot.rb", "#{PKG_DESTINATION}/config/boot.rb" - cp "environments/environment.rb", "#{PKG_DESTINATION}/config/environment.rb" - cp "environments/production.rb", "#{PKG_DESTINATION}/config/environments/production.rb" - cp "environments/development.rb", "#{PKG_DESTINATION}/config/environments/development.rb" - cp "environments/test.rb", "#{PKG_DESTINATION}/config/environments/test.rb" - -end - -task :copy_binfiles do - BIN_FILES.each do |file| - dest_file = File.join(PKG_DESTINATION, 'script', file) - copy_with_rewritten_ruby_path(File.join('bin', file), dest_file) - chmod 0755, dest_file - end -end - -task :copy_rootfiles do - cp "fresh_rakefile", "#{PKG_DESTINATION}/Rakefile" - cp "README", "#{PKG_DESTINATION}/README" - cp "CHANGELOG", "#{PKG_DESTINATION}/CHANGELOG" -end - -task :copy_test_helpers do - cp "helpers/test_helper.rb", "#{PKG_DESTINATION}/test/test_helper.rb" -end - -task :copy_app_doc_readme do - cp "doc/README_FOR_APP", "#{PKG_DESTINATION}/doc/README_FOR_APP" -end - -def copy_with_rewritten_ruby_path(src_file, dest_file) - ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) - - File.open(dest_file, 'w') do |df| - File.open(src_file) do |sf| - line = sf.gets - if (line =~ /#!.+ruby\s*/) != nil - df.puts("#!#{ruby}") - else - df.puts(line) - end - df.write(sf.read) - end - end -end - desc 'Generate guides (for authors), use ONLY=foo to process just "foo.textile"' task :generate_guides do ENV["WARN_BROKEN_LINKS"] = "1" # authors can't disable this @@ -265,7 +119,6 @@ task :generate_rails_framework_doc do end task :generate_app_doc do - cp "doc/README_FOR_APP", "#{PKG_DESTINATION}/doc/README_FOR_APP" system %{cd #{PKG_DESTINATION}; rake doc:app} end @@ -278,30 +131,17 @@ Rake::RDocTask.new { |rdoc| rdoc.rdoc_files.include('README', 'CHANGELOG') rdoc.rdoc_files.include('lib/*.rb') rdoc.rdoc_files.include('lib/rails/*.rb') - rdoc.rdoc_files.include('lib/rails_generator/*.rb') + rdoc.rdoc_files.include('lib/generators/*.rb') rdoc.rdoc_files.include('lib/commands/**/*.rb') } # Generate GEM ---------------------------------------------------------------------------- -task :copy_gem_environment do - cp "environments/environment.rb", "#{PKG_DESTINATION}/config/environment.rb" - chmod 0755, dest_file -end - - PKG_FILES = FileList[ '[a-zA-Z]*', 'bin/**/*', 'builtin/**/*', - 'configs/**/*', - 'doc/**/*', - 'dispatches/**/*', - 'environments/**/*', 'guides/**/*', - 'helpers/**/*', - 'generators/**/*', - 'html/**/*', 'lib/**/*' ] - [ 'test' ] diff --git a/railties/bin/rails b/railties/bin/rails index 6a0c675206..538d0cbc84 100755 --- a/railties/bin/rails +++ b/railties/bin/rails @@ -7,14 +7,9 @@ if %w(--version -v).include? ARGV.first exit(0) end -freeze = ARGV.any? { |option| %w(--freeze -f).include?(option) } +ARGV << "--help" if ARGV.empty? -app_path = ARGV.first +require File.dirname(__FILE__) + '/../lib/generators' +require 'generators/rails/app/app_generator' -require File.dirname(__FILE__) + '/../lib/rails_generator' - -require 'rails_generator/scripts/generate' -Rails::Generator::Base.use_application_sources! -Rails::Generator::Scripts::Generate.new.run(ARGV, :generator => 'app') - -Dir.chdir(app_path) { `rake rails:freeze:gems`; puts "froze" } if freeze
\ No newline at end of file +Rails::Generators::AppGenerator.start diff --git a/railties/lib/commands/destroy.rb b/railties/lib/commands/destroy.rb index f4b81d6511..5013d30b83 100644 --- a/railties/lib/commands/destroy.rb +++ b/railties/lib/commands/destroy.rb @@ -1,6 +1,10 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators')) require "#{RAILS_ROOT}/config/environment" -require 'rails_generator' -require 'rails_generator/scripts/destroy' -ARGV.shift if ['--help', '-h'].include?(ARGV[0]) -Rails::Generator::Scripts::Destroy.new.run(ARGV) +if ARGV.size == 0 + Rails::Generators.help + exit +end + +name = ARGV.shift +Rails::Generators.invoke name, ARGV, :behavior => :revoke diff --git a/railties/lib/commands/generate.rb b/railties/lib/commands/generate.rb index 3d3db3d856..32cabcab10 100755 --- a/railties/lib/commands/generate.rb +++ b/railties/lib/commands/generate.rb @@ -1,6 +1,10 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators')) require "#{RAILS_ROOT}/config/environment" -require 'rails_generator' -require 'rails_generator/scripts/generate' -ARGV.shift if ['--help', '-h'].include?(ARGV[0]) -Rails::Generator::Scripts::Generate.new.run(ARGV) +if ARGV.size == 0 + Rails::Generators.help + exit +end + +name = ARGV.shift +Rails::Generators.invoke name, ARGV, :behavior => :invoke diff --git a/railties/lib/commands/update.rb b/railties/lib/commands/update.rb index 83ef833300..f3b3ad0775 100644 --- a/railties/lib/commands/update.rb +++ b/railties/lib/commands/update.rb @@ -1,4 +1,10 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators')) require "#{RAILS_ROOT}/config/environment" -require 'rails_generator' -require 'rails_generator/scripts/update' -Rails::Generator::Scripts::Update.new.run(ARGV) + +if ARGV.size == 0 + Rails::Generators.help + exit +end + +name = ARGV.shift +Rails::Generators.invoke name, ARGV, :behavior => :skip diff --git a/railties/lib/generators.rb b/railties/lib/generators.rb new file mode 100644 index 0000000000..64ec808ee4 --- /dev/null +++ b/railties/lib/generators.rb @@ -0,0 +1,300 @@ +activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" +$:.unshift(activesupport_path) if File.directory?(activesupport_path) + +begin + require 'active_support/mini' +rescue LoadError + require 'rubygems' + gem 'activesupport' + require 'active_support/mini' +end + +$:.unshift(File.dirname(__FILE__)) + +require 'vendor/thor-0.11.3/lib/thor' +require 'generators/base' +require 'generators/named_base' + +module Rails + module Generators + DEFAULT_ALIASES = { + :rails => { + :actions => '-a', + :orm => '-o', + :resource_controller => '-c', + :scaffold_controller => '-c', + :stylesheets => '-y', + :template_engine => '-e', + :test_framework => '-t' + }, + + :test_unit => { + :fixture_replacement => '-r', + }, + + :plugin => { + :generator => '-g', + :tasks => '-r' + } + } + + DEFAULT_OPTIONS = { + :active_record => { + :migration => true, + :timestamps => true + }, + + :erb => { + :form => false, + :layout => true + }, + + :rails => { + :force_plural => false, + :helper => true, + :layout => true, + :orm => :active_record, + :integration_tool => :test_unit, + :performance_tool => :test_unit, + :resource_controller => :controller, + :scaffold_controller => :scaffold_controller, + :singleton => false, + :stylesheets => true, + :template_engine => :erb, + :test_framework => :test_unit + }, + + :test_unit => { + :fixture => true, + :fixture_replacement => nil + }, + + :plugin => { + :generator => false, + :tasks => false + } + } + + def self.aliases #:nodoc: + @@aliases ||= DEFAULT_ALIASES.dup + end + + def self.options #:nodoc: + @@options ||= DEFAULT_OPTIONS.dup + end + + # Hold configured generators fallbacks. If a plugin developer wants a + # generator group to fallback to another group in case of missing generators, + # they can add a fallback. + # + # For example, shoulda is considered a test_framework and is an extension + # of test_unit. However, most part of shoulda generators are similar to + # test_unit ones. + # + # Shoulda then can tell generators to search for test_unit generators when + # some of them are not available by adding a fallback: + # + # Rails::Generators.fallbacks[:shoulda] = :test_unit + # + def self.fallbacks + @@fallbacks ||= {} + end + + # Remove the color from output. + # + def self.no_color! + Thor::Base.shell = Thor::Shell::Basic + end + + # Generators load paths used on lookup. The lookup happens as: + # + # 1) builtin generators + # 2) frozen gems generators + # 3) rubygems gems generators (not available yet) + # 4) plugin generators + # 5) lib generators + # 6) ~/rails/generators + # + # TODO Add Rubygems generators (depends on dependencies system rework) + # TODO Remove hardcoded paths for all, except (1). + # + def self.load_path + @@load_path ||= begin + paths = [] + paths << File.expand_path(File.join(File.dirname(__FILE__), "generators")) + if defined?(RAILS_ROOT) + paths += Dir[File.join(RAILS_ROOT, "vendor", "gems", "*", "lib", "generators")] + paths += Dir[File.join(RAILS_ROOT, "vendor", "plugins", "*", "lib", "generators")] + paths << File.join(RAILS_ROOT, "lib", "generators") + end + paths << File.join(Thor::Util.user_home, ".rails", "generators") + paths + end + end + load_path # Cache load paths. Needed to avoid __FILE__ pointing to wrong paths. + + # Receives a namespace and tries different combinations to find a generator. + # + # ==== Examples + # + # find_by_namespace :webrat, :rails, :integration + # + # Will search for the following generators: + # + # "rails:generators:webrat", "webrat:generators:integration", "webrat" + # + # If the namespace has ":" included we consider that a absolute namespace + # was given and the lookup above does not happen. Just the name is searched. + # + # Finally, it deals with one kind of shortcut: + # + # find_by_namespace "test_unit:model" + # + # It will search for generators at: + # + # "test_unit:generators:model", "test_unit:model" + # + def self.find_by_namespace(name, base=nil, context=nil) #:nodoc: + name, attempts = name.to_s, [] + + case name.count(':') + when 1 + base, name = name.split(':') + return find_by_namespace(name, base) + when 0 + attempts << "#{base}:generators:#{name}" if base + attempts << "#{name}:generators:#{context}" if context + end + + attempts << name + unloaded = attempts - namespaces + lookup(unloaded) + + attempts.each do |namespace| + klass = Thor::Util.find_by_namespace(namespace) + return klass if klass + end + + invoke_fallbacks_for(name, base) + end + + # Receives a namespace, arguments and the behavior to invoke the generator. + # It's used as the default entry point for generate, destroy and update + # commands. + # + def self.invoke(namespace, args=ARGV, config={}) + if klass = find_by_namespace(namespace, "rails") + args << "--help" if klass.arguments.any? { |a| a.required? } && args.empty? + klass.start args, config + else + puts "Could not find generator #{namespace}." + end + end + + # Show help message with available generators. + # + def self.help + rails = Rails::Generators.builtin.map do |group, name| + name if group == "rails" + end + rails.compact! + rails.sort! + + puts "Please select a generator." + puts "Builtin: #{rails.join(', ')}." + + # Load paths and remove builtin + paths, others = load_path.dup, [] + paths.shift + + paths.each do |path| + tail = [ "*", "*", "*_generator.rb" ] + + until tail.empty? + others += Dir[File.join(path, *tail)].collect do |file| + file.split('/')[-tail.size, 2].join(':').sub(/_generator\.rb$/, '') + end + tail.shift + end + end + + others.sort! + puts "Others: #{others.join(', ')}." unless others.empty? + end + + protected + + # Return all defined namespaces. + # + def self.namespaces #:nodoc: + Thor::Base.subclasses.map{ |klass| klass.namespace } + end + + # Keep builtin generators in an Array[Array[group, name]]. + # + def self.builtin #:nodoc: + Dir[File.dirname(__FILE__) + '/generators/*/*'].collect do |file| + file.split('/')[-2, 2] + end + end + + # Try callbacks for the given base. + # + def self.invoke_fallbacks_for(name, base) + return nil unless base && fallbacks[base.to_sym] + + invoked_fallbacks = [] + + Array(fallbacks[base.to_sym]).each do |fallback| + next if invoked_fallbacks.include?(fallback) + invoked_fallbacks << fallback + + klass = find_by_namespace(name, fallback) + return klass if klass + end + + nil + end + + # Receives namespaces in an array and tries to find matching generators + # in the load path. Each path is traversed into directory lookups. For + # example: + # + # rails:generators:model + # + # Becomes: + # + # generators/rails/model/model_generator.rb + # generators/rails/model_generator.rb + # generators/model_generator.rb + # + def self.lookup(attempts) #:nodoc: + attempts.each do |attempt| + generators_path = ['.'] + + paths = attempt.gsub(':generators:', ':').split(':') + name = "#{paths.last}_generator.rb" + + until paths.empty? + generators_path.unshift File.join(*paths) + paths.pop + end + + generators_path.uniq! + generators_path = "{#{generators_path.join(',')}}" + + self.load_path.each do |path| + Dir[File.join(path, generators_path, name)].each do |file| + begin + require file + rescue Exception => e + warn "[WARNING] Could not load generator at #{file.inspect}. Error: #{e.message}" + end + end + end + end + end + + end +end + diff --git a/railties/lib/generators/action_orm.rb b/railties/lib/generators/action_orm.rb new file mode 100644 index 0000000000..69cf227fd7 --- /dev/null +++ b/railties/lib/generators/action_orm.rb @@ -0,0 +1,74 @@ +module Rails + module Generators + # ActionORM is a class to be implemented by each ORM to allow Rails to + # generate customized controller code. + # + # The API has the same methods as ActiveRecord, but each method returns a + # string that matches the ORM API. + # + # For example: + # + # ActiveRecord::Generators::ActionORM.find(Foo, "params[:id]") + # #=> "Foo.find(params[:id])" + # + # Datamapper::Generators::ActionORM.find(Foo, "params[:id]") + # #=> "Foo.get(params[:id])" + # + # On initialization, the ActionORM accepts the instance name that will + # receive the calls: + # + # builder = ActiveRecord::Generators::ActionORM.new "@foo" + # builder.save #=> "@foo.save" + # + # The only exception in ActionORM for ActiveRecord is the use of self.build + # instead of self.new. + # + class ActionORM + attr_reader :name + + def initialize(name) + @name = name + end + + # GET index + def self.all(klass) + raise NotImplementedError + end + + # GET show + # GET edit + # PUT update + # DELETE destroy + def self.find(klass, params=nil) + raise NotImplementedError + end + + # GET new + # POST create + def self.build(klass, params=nil) + raise NotImplementedError + end + + # POST create + def save + raise NotImplementedError + end + + # PUT update + def update_attributes(params=nil) + raise NotImplementedError + end + + # POST create + # PUT update + def errors + raise NotImplementedError + end + + # DELETE destroy + def destroy + raise NotImplementedError + end + end + end +end diff --git a/railties/lib/generators/actions.rb b/railties/lib/generators/actions.rb new file mode 100644 index 0000000000..55ef212abb --- /dev/null +++ b/railties/lib/generators/actions.rb @@ -0,0 +1,263 @@ +require 'open-uri' + +module Rails + module Generators + module Actions + + # Install a plugin. You must provide either a Subversion url or Git url. + # For a Git-hosted plugin, you can specify if it should be added as a submodule instead of cloned. + # + # ==== Examples + # + # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git' + # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true + # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk' + # + def plugin(name, options) + log :plugin, name + + if options[:git] && options[:submodule] + in_root do + run "git submodule add #{options[:git]} vendor/plugins/#{name}", :verbose => false + end + elsif options[:git] || options[:svn] + in_root do + run_ruby_script "script/plugin install #{options[:svn] || options[:git]}", :verbose => false + end + else + log "! no git or svn provided for #{name}. Skipping..." + end + end + + # Adds an entry into config/environment.rb for the supplied gem. If env + # is specified, add the gem to the given environment. + # + # ==== Example + # + # gem "rspec", :env => :test + # gem "technoweenie-restful-authentication", :lib => "restful-authentication", :source => "http://gems.github.com/" + # + def gem(name, options={}) + log :gem, name + env = options.delete(:env) + + gems_code = "config.gem '#{name}'" + + if options.any? + opts = options.inject([]) {|result, h| result << [":#{h[0]} => #{h[1].inspect.gsub('"',"'")}"] }.sort.join(", ") + gems_code << ", #{opts}" + end + + environment gems_code, :env => env + end + + # Adds a line inside the Initializer block for config/environment.rb. + # + # If options :env is specified, the line is appended to the corresponding + # file in config/environments. + # + def environment(data=nil, options={}, &block) + sentinel = "Rails::Initializer.run do |config|" + data = block.call if !data && block_given? + + in_root do + if options[:env].nil? + inject_into_file 'config/environment.rb', "\n #{data}", :after => sentinel, :verbose => false + else + Array.wrap(options[:env]).each do|env| + append_file "config/environments/#{env}.rb", "\n#{data}", :verbose => false + end + end + end + end + + # Run a command in git. + # + # ==== Examples + # + # git :init + # git :add => "this.file that.rb" + # git :add => "onefile.rb", :rm => "badfile.cxx" + # + def git(command={}) + in_root do + if command.is_a?(Symbol) + run "git #{command}" + else + command.each do |command, options| + run "git #{command} #{options}" + end + end + end + end + + # Create a new file in the vendor/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # vendor("sekrit.rb") do + # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--" + # "salt = '#{sekrit_salt}'" + # end + # + # vendor("foreign.rb", "# Foreign code is fun") + # + def vendor(filename, data=nil, &block) + log :vendor, filename + create_file("vendor/#{filename}", data, :verbose => false, &block) + end + + # Create a new file in the lib/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # lib("crypto.rb") do + # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'" + # end + # + # lib("foreign.rb", "# Foreign code is fun") + # + def lib(filename, data=nil, &block) + log :lib, filename + create_file("lib/#{filename}", data, :verbose => false, &block) + end + + # Create a new Rakefile with the provided code (either in a block or a string). + # + # ==== Examples + # + # rakefile("bootstrap.rake") do + # project = ask("What is the UNIX name of your project?") + # + # <<-TASK + # namespace :#{project} do + # task :bootstrap do + # puts "i like boots!" + # end + # end + # TASK + # end + # + # rakefile("seed.rake", "puts 'im plantin ur seedz'") + # + def rakefile(filename, data=nil, &block) + log :rakefile, filename + create_file("lib/tasks/#{filename}", data, :verbose => false, &block) + end + + # Create a new initializer with the provided code (either in a block or a string). + # + # ==== Examples + # + # initializer("globals.rb") do + # data = "" + # + # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do + # data << "#{const} = :entp" + # end + # + # data + # end + # + # initializer("api.rb", "API_KEY = '123456'") + # + def initializer(filename, data=nil, &block) + log :initializer, filename + create_file("config/initializers/#{filename}", data, :verbose => false, &block) + end + + # Generate something using a generator from Rails or a plugin. + # The second parameter is the argument string that is passed to + # the generator or an Array that is joined. + # + # ==== Example + # + # generate(:authenticated, "user session") + # + def generate(what, *args) + log :generate, what + argument = args.map {|arg| arg.to_s }.flatten.join(" ") + + in_root { run_ruby_script("script/generate #{what} #{argument}", :verbose => false) } + end + + # Runs the supplied rake task + # + # ==== Example + # + # rake("db:migrate") + # rake("db:migrate", :env => "production") + # rake("gems:install", :sudo => true) + # + def rake(command, options={}) + log :rake, command + env = options[:env] || 'development' + sudo = options[:sudo] && RUBY_PLATFORM !~ /mswin|mingw/ ? 'sudo ' : '' + in_root { run("#{sudo}#{extify(:rake)} #{command} RAILS_ENV=#{env}", :verbose => false) } + end + + # Just run the capify command in root + # + # ==== Example + # + # capify! + # + def capify! + log :capify, "" + in_root { run("#{extify(:capify)} .", :verbose => false) } + end + + # Add Rails to /vendor/rails + # + # ==== Example + # + # freeze! + # + def freeze!(args = {}) + log :vendor, "rails" + in_root { run("#{extify(:rake)} rails:freeze:edge", :verbose => false) } + end + + # Make an entry in Rails routing file conifg/routes.rb + # + # === Example + # + # route "map.root :controller => :welcome" + # + def route(routing_code) + log :route, routing_code + sentinel = "ActionController::Routing::Routes.draw do |map|" + + in_root do + inject_into_file 'config/routes.rb', "\n #{routing_code}\n", { :after => sentinel, :verbose => false } + end + end + + protected + + # Define log for backwards compatibility. If just one argument is sent, + # invoke say, otherwise invoke say_status. + # + def log(*args) + if args.size == 1 + say args.first.to_s + else + say_status *args + end + end + + # Add an extension to the given name based on the platform. + # + def extify(name) + if RUBY_PLATFORM =~ /mswin|mingw/ + "#{name}.bat" + else + name + end + end + + end + end +end diff --git a/railties/lib/generators/active_record.rb b/railties/lib/generators/active_record.rb new file mode 100644 index 0000000000..64bee3904e --- /dev/null +++ b/railties/lib/generators/active_record.rb @@ -0,0 +1,57 @@ +require 'generators/named_base' +require 'generators/migration' +require 'generators/action_orm' +require 'active_record' + +module ActiveRecord + module Generators + class Base < Rails::Generators::NamedBase #:nodoc: + include Rails::Generators::Migration + + protected + # Implement the required interface for Rails::Generators::Migration. + # + def next_migration_number(dirname) #:nodoc: + if ActiveRecord::Base.timestamped_migrations + Time.now.utc.strftime("%Y%m%d%H%M%S") + else + "%.3d" % (current_migration_number(dirname) + 1) + end + end + end + + class ActionORM < Rails::Generators::ActionORM #:nodoc: + def self.all(klass) + "#{klass}.all" + end + + def self.find(klass, params=nil) + "#{klass}.find(#{params})" + end + + def self.build(klass, params=nil) + if params + "#{klass}.new(#{params})" + else + "#{klass}.new" + end + end + + def save + "#{name}.save" + end + + def update_attributes(params=nil) + "#{name}.update_attributes(#{params})" + end + + def errors + "#{name}.errors" + end + + def destroy + "#{name}.destroy" + end + end + end +end diff --git a/railties/lib/generators/active_record/migration/migration_generator.rb b/railties/lib/generators/active_record/migration/migration_generator.rb new file mode 100644 index 0000000000..7939977f72 --- /dev/null +++ b/railties/lib/generators/active_record/migration/migration_generator.rb @@ -0,0 +1,25 @@ +require 'generators/active_record' + +module ActiveRecord + module Generators + class MigrationGenerator < Base + argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + + def create_migration_file + set_local_assigns! + migration_template "migration.rb", "db/migrate/#{file_name}.rb" + end + + protected + attr_reader :migration_action + + def set_local_assigns! + if file_name =~ /^(add|remove)_.*_(?:to|from)_(.*)/ + @migration_action = $1 + @table_name = $2.pluralize + end + end + + end + end +end diff --git a/railties/lib/rails_generator/generators/components/migration/templates/migration.rb b/railties/lib/generators/active_record/migration/templates/migration.rb index ca35a43229..bbb7c53d86 100644 --- a/railties/lib/rails_generator/generators/components/migration/templates/migration.rb +++ b/railties/lib/generators/active_record/migration/templates/migration.rb @@ -1,4 +1,4 @@ -class <%= class_name.underscore.camelize %> < ActiveRecord::Migration +class <%= migration_class_name %> < ActiveRecord::Migration def self.up<% attributes.each do |attribute| %> <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end -%> <%- end %> diff --git a/railties/lib/generators/active_record/model/model_generator.rb b/railties/lib/generators/active_record/model/model_generator.rb new file mode 100644 index 0000000000..54187aede0 --- /dev/null +++ b/railties/lib/generators/active_record/model/model_generator.rb @@ -0,0 +1,35 @@ +require 'generators/active_record' + +module ActiveRecord + module Generators + class ModelGenerator < Base + argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + + check_class_collision + + class_option :migration, :type => :boolean + class_option :timestamps, :type => :boolean + class_option :parent, :type => :string, :desc => "The parent class for the generated model" + + def create_migration_file + if options[:migration] && options[:parent].nil? + file_name = "create_#{file_path.gsub(/\//, '_').pluralize}" + migration_template "migration.rb", "db/migrate/#{file_name}.rb" + end + end + + def create_model_file + template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb") + end + + hook_for :test_framework + + protected + + def parent_class_name + options[:parent] || "ActiveRecord::Base" + end + + end + end +end diff --git a/railties/lib/rails_generator/generators/components/model/templates/migration.rb b/railties/lib/generators/active_record/model/templates/migration.rb index 382fd1156e..1f68487304 100644 --- a/railties/lib/rails_generator/generators/components/model/templates/migration.rb +++ b/railties/lib/generators/active_record/model/templates/migration.rb @@ -1,10 +1,10 @@ -class <%= migration_name %> < ActiveRecord::Migration +class <%= migration_class_name %> < ActiveRecord::Migration def self.up create_table :<%= table_name %> do |t| <% for attribute in attributes -%> t.<%= attribute.type %> :<%= attribute.name %> <% end -%> -<% unless options[:skip_timestamps] %> +<% if options[:timestamps] %> t.timestamps <% end -%> end diff --git a/railties/lib/rails_generator/generators/components/model/templates/model.rb b/railties/lib/generators/active_record/model/templates/model.rb index 0656b06dfe..21ae29e9f2 100644 --- a/railties/lib/rails_generator/generators/components/model/templates/model.rb +++ b/railties/lib/generators/active_record/model/templates/model.rb @@ -1,4 +1,4 @@ -class <%= class_name %> < ActiveRecord::Base +class <%= class_name %> < <%= parent_class_name.classify %> <% attributes.select {|attr| attr.reference? }.each do |attribute| -%> belongs_to :<%= attribute.name %> <% end -%> diff --git a/railties/lib/generators/active_record/observer/observer_generator.rb b/railties/lib/generators/active_record/observer/observer_generator.rb new file mode 100644 index 0000000000..a6b57423b8 --- /dev/null +++ b/railties/lib/generators/active_record/observer/observer_generator.rb @@ -0,0 +1,15 @@ +require 'generators/active_record' + +module ActiveRecord + module Generators + class ObserverGenerator < Base + check_class_collision :suffix => "Observer" + + def create_observer_file + template 'observer.rb', File.join('app/models', class_path, "#{file_name}_observer.rb") + end + + hook_for :test_framework + end + end +end diff --git a/railties/lib/rails_generator/generators/components/observer/templates/observer.rb b/railties/lib/generators/active_record/observer/templates/observer.rb index b9a3004161..b9a3004161 100644 --- a/railties/lib/rails_generator/generators/components/observer/templates/observer.rb +++ b/railties/lib/generators/active_record/observer/templates/observer.rb diff --git a/railties/lib/generators/active_record/session_migration/session_migration_generator.rb b/railties/lib/generators/active_record/session_migration/session_migration_generator.rb new file mode 100644 index 0000000000..d60da5c0a5 --- /dev/null +++ b/railties/lib/generators/active_record/session_migration/session_migration_generator.rb @@ -0,0 +1,20 @@ +require 'generators/active_record' + +module ActiveRecord + module Generators + class SessionMigrationGenerator < Base + argument :name, :type => :string, :default => "add_sessions_table" + + def create_migration_file + migration_template "migration.rb", "db/migrate/#{file_name}.rb" + end + + protected + + def session_table_name + ActiveRecord::Base.pluralize_table_names ? 'session'.pluralize : 'session' + end + + end + end +end diff --git a/railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb b/railties/lib/generators/active_record/session_migration/templates/migration.rb index ca220a5f23..919822af7b 100644 --- a/railties/lib/rails_generator/generators/components/session_migration/templates/migration.rb +++ b/railties/lib/generators/active_record/session_migration/templates/migration.rb @@ -1,4 +1,4 @@ -class <%= class_name %> < ActiveRecord::Migration +class <%= migration_class_name %> < ActiveRecord::Migration def self.up create_table :<%= session_table_name %> do |t| t.string :session_id, :null => false diff --git a/railties/lib/generators/base.rb b/railties/lib/generators/base.rb new file mode 100644 index 0000000000..cbe9c0a49d --- /dev/null +++ b/railties/lib/generators/base.rb @@ -0,0 +1,302 @@ +require 'generators/actions' + +module Rails + module Generators + class Error < Thor::Error + end + + class Base < Thor::Group + include Thor::Actions + include Rails::Generators::Actions + + # Automatically sets the source root based on the class name. + # + def self.source_root + @_rails_source_root ||= File.expand_path(File.join(File.dirname(__FILE__), + base_name, generator_name, 'templates')) + end + + # Tries to get the description from a USAGE file one folder above the source + # root otherwise uses a default description. + # + def self.desc(description=nil) + return super if description + usage = File.expand_path(File.join(source_root, "..", "USAGE")) + + @desc ||= if File.exist?(usage) + File.read(usage) + else + "Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator." + end + end + + # Convenience method to get the namespace from the class name. It's the + # same as Thor default except that the Generator at the end of the class + # is removed. + # + def self.namespace(name=nil) + return super if name + @namespace ||= super.sub(/_generator$/, '') + end + + # Invoke a generator based on the value supplied by the user to the + # given option named "name". A class option is created when this method + # is invoked and you can set a hash to customize it. + # + # ==== Examples + # + # class ControllerGenerator < Rails::Generators::Base + # hook_for :test_framework, :aliases => "-t" + # end + # + # The example above will create a test framework option and will invoke + # a generator based on the user supplied value. + # + # For example, if the user invoke the controller generator as: + # + # ruby script/generate controller Account --test-framework=test_unit + # + # The controller generator will then try to invoke the following generators: + # + # "rails:generators:test_unit", "test_unit:generators:controller", "test_unit" + # + # In this case, the "test_unit:generators:controller" is available and is + # invoked. This allows any test framework to hook into Rails as long as it + # provides any of the hooks above. + # + # Finally, if the user don't want to use any test framework, he can do: + # + # ruby script/generate controller Account --skip-test-framework + # + # Or similarly: + # + # ruby script/generate controller Account --no-test-framework + # + # ==== Boolean hooks + # + # In some cases, you want to provide a boolean hook. For example, webrat + # developers might want to have webrat available on controller generator. + # This can be achieved as: + # + # Rails::Generators::ControllerGenerator.hook_for :webrat, :type => :boolean + # + # Then, if you want, webrat to be invoked, just supply: + # + # ruby script/generate controller Account --webrat + # + # The hooks lookup is similar as above: + # + # "rails:generators:webrat", "webrat:generators:controller", "webrat" + # + # ==== Custom invocations + # + # You can also supply a block to hook_for to customize how the hook is + # going to be invoked. The block receives two parameters, an instance + # of the current class and the klass to be invoked. + # + # For example, in the resource generator, the controller should be invoked + # with a pluralized class name. By default, it is invoked with the same + # name as the resource generator, which is singular. To change this, we + # can give a block to customize how the controller can be invoked. + # + # hook_for :resource_controller do |instance, controller| + # instance.invoke controller, [ instance.name.pluralize ] + # end + # + def self.hook_for(*names, &block) + options = names.extract_options! + in_base = options.delete(:in) || base_name + as_hook = options.delete(:as) || generator_name + + names.each do |name| + defaults = if options[:type] == :boolean + { } + elsif [true, false].include?(default_value_for_option(name, options)) + { :banner => "" } + else + { :desc => "#{name.to_s.humanize} to be invoked", :banner => "NAME" } + end + + unless class_options.key?(name) + class_option name, defaults.merge!(options) + end + + hooks[name] = [ in_base, as_hook ] + invoke_from_option name, options, &block + end + end + + # Remove a previously added hook. + # + # ==== Examples + # + # remove_hook_for :orm + # + def self.remove_hook_for(*names) + remove_invocation *names + + names.each do |name| + hooks.delete(name) + end + end + + # Make class option aware of Rails::Generators.options and Rails::Generators.aliases. + # + def self.class_option(name, options={}) #:nodoc: + options[:desc] = "Indicates when to generate #{name.to_s.humanize.downcase}" unless options.key?(:desc) + options[:aliases] = default_aliases_for_option(name, options) + options[:default] = default_value_for_option(name, options) + super(name, options) + end + + # Cache source root and add lib/generators/base/generator/templates to + # source paths. + # + def self.inherited(base) #:nodoc: + super + base.source_root # Cache source root + + if defined?(RAILS_ROOT) && base.name !~ /Base$/ + path = File.expand_path(File.join(RAILS_ROOT, 'lib', 'templates')) + if base.name.include?('::') + base.source_paths << File.join(path, base.base_name, base.generator_name) + else + base.source_paths << File.join(path, base.generator_name) + end + end + end + + protected + + # Check whether the given class names are already taken by user + # application or Ruby on Rails. + # + def class_collisions(*class_names) #:nodoc: + return unless behavior == :invoke + + class_names.flatten.each do |class_name| + class_name = class_name.to_s + next if class_name.strip.empty? + + # Split the class from its module nesting + nesting = class_name.split('::') + last_name = nesting.pop + + # Hack to limit const_defined? to non-inherited on 1.9 + extra = [] + extra << false unless Object.method(:const_defined?).arity == 1 + + # Extract the last Module in the nesting + last = nesting.inject(Object) do |last, nest| + break unless last.const_defined?(nest, *extra) + last.const_get(nest) + end + + if last && last.const_defined?(last_name.camelize, *extra) + raise Error, "The name '#{class_name}' is either already used in your application " << + "or reserved by Ruby on Rails. Please choose an alternative and run " << + "this generator again." + end + end + end + + # Use Rails default banner. + # + def self.banner + "#{$0} #{generator_name} #{self.arguments.map{ |a| a.usage }.join(' ')} [options]" + end + + # Sets the base_name taking into account the current class namespace. + # + def self.base_name + @base_name ||= self.name.split('::').first.underscore + end + + # Removes the namespaces and get the generator name. For example, + # Rails::Generators::MetalGenerator will return "metal" as generator name. + # + def self.generator_name + @generator_name ||= begin + klass_name = self.name.split('::').last + klass_name.sub!(/Generator$/, '') + klass_name.underscore + end + end + + # Return the default value for the option name given doing a lookup in + # Rails::Generators.options. + # + def self.default_value_for_option(name, options) + config = Rails::Generators.options + generator, base = generator_name.to_sym, base_name.to_sym + + if config[generator] && config[generator].key?(name) + config[generator][name] + elsif config[base] && config[base].key?(name) + config[base][name] + elsif config[:rails].key?(name) + config[:rails][name] + else + options[:default] + end + end + + # Return default aliases for the option name given doing a lookup in + # Rails::Generators.aliases. + # + def self.default_aliases_for_option(name, options) + config = Rails::Generators.aliases + generator, base = generator_name.to_sym, base_name.to_sym + + if config[generator] && config[generator].key?(name) + config[generator][name] + elsif config[base] && config[base].key?(name) + config[base][name] + elsif config[:rails].key?(name) + config[:rails][name] + else + options[:aliases] + end + end + + # Keep hooks configuration that are used on prepare_for_invocation. + # + def self.hooks #:nodoc: + @hooks ||= from_superclass(:hooks, {}) + end + + # Prepare class invocation to search on Rails namespace if a previous + # added hook is being used. + # + def self.prepare_for_invocation(name, value) #:nodoc: + if value && constants = self.hooks[name] + Rails::Generators.find_by_namespace(value, *constants) + else + super + end + end + + # Small macro to add ruby as an option to the generator with proper + # default value plus an instance helper method called shebang. + # + def self.add_shebang_option! + class_option :ruby, :type => :string, :aliases => "-r", :default => Thor::Util.ruby_command, + :desc => "Path to the Ruby binary of your choice", :banner => "PATH" + + no_tasks { + define_method :shebang do + @shebang ||= begin + command = if options[:ruby] == Thor::Util.ruby_command + "/usr/bin/env #{File.basename(Thor::Util.ruby_command)}" + else + options[:ruby] + end + "#!#{command}" + end + end + } + end + + end + end +end diff --git a/railties/lib/generators/erb.rb b/railties/lib/generators/erb.rb new file mode 100644 index 0000000000..43d47109c7 --- /dev/null +++ b/railties/lib/generators/erb.rb @@ -0,0 +1,8 @@ +require 'generators/named_base' + +module Erb + module Generators + class Base < Rails::Generators::NamedBase #:nodoc: + end + end +end diff --git a/railties/lib/generators/erb/controller/controller_generator.rb b/railties/lib/generators/erb/controller/controller_generator.rb new file mode 100644 index 0000000000..ab7b273662 --- /dev/null +++ b/railties/lib/generators/erb/controller/controller_generator.rb @@ -0,0 +1,21 @@ +require 'generators/erb' + +module Erb + module Generators + class ControllerGenerator < Base + argument :actions, :type => :array, :default => [], :banner => "action action" + + def create_view_files + base_path = File.join("app/views", class_path, file_name) + empty_directory base_path + + actions.each do |action| + @action = action + @path = File.join(base_path, "#{action}.html.erb") + + template 'view.html.erb', @path + end + end + end + end +end diff --git a/railties/lib/generators/erb/controller/templates/view.html.erb b/railties/lib/generators/erb/controller/templates/view.html.erb new file mode 100644 index 0000000000..cd54d13d83 --- /dev/null +++ b/railties/lib/generators/erb/controller/templates/view.html.erb @@ -0,0 +1,2 @@ +<h1><%= class_name %>#<%= @action %></h1> +<p>Find me in <%= @path %></p> diff --git a/railties/lib/generators/erb/mailer/mailer_generator.rb b/railties/lib/generators/erb/mailer/mailer_generator.rb new file mode 100644 index 0000000000..4ec2f4c9f4 --- /dev/null +++ b/railties/lib/generators/erb/mailer/mailer_generator.rb @@ -0,0 +1,20 @@ +require 'generators/erb' + +module Erb + module Generators + class MailerGenerator < Base + argument :actions, :type => :array, :default => [], :banner => "method method" + + def create_view_folder + empty_directory File.join("app/views", file_path) + end + + def create_view_files + actions.each do |action| + @action, @path = action, File.join(file_path, action) + template "view.erb", File.join("app/views", "#{@path}.erb") + end + end + end + end +end diff --git a/railties/lib/generators/erb/mailer/templates/view.erb b/railties/lib/generators/erb/mailer/templates/view.erb new file mode 100644 index 0000000000..fcce7bd805 --- /dev/null +++ b/railties/lib/generators/erb/mailer/templates/view.erb @@ -0,0 +1,3 @@ +<%= class_name %>#<%= @action %> + +Find me in app/views/<%= @path %> diff --git a/railties/lib/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/generators/erb/scaffold/scaffold_generator.rb new file mode 100644 index 0000000000..955f22192a --- /dev/null +++ b/railties/lib/generators/erb/scaffold/scaffold_generator.rb @@ -0,0 +1,54 @@ +require 'generators/erb' + +module Erb + module Generators + class ScaffoldGenerator < Base + include Rails::Generators::ScaffoldBase + + argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + + class_option :form, :type => :boolean + class_option :layout, :type => :boolean + class_option :singleton, :type => :boolean, :desc => "Supply to skip index view" + + def create_root_folder + empty_directory File.join("app/views", controller_file_path) + end + + def copy_index_file + return if options[:singleton] + copy_view :index + end + + def copy_edit_file + copy_view :edit + end + + def copy_show_file + copy_view :show + end + + def copy_new_file + copy_view :new + end + + def copy_form_file + return unless options[:form] + copy_view :_form + end + + def copy_layout_file + return unless options[:layout] + template "layout.html.erb", + File.join("app/views/layouts", controller_class_path, "#{controller_file_name}.html.erb") + end + + protected + + def copy_view(view) + template "#{view}.html.erb", File.join("app/views", controller_file_path, "#{view}.html.erb") + end + + end + end +end diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb b/railties/lib/generators/erb/scaffold/templates/edit.html.erb index cca1d61c68..cca1d61c68 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/templates/view_edit.html.erb +++ b/railties/lib/generators/erb/scaffold/templates/edit.html.erb diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb b/railties/lib/generators/erb/scaffold/templates/index.html.erb index 69beb388db..5e6a4af9e0 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/templates/view_index.html.erb +++ b/railties/lib/generators/erb/scaffold/templates/index.html.erb @@ -3,7 +3,7 @@ <table> <tr> <% for attribute in attributes -%> - <th><%= attribute.column.human_name %></th> + <th><%= attribute.human_name %></th> <% end -%> <th></th> <th></th> @@ -24,4 +24,4 @@ <br /> -<%%= link_to 'New <%= singular_name %>', new_<%= singular_name %>_path %>
\ No newline at end of file +<%%= link_to 'New <%= singular_name %>', new_<%= singular_name %>_path %> diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb b/railties/lib/generators/erb/scaffold/templates/layout.html.erb index aacfbe4a8f..aacfbe4a8f 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb +++ b/railties/lib/generators/erb/scaffold/templates/layout.html.erb diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb b/railties/lib/generators/erb/scaffold/templates/new.html.erb index 96c89fc50e..96c89fc50e 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/templates/view_new.html.erb +++ b/railties/lib/generators/erb/scaffold/templates/new.html.erb diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb b/railties/lib/generators/erb/scaffold/templates/show.html.erb index adecaf70c6..25567957be 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/templates/view_show.html.erb +++ b/railties/lib/generators/erb/scaffold/templates/show.html.erb @@ -1,10 +1,10 @@ <% for attribute in attributes -%> <p> - <b><%= attribute.column.human_name %>:</b> + <b><%= attribute.human_name %>:</b> <%%=h @<%= singular_name %>.<%= attribute.name %> %> </p> <% end -%> <%%= link_to 'Edit', edit_<%= singular_name %>_path(@<%= singular_name %>) %> | -<%%= link_to 'Back', <%= plural_name %>_path %>
\ No newline at end of file +<%%= link_to 'Back', <%= plural_name %>_path %> diff --git a/railties/lib/rails_generator/generated_attribute.rb b/railties/lib/generators/generated_attribute.rb index a3d4a01142..e962308585 100644 --- a/railties/lib/rails_generator/generated_attribute.rb +++ b/railties/lib/generators/generated_attribute.rb @@ -1,13 +1,10 @@ -require 'optparse' - module Rails - module Generator + module Generators class GeneratedAttribute - attr_accessor :name, :type, :column + attr_accessor :name, :type def initialize(name, type) @name, @type = name, type.to_sym - @column = ActiveRecord::ConnectionAdapters::Column.new(name, nil, @type) end def field_type @@ -20,7 +17,7 @@ module Rails when :boolean then :check_box else :text_field - end + end end def default @@ -33,9 +30,14 @@ module Rails when :string then "MyString" when :text then "MyText" when :boolean then false + when :references, :belongs_to then nil else "" - end + end + end + + def human_name + name.to_s.humanize end def reference? diff --git a/railties/lib/generators/migration.rb b/railties/lib/generators/migration.rb new file mode 100644 index 0000000000..0a9151ecdf --- /dev/null +++ b/railties/lib/generators/migration.rb @@ -0,0 +1,65 @@ +module Rails + module Generators + # Holds common methods for migrations. It assumes that migrations has the + # [0-9]*_name format and can be used by another frameworks (like Sequel) + # just by implementing the next migration number method. + # + module Migration + def self.included(base) #:nodoc: + base.send :attr_reader, :migration_number, + :migration_file_name, + :migration_class_name + end + + # Creates a migration template at the given destination. The difference + # to the default template method is that the migration number is appended + # to the destination file name. + # + # The migration number, migration file name, migration class name are + # available as instance variables in the template to be rendered. + # + # ==== Examples + # + # migration_template "migration.rb", "db/migrate/add_foo_to_bar.rb" + # + def migration_template(source, destination=nil, config={}) + destination = File.expand_path(destination || source, self.destination_root) + + migration_dir = File.dirname(destination) + @migration_number = next_migration_number(migration_dir) + @migration_file_name = File.basename(destination).sub(/\.rb$/, '') + @migration_class_name = @migration_file_name.camelize + + destination = migration_exists?(migration_dir, @migration_file_name) + + if behavior == :invoke + raise Error, "Another migration is already named #{@migration_file_name}: #{destination}" if destination + destination = File.join(migration_dir, "#{@migration_number}_#{@migration_file_name}.rb") + end + + template(source, destination, config) + end + + protected + + def migration_lookup_at(dirname) #:nodoc: + Dir.glob("#{dirname}/[0-9]*_*.rb") + end + + def migration_exists?(dirname, file_name) #:nodoc: + migration_lookup_at(dirname).grep(/\d+_#{file_name}.rb$/).first + end + + def current_migration_number(dirname) #:nodoc: + migration_lookup_at(dirname).collect do |file| + File.basename(file).split("_").first.to_i + end.max.to_i + end + + def next_migration_number(dirname) #:nodoc: + raise NotImplementError + end + + end + end +end diff --git a/railties/lib/generators/named_base.rb b/railties/lib/generators/named_base.rb new file mode 100644 index 0000000000..699b8ed651 --- /dev/null +++ b/railties/lib/generators/named_base.rb @@ -0,0 +1,162 @@ +require 'generators/base' +require 'generators/generated_attribute' + +module Rails + module Generators + class NamedBase < Base + argument :name, :type => :string + + attr_reader :class_name, :singular_name, :plural_name, :table_name, + :class_path, :file_path, :class_nesting_depth + + alias :file_name :singular_name + + def initialize(*args) #:nodoc: + super + assign_names!(self.name) + parse_attributes! if respond_to?(:attributes) + end + + protected + + def assign_names!(given_name) #:nodoc: + base_name, @class_path, @file_path, class_nesting, @class_nesting_depth = extract_modules(given_name) + class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name) + + @table_name = if pluralize_table_names? + plural_name + else + singular_name + end + @table_name.gsub! '/', '_' + + if class_nesting.empty? + @class_name = class_name_without_nesting + else + @table_name = class_nesting.underscore << "_" << @table_name + @class_name = "#{class_nesting}::#{class_name_without_nesting}" + end + end + + # Convert attributes hash into an array with GeneratedAttribute objects. + # + def parse_attributes! #:nodoc: + self.attributes = (attributes || []).map do |key_value| + name, type = key_value.split(':') + Rails::Generators::GeneratedAttribute.new(name, type) + end + end + + # Extract modules from filesystem-style or ruby-style path. Both + # good/fun/stuff and Good::Fun::Stuff produce the same results. + # + def extract_modules(name) #:nodoc: + modules = name.include?('/') ? name.split('/') : name.split('::') + name = modules.pop + path = modules.map { |m| m.underscore } + + file_path = (path + [name.underscore]).join('/') + nesting = modules.map { |m| m.camelize }.join('::') + + [name, path, file_path, nesting, modules.size] + end + + # Receives name and return camelized, underscored and pluralized names. + # + def inflect_names(name) #:nodoc: + camel = name.camelize + under = camel.underscore + plural = under.pluralize + [camel, under, plural] + end + + def pluralize_table_names? + !defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names + end + + # Add a class collisions name to be checked on class initialization. You + # can supply a hash with a :prefix or :suffix to be tested. + # + # ==== Examples + # + # check_class_collision :suffix => "Observer" + # + # If the generator is invoked with class name Admin, it will check for + # the presence of "AdminObserver". + # + def self.check_class_collision(options={}) + define_method :check_class_collision do + name = if self.respond_to?(:controller_class_name) # for ScaffoldBase + controller_class_name + else + class_name + end + + class_collisions "#{options[:prefix]}#{name}#{options[:suffix]}" + end + end + end + + # Deal with controller names on scaffold. Also provide helpers to deal with + # ActionORM. + # + module ScaffoldBase + def self.included(base) #:nodoc: + base.send :attr_reader, :controller_name, :controller_class_name, :controller_file_name, + :controller_class_path, :controller_file_path + end + + # Set controller variables on initialization. + # + def initialize(*args) #:nodoc: + super + @controller_name = name.pluralize + + base_name, @controller_class_path, @controller_file_path, class_nesting, class_nesting_depth = extract_modules(@controller_name) + class_name_without_nesting, @controller_file_name, controller_plural_name = inflect_names(base_name) + + @controller_class_name = if class_nesting.empty? + class_name_without_nesting + else + "#{class_nesting}::#{class_name_without_nesting}" + end + end + + protected + + # Loads the ORM::Generators::ActionORM class. This class is responsable + # to tell scaffold entities how to generate an specific method for the + # ORM. Check Rails::Generators::ActionORM for more information. + # + def orm_class + @orm_class ||= begin + # Raise an error if the class_option :orm was not defined. + unless self.class.class_options[:orm] + raise "You need to have :orm as class option to invoke orm_class and orm_instance" + end + + action_orm = "#{options[:orm].to_s.classify}::Generators::ActionORM" + + # If the orm was not loaded, try to load it at "generators/orm", + # for example "generators/active_record" or "generators/sequel". + begin + klass = action_orm.constantize + rescue NameError + require "generators/#{options[:orm]}" + end + + # Try once again after loading the file with success. + klass ||= action_orm.constantize + rescue Exception => e + raise Error, "Could not load #{action_orm}, skipping controller. Error: #{e.message}." + end + end + + # Initialize ORM::Generators::ActionORM to access instance methods. + # + def orm_instance(name=file_name) + @orm_instance ||= @orm_class.new(name) + end + end + end +end diff --git a/railties/lib/rails_generator/generators/applications/app/USAGE b/railties/lib/generators/rails/app/USAGE index 36d6061a59..36d6061a59 100644 --- a/railties/lib/rails_generator/generators/applications/app/USAGE +++ b/railties/lib/generators/rails/app/USAGE diff --git a/railties/lib/generators/rails/app/app_generator.rb b/railties/lib/generators/rails/app/app_generator.rb new file mode 100644 index 0000000000..c8044d13b1 --- /dev/null +++ b/railties/lib/generators/rails/app/app_generator.rb @@ -0,0 +1,210 @@ +require 'digest/md5' +require 'active_support/secure_random' +require 'rails/version' unless defined?(Rails::VERSION) + +module Rails::Generators + class AppGenerator < Base + DATABASES = %w( mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db ) + add_shebang_option! + + argument :app_path, :type => :string + + class_option :database, :type => :string, :aliases => "-d", :default => "sqlite3", + :desc => "Preconfigure for selected database (options: #{DATABASES.join('/')})" + + class_option :freeze, :type => :boolean, :aliases => "-F", :default => false, + :desc => "Freeze Rails in vendor/rails from the gems" + + class_option :template, :type => :string, :aliases => "-m", + :desc => "Path to an application template (can be a filesystem path or URL)." + + class_option :with_dispatchers, :type => :boolean, :aliases => "-D", :default => false, + :desc => "Add CGI/FastCGI/mod_ruby dispatchers code" + + class_option :skip_activerecord, :type => :boolean, :aliases => "-O", :default => false, + :desc => "Skip ActiveRecord files" + + class_option :skip_testunit, :type => :boolean, :aliases => "-T", :default => false, + :desc => "Skip TestUnit files" + + class_option :skip_prototype, :type => :boolean, :aliases => "-J", :default => false, + :desc => "Skip Prototype files" + + # Add Rails options + # + 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" + + def initialize(*args) + super + if !options[:no_activerecord] && !DATABASES.include?(options[:database]) + raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}." + end + end + + def create_root + self.destination_root = File.expand_path(app_path, destination_root) + empty_directory '.' + + app_name # Sets the app name + FileUtils.cd(destination_root) + end + + def create_root_files + copy_file "Rakefile" + copy_file "README" + end + + def create_app_files + directory "app" + end + + def create_config_files + empty_directory "config" + + inside "config" do + copy_file "routes.rb" + template "environment.rb" + + directory "environments" + directory "initializers" + directory "locales" + end + end + + def create_boot_file + copy_file "config/boot.rb" + end + + def create_activerecord_files + return if options[:skip_activerecord] + template "config/databases/#{options[:database]}.yml", "config/database.yml" + end + + def create_db_files + directory "db" + end + + def create_doc_files + directory "doc" + end + + def create_lib_files + empty_directory "lib" + empty_directory "lib/tasks" + end + + def create_log_files + empty_directory "log" + + inside "log" do + %w( server production development test ).each do |file| + create_file "#{file}.log" + chmod "#{file}.log", 0666, :verbose => false + end + end + end + + def create_public_files + directory "public", "public", :recursive => false # Do small steps, so anyone can overwrite it. + end + + def create_dispatch_files + return unless options[:with_dispatchers] + copy_file "dispatchers/config.ru", "config.ru" + + template "dispatchers/dispatch.rb", "public/dispatch.rb" + chmod "public/dispatch.rb", 0755, :verbose => false + + template "dispatchers/dispatch.rb", "public/dispatch.cgi" + chmod "public/dispatch.cgi", 0755, :verbose => false + + template "dispatchers/dispatch.fcgi", "public/dispatch.fcgi" + chmod "public/dispatch.fcgi", 0755, :verbose => false + end + + def create_public_image_files + directory "public/images" + end + + def create_public_stylesheets_files + directory "public/stylesheets" + end + + def create_prototype_files + return if options[:skip_prototype] + directory "public/javascripts" + end + + def create_script_files + directory "script" + chmod "script", 0755, :verbose => false + end + + def create_test_files + return if options[:skip_testunit] + directory "test" + end + + def create_tmp_files + empty_directory "tmp" + + inside "tmp" do + %w(sessions sockets cache pids).each do |dir| + empty_directory dir + end + end + end + + def create_vendor_files + empty_directory "vendor/plugins" + end + + def apply_rails_template + apply options[:template] if options[:template] + rescue Thor::Error, LoadError, Errno::ENOENT => e + raise Error, "The template [#{options[:template]}] could not be loaded. Error: #{e}" + end + + def freeze? + freeze! if options[:freeze] + end + + protected + + # Define file as an alias to create_file for backwards compatibility. + # + def file(*args, &block) + create_file(*args, &block) + end + + def app_name + @app_name ||= File.basename(destination_root) + end + + def app_secret + ActiveSupport::SecureRandom.hex(64) + end + + def self.banner + "#{$0} #{self.arguments.map(&:usage).join(' ')} [options]" + end + + def mysql_socket + @mysql_socket ||= [ + "/tmp/mysql.sock", # default + "/var/run/mysqld/mysqld.sock", # debian/gentoo + "/var/tmp/mysql.sock", # freebsd + "/var/lib/mysql/mysql.sock", # fedora + "/opt/local/lib/mysql/mysql.sock", # fedora + "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql + "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 + "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 + "/opt/lampp/var/mysql/mysql.sock" # xampp for linux + ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ + end + end +end diff --git a/railties/lib/generators/rails/app/templates/README b/railties/lib/generators/rails/app/templates/README new file mode 100644 index 0000000000..37ec8ea211 --- /dev/null +++ b/railties/lib/generators/rails/app/templates/README @@ -0,0 +1,243 @@ +== Welcome to Rails + +Rails is a web-application framework that includes everything needed to create +database-backed web applications according to the Model-View-Control pattern. + +This pattern splits the view (also called the presentation) into "dumb" templates +that are primarily responsible for inserting pre-built data in between HTML tags. +The model contains the "smart" domain objects (such as Account, Product, Person, +Post) that holds all the business logic and knows how to persist themselves to +a database. The controller handles the incoming requests (such as Save New Account, +Update Product, Show Post) by manipulating the model and directing data to the view. + +In Rails, the model is handled by what's called an object-relational mapping +layer entitled Active Record. This layer allows you to present the data from +database rows as objects and embellish these data objects with business logic +methods. You can read more about Active Record in +link:files/vendor/rails/activerecord/README.html. + +The controller and view are handled by the Action Pack, which handles both +layers by its two parts: Action View and Action Controller. These two layers +are bundled in a single package due to their heavy interdependence. This is +unlike the relationship between the Active Record and Action Pack that is much +more separate. Each of these packages can be used independently outside of +Rails. You can read more about Action Pack in +link:files/vendor/rails/actionpack/README.html. + + +== Getting Started + +1. At the command prompt, start a new Rails application using the <tt>rails</tt> command + and your application name. Ex: rails myapp +2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options) +3. Go to http://localhost:3000/ and get "Welcome aboard: You're riding the Rails!" +4. Follow the guidelines to start developing your application + + +== Web Servers + +By default, Rails will try to use Mongrel if it's are installed when started with script/server, otherwise Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails +with a variety of other web servers. + +Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is +suitable for development and deployment of Rails applications. If you have Ruby Gems installed, +getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>. +More info at: http://mongrel.rubyforge.org + +Say other Ruby web servers like Thin and Ebb or regular web servers like Apache or LiteSpeed or +Lighttpd or IIS. The Ruby web servers are run through Rack and the latter can either be setup to use +FCGI or proxy to a pack of Mongrels/Thin/Ebb servers. + +== Apache .htaccess example for FCGI/CGI + +# General Apache options +AddHandler fastcgi-script .fcgi +AddHandler cgi-script .cgi +Options +FollowSymLinks +ExecCGI + +# If you don't want Rails to look in certain directories, +# use the following rewrite rules so that Apache won't rewrite certain requests +# +# Example: +# RewriteCond %{REQUEST_URI} ^/notrails.* +# RewriteRule .* - [L] + +# Redirect all requests not available on the filesystem to Rails +# By default the cgi dispatcher is used which is very slow +# +# For better performance replace the dispatcher with the fastcgi one +# +# Example: +# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] +RewriteEngine On + +# If your Rails application is accessed via an Alias directive, +# then you MUST also set the RewriteBase in this htaccess file. +# +# Example: +# Alias /myrailsapp /path/to/myrailsapp/public +# RewriteBase /myrailsapp + +RewriteRule ^$ index.html [QSA] +RewriteRule ^([^.]+)$ $1.html [QSA] +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^(.*)$ dispatch.cgi [QSA,L] + +# In case Rails experiences terminal errors +# Instead of displaying this message you can supply a file here which will be rendered instead +# +# Example: +# ErrorDocument 500 /500.html + +ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly" + + +== Debugging Rails + +Sometimes your application goes wrong. Fortunately there are a lot of tools that +will help you debug it and get it back on the rails. + +First area to check is the application log files. Have "tail -f" commands running +on the server.log and development.log. Rails will automatically display debugging +and runtime information to these files. Debugging info will also be shown in the +browser on requests from 127.0.0.1. + +You can also log your own messages directly into the log file from your code using +the Ruby logger class from inside your controllers. Example: + + class WeblogController < ActionController::Base + def destroy + @weblog = Weblog.find(params[:id]) + @weblog.destroy + logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") + end + end + +The result will be a message in your log file along the lines of: + + Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1 + +More information on how to use the logger is at http://www.ruby-doc.org/core/ + +Also, Ruby documentation can be found at http://www.ruby-lang.org/ including: + +* The Learning Ruby (Pickaxe) Book: http://www.ruby-doc.org/docs/ProgrammingRuby/ +* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) + +These two online (and free) books will bring you up to speed on the Ruby language +and also on programming in general. + + +== Debugger + +Debugger support is available through the debugger command when you start your Mongrel or +Webrick server with --debugger. This means that you can break out of execution at any point +in the code, investigate and change the model, AND then resume execution! +You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug' +Example: + + class WeblogController < ActionController::Base + def index + @posts = Post.find(:all) + debugger + end + end + +So the controller will accept the action, run the first line, then present you +with a IRB prompt in the server window. Here you can do things like: + + >> @posts.inspect + => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>, + #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]" + >> @posts.first.title = "hello from a debugger" + => "hello from a debugger" + +...and even better is that you can examine how your runtime objects actually work: + + >> f = @posts.first + => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}> + >> f. + Display all 152 possibilities? (y or n) + +Finally, when you're ready to resume execution, you enter "cont" + + +== Console + +You can interact with the domain model by starting the console through <tt>script/console</tt>. +Here you'll have all parts of the application configured, just like it is when the +application is running. You can inspect domain models, change values, and save to the +database. Starting the script without arguments will launch it in the development environment. +Passing an argument will specify a different environment, like <tt>script/console production</tt>. + +To reload your controllers and models after launching the console run <tt>reload!</tt> + +== dbconsole + +You can go to the command line of your database directly through <tt>script/dbconsole</tt>. +You would be connected to the database with the credentials defined in database.yml. +Starting the script without arguments will connect you to the development database. Passing an +argument will connect you to a different database, like <tt>script/dbconsole production</tt>. +Currently works for mysql, postgresql and sqlite. + +== Description of Contents + +app + Holds all the code that's specific to this particular application. + +app/controllers + Holds controllers that should be named like weblogs_controller.rb for + automated URL mapping. All controllers should descend from ApplicationController + which itself descends from ActionController::Base. + +app/models + Holds models that should be named like post.rb. + Most models will descend from ActiveRecord::Base. + +app/views + Holds the template files for the view that should be named like + weblogs/index.html.erb for the WeblogsController#index action. All views use eRuby + syntax. + +app/views/layouts + Holds the template files for layouts to be used with views. This models the common + header/footer method of wrapping views. In your views, define a layout using the + <tt>layout :default</tt> and create a file named default.html.erb. Inside default.html.erb, + call <% yield %> to render the view using this layout. + +app/helpers + Holds view helpers that should be named like weblogs_helper.rb. These are generated + for you automatically when using script/generate for controllers. Helpers can be used to + wrap functionality for your views into methods. + +config + Configuration files for the Rails environment, the routing map, the database, and other dependencies. + +db + Contains the database schema in schema.rb. db/migrate contains all + the sequence of Migrations for your schema. + +doc + This directory is where your application documentation will be stored when generated + using <tt>rake doc:app</tt> + +lib + Application specific libraries. Basically, any kind of custom code that doesn't + belong under controllers, models, or helpers. This directory is in the load path. + +public + The directory available for the web server. Contains subdirectories for images, stylesheets, + and javascripts. Also contains the dispatchers and the default HTML files. This should be + set as the DOCUMENT_ROOT of your web server. + +script + Helper scripts for automation and generation. + +test + Unit and functional tests along with fixtures. When using the script/generate scripts, template + test files will be generated for you and placed in this directory. + +vendor + External libraries that the application depends on. Also includes the plugins subdirectory. + If the app has frozen rails, those gems also go here, under vendor/rails/. + This directory is in the load path. diff --git a/railties/fresh_rakefile b/railties/lib/generators/rails/app/templates/Rakefile index 3bb0e8592a..3bb0e8592a 100755 --- a/railties/fresh_rakefile +++ b/railties/lib/generators/rails/app/templates/Rakefile diff --git a/railties/helpers/application_controller.rb b/railties/lib/generators/rails/app/templates/app/controllers/application_controller.rb index 6635a3f487..6635a3f487 100644 --- a/railties/helpers/application_controller.rb +++ b/railties/lib/generators/rails/app/templates/app/controllers/application_controller.rb diff --git a/railties/helpers/application_helper.rb b/railties/lib/generators/rails/app/templates/app/helpers/application_helper.rb index 22a7940eb2..22a7940eb2 100644 --- a/railties/helpers/application_helper.rb +++ b/railties/lib/generators/rails/app/templates/app/helpers/application_helper.rb diff --git a/railties/configs/empty.log b/railties/lib/generators/rails/app/templates/app/models/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/configs/empty.log +++ b/railties/lib/generators/rails/app/templates/app/models/.empty_directory diff --git a/railties/html/favicon.ico b/railties/lib/generators/rails/app/templates/app/views/layouts/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/html/favicon.ico +++ b/railties/lib/generators/rails/app/templates/app/views/layouts/.empty_directory diff --git a/railties/environments/boot.rb b/railties/lib/generators/rails/app/templates/config/boot.rb index 0ad0f787f8..0ad0f787f8 100644 --- a/railties/environments/boot.rb +++ b/railties/lib/generators/rails/app/templates/config/boot.rb diff --git a/railties/configs/databases/frontbase.yml b/railties/lib/generators/rails/app/templates/config/databases/frontbase.yml index c0c3588be1..c0c3588be1 100644 --- a/railties/configs/databases/frontbase.yml +++ b/railties/lib/generators/rails/app/templates/config/databases/frontbase.yml diff --git a/railties/configs/databases/ibm_db.yml b/railties/lib/generators/rails/app/templates/config/databases/ibm_db.yml index a9716ddb44..a9716ddb44 100644 --- a/railties/configs/databases/ibm_db.yml +++ b/railties/lib/generators/rails/app/templates/config/databases/ibm_db.yml diff --git a/railties/configs/databases/mysql.yml b/railties/lib/generators/rails/app/templates/config/databases/mysql.yml index 1a14bfb332..6bf2f7b1fd 100644 --- a/railties/configs/databases/mysql.yml +++ b/railties/lib/generators/rails/app/templates/config/databases/mysql.yml @@ -22,8 +22,8 @@ development: pool: 5 username: root password: -<% if socket -%> - socket: <%= socket %> +<% if mysql_socket -%> + socket: <%= mysql_socket %> <% else -%> host: localhost <% end -%> @@ -39,8 +39,8 @@ test: pool: 5 username: root password: -<% if socket -%> - socket: <%= socket %> +<% if mysql_socket -%> + socket: <%= mysql_socket %> <% else -%> host: localhost <% end -%> @@ -53,8 +53,8 @@ production: pool: 5 username: root password: -<% if socket -%> - socket: <%= socket %> +<% if mysql_socket -%> + socket: <%= mysql_socket %> <% else -%> host: localhost <% end -%> diff --git a/railties/configs/databases/oracle.yml b/railties/lib/generators/rails/app/templates/config/databases/oracle.yml index a1883f6256..a1883f6256 100644 --- a/railties/configs/databases/oracle.yml +++ b/railties/lib/generators/rails/app/templates/config/databases/oracle.yml diff --git a/railties/configs/databases/postgresql.yml b/railties/lib/generators/rails/app/templates/config/databases/postgresql.yml index f600e054cf..f600e054cf 100644 --- a/railties/configs/databases/postgresql.yml +++ b/railties/lib/generators/rails/app/templates/config/databases/postgresql.yml diff --git a/railties/configs/databases/sqlite2.yml b/railties/lib/generators/rails/app/templates/config/databases/sqlite2.yml index 46f01cb42c..46f01cb42c 100644 --- a/railties/configs/databases/sqlite2.yml +++ b/railties/lib/generators/rails/app/templates/config/databases/sqlite2.yml diff --git a/railties/configs/databases/sqlite3.yml b/railties/lib/generators/rails/app/templates/config/databases/sqlite3.yml index 025d62a8d8..025d62a8d8 100644 --- a/railties/configs/databases/sqlite3.yml +++ b/railties/lib/generators/rails/app/templates/config/databases/sqlite3.yml diff --git a/railties/environments/environment.rb b/railties/lib/generators/rails/app/templates/config/environment.rb index 4a2df36307..94c90475c6 100644 --- a/railties/environments/environment.rb +++ b/railties/lib/generators/rails/app/templates/config/environment.rb @@ -1,7 +1,7 @@ # Be sure to restart your server when you modify this file # Specifies gem version of Rails to use when vendor/rails is not present -<%= '# ' if freeze %>RAILS_GEM_VERSION = '<%= Rails::VERSION::STRING %>' unless defined? RAILS_GEM_VERSION +<%= '# ' if options[:freeze] %>RAILS_GEM_VERSION = '<%= Rails::VERSION::STRING %>' unless defined? RAILS_GEM_VERSION # Bootstrap the Rails environment, frameworks, and default configuration require File.join(File.dirname(__FILE__), 'boot') @@ -26,10 +26,14 @@ Rails::Initializer.run do |config| # Skip frameworks you're not going to use. To use Rails without a database, # you must remove the Active Record framework. +<% if options[:skip_activerecord] -%> + config.frameworks -= [ :active_record ] +<% else -%> # config.frameworks -= [ :active_record, :active_resource, :action_mailer ] # Activate observers that should always be running # config.active_record.observers = :cacher, :garbage_collector, :forum_observer +<% end -%> # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. @@ -38,4 +42,12 @@ Rails::Initializer.run do |config| # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')] # config.i18n.default_locale = :de -end
\ No newline at end of file + + # Configure generators values. Many other options are available, be sure to + # check the documentation. + config.generators do |g| + g.orm :active_record + g.template_engine :erb + g.test_framework :test_unit, :fixture => true + end +end diff --git a/railties/environments/development.rb b/railties/lib/generators/rails/app/templates/config/environments/development.rb index 85c9a6080e..85c9a6080e 100644 --- a/railties/environments/development.rb +++ b/railties/lib/generators/rails/app/templates/config/environments/development.rb diff --git a/railties/environments/production.rb b/railties/lib/generators/rails/app/templates/config/environments/production.rb index 1fc9f6b923..1fc9f6b923 100644 --- a/railties/environments/production.rb +++ b/railties/lib/generators/rails/app/templates/config/environments/production.rb diff --git a/railties/environments/test.rb b/railties/lib/generators/rails/app/templates/config/environments/test.rb index 496eb9572b..496eb9572b 100644 --- a/railties/environments/test.rb +++ b/railties/lib/generators/rails/app/templates/config/environments/test.rb diff --git a/railties/configs/initializers/backtrace_silencers.rb b/railties/lib/generators/rails/app/templates/config/initializers/backtrace_silencers.rb index 839d4cde19..839d4cde19 100644 --- a/railties/configs/initializers/backtrace_silencers.rb +++ b/railties/lib/generators/rails/app/templates/config/initializers/backtrace_silencers.rb diff --git a/railties/configs/initializers/inflections.rb b/railties/lib/generators/rails/app/templates/config/initializers/inflections.rb index d531b8bb82..d531b8bb82 100644 --- a/railties/configs/initializers/inflections.rb +++ b/railties/lib/generators/rails/app/templates/config/initializers/inflections.rb diff --git a/railties/configs/initializers/mime_types.rb b/railties/lib/generators/rails/app/templates/config/initializers/mime_types.rb index 72aca7e441..72aca7e441 100644 --- a/railties/configs/initializers/mime_types.rb +++ b/railties/lib/generators/rails/app/templates/config/initializers/mime_types.rb diff --git a/railties/configs/initializers/new_rails_defaults.rb b/railties/lib/generators/rails/app/templates/config/initializers/new_rails_defaults.rb index 8ec3186c84..8ec3186c84 100644 --- a/railties/configs/initializers/new_rails_defaults.rb +++ b/railties/lib/generators/rails/app/templates/config/initializers/new_rails_defaults.rb diff --git a/railties/configs/initializers/session_store.rb b/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt index 4499ab84b6..4499ab84b6 100644 --- a/railties/configs/initializers/session_store.rb +++ b/railties/lib/generators/rails/app/templates/config/initializers/session_store.rb.tt diff --git a/railties/configs/locales/en.yml b/railties/lib/generators/rails/app/templates/config/locales/en.yml index f265c068d8..f265c068d8 100644 --- a/railties/configs/locales/en.yml +++ b/railties/lib/generators/rails/app/templates/config/locales/en.yml diff --git a/railties/configs/routes.rb b/railties/lib/generators/rails/app/templates/config/routes.rb index ea14ce1bfc..ea14ce1bfc 100644 --- a/railties/configs/routes.rb +++ b/railties/lib/generators/rails/app/templates/config/routes.rb diff --git a/railties/configs/seeds.rb b/railties/lib/generators/rails/app/templates/db/seeds.rb index bc8695e6f0..bc8695e6f0 100644 --- a/railties/configs/seeds.rb +++ b/railties/lib/generators/rails/app/templates/db/seeds.rb diff --git a/railties/dispatches/config.ru b/railties/lib/generators/rails/app/templates/dispatchers/config.ru index acbfe4e9ae..acbfe4e9ae 100644 --- a/railties/dispatches/config.ru +++ b/railties/lib/generators/rails/app/templates/dispatchers/config.ru diff --git a/railties/dispatches/dispatch.fcgi b/railties/lib/generators/rails/app/templates/dispatchers/dispatch.fcgi index 664dbbbee8..f5b3b71875 100755 --- a/railties/dispatches/dispatch.fcgi +++ b/railties/lib/generators/rails/app/templates/dispatchers/dispatch.fcgi @@ -1,4 +1,4 @@ -#!/usr/bin/env ruby +<%= shebang %> # # You may specify the path to the FastCGI crash log (a log of unhandled # exceptions which forced the FastCGI instance to exit, great for debugging) diff --git a/railties/dispatches/dispatch.rb b/railties/lib/generators/rails/app/templates/dispatchers/dispatch.rb index 32fa3b2665..48e888113a 100755 --- a/railties/dispatches/dispatch.rb +++ b/railties/lib/generators/rails/app/templates/dispatchers/dispatch.rb @@ -1,4 +1,4 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) diff --git a/railties/dispatches/gateway.cgi b/railties/lib/generators/rails/app/templates/dispatchers/gateway.cgi index 0305b7f810..bdc1055a22 100755 --- a/railties/dispatches/gateway.cgi +++ b/railties/lib/generators/rails/app/templates/dispatchers/gateway.cgi @@ -1,4 +1,4 @@ -#!/usr/bin/env ruby +<%= shebang %> require 'drb' diff --git a/railties/doc/README_FOR_APP b/railties/lib/generators/rails/app/templates/doc/README_FOR_APP index fe41f5cc24..fe41f5cc24 100644 --- a/railties/doc/README_FOR_APP +++ b/railties/lib/generators/rails/app/templates/doc/README_FOR_APP diff --git a/railties/html/404.html b/railties/lib/generators/rails/app/templates/public/404.html index 88ee108e90..88ee108e90 100644 --- a/railties/html/404.html +++ b/railties/lib/generators/rails/app/templates/public/404.html diff --git a/railties/html/422.html b/railties/lib/generators/rails/app/templates/public/422.html index 9c3c96670b..9c3c96670b 100644 --- a/railties/html/422.html +++ b/railties/lib/generators/rails/app/templates/public/422.html diff --git a/railties/html/500.html b/railties/lib/generators/rails/app/templates/public/500.html index f71c86e652..f71c86e652 100644 --- a/railties/html/500.html +++ b/railties/lib/generators/rails/app/templates/public/500.html diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml b/railties/lib/generators/rails/app/templates/public/favicon.ico index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/rails_generator/generators/components/mailer/templates/fixture.rhtml +++ b/railties/lib/generators/rails/app/templates/public/favicon.ico diff --git a/railties/html/images/rails.png b/railties/lib/generators/rails/app/templates/public/images/rails.png Binary files differindex d5edc04e65..d5edc04e65 100644 --- a/railties/html/images/rails.png +++ b/railties/lib/generators/rails/app/templates/public/images/rails.png diff --git a/railties/html/index.html b/railties/lib/generators/rails/app/templates/public/index.html index ff2dfd3193..ff2dfd3193 100644 --- a/railties/html/index.html +++ b/railties/lib/generators/rails/app/templates/public/index.html diff --git a/railties/html/javascripts/application.js b/railties/lib/generators/rails/app/templates/public/javascripts/application.js index fe4577696b..fe4577696b 100644 --- a/railties/html/javascripts/application.js +++ b/railties/lib/generators/rails/app/templates/public/javascripts/application.js diff --git a/railties/html/javascripts/controls.js b/railties/lib/generators/rails/app/templates/public/javascripts/controls.js index ca29aefdd1..ca29aefdd1 100644 --- a/railties/html/javascripts/controls.js +++ b/railties/lib/generators/rails/app/templates/public/javascripts/controls.js diff --git a/railties/html/javascripts/dragdrop.js b/railties/lib/generators/rails/app/templates/public/javascripts/dragdrop.js index 07229f986f..07229f986f 100644 --- a/railties/html/javascripts/dragdrop.js +++ b/railties/lib/generators/rails/app/templates/public/javascripts/dragdrop.js diff --git a/railties/html/javascripts/effects.js b/railties/lib/generators/rails/app/templates/public/javascripts/effects.js index 5a639d2dea..5a639d2dea 100644 --- a/railties/html/javascripts/effects.js +++ b/railties/lib/generators/rails/app/templates/public/javascripts/effects.js diff --git a/railties/html/javascripts/prototype.js b/railties/lib/generators/rails/app/templates/public/javascripts/prototype.js index dfe8ab4e13..dfe8ab4e13 100644 --- a/railties/html/javascripts/prototype.js +++ b/railties/lib/generators/rails/app/templates/public/javascripts/prototype.js diff --git a/railties/html/robots.txt b/railties/lib/generators/rails/app/templates/public/robots.txt index 085187fa58..085187fa58 100644 --- a/railties/html/robots.txt +++ b/railties/lib/generators/rails/app/templates/public/robots.txt diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/view.rhtml b/railties/lib/generators/rails/app/templates/public/stylesheets/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/lib/rails_generator/generators/components/mailer/templates/view.rhtml +++ b/railties/lib/generators/rails/app/templates/public/stylesheets/.empty_directory diff --git a/railties/bin/about b/railties/lib/generators/rails/app/templates/script/about.tt index 1eeb6eb915..afbab8b533 100755 --- a/railties/bin/about +++ b/railties/lib/generators/rails/app/templates/script/about.tt @@ -1,4 +1,4 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.expand_path('../../config/boot', __FILE__) $LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info" require 'commands/about' diff --git a/railties/bin/console b/railties/lib/generators/rails/app/templates/script/console.tt index 235a1f2780..2f8b95c923 100755 --- a/railties/bin/console +++ b/railties/lib/generators/rails/app/templates/script/console.tt @@ -1,3 +1,3 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.expand_path('../../config/boot', __FILE__) require 'commands/console' diff --git a/railties/bin/dbconsole b/railties/lib/generators/rails/app/templates/script/dbconsole.tt index 83c8436a9d..7dce6d16dd 100755 --- a/railties/bin/dbconsole +++ b/railties/lib/generators/rails/app/templates/script/dbconsole.tt @@ -1,3 +1,3 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.expand_path('../../config/boot', __FILE__) require 'commands/dbconsole' diff --git a/railties/bin/destroy b/railties/lib/generators/rails/app/templates/script/destroy.tt index 88d295f7aa..053d3dea39 100755 --- a/railties/bin/destroy +++ b/railties/lib/generators/rails/app/templates/script/destroy.tt @@ -1,3 +1,3 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.expand_path('../../config/boot', __FILE__) require 'commands/destroy' diff --git a/railties/bin/generate b/railties/lib/generators/rails/app/templates/script/generate.tt index 62a8a4c0c5..ea4a0efd43 100755 --- a/railties/bin/generate +++ b/railties/lib/generators/rails/app/templates/script/generate.tt @@ -1,3 +1,3 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.expand_path('../../config/boot', __FILE__) require 'commands/generate' diff --git a/railties/bin/performance/benchmarker b/railties/lib/generators/rails/app/templates/script/performance/benchmarker.tt index 3bff809fb3..da9df7fab9 100755 --- a/railties/bin/performance/benchmarker +++ b/railties/lib/generators/rails/app/templates/script/performance/benchmarker.tt @@ -1,3 +1,3 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.expand_path('../../../config/boot', __FILE__) require 'commands/performance/benchmarker' diff --git a/railties/bin/performance/profiler b/railties/lib/generators/rails/app/templates/script/performance/profiler.tt index 07640575cd..5a0e2b0c28 100755 --- a/railties/bin/performance/profiler +++ b/railties/lib/generators/rails/app/templates/script/performance/profiler.tt @@ -1,3 +1,3 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.expand_path('../../../config/boot', __FILE__) require 'commands/performance/profiler' diff --git a/railties/bin/plugin b/railties/lib/generators/rails/app/templates/script/plugin.tt index b82201fa83..68b2148bba 100755 --- a/railties/bin/plugin +++ b/railties/lib/generators/rails/app/templates/script/plugin.tt @@ -1,3 +1,3 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.expand_path('../../config/boot', __FILE__) require 'commands/plugin' diff --git a/railties/bin/runner b/railties/lib/generators/rails/app/templates/script/runner.tt index be4c5d4572..a2b313fa32 100755 --- a/railties/bin/runner +++ b/railties/lib/generators/rails/app/templates/script/runner.tt @@ -1,3 +1,3 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.expand_path('../../config/boot', __FILE__) require 'commands/runner' diff --git a/railties/bin/server b/railties/lib/generators/rails/app/templates/script/server.tt index b9fcb71793..c8868155f3 100755 --- a/railties/bin/server +++ b/railties/lib/generators/rails/app/templates/script/server.tt @@ -1,3 +1,3 @@ -#!/usr/bin/env ruby +<%= shebang %> require File.expand_path('../../config/boot', __FILE__) require 'commands/server' diff --git a/railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb b/railties/lib/generators/rails/app/templates/test/fixtures/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb +++ b/railties/lib/generators/rails/app/templates/test/fixtures/.empty_directory diff --git a/railties/test/fixtures/lib/generators/missing_class/templates/.gitignore b/railties/lib/generators/rails/app/templates/test/functional/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/test/fixtures/lib/generators/missing_class/templates/.gitignore +++ b/railties/lib/generators/rails/app/templates/test/functional/.empty_directory diff --git a/railties/test/fixtures/lib/generators/missing_generator/templates/.gitignore b/railties/lib/generators/rails/app/templates/test/integration/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/test/fixtures/lib/generators/missing_generator/templates/.gitignore +++ b/railties/lib/generators/rails/app/templates/test/integration/.empty_directory diff --git a/railties/helpers/performance_test.rb b/railties/lib/generators/rails/app/templates/test/performance/browsing_test.rb index 4b60558b43..4b60558b43 100644 --- a/railties/helpers/performance_test.rb +++ b/railties/lib/generators/rails/app/templates/test/performance/browsing_test.rb diff --git a/railties/helpers/test_helper.rb b/railties/lib/generators/rails/app/templates/test/test_helper.rb index b9fe2517c8..b9fe2517c8 100644 --- a/railties/helpers/test_helper.rb +++ b/railties/lib/generators/rails/app/templates/test/test_helper.rb diff --git a/railties/test/fixtures/lib/generators/missing_templates/.gitignore b/railties/lib/generators/rails/app/templates/test/unit/.empty_directory index e69de29bb2..e69de29bb2 100644 --- a/railties/test/fixtures/lib/generators/missing_templates/.gitignore +++ b/railties/lib/generators/rails/app/templates/test/unit/.empty_directory diff --git a/railties/lib/generators/rails/controller/USAGE b/railties/lib/generators/rails/controller/USAGE new file mode 100644 index 0000000000..6ed4b2edfc --- /dev/null +++ b/railties/lib/generators/rails/controller/USAGE @@ -0,0 +1,18 @@ +Description: + Stubs out a new controller and its views. Pass the controller name, either + CamelCased or under_scored, and a list of views as arguments. + + To create a controller within a module, specify the controller name as a + path like 'parent_module/controller_name'. + + This generates a controller class in app/controllers and invokes helper, + template engine and test framework generators. + +Example: + `./script/generate controller CreditCard open debit credit close` + + Credit card controller with URLs like /credit_card/debit. + Controller: app/controllers/credit_card_controller.rb + Functional Test: test/functional/credit_card_controller_test.rb + Views: app/views/credit_card/debit.html.erb [...] + Helper: app/helpers/credit_card_helper.rb diff --git a/railties/lib/generators/rails/controller/controller_generator.rb b/railties/lib/generators/rails/controller/controller_generator.rb new file mode 100644 index 0000000000..91470be833 --- /dev/null +++ b/railties/lib/generators/rails/controller/controller_generator.rb @@ -0,0 +1,14 @@ +module Rails + module Generators + class ControllerGenerator < NamedBase + argument :actions, :type => :array, :default => [], :banner => "action action" + check_class_collision :suffix => "Controller" + + def create_controller_files + template 'controller.rb', File.join('app/controllers', class_path, "#{file_name}_controller.rb") + end + + hook_for :template_engine, :test_framework, :helper + end + end +end diff --git a/railties/lib/rails_generator/generators/components/controller/templates/controller.rb b/railties/lib/generators/rails/controller/templates/controller.rb index cda2659e69..cda2659e69 100644 --- a/railties/lib/rails_generator/generators/components/controller/templates/controller.rb +++ b/railties/lib/generators/rails/controller/templates/controller.rb diff --git a/railties/lib/generators/rails/generator/USAGE b/railties/lib/generators/rails/generator/USAGE new file mode 100644 index 0000000000..ca7d3f62d0 --- /dev/null +++ b/railties/lib/generators/rails/generator/USAGE @@ -0,0 +1,11 @@ +Description: + Stubs out a new generator at lib/generators. Pass the generator name, either + CamelCased or under_scored, as an argument. + +Example: + `./script/generate generator Awesome` + + creates a standard awesome generator: + lib/generators/awesome/ + lib/generators/awesome/awesome_generator.rb + lib/generators/awesome/templates/ diff --git a/railties/lib/generators/rails/generator/generator_generator.rb b/railties/lib/generators/rails/generator/generator_generator.rb new file mode 100644 index 0000000000..2fc97b20d3 --- /dev/null +++ b/railties/lib/generators/rails/generator/generator_generator.rb @@ -0,0 +1,25 @@ +module Rails + module Generators + class GeneratorGenerator < NamedBase + check_class_collision :suffix => "Generator" + + class_option :namespace, :type => :boolean, :default => true, + :desc => "Namespace generator under lib/generators/name" + + def craete_generator_files + directory '.', generator_dir + end + + protected + + def generator_dir + if options[:namespace] + File.join("lib", "generators", file_name) + else + File.join("lib", "generators") + end + end + + end + end +end diff --git a/railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt b/railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt new file mode 100644 index 0000000000..675f00043f --- /dev/null +++ b/railties/lib/generators/rails/generator/templates/%file_name%_generator.rb.tt @@ -0,0 +1,5 @@ +class <%= class_name %>Generator < Rails::Generators::NamedBase + def self.source_root + @source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates')) + end +end diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/USAGE b/railties/lib/generators/rails/generator/templates/USAGE.tt index ea9f4f12cc..ea9f4f12cc 100644 --- a/railties/lib/rails_generator/generators/components/plugin/templates/USAGE +++ b/railties/lib/generators/rails/generator/templates/USAGE.tt diff --git a/railties/lib/generators/rails/generator/templates/templates/.empty_directory b/railties/lib/generators/rails/generator/templates/templates/.empty_directory new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/railties/lib/generators/rails/generator/templates/templates/.empty_directory diff --git a/railties/lib/generators/rails/helper/USAGE b/railties/lib/generators/rails/helper/USAGE new file mode 100644 index 0000000000..531c9b390a --- /dev/null +++ b/railties/lib/generators/rails/helper/USAGE @@ -0,0 +1,17 @@ +Description: + Stubs out a new helper. Pass the helper name, either CamelCased + or under_scored. + + To create a helper within a module, specify the helper name as a + path like 'parent_module/helper_name'. + + This generates a helper class in app/helpers and invokes the configured + test framework. + +Example: + `./script/generate helper CreditCard` + + Credit card helper. + Helper: app/helpers/credit_card_helper.rb + Test: test/unit/helpers/credit_card_helper_test.rb + diff --git a/railties/lib/generators/rails/helper/helper_generator.rb b/railties/lib/generators/rails/helper/helper_generator.rb new file mode 100644 index 0000000000..ad66388591 --- /dev/null +++ b/railties/lib/generators/rails/helper/helper_generator.rb @@ -0,0 +1,13 @@ +module Rails + module Generators + class HelperGenerator < NamedBase + check_class_collision :suffix => "Helper" + + def create_helper_files + template 'helper.rb', File.join('app/helpers', class_path, "#{file_name}_helper.rb") + end + + hook_for :test_framework + end + end +end diff --git a/railties/lib/rails_generator/generators/components/controller/templates/helper.rb b/railties/lib/generators/rails/helper/templates/helper.rb index 3fe2ecdc74..3fe2ecdc74 100644 --- a/railties/lib/rails_generator/generators/components/controller/templates/helper.rb +++ b/railties/lib/generators/rails/helper/templates/helper.rb diff --git a/railties/lib/rails_generator/generators/components/integration_test/USAGE b/railties/lib/generators/rails/integration_test/USAGE index 09e2691f69..b76c35a702 100644 --- a/railties/lib/rails_generator/generators/components/integration_test/USAGE +++ b/railties/lib/generators/rails/integration_test/USAGE @@ -1,7 +1,9 @@ Description: Stubs out a new integration test. Pass the name of the test, either - CamelCased or under_scored, as an argument. The new test class is - generated in test/integration/testname_test.rb + CamelCased or under_scored, as an argument. + + This generator invokes the current integration tool, which defaults to + TestUnit. Example: `./script/generate integration_test GeneralStories` creates a GeneralStories diff --git a/railties/lib/generators/rails/integration_test/integration_test_generator.rb b/railties/lib/generators/rails/integration_test/integration_test_generator.rb new file mode 100644 index 0000000000..363a327fcb --- /dev/null +++ b/railties/lib/generators/rails/integration_test/integration_test_generator.rb @@ -0,0 +1,7 @@ +module Rails + module Generators + class IntegrationTestGenerator < NamedBase + hook_for :integration_tool, :as => :integration + end + end +end diff --git a/railties/lib/rails_generator/generators/components/mailer/USAGE b/railties/lib/generators/rails/mailer/USAGE index 61a649ed4d..c56095b2c8 100644 --- a/railties/lib/rails_generator/generators/components/mailer/USAGE +++ b/railties/lib/generators/rails/mailer/USAGE @@ -2,9 +2,8 @@ Description: Stubs out a new mailer and its views. Pass the mailer name, either CamelCased or under_scored, and an optional list of emails as arguments. - This generates a mailer class in app/models, view templates in - app/views/mailer_name, a unit test in test/unit, and fixtures in - test/fixtures. + This generates a mailer class in app/models and invokes your template + engine and test framework generators. Example: `./script/generate mailer Notifications signup forgot_password invoice` diff --git a/railties/lib/generators/rails/mailer/mailer_generator.rb b/railties/lib/generators/rails/mailer/mailer_generator.rb new file mode 100644 index 0000000000..33f1665b83 --- /dev/null +++ b/railties/lib/generators/rails/mailer/mailer_generator.rb @@ -0,0 +1,14 @@ +module Rails + module Generators + class MailerGenerator < NamedBase + argument :actions, :type => :array, :default => [], :banner => "method method" + check_class_collision + + def create_mailer_file + template "mailer.rb", File.join('app/models', class_path, "#{file_name}.rb") + end + + hook_for :template_engine, :test_framework + end + end +end diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb b/railties/lib/generators/rails/mailer/templates/mailer.rb index ce15ae9de9..ce15ae9de9 100644 --- a/railties/lib/rails_generator/generators/components/mailer/templates/mailer.rb +++ b/railties/lib/generators/rails/mailer/templates/mailer.rb diff --git a/railties/lib/rails_generator/generators/components/metal/USAGE b/railties/lib/generators/rails/metal/USAGE index 123ec6c03f..123ec6c03f 100644 --- a/railties/lib/rails_generator/generators/components/metal/USAGE +++ b/railties/lib/generators/rails/metal/USAGE diff --git a/railties/lib/generators/rails/metal/metal_generator.rb b/railties/lib/generators/rails/metal/metal_generator.rb new file mode 100644 index 0000000000..fe4f945cad --- /dev/null +++ b/railties/lib/generators/rails/metal/metal_generator.rb @@ -0,0 +1,11 @@ +module Rails + module Generators + class MetalGenerator < NamedBase + check_class_collision + + def create_metal_file + template "metal.rb", "app/metal/#{file_name}.rb" + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/metal/templates/metal.rb b/railties/lib/generators/rails/metal/templates/metal.rb index e94982b69a..e94982b69a 100644 --- a/railties/lib/rails_generator/generators/components/metal/templates/metal.rb +++ b/railties/lib/generators/rails/metal/templates/metal.rb diff --git a/railties/lib/rails_generator/generators/components/migration/USAGE b/railties/lib/generators/rails/migration/USAGE index b83c657963..d91127aac3 100644 --- a/railties/lib/rails_generator/generators/components/migration/USAGE +++ b/railties/lib/generators/rails/migration/USAGE @@ -14,7 +14,7 @@ Example: db/migrate/20080514090912_add_ssl_flag.rb `./script/generate migration AddTitleBodyToPost title:string body:text published:boolean` - + This will create the AddTitleBodyToPost in db/migrate/20080514090912_add_title_body_to_post.rb with this in the Up migration: @@ -23,7 +23,7 @@ Example: add_column :posts, :published, :boolean And this in the Down migration: - + remove_column :posts, :published remove_column :posts, :body remove_column :posts, :title diff --git a/railties/lib/generators/rails/migration/migration_generator.rb b/railties/lib/generators/rails/migration/migration_generator.rb new file mode 100644 index 0000000000..39fa5b63b1 --- /dev/null +++ b/railties/lib/generators/rails/migration/migration_generator.rb @@ -0,0 +1,8 @@ +module Rails + module Generators + class MigrationGenerator < NamedBase #metagenerator + argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + hook_for :orm, :required => true + end + end +end diff --git a/railties/lib/rails_generator/generators/components/model/USAGE b/railties/lib/generators/rails/model/USAGE index 24b03b4d4a..b056d5df8b 100644 --- a/railties/lib/rails_generator/generators/components/model/USAGE +++ b/railties/lib/generators/rails/model/USAGE @@ -2,21 +2,24 @@ Description: Stubs out a new model. Pass the model name, either CamelCased or under_scored, and an optional list of attribute pairs as arguments. - Attribute pairs are column_name:sql_type arguments specifying the + Attribute pairs are field:type arguments specifying the model's attributes. Timestamps are added by default, so you don't have to specify them by hand as 'created_at:datetime updated_at:datetime'. You don't have to think up every attribute up front, but it helps to sketch out a few so you can start working with the model immediately. - This generates a model class in app/models, a unit test in test/unit, - a test fixture in test/fixtures/singular_name.yml, and a migration in - db/migrate. + This generator invokes your configured ORM and test framework, which + defaults to ActiveRecord and TestUnit. + + Finally, if --parent option is given, it's used as superclass of the + created model. This allows you create Single Table Inheritance models. Examples: `./script/generate model account` - creates an Account model, test, fixture, and migration: + For ActiveRecord and TestUnit it creates: + Model: app/models/account.rb Test: test/unit/account_test.rb Fixtures: test/fixtures/accounts.yml @@ -24,4 +27,4 @@ Examples: `./script/generate model post title:string body:text published:boolean` - creates a Post model with a string title, text body, and published flag. + Creates a Post model with a string title, text body, and published flag. diff --git a/railties/lib/generators/rails/model/model_generator.rb b/railties/lib/generators/rails/model/model_generator.rb new file mode 100644 index 0000000000..629d5eed3f --- /dev/null +++ b/railties/lib/generators/rails/model/model_generator.rb @@ -0,0 +1,8 @@ +module Rails + module Generators + class ModelGenerator < NamedBase #metagenerator + argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + hook_for :orm, :required => true + end + end +end diff --git a/railties/lib/generators/rails/model_subclass/model_subclass_generator.rb b/railties/lib/generators/rails/model_subclass/model_subclass_generator.rb new file mode 100644 index 0000000000..4649709780 --- /dev/null +++ b/railties/lib/generators/rails/model_subclass/model_subclass_generator.rb @@ -0,0 +1,11 @@ +module Rails + module Generators + class ModelSubclassGenerator < Base + desc "model_subclass is deprecated. Invoke model with --parent option instead." + + def say_deprecation_warn + say self.class.desc + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/observer/USAGE b/railties/lib/generators/rails/observer/USAGE index a5d744a3c2..9a20f55a89 100644 --- a/railties/lib/rails_generator/generators/components/observer/USAGE +++ b/railties/lib/generators/rails/observer/USAGE @@ -2,12 +2,11 @@ Description: Stubs out a new observer. Pass the observer name, either CamelCased or under_scored, as an argument. - The generator creates an observer class in app/models and a unit test in - test/unit. + This generator only invokes your ORM and test framework generators. Example: `./script/generate observer Account` - creates an Account observer and unit test: + For ActiveRecord and TestUnit it creates: Observer: app/models/account_observer.rb - Test: test/unit/account_observer_test.rb + TestUnit: test/unit/account_observer_test.rb diff --git a/railties/lib/generators/rails/observer/observer_generator.rb b/railties/lib/generators/rails/observer/observer_generator.rb new file mode 100644 index 0000000000..f5cedee91f --- /dev/null +++ b/railties/lib/generators/rails/observer/observer_generator.rb @@ -0,0 +1,7 @@ +module Rails + module Generators + class ObserverGenerator < NamedBase #metagenerator + hook_for :orm, :required => true + end + end +end diff --git a/railties/lib/rails_generator/generators/components/performance_test/USAGE b/railties/lib/generators/rails/performance_test/USAGE index d84051eb02..ee82578cdb 100644 --- a/railties/lib/rails_generator/generators/components/performance_test/USAGE +++ b/railties/lib/generators/rails/performance_test/USAGE @@ -1,7 +1,9 @@ Description: Stubs out a new performance test. Pass the name of the test, either - CamelCased or under_scored, as an argument. The new test class is - generated in test/performance/testname_test.rb + CamelCased or under_scored, as an argument. + + This generator invokes the current performance tool, which defaults to + TestUnit. Example: `./script/generate performance_test GeneralStories` creates a GeneralStories diff --git a/railties/lib/generators/rails/performance_test/performance_test_generator.rb b/railties/lib/generators/rails/performance_test/performance_test_generator.rb new file mode 100644 index 0000000000..d1c71ab8ed --- /dev/null +++ b/railties/lib/generators/rails/performance_test/performance_test_generator.rb @@ -0,0 +1,7 @@ +module Rails + module Generators + class PerformanceTestGenerator < NamedBase + hook_for :performance_tool, :as => :performance + end + end +end diff --git a/railties/lib/generators/rails/plugin/USAGE b/railties/lib/generators/rails/plugin/USAGE new file mode 100644 index 0000000000..8a17fa4dec --- /dev/null +++ b/railties/lib/generators/rails/plugin/USAGE @@ -0,0 +1,13 @@ +Description: + Stubs out a new plugin at vendor/plugins. Pass the plugin name, either + CamelCased or under_scored, as an argument. + +Example: + `./script/generate plugin BrowserFilters` + + creates a standard browser_filters plugin: + vendor/plugins/browser_filters/README + vendor/plugins/browser_filters/init.rb + vendor/plugins/browser_filters/install.rb + vendor/plugins/browser_filters/lib/browser_filters.rb + vendor/plugins/browser_filters/test/browser_filters_test.rb diff --git a/railties/lib/generators/rails/plugin/plugin_generator.rb b/railties/lib/generators/rails/plugin/plugin_generator.rb new file mode 100644 index 0000000000..ee785caf7d --- /dev/null +++ b/railties/lib/generators/rails/plugin/plugin_generator.rb @@ -0,0 +1,47 @@ +require 'generators/rails/generator/generator_generator' + +module Rails + module Generators + class PluginGenerator < NamedBase + class_option :tasks, :desc => "When supplied creates tasks base files." + + check_class_collision + + def create_root_files + directory '.', plugin_dir, :recursive => false + end + + def create_lib_files + directory 'lib', plugin_dir('lib'), :recursive => false + end + + def create_tasks_files + return unless options[:tasks] + directory 'tasks', plugin_dir('tasks') + end + + hook_for :generator do |instance, generator| + instance.inside instance.send(:plugin_dir), :verbose => true do + instance.invoke generator, [ instance.name ], :namespace => false + end + end + + hook_for :test_framework do |instance, test_framework| + instance.inside instance.send(:plugin_dir), :verbose => true do + instance.invoke test_framework + end + end + + protected + + def plugin_dir(join=nil) + if join + File.join(plugin_dir, join) + else + "vendor/plugins/#{file_name}" + end + end + + end + end +end diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE b/railties/lib/generators/rails/plugin/templates/MIT-LICENSE index 8717df053d..8717df053d 100644 --- a/railties/lib/rails_generator/generators/components/plugin/templates/MIT-LICENSE +++ b/railties/lib/generators/rails/plugin/templates/MIT-LICENSE diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/README b/railties/lib/generators/rails/plugin/templates/README index 702db07cb1..702db07cb1 100644 --- a/railties/lib/rails_generator/generators/components/plugin/templates/README +++ b/railties/lib/generators/rails/plugin/templates/README diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile b/railties/lib/generators/rails/plugin/templates/Rakefile index 85e8ff1834..85e8ff1834 100644 --- a/railties/lib/rails_generator/generators/components/plugin/templates/Rakefile +++ b/railties/lib/generators/rails/plugin/templates/Rakefile diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/init.rb b/railties/lib/generators/rails/plugin/templates/init.rb index 3c19a743c9..3c19a743c9 100644 --- a/railties/lib/rails_generator/generators/components/plugin/templates/init.rb +++ b/railties/lib/generators/rails/plugin/templates/init.rb diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/install.rb b/railties/lib/generators/rails/plugin/templates/install.rb index f7732d3796..f7732d3796 100644 --- a/railties/lib/rails_generator/generators/components/plugin/templates/install.rb +++ b/railties/lib/generators/rails/plugin/templates/install.rb diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb b/railties/lib/generators/rails/plugin/templates/lib/%file_name%.rb.tt index d8d908a959..d8d908a959 100644 --- a/railties/lib/rails_generator/generators/components/plugin/templates/plugin.rb +++ b/railties/lib/generators/rails/plugin/templates/lib/%file_name%.rb.tt diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake b/railties/lib/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt index 72920a9d3a..72920a9d3a 100644 --- a/railties/lib/rails_generator/generators/components/plugin/templates/tasks.rake +++ b/railties/lib/generators/rails/plugin/templates/tasks/%file_name%_tasks.rake.tt diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb b/railties/lib/generators/rails/plugin/templates/uninstall.rb index 9738333463..9738333463 100644 --- a/railties/lib/rails_generator/generators/components/plugin/templates/uninstall.rb +++ b/railties/lib/generators/rails/plugin/templates/uninstall.rb diff --git a/railties/lib/rails_generator/generators/components/resource/USAGE b/railties/lib/generators/rails/resource/USAGE index e6043f1de1..936619b0db 100644 --- a/railties/lib/rails_generator/generators/components/resource/USAGE +++ b/railties/lib/generators/rails/resource/USAGE @@ -4,15 +4,15 @@ Description: either CamelCased or under_scored, as the first argument, and an optional list of attribute pairs. - Attribute pairs are column_name:sql_type arguments specifying the + Attribute pairs are field:type arguments specifying the model's attributes. Timestamps are added by default, so you don't have to specify them by hand as 'created_at:datetime updated_at:datetime'. You don't have to think up every attribute up front, but it helps to - sketch out a few so you can start working with the resource immediately. + sketch out a few so you can start working with the model immediately. - This creates a model, controller, helper, tests and fixtures for all of them, - and the corresponding map.resources declaration in config/routes.rb + This generator invokes your configured ORM and test framework, besides + creating helpers and add routes to config/routes.rb. Unlike the scaffold generator, the resource generator does not create views or add any methods to the generated controller. diff --git a/railties/lib/generators/rails/resource/resource_generator.rb b/railties/lib/generators/rails/resource/resource_generator.rb new file mode 100644 index 0000000000..70babc0550 --- /dev/null +++ b/railties/lib/generators/rails/resource/resource_generator.rb @@ -0,0 +1,40 @@ +require 'generators/rails/model/model_generator' + +module Rails + module Generators + class ResourceGenerator < ModelGenerator #metagenerator + hook_for :resource_controller, :required => true do |base, controller| + base.invoke controller, [ base.name.pluralize, base.options[:actions] ] + end + + class_option :actions, :type => :array, :banner => "ACTION ACTION", :default => [], + :desc => "Actions for the resource controller" + + class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller" + class_option :force_plural, :type => :boolean, :desc => "Forces the use of a plural ModelName" + + def initialize(*args) + super + if name == name.pluralize && !options[:force_plural] + say "Plural version of the model detected, using singularized version. Override with --force-plural." + name.replace name.singularize + end + end + + def add_resource_route + route "map.resource#{:s unless options[:singleton]} :#{pluralize?(file_name)}" + end + + protected + + def pluralize?(name) + if options[:singleton] + name + else + name.pluralize + end + end + + end + end +end diff --git a/railties/lib/rails_generator/generators/components/scaffold/USAGE b/railties/lib/generators/rails/scaffold/USAGE index 810aea16f1..71edd2f469 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/USAGE +++ b/railties/lib/generators/rails/scaffold/USAGE @@ -7,7 +7,7 @@ Description: under_scored, as the first argument, and an optional list of attribute pairs. - Attribute pairs are column_name:sql_type arguments specifying the + Attribute pairs are field:type arguments specifying the model's attributes. Timestamps are added by default, so you don't have to specify them by hand as 'created_at:datetime updated_at:datetime'. diff --git a/railties/lib/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/generators/rails/scaffold/scaffold_generator.rb new file mode 100644 index 0000000000..af44c8ba65 --- /dev/null +++ b/railties/lib/generators/rails/scaffold/scaffold_generator.rb @@ -0,0 +1,12 @@ +require 'generators/rails/resource/resource_generator' + +module Rails + module Generators + class ScaffoldGenerator < ResourceGenerator #metagenerator + remove_hook_for :actions, :resource_controller + + hook_for :scaffold_controller, :required => true + hook_for :stylesheets + end + end +end diff --git a/railties/lib/generators/rails/scaffold_controller/USAGE b/railties/lib/generators/rails/scaffold_controller/USAGE new file mode 100644 index 0000000000..d60a3c3680 --- /dev/null +++ b/railties/lib/generators/rails/scaffold_controller/USAGE @@ -0,0 +1,20 @@ +Description: + Stubs out a scaffolded controller and its views. Pass the model name, + either CamelCased or under_scored, and a list of views as arguments. + The controller name is retrieved as a pluralized version of the model + name. + + To create a controller within a module, specify the model name as a + path like 'parent_module/controller_name'. + + This generates a controller class in app/controllers and invokes helper, + template engine and test framework generators. + +Example: + `./script/generate scaffold_controller CreditCard` + + Credit card controller with URLs like /credit_card/debit. + Controller: app/controllers/credit_cards_controller.rb + Functional Test: test/functional/credit_cards_controller_test.rb + Views: app/views/credit_cards/index.html.erb [...] + Helper: app/helpers/credit_cards_helper.rb diff --git a/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb new file mode 100644 index 0000000000..972be5a33b --- /dev/null +++ b/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb @@ -0,0 +1,26 @@ +module Rails + module Generators + class ScaffoldControllerGenerator < NamedBase + # Add controller methods and ActionORM settings. + include ScaffoldBase + + check_class_collision :suffix => "Controller" + + class_option :orm, :banner => "NAME", :type => :string, :required => true, + :desc => "ORM to generate the controller for" + + class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller" + + def create_controller_files + template 'controller.rb', File.join('app/controllers', class_path, "#{controller_file_name}_controller.rb") + end + + hook_for :template_engine, :test_framework, :as => :scaffold + + # Invoke the helper using the controller (pluralized) name. + hook_for :helper, :as => :scaffold do |base, invoked| + base.invoke invoked, [ base.controller_name ] + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb b/railties/lib/generators/rails/scaffold_controller/templates/controller.rb index 4d190b9362..3cc8bbf8e7 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb +++ b/railties/lib/generators/rails/scaffold_controller/templates/controller.rb @@ -1,19 +1,21 @@ class <%= controller_class_name %>Controller < ApplicationController +<% unless options[:singleton] -%> # GET /<%= table_name %> # GET /<%= table_name %>.xml def index - @<%= table_name %> = <%= class_name %>.all + @<%= table_name %> = <%= orm_class.all(class_name) %> respond_to do |format| format.html # index.html.erb format.xml { render :xml => @<%= table_name %> } end end +<% end -%> # GET /<%= table_name %>/1 # GET /<%= table_name %>/1.xml def show - @<%= file_name %> = <%= class_name %>.find(params[:id]) + @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %> respond_to do |format| format.html # show.html.erb @@ -24,7 +26,7 @@ class <%= controller_class_name %>Controller < ApplicationController # GET /<%= table_name %>/new # GET /<%= table_name %>/new.xml def new - @<%= file_name %> = <%= class_name %>.new + @<%= file_name %> = <%= orm_class.build(class_name) %> respond_to do |format| format.html # new.html.erb @@ -34,22 +36,22 @@ class <%= controller_class_name %>Controller < ApplicationController # GET /<%= table_name %>/1/edit def edit - @<%= file_name %> = <%= class_name %>.find(params[:id]) + @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %> end # POST /<%= table_name %> # POST /<%= table_name %>.xml def create - @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>]) + @<%= file_name %> = <%= orm_class.build(class_name, "params[:#{file_name}]") %> respond_to do |format| - if @<%= file_name %>.save + if @<%= orm_instance.save %> flash[:notice] = '<%= class_name %> was successfully created.' format.html { redirect_to(@<%= file_name %>) } format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> } else format.html { render :action => "new" } - format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity } + format.xml { render :xml => @<%= orm_instance.errors %>, :status => :unprocessable_entity } end end end @@ -57,16 +59,16 @@ class <%= controller_class_name %>Controller < ApplicationController # PUT /<%= table_name %>/1 # PUT /<%= table_name %>/1.xml def update - @<%= file_name %> = <%= class_name %>.find(params[:id]) + @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %> respond_to do |format| - if @<%= file_name %>.update_attributes(params[:<%= file_name %>]) + if @<%= orm_instance.update_attributes("params[:#{file_name}]") %> flash[:notice] = '<%= class_name %> was successfully updated.' format.html { redirect_to(@<%= file_name %>) } format.xml { head :ok } else format.html { render :action => "edit" } - format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity } + format.xml { render :xml => @<%= orm_instance.errors %>, :status => :unprocessable_entity } end end end @@ -74,8 +76,8 @@ class <%= controller_class_name %>Controller < ApplicationController # DELETE /<%= table_name %>/1 # DELETE /<%= table_name %>/1.xml def destroy - @<%= file_name %> = <%= class_name %>.find(params[:id]) - @<%= file_name %>.destroy + @<%= file_name %> = <%= orm_class.find(class_name, "params[:id]") %> + @<%= orm_instance.destroy %> respond_to do |format| format.html { redirect_to(<%= table_name %>_url) } diff --git a/railties/lib/generators/rails/session_migration/USAGE b/railties/lib/generators/rails/session_migration/USAGE new file mode 100644 index 0000000000..e106f6ecc8 --- /dev/null +++ b/railties/lib/generators/rails/session_migration/USAGE @@ -0,0 +1,8 @@ +Description: + Creates a migration to add the sessions table used by the ORM session store. + Pass the migration name, either CamelCased or under_scored, as an argument. + + Before invoking this generator, be sure that your ORM supports session stores. + +Example: + `./script/generate session_migration CreateSessionTable` diff --git a/railties/lib/generators/rails/session_migration/session_migration_generator.rb b/railties/lib/generators/rails/session_migration/session_migration_generator.rb new file mode 100644 index 0000000000..258cc5b4a0 --- /dev/null +++ b/railties/lib/generators/rails/session_migration/session_migration_generator.rb @@ -0,0 +1,8 @@ +module Rails + module Generators + class SessionMigrationGenerator < NamedBase #metagenerator + argument :name, :type => :string, :default => "add_sessions_table" + hook_for :orm, :required => true + end + end +end diff --git a/railties/lib/generators/rails/stylesheets/USAGE b/railties/lib/generators/rails/stylesheets/USAGE new file mode 100644 index 0000000000..d6a81e51d0 --- /dev/null +++ b/railties/lib/generators/rails/stylesheets/USAGE @@ -0,0 +1,5 @@ +Description: + Copies scaffold stylesheets to public/stylesheets/. + +Examples: + `./script/generate stylesheets` diff --git a/railties/lib/generators/rails/stylesheets/stylesheets_generator.rb b/railties/lib/generators/rails/stylesheets/stylesheets_generator.rb new file mode 100644 index 0000000000..ce68443c39 --- /dev/null +++ b/railties/lib/generators/rails/stylesheets/stylesheets_generator.rb @@ -0,0 +1,9 @@ +module Rails + module Generators + class StylesheetsGenerator < Base + def copy_stylesheets_file + template "scaffold.css", "public/stylesheets/scaffold.css" if behavior == :invoke + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/style.css b/railties/lib/generators/rails/stylesheets/templates/scaffold.css index 093c20994d..093c20994d 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/templates/style.css +++ b/railties/lib/generators/rails/stylesheets/templates/scaffold.css diff --git a/railties/lib/generators/test_unit.rb b/railties/lib/generators/test_unit.rb new file mode 100644 index 0000000000..2763feb017 --- /dev/null +++ b/railties/lib/generators/test_unit.rb @@ -0,0 +1,8 @@ +require 'generators/named_base' + +module TestUnit + module Generators + class Base < Rails::Generators::NamedBase #:nodoc: + end + end +end diff --git a/railties/lib/generators/test_unit/controller/controller_generator.rb b/railties/lib/generators/test_unit/controller/controller_generator.rb new file mode 100644 index 0000000000..b57a6e794f --- /dev/null +++ b/railties/lib/generators/test_unit/controller/controller_generator.rb @@ -0,0 +1,14 @@ +require 'generators/test_unit' + +module TestUnit + module Generators + class ControllerGenerator < Base + check_class_collision :suffix => "ControllerTest" + + def create_test_files + template 'functional_test.rb', + File.join('test/functional', class_path, "#{file_name}_controller_test.rb") + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb b/railties/lib/generators/test_unit/controller/templates/functional_test.rb index 62fa5d86fd..62fa5d86fd 100644 --- a/railties/lib/rails_generator/generators/components/controller/templates/functional_test.rb +++ b/railties/lib/generators/test_unit/controller/templates/functional_test.rb diff --git a/railties/lib/generators/test_unit/helper/helper_generator.rb b/railties/lib/generators/test_unit/helper/helper_generator.rb new file mode 100644 index 0000000000..9ecfaa45ab --- /dev/null +++ b/railties/lib/generators/test_unit/helper/helper_generator.rb @@ -0,0 +1,13 @@ +require 'generators/test_unit' + +module TestUnit + module Generators + class HelperGenerator < Base + check_class_collision :suffix => "HelperTest" + + def create_helper_files + template 'helper_test.rb', File.join('test/unit/helpers', class_path, "#{file_name}_helper_test.rb") + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb b/railties/lib/generators/test_unit/helper/templates/helper_test.rb index 591e40900e..591e40900e 100644 --- a/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb +++ b/railties/lib/generators/test_unit/helper/templates/helper_test.rb diff --git a/railties/lib/generators/test_unit/integration/integration_generator.rb b/railties/lib/generators/test_unit/integration/integration_generator.rb new file mode 100644 index 0000000000..d9d9b3bf1d --- /dev/null +++ b/railties/lib/generators/test_unit/integration/integration_generator.rb @@ -0,0 +1,13 @@ +require 'generators/test_unit' + +module TestUnit + module Generators + class IntegrationGenerator < Base + check_class_collision :suffix => "Test" + + def create_test_files + template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb") + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb b/railties/lib/generators/test_unit/integration/templates/integration_test.rb index 2c57158b1c..2c57158b1c 100644 --- a/railties/lib/rails_generator/generators/components/integration_test/templates/integration_test.rb +++ b/railties/lib/generators/test_unit/integration/templates/integration_test.rb diff --git a/railties/lib/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/generators/test_unit/mailer/mailer_generator.rb new file mode 100644 index 0000000000..ef350a6224 --- /dev/null +++ b/railties/lib/generators/test_unit/mailer/mailer_generator.rb @@ -0,0 +1,21 @@ +require 'generators/test_unit' + +module TestUnit + module Generators + class MailerGenerator < Base + argument :actions, :type => :array, :default => [], :banner => "method method" + check_class_collision :suffix => "Test" + + def create_test_files + template "unit_test.rb", File.join('test/unit', class_path, "#{file_name}_test.rb") + end + + def create_fixtures_files + actions.each do |action| + @action, @path = action, File.join(file_path, action) + template "fixture", File.join("test/fixtures", @path) + end + end + end + end +end diff --git a/railties/lib/generators/test_unit/mailer/templates/fixture b/railties/lib/generators/test_unit/mailer/templates/fixture new file mode 100644 index 0000000000..fcce7bd805 --- /dev/null +++ b/railties/lib/generators/test_unit/mailer/templates/fixture @@ -0,0 +1,3 @@ +<%= class_name %>#<%= @action %> + +Find me in app/views/<%= @path %> diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb b/railties/lib/generators/test_unit/mailer/templates/unit_test.rb index 4de94076e9..4de94076e9 100644 --- a/railties/lib/rails_generator/generators/components/mailer/templates/unit_test.rb +++ b/railties/lib/generators/test_unit/mailer/templates/unit_test.rb diff --git a/railties/lib/generators/test_unit/model/model_generator.rb b/railties/lib/generators/test_unit/model/model_generator.rb new file mode 100644 index 0000000000..469306e6c5 --- /dev/null +++ b/railties/lib/generators/test_unit/model/model_generator.rb @@ -0,0 +1,24 @@ +require 'generators/test_unit' + +module TestUnit + module Generators + class ModelGenerator < Base + argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + class_option :fixture, :type => :boolean + + check_class_collision :suffix => "Test" + + def create_test_file + template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb") + end + + hook_for :fixture_replacement + + def create_fixture_file + if options[:fixture] && options[:fixture_replacement].nil? + template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml") + end + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/model/templates/fixtures.yml b/railties/lib/generators/test_unit/model/templates/fixtures.yml index c21035113e..c21035113e 100644 --- a/railties/lib/rails_generator/generators/components/model/templates/fixtures.yml +++ b/railties/lib/generators/test_unit/model/templates/fixtures.yml diff --git a/railties/lib/rails_generator/generators/components/model/templates/unit_test.rb b/railties/lib/generators/test_unit/model/templates/unit_test.rb index 3e0bc29d3a..3e0bc29d3a 100644 --- a/railties/lib/rails_generator/generators/components/model/templates/unit_test.rb +++ b/railties/lib/generators/test_unit/model/templates/unit_test.rb diff --git a/railties/lib/generators/test_unit/observer/observer_generator.rb b/railties/lib/generators/test_unit/observer/observer_generator.rb new file mode 100644 index 0000000000..14181f4e49 --- /dev/null +++ b/railties/lib/generators/test_unit/observer/observer_generator.rb @@ -0,0 +1,13 @@ +require 'generators/test_unit' + +module TestUnit + module Generators + class ObserverGenerator < Base + check_class_collision :suffix => "ObserverTest" + + def create_test_files + template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_observer_test.rb") + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb b/railties/lib/generators/test_unit/observer/templates/unit_test.rb index 03f6d5666e..03f6d5666e 100644 --- a/railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb +++ b/railties/lib/generators/test_unit/observer/templates/unit_test.rb diff --git a/railties/lib/generators/test_unit/performance/performance_generator.rb b/railties/lib/generators/test_unit/performance/performance_generator.rb new file mode 100644 index 0000000000..0d9c646b26 --- /dev/null +++ b/railties/lib/generators/test_unit/performance/performance_generator.rb @@ -0,0 +1,13 @@ +require 'generators/test_unit' + +module TestUnit + module Generators + class PerformanceGenerator < Base + check_class_collision :suffix => "Test" + + def create_test_files + template 'performance_test.rb', File.join('test/performance', class_path, "#{file_name}_test.rb") + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb b/railties/lib/generators/test_unit/performance/templates/performance_test.rb index 27c91b0fca..27c91b0fca 100644 --- a/railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb +++ b/railties/lib/generators/test_unit/performance/templates/performance_test.rb diff --git a/railties/lib/generators/test_unit/plugin/plugin_generator.rb b/railties/lib/generators/test_unit/plugin/plugin_generator.rb new file mode 100644 index 0000000000..05adf58c4f --- /dev/null +++ b/railties/lib/generators/test_unit/plugin/plugin_generator.rb @@ -0,0 +1,13 @@ +require 'generators/test_unit' + +module TestUnit + module Generators + class PluginGenerator < Base + check_class_collision :suffix => "Test" + + def create_test_files + directory '.', 'test' + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/model_subclass/templates/unit_test.rb b/railties/lib/generators/test_unit/plugin/templates/%file_name%_test.rb.tt index 3e0bc29d3a..3e0bc29d3a 100644 --- a/railties/lib/rails_generator/generators/components/model_subclass/templates/unit_test.rb +++ b/railties/lib/generators/test_unit/plugin/templates/%file_name%_test.rb.tt diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb b/railties/lib/generators/test_unit/plugin/templates/test_helper.rb index cf148b8b47..cf148b8b47 100644 --- a/railties/lib/rails_generator/generators/components/plugin/templates/test_helper.rb +++ b/railties/lib/generators/test_unit/plugin/templates/test_helper.rb diff --git a/railties/lib/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/generators/test_unit/scaffold/scaffold_generator.rb new file mode 100644 index 0000000000..78fcea1e9c --- /dev/null +++ b/railties/lib/generators/test_unit/scaffold/scaffold_generator.rb @@ -0,0 +1,17 @@ +require 'generators/test_unit' + +module TestUnit + module Generators + class ScaffoldGenerator < Base + include Rails::Generators::ScaffoldBase + + class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller" + check_class_collision :suffix => "ControllerTest" + + def create_test_files + template 'functional_test.rb', + File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb") + end + end + end +end diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb b/railties/lib/generators/test_unit/scaffold/templates/functional_test.rb index cd2fc578bf..e4bf4035da 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/templates/functional_test.rb +++ b/railties/lib/generators/test_unit/scaffold/templates/functional_test.rb @@ -1,11 +1,13 @@ require 'test_helper' class <%= controller_class_name %>ControllerTest < ActionController::TestCase +<% unless options[:singleton] -%> test "should get index" do get :index assert_response :success assert_not_nil assigns(:<%= table_name %>) end +<% end -%> test "should get new" do get :new diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index f0fb78c8f4..bb04dfa370 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -568,4 +568,13 @@ Run `rake gems:install` to install the missing gems. ActiveSupport::Dependencies.unhook! end end + + # Configure generators if they were already loaded + Initializer.default.add :initialize_generators do + if defined?(Rails::Generators) + Rails::Generators.no_color! unless config.generators.colorize_logging + Rails::Generators.aliases.deep_merge! config.generators.aliases + Rails::Generators.options.deep_merge! config.generators.options + end + end end diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 1a2f217d20..fe3cb67d3a 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -249,5 +249,51 @@ module Rails def reload_plugins? @reload_plugins end + + # Holds generators configuration: + # + # config.generators do |g| + # g.orm :datamapper, :migration => true + # g.template_engine :haml + # g.test_framework :rspec + # end + # + # If you want to disable color in console, do: + # + # config.generators.colorize_logging = false + # + def generators + @generators ||= Generators.new + if block_given? + yield @generators + else + @generators + end + end + + class Generators #:nodoc: + attr_accessor :aliases, :options, :colorize_logging + + def initialize + @aliases = Hash.new { |h,k| h[k] = {} } + @options = Hash.new { |h,k| h[k] = {} } + @colorize_logging = true + end + + def method_missing(method, *args) + method = method.to_s.sub(/=$/, '').to_sym + namespace = args.first.is_a?(Symbol) ? args.shift : nil + configuration = args.first.is_a?(Hash) ? args.shift : nil + + @options[:rails][method] = namespace if namespace + namespace ||= method + + if configuration + aliases = configuration.delete(:aliases) + @aliases[namespace].merge!(aliases) if aliases + @options[namespace].merge!(configuration) + end + end + end end -end
\ No newline at end of file +end diff --git a/railties/lib/rails_generator.rb b/railties/lib/rails_generator.rb deleted file mode 100644 index 85400932dd..0000000000 --- a/railties/lib/rails_generator.rb +++ /dev/null @@ -1,38 +0,0 @@ -#-- -# Copyright (c) 2004 Jeremy Kemper -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -#++ - -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift(activesupport_path) if File.directory?(activesupport_path) -require 'active_support/all' - -$:.unshift(File.dirname(__FILE__)) -require 'rails_generator/base' -require 'rails_generator/lookup' -require 'rails_generator/commands' - -Rails::Generator::Base.send(:include, Rails::Generator::Lookup) -Rails::Generator::Base.send(:include, Rails::Generator::Commands) - -# Set up a default logger for convenience. -require 'rails_generator/simple_logger' -Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(STDOUT) diff --git a/railties/lib/rails_generator/base.rb b/railties/lib/rails_generator/base.rb deleted file mode 100644 index aa7081f8da..0000000000 --- a/railties/lib/rails_generator/base.rb +++ /dev/null @@ -1,266 +0,0 @@ -require File.dirname(__FILE__) + '/options' -require File.dirname(__FILE__) + '/manifest' -require File.dirname(__FILE__) + '/spec' -require File.dirname(__FILE__) + '/generated_attribute' - -module Rails - # Rails::Generator is a code generation platform tailored for the Rails - # web application framework. Generators are easily invoked within Rails - # applications to add and remove components such as models and controllers. - # New generators are easy to create and may be distributed as RubyGems, - # tarballs, or Rails plugins for inclusion system-wide, per-user, - # or per-application. - # - # For actual examples see the rails_generator/generators directory in the - # Rails source (or the +railties+ directory if you have frozen the Rails - # source in your application). - # - # Generators may subclass other generators to provide variations that - # require little or no new logic but replace the template files. - # - # For a RubyGem, put your generator class and templates in the +lib+ - # directory. For a Rails plugin, make a +generators+ directory at the - # root of your plugin. - # - # The layout of generator files can be seen in the built-in - # +controller+ generator: - # - # generators/ - # components/ - # controller/ - # controller_generator.rb - # templates/ - # controller.rb - # functional_test.rb - # helper.rb - # view.html.erb - # - # The directory name (+controller+) matches the name of the generator file - # (controller_generator.rb) and class (ControllerGenerator). The files - # that will be copied or used as templates are stored in the +templates+ - # directory. - # - # The filenames of the templates don't matter, but choose something that - # will be self-explanatory since you will be referencing these in the - # +manifest+ method inside your generator subclass. - # - # - module Generator - class GeneratorError < StandardError; end - class UsageError < GeneratorError; end - - - # The base code generator is bare-bones. It sets up the source and - # destination paths and tells the logger whether to keep its trap shut. - # - # It's useful for copying files such as stylesheets, images, or - # javascripts. - # - # For more comprehensive template-based passive code generation with - # arguments, you'll want Rails::Generator::NamedBase. - # - # Generators create a manifest of the actions they perform then hand - # the manifest to a command which replays the actions to do the heavy - # lifting (such as checking for existing files or creating directories - # if needed). Create, destroy, and list commands are included. Since a - # single manifest may be used by any command, creating new generators is - # as simple as writing some code templates and declaring what you'd like - # to do with them. - # - # The manifest method must be implemented by subclasses, returning a - # Rails::Generator::Manifest. The +record+ method is provided as a - # convenience for manifest creation. Example: - # - # class StylesheetGenerator < Rails::Generator::Base - # def manifest - # record do |m| - # m.directory('public/stylesheets') - # m.file('application.css', 'public/stylesheets/application.css') - # end - # end - # end - # - # See Rails::Generator::Commands::Create for a list of methods available - # to the manifest. - class Base - include Options - - # Declare default options for the generator. These options - # are inherited to subclasses. - default_options :collision => :ask, :quiet => false - - # A logger instance available everywhere in the generator. - cattr_accessor :logger - - # Every generator that is dynamically looked up is tagged with a - # Spec describing where it was found. - class_inheritable_accessor :spec - - attr_reader :source_root, :destination_root, :args - - def initialize(runtime_args, runtime_options = {}) - @args = runtime_args - parse!(@args, runtime_options) - - # Derive source and destination paths. - @source_root = options[:source] || File.join(spec.path, 'templates') - if options[:destination] - @destination_root = options[:destination] - elsif defined? ::RAILS_ROOT - @destination_root = ::RAILS_ROOT - end - - # Silence the logger if requested. - logger.quiet = options[:quiet] - - # Raise usage error if help is requested. - usage if options[:help] - end - - # Generators must provide a manifest. Use the +record+ method to create - # a new manifest and record your generator's actions. - def manifest - raise NotImplementedError, "No manifest for '#{spec.name}' generator." - end - - # Return the full path from the source root for the given path. - # Example for source_root = '/source': - # source_path('some/path.rb') == '/source/some/path.rb' - # - # The given path may include a colon ':' character to indicate that - # the file belongs to another generator. This notation allows any - # generator to borrow files from another. Example: - # source_path('model:fixture.yml') = '/model/source/path/fixture.yml' - def source_path(relative_source) - # Check whether we're referring to another generator's file. - name, path = relative_source.split(':', 2) - - # If not, return the full path to our source file. - if path.nil? - File.join(source_root, name) - - # Otherwise, ask our referral for the file. - else - # FIXME: this is broken, though almost always true. Others' - # source_root are not necessarily the templates dir. - File.join(self.class.lookup(name).path, 'templates', path) - end - end - - # Return the full path from the destination root for the given path. - # Example for destination_root = '/dest': - # destination_path('some/path.rb') == '/dest/some/path.rb' - def destination_path(relative_destination) - File.join(destination_root, relative_destination) - end - - def after_generate - end - - protected - # Convenience method for generator subclasses to record a manifest. - def record - Rails::Generator::Manifest.new(self) { |m| yield m } - end - - # Override with your own usage banner. - def banner - "Usage: #{$0} #{spec.name} [options]" - end - - # Read USAGE from file in generator base path. - def usage_message - File.read(File.join(spec.path, 'USAGE')) rescue '' - end - end - - - # The base generator for named components: models, controllers, mailers, - # etc. The target name is taken as the first argument and inflected to - # singular, plural, class, file, and table forms for your convenience. - # The remaining arguments are aliased to +actions+ as an array for - # controller and mailer convenience. - # - # Several useful local variables and methods are populated in the - # +initialize+ method. See below for a list of Attributes and - # External Aliases available to both the manifest and to all templates. - # - # If no name is provided, the generator raises a usage error with content - # optionally read from the USAGE file in the generator's base path. - # - # For example, the +controller+ generator takes the first argument as - # the name of the class and subsequent arguments as the names of - # actions to be generated: - # - # ./script/generate controller Article index new create - # - # See Rails::Generator::Base for a discussion of manifests, - # Rails::Generator::Commands::Create for methods available to the manifest, - # and Rails::Generator for a general discussion of generators. - class NamedBase < Base - attr_reader :name, :class_name, :singular_name, :plural_name, :table_name - attr_reader :class_path, :file_path, :class_nesting, :class_nesting_depth - alias_method :file_name, :singular_name - alias_method :actions, :args - - def initialize(runtime_args, runtime_options = {}) - super - - # Name argument is required. - usage if runtime_args.empty? - - @args = runtime_args.dup - base_name = @args.shift - assign_names!(base_name) - end - - protected - # Override with your own usage banner. - def banner - "Usage: #{$0} #{spec.name} #{spec.name.camelize}Name [options]" - end - - def attributes - @attributes ||= @args.collect do |attribute| - Rails::Generator::GeneratedAttribute.new(*attribute.split(":")) - end - end - - - private - def assign_names!(name) - @name = name - base_name, @class_path, @file_path, @class_nesting, @class_nesting_depth = extract_modules(@name) - @class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name) - @table_name = (!defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names) ? plural_name : singular_name - @table_name.gsub! '/', '_' - if @class_nesting.empty? - @class_name = @class_name_without_nesting - else - @table_name = @class_nesting.underscore << "_" << @table_name - @class_name = "#{@class_nesting}::#{@class_name_without_nesting}" - end - end - - # Extract modules from filesystem-style or ruby-style path: - # good/fun/stuff - # Good::Fun::Stuff - # produce the same results. - def extract_modules(name) - modules = name.include?('/') ? name.split('/') : name.split('::') - name = modules.pop - path = modules.map { |m| m.underscore } - file_path = (path + [name.underscore]).join('/') - nesting = modules.map { |m| m.camelize }.join('::') - [name, path, file_path, nesting, modules.size] - end - - def inflect_names(name) - camel = name.camelize - under = camel.underscore - plural = under.pluralize - [camel, under, plural] - end - end - end -end diff --git a/railties/lib/rails_generator/commands.rb b/railties/lib/rails_generator/commands.rb deleted file mode 100644 index b684dc92be..0000000000 --- a/railties/lib/rails_generator/commands.rb +++ /dev/null @@ -1,621 +0,0 @@ -require 'delegate' -require 'optparse' -require 'fileutils' -require 'tempfile' -require 'erb' - -module Rails - module Generator - module Commands - # Here's a convenient way to get a handle on generator commands. - # Command.instance('destroy', my_generator) instantiates a Destroy - # delegate of my_generator ready to do your dirty work. - def self.instance(command, generator) - const_get(command.to_s.camelize).new(generator) - end - - # Even more convenient access to commands. Include Commands in - # the generator Base class to get a nice #command instance method - # which returns a delegate for the requested command. - def self.included(base) - base.send(:define_method, :command) do |command| - Commands.instance(command, self) - end - end - - - # Generator commands delegate Rails::Generator::Base and implement - # a standard set of actions. Their behavior is defined by the way - # they respond to these actions: Create brings life; Destroy brings - # death; List passively observes. - # - # Commands are invoked by replaying (or rewinding) the generator's - # manifest of actions. See Rails::Generator::Manifest and - # Rails::Generator::Base#manifest method that generator subclasses - # are required to override. - # - # Commands allows generators to "plug in" invocation behavior, which - # corresponds to the GoF Strategy pattern. - class Base < DelegateClass(Rails::Generator::Base) - # Replay action manifest. RewindBase subclass rewinds manifest. - def invoke! - manifest.replay(self) - after_generate - end - - def dependency(generator_name, args, runtime_options = {}) - logger.dependency(generator_name) do - self.class.new(instance(generator_name, args, full_options(runtime_options))).invoke! - end - end - - # Does nothing for all commands except Create. - def class_collisions(*class_names) - end - - # Does nothing for all commands except Create. - def readme(*args) - end - - protected - def current_migration_number - Dir.glob("#{RAILS_ROOT}/#{@migration_directory}/[0-9]*_*.rb").inject(0) do |max, file_path| - n = File.basename(file_path).split('_', 2).first.to_i - if n > max then n else max end - end - end - - def next_migration_number - current_migration_number + 1 - end - - def migration_directory(relative_path) - directory(@migration_directory = relative_path) - end - - def existing_migrations(file_name) - Dir.glob("#{@migration_directory}/[0-9]*_*.rb").grep(/[0-9]+_#{file_name}.rb$/) - end - - def migration_exists?(file_name) - not existing_migrations(file_name).empty? - end - - def next_migration_string(padding = 3) - if ActiveRecord::Base.timestamped_migrations - Time.now.utc.strftime("%Y%m%d%H%M%S") - else - "%.#{padding}d" % next_migration_number - end - end - - def gsub_file(relative_destination, regexp, *args, &block) - path = destination_path(relative_destination) - content = File.read(path).gsub(regexp, *args, &block) - File.open(path, 'wb') { |file| file.write(content) } - end - - private - # Ask the user interactively whether to force collision. - def force_file_collision?(destination, src, dst, file_options = {}, &block) - $stdout.print "overwrite #{destination}? (enter \"h\" for help) [Ynaqdh] " - case $stdin.gets.chomp - when /\Ad\z/i - Tempfile.open(File.basename(destination), File.dirname(dst)) do |temp| - temp.write render_file(src, file_options, &block) - temp.rewind - $stdout.puts `#{diff_cmd} "#{dst}" "#{temp.path}"` - end - puts "retrying" - raise 'retry diff' - when /\Aa\z/i - $stdout.puts "forcing #{spec.name}" - options[:collision] = :force - when /\Aq\z/i - $stdout.puts "aborting #{spec.name}" - raise SystemExit - when /\An\z/i then :skip - when /\Ay\z/i then :force - else - $stdout.puts <<-HELP -Y - yes, overwrite -n - no, do not overwrite -a - all, overwrite this and all others -q - quit, abort -d - diff, show the differences between the old and the new -h - help, show this help -HELP - raise 'retry' - end - rescue - retry - end - - def diff_cmd - ENV['RAILS_DIFF'] || 'diff -u' - end - - def render_template_part(template_options) - # Getting Sandbox to evaluate part template in it - part_binding = template_options[:sandbox].call.sandbox_binding - part_rel_path = template_options[:insert] - part_path = source_path(part_rel_path) - - # Render inner template within Sandbox binding - rendered_part = ERB.new(File.readlines(part_path).join, nil, '-').result(part_binding) - begin_mark = template_part_mark(template_options[:begin_mark], template_options[:mark_id]) - end_mark = template_part_mark(template_options[:end_mark], template_options[:mark_id]) - begin_mark + rendered_part + end_mark - end - - def template_part_mark(name, id) - "<!--[#{name}:#{id}]-->\n" - end - end - - # Base class for commands which handle generator actions in reverse, such as Destroy. - class RewindBase < Base - # Rewind action manifest. - def invoke! - manifest.rewind(self) - end - end - - - # Create is the premier generator command. It copies files, creates - # directories, renders templates, and more. - class Create < Base - - # Check whether the given class names are already taken by - # Ruby or Rails. In the future, expand to check other namespaces - # such as the rest of the user's app. - def class_collisions(*class_names) - path = class_names.shift - class_names.flatten.each do |class_name| - # Convert to string to allow symbol arguments. - class_name = class_name.to_s - - # Skip empty strings. - next if class_name.strip.empty? - - # Split the class from its module nesting. - nesting = class_name.split('::') - name = nesting.pop - - # Hack to limit const_defined? to non-inherited on 1.9. - extra = [] - extra << false unless Object.method(:const_defined?).arity == 1 - - # Extract the last Module in the nesting. - last = nesting.inject(Object) { |last, nest| - break unless last.const_defined?(nest, *extra) - last.const_get(nest) - } - - # If the last Module exists, check whether the given - # class exists and raise a collision if so. - if last and last.const_defined?(name.camelize, *extra) - raise_class_collision(class_name) - end - end - end - - # Copy a file from source to destination with collision checking. - # - # The file_options hash accepts :chmod and :shebang and :collision options. - # :chmod sets the permissions of the destination file: - # file 'config/empty.log', 'log/test.log', :chmod => 0664 - # :shebang sets the #!/usr/bin/ruby line for scripts - # file 'bin/generate.rb', 'script/generate', :chmod => 0755, :shebang => '/usr/bin/env ruby' - # :collision sets the collision option only for the destination file: - # file 'settings/server.yml', 'config/server.yml', :collision => :skip - # - # Collisions are handled by checking whether the destination file - # exists and either skipping the file, forcing overwrite, or asking - # the user what to do. - def file(relative_source, relative_destination, file_options = {}, &block) - # Determine full paths for source and destination files. - source = source_path(relative_source) - destination = destination_path(relative_destination) - destination_exists = File.exist?(destination) - - # If source and destination are identical then we're done. - if destination_exists and identical?(source, destination, &block) - return logger.identical(relative_destination) - end - - # Check for and resolve file collisions. - if destination_exists - - # Make a choice whether to overwrite the file. :force and - # :skip already have their mind made up, but give :ask a shot. - choice = case (file_options[:collision] || options[:collision]).to_sym #|| :ask - when :ask then force_file_collision?(relative_destination, source, destination, file_options, &block) - when :force then :force - when :skip then :skip - else raise "Invalid collision option: #{options[:collision].inspect}" - end - - # Take action based on our choice. Bail out if we chose to - # skip the file; otherwise, log our transgression and continue. - case choice - when :force then logger.force(relative_destination) - when :skip then return(logger.skip(relative_destination)) - else raise "Invalid collision choice: #{choice}.inspect" - end - - # File doesn't exist so log its unbesmirched creation. - else - logger.create relative_destination - end - - # If we're pretending, back off now. - return if options[:pretend] - - # Write destination file with optional shebang. Yield for content - # if block given so templaters may render the source file. If a - # shebang is requested, replace the existing shebang or insert a - # new one. - File.open(destination, 'wb') do |dest| - dest.write render_file(source, file_options, &block) - end - - # Optionally change permissions. - if file_options[:chmod] - FileUtils.chmod(file_options[:chmod], destination) - end - - # Optionally add file to subversion or git - system("svn add #{destination}") if options[:svn] - system("git add -v #{relative_destination}") if options[:git] - end - - # Checks if the source and the destination file are identical. If - # passed a block then the source file is a template that needs to first - # be evaluated before being compared to the destination. - def identical?(source, destination, &block) - return false if File.directory? destination - source = block_given? ? File.open(source) {|sf| yield(sf)} : IO.read(source) - destination = IO.read(destination) - source == destination - end - - # Generate a file for a Rails application using an ERuby template. - # Looks up and evaluates a template by name and writes the result. - # - # 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. - # - # A hash of template options may be passed as the last argument. - # The options accepted by the file are accepted as well as :assigns, - # a hash of variable bindings. Example: - # template 'foo', 'bar', :assigns => { :action => 'view' } - # - # Template is implemented in terms of file. It calls file with a - # block which takes a file handle and returns its rendered contents. - def template(relative_source, relative_destination, template_options = {}) - file(relative_source, relative_destination, template_options) do |file| - # Evaluate any assignments in a temporary, throwaway binding. - vars = template_options[:assigns] || {} - b = template_options[:binding] || binding - vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b } - - # Render the source file with the temporary binding. - ERB.new(file.read, nil, '-').result(b) - end - end - - def complex_template(relative_source, relative_destination, template_options = {}) - options = template_options.dup - options[:assigns] ||= {} - options[:assigns]['template_for_inclusion'] = render_template_part(template_options) - template(relative_source, relative_destination, options) - end - - # Create a directory including any missing parent directories. - # Always skips directories which exist. - def directory(relative_path) - path = destination_path(relative_path) - if File.exist?(path) - logger.exists relative_path - else - logger.create relative_path - unless options[:pretend] - FileUtils.mkdir_p(path) - # git doesn't require adding the paths, adding the files later will - # automatically do a path add. - - # Subversion doesn't do path adds, so we need to add - # each directory individually. - # So stack up the directory tree and add the paths to - # subversion in order without recursion. - if options[:svn] - stack = [relative_path] - until File.dirname(stack.last) == stack.last # dirname('.') == '.' - stack.push File.dirname(stack.last) - end - stack.reverse_each do |rel_path| - svn_path = destination_path(rel_path) - system("svn add -N #{svn_path}") unless File.directory?(File.join(svn_path, '.svn')) - end - end - end - end - end - - # Display a README. - def readme(*relative_sources) - relative_sources.flatten.each do |relative_source| - logger.readme relative_source - puts File.read(source_path(relative_source)) unless options[:pretend] - end - end - - # When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template. - def migration_template(relative_source, relative_destination, template_options = {}) - migration_directory relative_destination - migration_file_name = template_options[:migration_file_name] || file_name - raise "Another migration is already named #{migration_file_name}: #{existing_migrations(migration_file_name).first}" if migration_exists?(migration_file_name) - template(relative_source, "#{relative_destination}/#{next_migration_string}_#{migration_file_name}.rb", template_options) - end - - def route_resources(*resources) - resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') - sentinel = 'ActionController::Routing::Routes.draw do |map|' - - logger.route "map.resources #{resource_list}" - unless options[:pretend] - gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| - "#{match}\n map.resources #{resource_list}\n" - end - end - end - - private - def render_file(path, options = {}) - File.open(path, 'rb') do |file| - if block_given? - yield file - else - content = '' - if shebang = options[:shebang] - content << "#!#{shebang}\n" - if line = file.gets - content << "line\n" if line !~ /^#!/ - end - end - content << file.read - end - end - end - - # Raise a usage error with an informative WordNet suggestion. - # Thanks to Florian Gross (flgr). - def raise_class_collision(class_name) - message = <<end_message - The name '#{class_name}' is either already used in your application or reserved by Ruby on Rails. - Please choose an alternative and run this generator again. -end_message - if suggest = find_synonyms(class_name) - if suggest.any? - message << "\n Suggestions: \n\n" - message << suggest.join("\n") - end - end - raise UsageError, message - end - - SYNONYM_LOOKUP_URI = "http://wordnet.princeton.edu/perl/webwn?s=%s" - - # Look up synonyms on WordNet. Thanks to Florian Gross (flgr). - def find_synonyms(word) - require 'open-uri' - require 'timeout' - timeout(5) do - open(SYNONYM_LOOKUP_URI % word) do |stream| - # Grab words linked to dictionary entries as possible synonyms - data = stream.read.gsub(" ", " ").scan(/<a href="webwn.*?">([\w ]*?)<\/a>/s).uniq - end - end - rescue Exception - return nil - end - end - - - # Undo the actions performed by a generator. Rewind the action - # manifest and attempt to completely erase the results of each action. - class Destroy < RewindBase - # Remove a file if it exists and is a file. - def file(relative_source, relative_destination, file_options = {}) - destination = destination_path(relative_destination) - if File.exist?(destination) - logger.rm relative_destination - unless options[:pretend] - if options[:svn] - # If the file has been marked to be added - # but has not yet been checked in, revert and delete - if options[:svn][relative_destination] - system("svn revert #{destination}") - FileUtils.rm(destination) - else - # If the directory is not in the status list, it - # has no modifications so we can simply remove it - system("svn rm #{destination}") - end - elsif options[:git] - if options[:git][:new][relative_destination] - # file has been added, but not committed - system("git reset HEAD #{relative_destination}") - FileUtils.rm(destination) - elsif options[:git][:modified][relative_destination] - # file is committed and modified - system("git rm -f #{relative_destination}") - else - # If the directory is not in the status list, it - # has no modifications so we can simply remove it - system("git rm #{relative_destination}") - end - else - FileUtils.rm(destination) - end - end - else - logger.missing relative_destination - return - end - end - - # Templates are deleted just like files and the actions take the - # same parameters, so simply alias the file method. - alias_method :template, :file - - # Remove each directory in the given path from right to left. - # Remove each subdirectory if it exists and is a directory. - def directory(relative_path) - parts = relative_path.split('/') - until parts.empty? - partial = File.join(parts) - path = destination_path(partial) - if File.exist?(path) - if Dir[File.join(path, '*')].empty? - logger.rmdir partial - unless options[:pretend] - if options[:svn] - # If the directory has been marked to be added - # but has not yet been checked in, revert and delete - if options[:svn][relative_path] - system("svn revert #{path}") - FileUtils.rmdir(path) - else - # If the directory is not in the status list, it - # has no modifications so we can simply remove it - system("svn rm #{path}") - end - # I don't think git needs to remove directories?.. - # or maybe they have special consideration... - else - FileUtils.rmdir(path) - end - end - else - logger.notempty partial - end - else - logger.missing partial - end - parts.pop - end - end - - def complex_template(*args) - # nothing should be done here - end - - # When deleting a migration, it knows to delete every file named "[0-9]*_#{file_name}". - def migration_template(relative_source, relative_destination, template_options = {}) - migration_directory relative_destination - - migration_file_name = template_options[:migration_file_name] || file_name - unless migration_exists?(migration_file_name) - puts "There is no migration named #{migration_file_name}" - return - end - - - existing_migrations(migration_file_name).each do |file_path| - file(relative_source, file_path, template_options) - end - end - - def route_resources(*resources) - resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') - look_for = "\n map.resources #{resource_list}\n" - logger.route "map.resources #{resource_list}" - gsub_file 'config/routes.rb', /(#{look_for})/mi, '' - end - end - - - # List a generator's action manifest. - class List < Base - def dependency(generator_name, args, options = {}) - logger.dependency "#{generator_name}(#{args.join(', ')}, #{options.inspect})" - end - - def class_collisions(*class_names) - logger.class_collisions class_names.join(', ') - end - - def file(relative_source, relative_destination, options = {}) - logger.file relative_destination - end - - def template(relative_source, relative_destination, options = {}) - logger.template relative_destination - end - - def complex_template(relative_source, relative_destination, options = {}) - logger.template "#{options[:insert]} inside #{relative_destination}" - end - - def directory(relative_path) - logger.directory "#{destination_path(relative_path)}/" - end - - def readme(*args) - logger.readme args.join(', ') - end - - def migration_template(relative_source, relative_destination, options = {}) - migration_directory relative_destination - logger.migration_template file_name - end - - def route_resources(*resources) - resource_list = resources.map { |r| r.to_sym.inspect }.join(', ') - logger.route "map.resources #{resource_list}" - end - end - - # Update generator's action manifest. - class Update < Create - def file(relative_source, relative_destination, options = {}) - # logger.file relative_destination - end - - def template(relative_source, relative_destination, options = {}) - # logger.template relative_destination - end - - def complex_template(relative_source, relative_destination, template_options = {}) - - begin - dest_file = destination_path(relative_destination) - source_to_update = File.readlines(dest_file).join - rescue Errno::ENOENT - logger.missing relative_destination - return - end - - logger.refreshing "#{template_options[:insert].gsub(/\.erb/,'')} inside #{relative_destination}" - - begin_mark = Regexp.quote(template_part_mark(template_options[:begin_mark], template_options[:mark_id])) - end_mark = Regexp.quote(template_part_mark(template_options[:end_mark], template_options[:mark_id])) - - # Refreshing inner part of the template with freshly rendered part. - rendered_part = render_template_part(template_options) - source_to_update.gsub!(/#{begin_mark}.*?#{end_mark}/m, rendered_part) - - File.open(dest_file, 'w') { |file| file.write(source_to_update) } - end - - def directory(relative_path) - # logger.directory "#{destination_path(relative_path)}/" - end - end - - end - end -end diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb deleted file mode 100644 index c8c2239f34..0000000000 --- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb +++ /dev/null @@ -1,263 +0,0 @@ -require 'rbconfig' -require File.dirname(__FILE__) + '/template_runner' -require 'digest/md5' -require 'active_support/secure_random' - -class AppGenerator < Rails::Generator::Base - DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) - - DATABASES = %w( mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db ) - DEFAULT_DATABASE = 'sqlite3' - - mandatory_options :source => "#{File.dirname(__FILE__)}/../../../../.." - default_options :db => (ENV["RAILS_DEFAULT_DATABASE"] || DEFAULT_DATABASE), - :shebang => DEFAULT_SHEBANG, :with_dispatchers => false, :freeze => false - - - def initialize(runtime_args, runtime_options = {}) - super - - usage if args.empty? - usage("Databases supported for preconfiguration are: #{DATABASES.join(", ")}") if (options[:db] && !DATABASES.include?(options[:db])) - - @destination_root = args.shift - @app_name = File.basename(File.expand_path(@destination_root)) - end - - def manifest - record do |m| - create_directories(m) - create_root_files(m) - create_app_files(m) - create_config_files(m) - create_script_files(m) - create_test_files(m) - create_public_files(m) - create_documentation_file(m) - create_log_files(m) - end - end - - def after_generate - if options[:template] - Rails::TemplateRunner.new(options[:template], @destination_root) - end - end - - protected - def banner - "Usage: #{$0} /path/to/your/app [options]" - end - - def add_options!(opt) - opt.separator '' - opt.separator 'Options:' - opt.on("-r", "--ruby=path", String, - "Path to the Ruby binary of your choice (otherwise scripts use env, dispatchers current path).", - "Default: #{DEFAULT_SHEBANG}") { |v| options[:shebang] = v } - - opt.on("-d", "--database=name", String, - "Preconfigure for selected database (options: #{DATABASES.join('/')}).", - "Default: #{DEFAULT_DATABASE}") { |v| options[:db] = v } - - opt.on("-D", "--with-dispatchers", - "Add CGI/FastCGI/mod_ruby dispatches code to generated application skeleton", - "Default: false") { |v| options[:with_dispatchers] = v } - - opt.on("-f", "--freeze", - "Freeze Rails in vendor/rails from the gems generating the skeleton", - "Default: false") { |v| options[:freeze] = v } - - opt.on("-m", "--template=path", String, - "Use an application template that lives at path (can be a filesystem path or URL).", - "Default: (none)") { |v| options[:template] = v } - - end - - - private - def create_directories(m) - m.directory '' - - # Intermediate directories are automatically created so don't sweat their absence here. - %w( - 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/performance - test/fixtures - test/functional - test/integration - test/performance - test/unit - vendor - vendor/plugins - tmp/sessions - tmp/sockets - tmp/cache - tmp/pids - ).each { |path| m.directory(path) } - end - - def create_root_files(m) - m.file "fresh_rakefile", "Rakefile" - m.file "README", "README" - end - - def create_app_files(m) - m.file "helpers/application_controller.rb", "app/controllers/application_controller.rb" - m.file "helpers/application_helper.rb", "app/helpers/application_helper.rb" - end - - def create_config_files(m) - create_database_configuration_file(m) - create_routes_file(m) - create_locale_file(m) - create_seeds_file(m) - create_initializer_files(m) - create_environment_files(m) - end - - def create_documentation_file(m) - m.file "doc/README_FOR_APP", "doc/README_FOR_APP" - end - - def create_log_files(m) - %w( server production development test ).each do |file| - m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666 - end - end - - def create_public_files(m) - create_dispatch_files(m) - create_error_files(m) - create_welcome_file(m) - create_browser_convention_files(m) - create_rails_image(m) - create_javascript_files(m) - end - - def create_script_files(m) - %w( - about console dbconsole destroy generate runner server plugin - performance/benchmarker performance/profiler - ).each do |file| - m.file "bin/#{file}", "script/#{file}", { - :chmod => 0755, - :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] - } - end - end - - def create_test_files(m) - m.file "helpers/test_helper.rb", "test/test_helper.rb" - m.file "helpers/performance_test.rb", "test/performance/browsing_test.rb" - end - - - def create_database_configuration_file(m) - m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => { - :app_name => @app_name, - :socket => options[:db] == "mysql" ? mysql_socket_location : nil } - end - - def create_routes_file(m) - m.file "configs/routes.rb", "config/routes.rb" - end - - def create_seeds_file(m) - m.file "configs/seeds.rb", "db/seeds.rb" - end - - def create_initializer_files(m) - %w( - backtrace_silencers - inflections - mime_types - new_rails_defaults - ).each do |initializer| - m.file "configs/initializers/#{initializer}.rb", "config/initializers/#{initializer}.rb" - end - - m.template "configs/initializers/session_store.rb", "config/initializers/session_store.rb", - :assigns => { :app_name => @app_name, :app_secret => ActiveSupport::SecureRandom.hex(64) } - end - - def create_locale_file(m) - m.file "configs/locales/en.yml", "config/locales/en.yml" - end - - def create_environment_files(m) - m.template "environments/environment.rb", "config/environment.rb", - :assigns => { :freeze => options[:freeze] } - - m.file "environments/boot.rb", "config/boot.rb" - m.file "environments/production.rb", "config/environments/production.rb" - m.file "environments/development.rb", "config/environments/development.rb" - m.file "environments/test.rb", "config/environments/test.rb" - end - - - def create_dispatch_files(m) - if options[:with_dispatchers] - dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] } - - m.file "dispatches/config.ru", "config.ru" - m.file "dispatches/dispatch.rb", "public/dispatch.rb", dispatcher_options - m.file "dispatches/dispatch.rb", "public/dispatch.cgi", dispatcher_options - m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options - end - end - - def create_error_files(m) - %w( 404 422 500 ).each do |file| - m.file "html/#{file}.html", "public/#{file}.html" - end - end - - def create_welcome_file(m) - m.file 'html/index.html', 'public/index.html' - end - - def create_browser_convention_files(m) - m.file "html/favicon.ico", "public/favicon.ico" - m.file "html/robots.txt", "public/robots.txt" - end - - def create_rails_image(m) - m.file "html/images/rails.png", "public/images/rails.png" - end - - def create_javascript_files(m) - %w( prototype effects dragdrop controls application ).each do |javascript| - m.file "html/javascripts/#{javascript}.js", "public/javascripts/#{javascript}.js" - end - end - - - def mysql_socket_location - [ - "/tmp/mysql.sock", # default - "/var/run/mysqld/mysqld.sock", # debian/gentoo - "/var/tmp/mysql.sock", # freebsd - "/var/lib/mysql/mysql.sock", # fedora - "/opt/local/lib/mysql/mysql.sock", # fedora - "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql - "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 - "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 - "/opt/lampp/var/mysql/mysql.sock" # xampp for linux - ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ - end -end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/scm/git.rb b/railties/lib/rails_generator/generators/applications/app/scm/git.rb deleted file mode 100644 index 445de6ab42..0000000000 --- a/railties/lib/rails_generator/generators/applications/app/scm/git.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Rails - class Git < Scm - def self.clone(repos, branch=nil) - `git clone #{repos}` - - if branch - `cd #{repos.split('/').last}/` - `git checkout #{branch}` - end - end - - def self.run(command) - `git #{command}` - end - end -end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/scm/scm.rb b/railties/lib/rails_generator/generators/applications/app/scm/scm.rb deleted file mode 100644 index f6c08cad39..0000000000 --- a/railties/lib/rails_generator/generators/applications/app/scm/scm.rb +++ /dev/null @@ -1,8 +0,0 @@ -module Rails - class Scm - private - def self.hash_to_parameters(hash) - hash.collect { |key, value| "--#{key} #{(value.kind_of?(String) ? value : "")}"}.join(" ") - end - end -end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/scm/svn.rb b/railties/lib/rails_generator/generators/applications/app/scm/svn.rb deleted file mode 100644 index 22b5966d25..0000000000 --- a/railties/lib/rails_generator/generators/applications/app/scm/svn.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Rails - class Svn < Scm - def self.checkout(repos, branch = nil) - `svn checkout #{repos}/#{branch || "trunk"}` - end - end -end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/template_runner.rb b/railties/lib/rails_generator/generators/applications/app/template_runner.rb deleted file mode 100644 index 0e24d11950..0000000000 --- a/railties/lib/rails_generator/generators/applications/app/template_runner.rb +++ /dev/null @@ -1,401 +0,0 @@ -require File.dirname(__FILE__) + '/scm/scm' -require File.dirname(__FILE__) + '/scm/git' -require File.dirname(__FILE__) + '/scm/svn' - -require 'open-uri' -require 'fileutils' - -module Rails - class TemplateRunner - attr_reader :root - attr_writer :logger - - def initialize(template, root = '') # :nodoc: - @root = File.expand_path(File.directory?(root) ? root : File.join(Dir.pwd, root)) - - log 'applying', "template: #{template}" - - load_template(template) - - log 'applied', "#{template}" - end - - def load_template(template) - begin - code = open(template).read - in_root { self.instance_eval(code) } - rescue LoadError, Errno::ENOENT => e - raise "The template [#{template}] could not be loaded. Error: #{e}" - end - end - - # Create a new file in the Rails project folder. Specify the - # relative path from RAILS_ROOT. Data is the return value of a block - # or a data string. - # - # ==== Examples - # - # file("lib/fun_party.rb") do - # hostname = ask("What is the virtual hostname I should use?") - # "vhost.name = #{hostname}" - # end - # - # file("config/apach.conf", "your apache config") - # - def file(filename, data = nil, log_action = true, &block) - log 'file', filename if log_action - dir, file = [File.dirname(filename), File.basename(filename)] - - inside(dir) do - File.open(file, "w") do |f| - if block_given? - f.write(block.call) - else - f.write(data) - end - end - end - end - - # Install a plugin. You must provide either a Subversion url or Git url. - # For a Git-hosted plugin, you can specify if it should be added as a submodule instead of cloned. - # - # ==== Examples - # - # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git' - # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git', :submodule => true - # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk' - # - def plugin(name, options) - log 'plugin', name - - if options[:git] && options[:submodule] - in_root do - Git.run("submodule add #{options[:git]} vendor/plugins/#{name}") - end - elsif options[:git] || options[:svn] - in_root do - run_ruby_script("script/plugin install #{options[:svn] || options[:git]}", false) - end - else - log "! no git or svn provided for #{name}. skipping..." - end - end - - # Adds an entry into config/environment.rb for the supplied gem : - def gem(name, options = {}) - log 'gem', name - env = options.delete(:env) - - gems_code = "config.gem '#{name}'" - - if options.any? - opts = options.inject([]) {|result, h| result << [":#{h[0]} => #{h[1].inspect.gsub('"',"'")}"] }.sort.join(", ") - gems_code << ", #{opts}" - end - - environment gems_code, :env => env - end - - # Adds a line inside the Initializer block for config/environment.rb. Used by #gem - # If options :env is specified, the line is appended to the corresponding - # file in config/environments/#{env}.rb - def environment(data = nil, options = {}, &block) - sentinel = 'Rails::Initializer.run do |config|' - - data = block.call if !data && block_given? - - in_root do - if options[:env].nil? - gsub_file 'config/environment.rb', /(#{Regexp.escape(sentinel)})/mi do |match| - "#{match}\n " << data - end - else - Array.wrap(options[:env]).each do|env| - append_file "config/environments/#{env}.rb", "\n#{data}" - end - end - end - end - - # Run a command in git. - # - # ==== Examples - # - # git :init - # git :add => "this.file that.rb" - # git :add => "onefile.rb", :rm => "badfile.cxx" - # - def git(command = {}) - in_root do - if command.is_a?(Symbol) - log 'running', "git #{command}" - Git.run(command.to_s) - else - command.each do |command, options| - log 'running', "git #{command} #{options}" - Git.run("#{command} #{options}") - end - end - end - end - - # Create a new file in the vendor/ directory. Code can be specified - # in a block or a data string can be given. - # - # ==== Examples - # - # vendor("sekrit.rb") do - # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--" - # "salt = '#{sekrit_salt}'" - # end - # - # vendor("foreign.rb", "# Foreign code is fun") - # - def vendor(filename, data = nil, &block) - log 'vendoring', filename - file("vendor/#{filename}", data, false, &block) - end - - # Create a new file in the lib/ directory. Code can be specified - # in a block or a data string can be given. - # - # ==== Examples - # - # lib("crypto.rb") do - # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'" - # end - # - # lib("foreign.rb", "# Foreign code is fun") - # - def lib(filename, data = nil, &block) - log 'lib', filename - file("lib/#{filename}", data, false, &block) - end - - # Create a new Rakefile with the provided code (either in a block or a string). - # - # ==== Examples - # - # rakefile("bootstrap.rake") do - # project = ask("What is the UNIX name of your project?") - # - # <<-TASK - # namespace :#{project} do - # task :bootstrap do - # puts "i like boots!" - # end - # end - # TASK - # end - # - # rakefile("seed.rake", "puts 'im plantin ur seedz'") - # - def rakefile(filename, data = nil, &block) - log 'rakefile', filename - file("lib/tasks/#{filename}", data, false, &block) - end - - # Create a new initializer with the provided code (either in a block or a string). - # - # ==== Examples - # - # initializer("globals.rb") do - # data = "" - # - # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do - # data << "#{const} = :entp" - # end - # - # data - # end - # - # initializer("api.rb", "API_KEY = '123456'") - # - def initializer(filename, data = nil, &block) - log 'initializer', filename - file("config/initializers/#{filename}", data, false, &block) - end - - # Generate something using a generator from Rails or a plugin. - # The second parameter is the argument string that is passed to - # the generator or an Array that is joined. - # - # ==== Example - # - # generate(:authenticated, "user session") - # - def generate(what, *args) - log 'generating', what - argument = args.map {|arg| arg.to_s }.flatten.join(" ") - - in_root { run_ruby_script("script/generate #{what} #{argument}", false) } - end - - # Executes a command - # - # ==== Example - # - # inside('vendor') do - # run('ln -s ~/edge rails) - # end - # - def run(command, log_action = true) - log 'executing', "#{command} from #{Dir.pwd}" if log_action - `#{command}` - end - - # Executes a ruby script (taking into account WIN32 platform quirks) - def run_ruby_script(command, log_action = true) - ruby_command = RUBY_PLATFORM=~ /win32/ ? 'ruby ' : '' - run("#{ruby_command}#{command}", log_action) - end - - # Runs the supplied rake task - # - # ==== Example - # - # rake("db:migrate") - # rake("db:migrate", :env => "production") - # rake("gems:install", :sudo => true) - # - def rake(command, options = {}) - log 'rake', command - env = options[:env] || 'development' - sudo = options[:sudo] ? 'sudo ' : '' - in_root { run("#{sudo}rake #{command} RAILS_ENV=#{env}", false) } - end - - # Just run the capify command in root - # - # ==== Example - # - # capify! - # - def capify! - log 'capifying' - in_root { run('capify .', false) } - end - - # Add Rails to /vendor/rails - # - # ==== Example - # - # freeze! - # - def freeze!(args = {}) - log 'vendor', 'rails edge' - in_root { run('rake rails:freeze:edge', false) } - end - - # Make an entry in Rails routing file conifg/routes.rb - # - # === Example - # - # route "map.root :controller => :welcome" - # - def route(routing_code) - log 'route', routing_code - sentinel = 'ActionController::Routing::Routes.draw do |map|' - - in_root do - gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| - "#{match}\n #{routing_code}\n" - end - end - end - - protected - - # Get a user's input - # - # ==== Example - # - # answer = ask("Should I freeze the latest Rails?") - # freeze! if ask("Should I freeze the latest Rails?") == "yes" - # - def ask(string) - log '', string - STDIN.gets.strip - end - - # Do something in the root of the Rails application or - # a provided subfolder; the full path is yielded to the block you provide. - # The path is set back to the previous path when the method exits. - def inside(dir = '', &block) - folder = File.join(root, dir) - FileUtils.mkdir_p(folder) unless File.exist?(folder) - FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield } - end - - def in_root - FileUtils.cd(root) { yield } - end - - # Helper to test if the user says yes(y)? - # - # ==== Example - # - # freeze! if yes?("Should I freeze the latest Rails?") - # - def yes?(question) - answer = ask(question).downcase - answer == "y" || answer == "yes" - end - - # Helper to test if the user does NOT say yes(y)? - # - # ==== Example - # - # capify! if no?("Will you be using vlad to deploy your application?") - # - def no?(question) - !yes?(question) - end - - # Run a regular expression replacement on a file - # - # ==== Example - # - # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' - # - def gsub_file(relative_destination, regexp, *args, &block) - path = destination_path(relative_destination) - content = File.read(path).gsub(regexp, *args, &block) - File.open(path, 'wb') { |file| file.write(content) } - end - - # Append text to a file - # - # ==== Example - # - # append_file 'config/environments/test.rb', 'config.gem "rspec"' - # - def append_file(relative_destination, data) - path = destination_path(relative_destination) - File.open(path, 'ab') { |file| file.write(data) } - end - - def destination_path(relative_destination) - File.join(root, relative_destination) - end - - def log(action, message = '') - logger.log(action, message) - end - - def logger - @logger ||= Rails::Generator::Base.logger - end - - def logger - @logger ||= if defined?(Rails::Generator::Base) - Rails::Generator::Base.logger - else - require 'rails_generator/simple_logger' - Rails::Generator::SimpleLogger.new(STDOUT) - end - end - - end -end diff --git a/railties/lib/rails_generator/generators/components/controller/USAGE b/railties/lib/rails_generator/generators/components/controller/USAGE deleted file mode 100644 index 362872e84a..0000000000 --- a/railties/lib/rails_generator/generators/components/controller/USAGE +++ /dev/null @@ -1,30 +0,0 @@ -Description: - Stubs out a new controller and its views. Pass the controller name, either - CamelCased or under_scored, and a list of views as arguments. - - To create a controller within a module, specify the controller name as a - path like 'parent_module/controller_name'. - - This generates a controller class in app/controllers, view templates in - app/views/controller_name, a helper class in app/helpers, a functional - test suite in test/functional and a helper test suite in test/unit/helpers. - -Example: - `./script/generate controller CreditCard open debit credit close` - - Credit card controller with URLs like /credit_card/debit. - Controller: app/controllers/credit_card_controller.rb - Functional Test: test/functional/credit_card_controller_test.rb - Views: app/views/credit_card/debit.html.erb [...] - Helper: app/helpers/credit_card_helper.rb - Helper Test: test/unit/helpers/credit_card_helper_test.rb - -Modules Example: - `./script/generate controller 'admin/credit_card' suspend late_fee` - - Credit card admin controller with URLs /admin/credit_card/suspend. - Controller: app/controllers/admin/credit_card_controller.rb - Functional Test: test/functional/admin/credit_card_controller_test.rb - Views: app/views/admin/credit_card/debit.html.erb [...] - Helper: app/helpers/admin/credit_card_helper.rb - Helper Test: test/unit/helpers/admin/credit_card_helper_test.rb diff --git a/railties/lib/rails_generator/generators/components/controller/controller_generator.rb b/railties/lib/rails_generator/generators/components/controller/controller_generator.rb deleted file mode 100644 index dc126e8a98..0000000000 --- a/railties/lib/rails_generator/generators/components/controller/controller_generator.rb +++ /dev/null @@ -1,43 +0,0 @@ -class ControllerGenerator < Rails::Generator::NamedBase - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper", "#{class_name}HelperTest" - - # Controller, helper, views, and test directories. - m.directory File.join('app/controllers', class_path) - m.directory File.join('app/helpers', class_path) - m.directory File.join('app/views', class_path, file_name) - m.directory File.join('test/functional', class_path) - m.directory File.join('test/unit/helpers', class_path) - - # Controller class, functional test, and helper class. - m.template 'controller.rb', - File.join('app/controllers', - class_path, - "#{file_name}_controller.rb") - - m.template 'functional_test.rb', - File.join('test/functional', - class_path, - "#{file_name}_controller_test.rb") - - m.template 'helper.rb', - File.join('app/helpers', - class_path, - "#{file_name}_helper.rb") - - m.template 'helper_test.rb', - File.join('test/unit/helpers', - class_path, - "#{file_name}_helper_test.rb") - - # View template for each action. - actions.each do |action| - path = File.join('app/views', class_path, file_name, "#{action}.html.erb") - m.template 'view.html.erb', path, - :assigns => { :action => action, :path => path } - end - end - end -end diff --git a/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb b/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb deleted file mode 100644 index ad85431f98..0000000000 --- a/railties/lib/rails_generator/generators/components/controller/templates/view.html.erb +++ /dev/null @@ -1,2 +0,0 @@ -<h1><%= class_name %>#<%= action %></h1> -<p>Find me in <%= path %></p> diff --git a/railties/lib/rails_generator/generators/components/helper/USAGE b/railties/lib/rails_generator/generators/components/helper/USAGE deleted file mode 100644 index ef27ca617e..0000000000 --- a/railties/lib/rails_generator/generators/components/helper/USAGE +++ /dev/null @@ -1,24 +0,0 @@ -Description: - Stubs out a new helper. Pass the helper name, either - CamelCased or under_scored. - - To create a helper within a module, specify the helper name as a - path like 'parent_module/helper_name'. - - This generates a helper class in app/helpers and a helper test - suite in test/unit/helpers. - -Example: - `./script/generate helper CreditCard` - - Credit card helper. - Helper: app/helpers/credit_card_helper.rb - Test: test/unit/helpers/credit_card_helper_test.rb - -Modules Example: - `./script/generate helper 'admin/credit_card'` - - Credit card admin helper. - Helper: app/helpers/admin/credit_card_helper.rb - Test: test/unit/helpers/admin/credit_card_helper_test.rb - diff --git a/railties/lib/rails_generator/generators/components/helper/helper_generator.rb b/railties/lib/rails_generator/generators/components/helper/helper_generator.rb deleted file mode 100644 index f7831f7c7a..0000000000 --- a/railties/lib/rails_generator/generators/components/helper/helper_generator.rb +++ /dev/null @@ -1,25 +0,0 @@ -class HelperGenerator < Rails::Generator::NamedBase - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions class_path, "#{class_name}Helper", "#{class_name}HelperTest" - - # Helper and helper test directories. - m.directory File.join('app/helpers', class_path) - m.directory File.join('test/unit/helpers', class_path) - - # Helper and helper test class. - - m.template 'helper.rb', - File.join('app/helpers', - class_path, - "#{file_name}_helper.rb") - - m.template 'helper_test.rb', - File.join('test/unit/helpers', - class_path, - "#{file_name}_helper_test.rb") - - end - end -end diff --git a/railties/lib/rails_generator/generators/components/helper/templates/helper.rb b/railties/lib/rails_generator/generators/components/helper/templates/helper.rb deleted file mode 100644 index 3fe2ecdc74..0000000000 --- a/railties/lib/rails_generator/generators/components/helper/templates/helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module <%= class_name %>Helper -end diff --git a/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb b/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb deleted file mode 100644 index 591e40900e..0000000000 --- a/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'test_helper' - -class <%= class_name %>HelperTest < ActionView::TestCase -end diff --git a/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb b/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb deleted file mode 100644 index 44323f28ca..0000000000 --- a/railties/lib/rails_generator/generators/components/integration_test/integration_test_generator.rb +++ /dev/null @@ -1,16 +0,0 @@ -class IntegrationTestGenerator < Rails::Generator::NamedBase - default_options :skip_migration => false - - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions class_name, "#{class_name}Test" - - # integration test directory - m.directory File.join('test/integration', class_path) - - # integration test stub - m.template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb") - end - end -end diff --git a/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb b/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb deleted file mode 100644 index ba6d60cac6..0000000000 --- a/railties/lib/rails_generator/generators/components/mailer/mailer_generator.rb +++ /dev/null @@ -1,30 +0,0 @@ -class MailerGenerator < Rails::Generator::NamedBase - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions class_name, "#{class_name}Test" - - # Mailer, view, test, and fixture directories. - m.directory File.join('app/models', class_path) - m.directory File.join('app/views', file_path) - m.directory File.join('test/unit', class_path) - m.directory File.join('test/fixtures', file_path) - - # Mailer class and unit test. - m.template "mailer.rb", File.join('app/models', class_path, "#{file_name}.rb") - m.template "unit_test.rb", File.join('test/unit', class_path, "#{file_name}_test.rb") - - # View template and fixture for each action. - actions.each do |action| - relative_path = File.join(file_path, action) - view_path = File.join('app/views', "#{relative_path}.erb") - fixture_path = File.join('test/fixtures', relative_path) - - m.template "view.erb", view_path, - :assigns => { :action => action, :path => view_path } - m.template "fixture.erb", fixture_path, - :assigns => { :action => action, :path => view_path } - end - end - end -end diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb b/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb deleted file mode 100644 index 6899257ddc..0000000000 --- a/railties/lib/rails_generator/generators/components/mailer/templates/fixture.erb +++ /dev/null @@ -1,3 +0,0 @@ -<%= class_name %>#<%= action %> - -Find me in <%= path %> diff --git a/railties/lib/rails_generator/generators/components/mailer/templates/view.erb b/railties/lib/rails_generator/generators/components/mailer/templates/view.erb deleted file mode 100644 index 6899257ddc..0000000000 --- a/railties/lib/rails_generator/generators/components/mailer/templates/view.erb +++ /dev/null @@ -1,3 +0,0 @@ -<%= class_name %>#<%= action %> - -Find me in <%= path %> diff --git a/railties/lib/rails_generator/generators/components/metal/metal_generator.rb b/railties/lib/rails_generator/generators/components/metal/metal_generator.rb deleted file mode 100644 index 64f49d929d..0000000000 --- a/railties/lib/rails_generator/generators/components/metal/metal_generator.rb +++ /dev/null @@ -1,8 +0,0 @@ -class MetalGenerator < Rails::Generator::NamedBase - def manifest - record do |m| - m.directory 'app/metal' - m.template 'metal.rb', File.join('app/metal', "#{file_name}.rb") - end - end -end diff --git a/railties/lib/rails_generator/generators/components/migration/migration_generator.rb b/railties/lib/rails_generator/generators/components/migration/migration_generator.rb deleted file mode 100644 index acf41e07df..0000000000 --- a/railties/lib/rails_generator/generators/components/migration/migration_generator.rb +++ /dev/null @@ -1,20 +0,0 @@ -class MigrationGenerator < Rails::Generator::NamedBase - def manifest - record do |m| - m.migration_template 'migration.rb', 'db/migrate', :assigns => get_local_assigns - end - end - - - private - def get_local_assigns - returning(assigns = {}) do - if class_name.underscore =~ /^(add|remove)_.*_(?:to|from)_(.*)/ - assigns[:migration_action] = $1 - assigns[:table_name] = $2.pluralize - else - assigns[:attributes] = [] - end - end - end -end diff --git a/railties/lib/rails_generator/generators/components/model/model_generator.rb b/railties/lib/rails_generator/generators/components/model/model_generator.rb deleted file mode 100644 index 582a28922f..0000000000 --- a/railties/lib/rails_generator/generators/components/model/model_generator.rb +++ /dev/null @@ -1,45 +0,0 @@ -class ModelGenerator < Rails::Generator::NamedBase - default_options :skip_timestamps => false, :skip_migration => false, :skip_fixture => false - - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions class_name, "#{class_name}Test" - - # Model, test, and fixture directories. - m.directory File.join('app/models', class_path) - m.directory File.join('test/unit', class_path) - m.directory File.join('test/fixtures', class_path) - - # Model class, unit test, and fixtures. - m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb") - m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb") - - unless options[:skip_fixture] - m.template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml") - end - - unless options[:skip_migration] - m.migration_template 'migration.rb', 'db/migrate', :assigns => { - :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}" - }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}" - end - end - end - - protected - def banner - "Usage: #{$0} #{spec.name} ModelName [field:type, field:type]" - end - - def add_options!(opt) - opt.separator '' - opt.separator 'Options:' - opt.on("--skip-timestamps", - "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v } - opt.on("--skip-migration", - "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } - opt.on("--skip-fixture", - "Don't generation a fixture file for this model") { |v| options[:skip_fixture] = v} - end -end diff --git a/railties/lib/rails_generator/generators/components/model_subclass/USAGE b/railties/lib/rails_generator/generators/components/model_subclass/USAGE deleted file mode 100644 index a4b558a401..0000000000 --- a/railties/lib/rails_generator/generators/components/model_subclass/USAGE +++ /dev/null @@ -1,13 +0,0 @@ -Description: - Create a model subclass of parent, used for Single Table Inheritance. - - Both subclass and parent name can be either CamelCased or under_scored. - - This generates a model class in app/models and a unit test in test/unit. - -Examples: - `./script/generate model_subclass admin user` - - creates an Admin model, which will inheritate from User model, test: - Model: app/models/admin.rb - Test: test/unit/admin_test.rb diff --git a/railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb b/railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb deleted file mode 100644 index e8ac3da2cd..0000000000 --- a/railties/lib/rails_generator/generators/components/model_subclass/model_subclass_generator.rb +++ /dev/null @@ -1,32 +0,0 @@ -class ModelSubclassGenerator < Rails::Generator::NamedBase - default_options :skip_unit_test => false - - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions class_name, "#{class_name}Test" - - # Model and test directories. - m.directory File.join('app/models', class_path) - m.directory File.join('test/unit', class_path) - - # Model class and unit test - m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb"), :assigns => assigns - m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb"), :assigns => assigns - - end - end - - protected - def banner - "Usage: #{$0} #{spec.name} Subclass Parent" - end - - def assigns - {:parent_class_name => parent_class_name} - end - - def parent_class_name - @args.first.try(:camelize) || usage - end -end diff --git a/railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb b/railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb deleted file mode 100644 index d0037b322b..0000000000 --- a/railties/lib/rails_generator/generators/components/model_subclass/templates/model.rb +++ /dev/null @@ -1,3 +0,0 @@ -class <%= class_name %> < <%= parent_class_name %> - -end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/components/observer/observer_generator.rb b/railties/lib/rails_generator/generators/components/observer/observer_generator.rb deleted file mode 100644 index 3c4b330a80..0000000000 --- a/railties/lib/rails_generator/generators/components/observer/observer_generator.rb +++ /dev/null @@ -1,16 +0,0 @@ -class ObserverGenerator < Rails::Generator::NamedBase - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions "#{class_name}Observer", "#{class_name}ObserverTest" - - # Observer, and test directories. - m.directory File.join('app/models', class_path) - m.directory File.join('test/unit', class_path) - - # Observer class and unit test fixtures. - m.template 'observer.rb', File.join('app/models', class_path, "#{file_name}_observer.rb") - m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_observer_test.rb") - end - end -end diff --git a/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb b/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb deleted file mode 100644 index 83ce8ac674..0000000000 --- a/railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb +++ /dev/null @@ -1,16 +0,0 @@ -class PerformanceTestGenerator < Rails::Generator::NamedBase - default_options :skip_migration => false - - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions class_name, "#{class_name}Test" - - # performance test directory - m.directory File.join('test/performance', class_path) - - # performance test stub - m.template 'performance_test.rb', File.join('test/performance', class_path, "#{file_name}_test.rb") - end - end -end diff --git a/railties/lib/rails_generator/generators/components/plugin/USAGE b/railties/lib/rails_generator/generators/components/plugin/USAGE deleted file mode 100644 index d2ecfc2d59..0000000000 --- a/railties/lib/rails_generator/generators/components/plugin/USAGE +++ /dev/null @@ -1,25 +0,0 @@ -Description: - Stubs out a new plugin. Pass the plugin name, either CamelCased or - under_scored, as an argument. Pass --with-generator to add an example - generator also. - - This creates a plugin in vendor/plugins including an init.rb and README - as well as standard lib, task, and test directories. - -Example: - `./script/generate plugin BrowserFilters` - - creates a standard browser_filters plugin: - vendor/plugins/browser_filters/README - vendor/plugins/browser_filters/init.rb - vendor/plugins/browser_filters/install.rb - vendor/plugins/browser_filters/lib/browser_filters.rb - vendor/plugins/browser_filters/test/browser_filters_test.rb - vendor/plugins/browser_filters/tasks/browser_filters_tasks.rake - - ./script/generate plugin BrowserFilters --with-generator - - creates a browser_filters generator also: - vendor/plugins/browser_filters/generators/browser_filters/browser_filters_generator.rb - vendor/plugins/browser_filters/generators/browser_filters/USAGE - vendor/plugins/browser_filters/generators/browser_filters/templates/ diff --git a/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb b/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb deleted file mode 100644 index 6826998252..0000000000 --- a/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb +++ /dev/null @@ -1,39 +0,0 @@ -class PluginGenerator < Rails::Generator::NamedBase - attr_reader :plugin_path - - def initialize(runtime_args, runtime_options = {}) - @with_generator = runtime_args.delete("--with-generator") - super - @plugin_path = "vendor/plugins/#{file_name}" - end - - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions class_name - - m.directory "#{plugin_path}/lib" - m.directory "#{plugin_path}/tasks" - m.directory "#{plugin_path}/test" - - m.template 'README', "#{plugin_path}/README" - m.template 'MIT-LICENSE', "#{plugin_path}/MIT-LICENSE" - m.template 'Rakefile', "#{plugin_path}/Rakefile" - m.template 'init.rb', "#{plugin_path}/init.rb" - m.template 'install.rb', "#{plugin_path}/install.rb" - m.template 'uninstall.rb', "#{plugin_path}/uninstall.rb" - m.template 'plugin.rb', "#{plugin_path}/lib/#{file_name}.rb" - m.template 'tasks.rake', "#{plugin_path}/tasks/#{file_name}_tasks.rake" - m.template 'unit_test.rb', "#{plugin_path}/test/#{file_name}_test.rb" - m.template 'test_helper.rb', "#{plugin_path}/test/test_helper.rb" - if @with_generator - m.directory "#{plugin_path}/generators" - m.directory "#{plugin_path}/generators/#{file_name}" - m.directory "#{plugin_path}/generators/#{file_name}/templates" - - m.template 'generator.rb', "#{plugin_path}/generators/#{file_name}/#{file_name}_generator.rb" - m.template 'USAGE', "#{plugin_path}/generators/#{file_name}/USAGE" - end - end - end -end diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb b/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb deleted file mode 100644 index 3e800df6c5..0000000000 --- a/railties/lib/rails_generator/generators/components/plugin/templates/generator.rb +++ /dev/null @@ -1,8 +0,0 @@ -class <%= class_name %>Generator < Rails::Generator::NamedBase - def manifest - record do |m| - # m.directory "lib" - # m.template 'README', "README" - end - end -end diff --git a/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb b/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb deleted file mode 100644 index 3e0bc29d3a..0000000000 --- a/railties/lib/rails_generator/generators/components/plugin/templates/unit_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'test_helper' - -class <%= class_name %>Test < ActiveSupport::TestCase - # Replace this with your real tests. - test "the truth" do - assert true - end -end diff --git a/railties/lib/rails_generator/generators/components/resource/resource_generator.rb b/railties/lib/rails_generator/generators/components/resource/resource_generator.rb deleted file mode 100644 index 4ee2fbff63..0000000000 --- a/railties/lib/rails_generator/generators/components/resource/resource_generator.rb +++ /dev/null @@ -1,76 +0,0 @@ -class ResourceGenerator < Rails::Generator::NamedBase - default_options :skip_timestamps => false, :skip_migration => false - - attr_reader :controller_name, - :controller_class_path, - :controller_file_path, - :controller_class_nesting, - :controller_class_nesting_depth, - :controller_class_name, - :controller_singular_name, - :controller_plural_name - alias_method :controller_file_name, :controller_singular_name - alias_method :controller_table_name, :controller_plural_name - - def initialize(runtime_args, runtime_options = {}) - super - - @controller_name = @name.pluralize - - base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name) - @controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name) - - if @controller_class_nesting.empty? - @controller_class_name = @controller_class_name_without_nesting - else - @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}" - end - end - - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper") - m.class_collisions(class_name) - - # Controller, helper, views, and test directories. - m.directory(File.join('app/models', class_path)) - m.directory(File.join('app/controllers', controller_class_path)) - m.directory(File.join('app/helpers', controller_class_path)) - m.directory(File.join('app/views', controller_class_path, controller_file_name)) - m.directory(File.join('test/functional', controller_class_path)) - m.directory(File.join('test/unit', class_path)) - m.directory(File.join('test/unit/helpers', class_path)) - - m.dependency 'model', [name] + @args, :collision => :skip - - m.template( - 'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb") - ) - - m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb")) - m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")) - m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb")) - - m.route_resources controller_file_name - end - end - - protected - def banner - "Usage: #{$0} resource ModelName [field:type, field:type]" - end - - def add_options!(opt) - opt.separator '' - opt.separator 'Options:' - opt.on("--skip-timestamps", - "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v } - opt.on("--skip-migration", - "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } - end - - def model_name - class_name.demodulize - end -end diff --git a/railties/lib/rails_generator/generators/components/resource/templates/controller.rb b/railties/lib/rails_generator/generators/components/resource/templates/controller.rb deleted file mode 100644 index 765a942694..0000000000 --- a/railties/lib/rails_generator/generators/components/resource/templates/controller.rb +++ /dev/null @@ -1,2 +0,0 @@ -class <%= controller_class_name %>Controller < ApplicationController -end diff --git a/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb b/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb deleted file mode 100644 index b1bb1dacbf..0000000000 --- a/railties/lib/rails_generator/generators/components/resource/templates/functional_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'test_helper' - -class <%= controller_class_name %>ControllerTest < ActionController::TestCase - # Replace this with your real tests. - test "the truth" do - assert true - end -end diff --git a/railties/lib/rails_generator/generators/components/resource/templates/helper.rb b/railties/lib/rails_generator/generators/components/resource/templates/helper.rb deleted file mode 100644 index 9bd821b1b2..0000000000 --- a/railties/lib/rails_generator/generators/components/resource/templates/helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module <%= controller_class_name %>Helper -end diff --git a/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb b/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb deleted file mode 100644 index 061f64a5e3..0000000000 --- a/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'test_helper' - -class <%= controller_class_name %>HelperTest < ActionView::TestCase -end diff --git a/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb b/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb deleted file mode 100644 index 2a5edeedb6..0000000000 --- a/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb +++ /dev/null @@ -1,102 +0,0 @@ -class ScaffoldGenerator < Rails::Generator::NamedBase - default_options :skip_timestamps => false, :skip_migration => false, :force_plural => false - - attr_reader :controller_name, - :controller_class_path, - :controller_file_path, - :controller_class_nesting, - :controller_class_nesting_depth, - :controller_class_name, - :controller_underscore_name, - :controller_singular_name, - :controller_plural_name - alias_method :controller_file_name, :controller_underscore_name - alias_method :controller_table_name, :controller_plural_name - - def initialize(runtime_args, runtime_options = {}) - super - - if @name == @name.pluralize && !options[:force_plural] - logger.warning "Plural version of the model detected, using singularized version. Override with --force-plural." - @name = @name.singularize - end - - @controller_name = @name.pluralize - - base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name) - @controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name) - @controller_singular_name=base_name.singularize - if @controller_class_nesting.empty? - @controller_class_name = @controller_class_name_without_nesting - else - @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}" - end - end - - def manifest - record do |m| - # Check for class naming collisions. - m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper") - m.class_collisions(class_name) - - # Controller, helper, views, test and stylesheets directories. - m.directory(File.join('app/models', class_path)) - m.directory(File.join('app/controllers', controller_class_path)) - m.directory(File.join('app/helpers', controller_class_path)) - m.directory(File.join('app/views', controller_class_path, controller_file_name)) - m.directory(File.join('app/views/layouts', controller_class_path)) - m.directory(File.join('test/functional', controller_class_path)) - m.directory(File.join('test/unit', class_path)) - m.directory(File.join('test/unit/helpers', class_path)) - m.directory(File.join('public/stylesheets', class_path)) - - for action in scaffold_views - m.template( - "view_#{action}.html.erb", - File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.erb") - ) - end - - # Layout and stylesheet. - m.template('layout.html.erb', File.join('app/views/layouts', controller_class_path, "#{controller_file_name}.html.erb")) - m.template('style.css', 'public/stylesheets/scaffold.css') - - m.template( - 'controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb") - ) - - m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb")) - m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")) - m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb")) - - m.route_resources controller_file_name - - m.dependency 'model', [name] + @args, :collision => :skip - end - end - - protected - # Override with your own usage banner. - def banner - "Usage: #{$0} scaffold ModelName [field:type, field:type]" - end - - def add_options!(opt) - opt.separator '' - opt.separator 'Options:' - opt.on("--skip-timestamps", - "Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v } - opt.on("--skip-migration", - "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } - opt.on("--force-plural", - "Forces the generation of a plural ModelName") { |v| options[:force_plural] = v } - end - - def scaffold_views - %w[ index show new edit ] - end - - def model_name - class_name.demodulize - end -end diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb b/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb deleted file mode 100644 index 9bd821b1b2..0000000000 --- a/railties/lib/rails_generator/generators/components/scaffold/templates/helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module <%= controller_class_name %>Helper -end diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb b/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb deleted file mode 100644 index 061f64a5e3..0000000000 --- a/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'test_helper' - -class <%= controller_class_name %>HelperTest < ActionView::TestCase -end diff --git a/railties/lib/rails_generator/generators/components/session_migration/USAGE b/railties/lib/rails_generator/generators/components/session_migration/USAGE deleted file mode 100644 index 87117a3cb6..0000000000 --- a/railties/lib/rails_generator/generators/components/session_migration/USAGE +++ /dev/null @@ -1,10 +0,0 @@ -Description: - Creates a migration to add the sessions table used by the Active Record - session store. Pass the migration name, either CamelCased or under_scored, - as an argument. - -Example: - `./script/generate session_migration CreateSessionTable` - - With 4 existing migrations, this creates the AddSessionTable migration - in db/migrate/005_add_session_table.rb diff --git a/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb b/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb deleted file mode 100644 index 2e177033a1..0000000000 --- a/railties/lib/rails_generator/generators/components/session_migration/session_migration_generator.rb +++ /dev/null @@ -1,18 +0,0 @@ -class SessionMigrationGenerator < Rails::Generator::NamedBase - def initialize(runtime_args, runtime_options = {}) - runtime_args << 'add_session_table' if runtime_args.empty? - super - end - - def manifest - record do |m| - m.migration_template 'migration.rb', 'db/migrate', - :assigns => { :session_table_name => default_session_table_name } - end - end - - protected - def default_session_table_name - ActiveRecord::Base.pluralize_table_names ? 'session'.pluralize : 'session' - end -end diff --git a/railties/lib/rails_generator/lookup.rb b/railties/lib/rails_generator/lookup.rb deleted file mode 100644 index a3525364a2..0000000000 --- a/railties/lib/rails_generator/lookup.rb +++ /dev/null @@ -1,249 +0,0 @@ -require 'pathname' - -require File.dirname(__FILE__) + '/spec' - -class Object - class << self - # Lookup missing generators using const_missing. This allows any - # generator to reference another without having to know its location: - # RubyGems, ~/.rails/generators, and RAILS_ROOT/generators. - def lookup_missing_generator(class_id) - if md = /(.+)Generator$/.match(class_id.to_s) - name = md.captures.first.demodulize.underscore - Rails::Generator::Base.lookup(name).klass - else - const_missing_before_generators(class_id) - end - end - - unless respond_to?(:const_missing_before_generators) - alias_method :const_missing_before_generators, :const_missing - alias_method :const_missing, :lookup_missing_generator - end - end -end - -# User home directory lookup adapted from RubyGems. -def Dir.user_home - if ENV['HOME'] - ENV['HOME'] - elsif ENV['USERPROFILE'] - ENV['USERPROFILE'] - elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH'] - "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}" - else - File.expand_path '~' - end -end - - -module Rails - module Generator - - # Generator lookup is managed by a list of sources which return specs - # describing where to find and how to create generators. This module - # provides class methods for manipulating the source list and looking up - # generator specs, and an #instance wrapper for quickly instantiating - # generators by name. - # - # A spec is not a generator: it's a description of where to find - # the generator and how to create it. A source is anything that - # yields generators from #each. PathSource and GemGeneratorSource are provided. - module Lookup - def self.included(base) - base.extend(ClassMethods) - base.use_component_sources! - end - - # Convenience method to instantiate another generator. - def instance(generator_name, args, runtime_options = {}) - self.class.instance(generator_name, args, runtime_options) - end - - module ClassMethods - # The list of sources where we look, in order, for generators. - def sources - read_inheritable_attribute(:sources) or use_component_sources! - end - - # Add a source to the end of the list. - def append_sources(*args) - sources.concat(args.flatten) - invalidate_cache! - end - - # Add a source to the beginning of the list. - def prepend_sources(*args) - write_inheritable_array(:sources, args.flatten + sources) - invalidate_cache! - end - - # Reset the source list. - def reset_sources - write_inheritable_attribute(:sources, []) - invalidate_cache! - end - - # Use application generators (app, ?). - def use_application_sources! - reset_sources - sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/applications") - end - - # Use component generators (model, controller, etc). - # 1. Rails application. If RAILS_ROOT is defined we know we're - # generating in the context of a Rails application, so search - # RAILS_ROOT/generators. - # 2. Look in plugins, either for generators/ or rails_generators/ - # directories within each plugin - # 3. User home directory. Search ~/.rails/generators. - # 4. RubyGems. Search for gems named *_generator, and look for - # generators within any RubyGem's - # /rails_generators/<generator_name>_generator.rb file. - # 5. Builtins. Model, controller, mailer, scaffold, and so on. - def use_component_sources! - reset_sources - if defined? ::RAILS_ROOT - sources << PathSource.new(:lib, "#{::RAILS_ROOT}/lib/generators") - sources << PathSource.new(:vendor, "#{::RAILS_ROOT}/vendor/generators") - Rails.configuration.plugin_paths.each do |path| - relative_path = Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(::RAILS_ROOT)) - sources << PathSource.new(:"plugins (#{relative_path})", "#{path}/*/**/{,rails_}generators") - end - end - sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators") - if Object.const_defined?(:Gem) - sources << GemGeneratorSource.new - sources << GemPathSource.new - end - sources << PathSource.new(:builtin, "#{File.dirname(__FILE__)}/generators/components") - end - - # Lookup knows how to find generators' Specs from a list of Sources. - # Searches the sources, in order, for the first matching name. - def lookup(generator_name) - @found ||= {} - generator_name = generator_name.to_s.downcase - @found[generator_name] ||= cache.find { |spec| spec.name == generator_name } - unless @found[generator_name] - chars = generator_name.scan(/./).map{|c|"#{c}.*?"} - rx = /^#{chars}$/ - gns = cache.select{|spec| spec.name =~ rx } - @found[generator_name] ||= gns.first if gns.length == 1 - raise GeneratorError, "Pattern '#{generator_name}' matches more than one generator: #{gns.map{|sp|sp.name}.join(', ')}" if gns.length > 1 - end - @found[generator_name] or raise GeneratorError, "Couldn't find '#{generator_name}' generator" - end - - # Convenience method to lookup and instantiate a generator. - def instance(generator_name, args = [], runtime_options = {}) - lookup(generator_name).klass.new(args, full_options(runtime_options)) - end - - private - # Lookup and cache every generator from the source list. - def cache - @cache ||= sources.inject([]) { |cache, source| cache + source.to_a } - end - - # Clear the cache whenever the source list changes. - def invalidate_cache! - @cache = nil - end - end - end - - # Sources enumerate (yield from #each) generator specs which describe - # where to find and how to create generators. Enumerable is mixed in so, - # for example, source.collect will retrieve every generator. - # Sources may be assigned a label to distinguish them. - class Source - include Enumerable - - attr_reader :label - def initialize(label) - @label = label - end - - # The each method must be implemented in subclasses. - # The base implementation raises an error. - def each - raise NotImplementedError - end - - # Return a convenient sorted list of all generator names. - def names - map { |spec| spec.name }.sort - end - end - - - # PathSource looks for generators in a filesystem directory. - class PathSource < Source - attr_reader :path - - def initialize(label, path) - super label - @path = path - end - - # Yield each eligible subdirectory. - def each - Dir["#{path}/[a-z]*"].each do |dir| - if File.directory?(dir) - yield Spec.new(File.basename(dir), dir, label) - end - end - end - end - - class AbstractGemSource < Source - def initialize - super :RubyGems - end - end - - # GemGeneratorSource hits the mines to quarry for generators. The latest versions - # of gems named *_generator are selected. - class GemGeneratorSource < AbstractGemSource - # Yield latest versions of generator gems. - def each - dependency = Gem::Dependency.new(/_generator$/, Gem::Requirement.default) - Gem::cache.search(dependency).inject({}) { |latest, gem| - hem = latest[gem.name] - latest[gem.name] = gem if hem.nil? or gem.version > hem.version - latest - }.values.each { |gem| - yield Spec.new(gem.name.sub(/_generator$/, ''), gem.full_gem_path, label) - } - end - end - - # GemPathSource looks for generators within any RubyGem's /rails_generators/<generator_name>_generator.rb file. - class GemPathSource < AbstractGemSource - # Yield each generator within rails_generator subdirectories. - def each - generator_full_paths.each do |generator| - yield Spec.new(File.basename(generator).sub(/_generator.rb$/, ''), File.dirname(generator), label) - end - end - - private - def generator_full_paths - @generator_full_paths ||= - Gem::cache.inject({}) do |latest, name_gem| - name, gem = name_gem - hem = latest[gem.name] - latest[gem.name] = gem if hem.nil? or gem.version > hem.version - latest - end.values.inject([]) do |mem, gem| - Dir[gem.full_gem_path + '/{rails_,}generators/**/*_generator.rb'].each do |generator| - mem << generator - end - mem - end - end - end - - end -end diff --git a/railties/lib/rails_generator/manifest.rb b/railties/lib/rails_generator/manifest.rb deleted file mode 100644 index 702effa76f..0000000000 --- a/railties/lib/rails_generator/manifest.rb +++ /dev/null @@ -1,53 +0,0 @@ -module Rails - module Generator - - # Manifest captures the actions a generator performs. Instantiate - # a manifest with an optional target object, hammer it with actions, - # then replay or rewind on the object of your choice. - # - # Example: - # manifest = Manifest.new { |m| - # m.make_directory '/foo' - # m.create_file '/foo/bar.txt' - # } - # manifest.replay(creator) - # manifest.rewind(destroyer) - class Manifest - attr_reader :target - - # Take a default action target. Yield self if block given. - def initialize(target = nil) - @target, @actions = target, [] - yield self if block_given? - end - - # Record an action. - def method_missing(action, *args, &block) - @actions << [action, args, block] - end - - # Replay recorded actions. - def replay(target = nil) - send_actions(target || @target, @actions) - end - - # Rewind recorded actions. - def rewind(target = nil) - send_actions(target || @target, @actions.reverse) - end - - # Erase recorded actions. - def erase - @actions = [] - end - - private - def send_actions(target, actions) - actions.each do |method, args, block| - target.send(method, *args, &block) - end - end - end - - end -end diff --git a/railties/lib/rails_generator/options.rb b/railties/lib/rails_generator/options.rb deleted file mode 100644 index 5f6aefa921..0000000000 --- a/railties/lib/rails_generator/options.rb +++ /dev/null @@ -1,150 +0,0 @@ -require 'optparse' - -module Rails - module Generator - module Options - def self.included(base) - base.extend(ClassMethods) - class << base - if respond_to?(:inherited) - alias_method :inherited_without_options, :inherited - end - alias_method :inherited, :inherited_with_options - end - end - - module ClassMethods - def inherited_with_options(sub) - inherited_without_options(sub) if respond_to?(:inherited_without_options) - sub.extend(Rails::Generator::Options::ClassMethods) - end - - def mandatory_options(options = nil) - if options - write_inheritable_attribute(:mandatory_options, options) - else - read_inheritable_attribute(:mandatory_options) or write_inheritable_attribute(:mandatory_options, {}) - end - end - - def default_options(options = nil) - if options - write_inheritable_attribute(:default_options, options) - else - read_inheritable_attribute(:default_options) or write_inheritable_attribute(:default_options, {}) - end - end - - # Merge together our class options. In increasing precedence: - # default_options (class default options) - # runtime_options (provided as argument) - # mandatory_options (class mandatory options) - def full_options(runtime_options = {}) - default_options.merge(runtime_options).merge(mandatory_options) - end - - end - - # Each instance has an options hash that's populated by #parse. - def options - @options ||= {} - end - attr_writer :options - - protected - # Convenient access to class mandatory options. - def mandatory_options - self.class.mandatory_options - end - - # Convenient access to class default options. - def default_options - self.class.default_options - end - - # Merge together our instance options. In increasing precedence: - # default_options (class default options) - # options (instance options) - # runtime_options (provided as argument) - # mandatory_options (class mandatory options) - def full_options(runtime_options = {}) - self.class.full_options(options.merge(runtime_options)) - end - - # Parse arguments into the options hash. Classes may customize - # parsing behavior by overriding these methods: - # #banner Usage: ./script/generate [options] - # #add_options! Options: - # some options.. - # #add_general_options! General Options: - # general options.. - def parse!(args, runtime_options = {}) - self.options = {} - - @option_parser = OptionParser.new do |opt| - opt.banner = banner - add_options!(opt) - add_general_options!(opt) - opt.parse!(args) - end - - return args - ensure - self.options = full_options(runtime_options) - end - - # Raise a usage error. Override usage_message to provide a blurb - # after the option parser summary. - def usage(message = usage_message) - raise UsageError, "#{@option_parser}\n#{message}" - end - - def usage_message - '' - end - - # Override with your own usage banner. - def banner - "Usage: #{$0} [options]" - end - - # Override to add your options to the parser: - # def add_options!(opt) - # opt.on('-v', '--verbose') { |value| options[:verbose] = value } - # end - def add_options!(opt) - end - - # Adds general options like -h and --quiet. Usually don't override. - def add_general_options!(opt) - opt.separator '' - opt.separator 'Rails Info:' - opt.on('-v', '--version', 'Show the Rails version number and quit.') - opt.on('-h', '--help', 'Show this help message and quit.') { |v| options[:help] = v } - - opt.separator '' - opt.separator 'General Options:' - - opt.on('-p', '--pretend', 'Run but do not make any changes.') { |v| options[:pretend] = v } - opt.on('-f', '--force', 'Overwrite files that already exist.') { options[:collision] = :force } - opt.on('-s', '--skip', 'Skip files that already exist.') { options[:collision] = :skip } - opt.on('-q', '--quiet', 'Suppress normal output.') { |v| options[:quiet] = v } - opt.on('-t', '--backtrace', 'Debugging: show backtrace on errors.') { |v| options[:backtrace] = v } - opt.on('-c', '--svn', 'Modify files with subversion. (Note: svn must be in path)') do - options[:svn] = `svn status`.inject({}) do |opt, e| - opt[e.chomp[7..-1]] = true - opt - end - end - opt.on('-g', '--git', 'Modify files with git. (Note: git must be in path)') do - options[:git] = `git status`.inject({:new => {}, :modified => {}}) do |opt, e| - opt[:new][e.chomp[14..-1]] = true if e =~ /new file:/ - opt[:modified][e.chomp[14..-1]] = true if e =~ /modified:/ - opt - end - end - end - - end - end -end diff --git a/railties/lib/rails_generator/scripts.rb b/railties/lib/rails_generator/scripts.rb deleted file mode 100644 index 3763b75dba..0000000000 --- a/railties/lib/rails_generator/scripts.rb +++ /dev/null @@ -1,89 +0,0 @@ -require File.dirname(__FILE__) + '/options' - -module Rails - module Generator - module Scripts - - # Generator scripts handle command-line invocation. Each script - # responds to an invoke! class method which handles option parsing - # and generator invocation. - class Base - include Options - default_options :collision => :ask, :quiet => false - - # Run the generator script. Takes an array of unparsed arguments - # and a hash of parsed arguments, takes the generator as an option - # or first remaining argument, and invokes the requested command. - def run(args = [], runtime_options = {}) - begin - parse!(args.dup, runtime_options) - rescue OptionParser::InvalidOption => e - # Don't cry, script. Generators want what you think is invalid. - end - - # Generator name is the only required option. - unless options[:generator] - usage if args.empty? - options[:generator] ||= args.shift - end - - # Look up generator instance and invoke command on it. - Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke! - rescue => e - puts e - puts " #{e.backtrace.join("\n ")}\n" if options[:backtrace] - raise SystemExit - end - - protected - # Override with your own script usage banner. - def banner - "Usage: #{$0} generator [options] [args]" - end - - def usage_message - usage = "\nInstalled Generators\n" - Rails::Generator::Base.sources.inject([]) do |mem, source| - # Using an association list instead of a hash to preserve order, - # for aesthetic reasons more than anything else. - label = source.label.to_s.capitalize - pair = mem.assoc(label) - mem << (pair = [label, []]) if pair.nil? - pair[1] |= source.names - mem - end.each do |label, names| - usage << " #{label}: #{names.join(', ')}\n" unless names.empty? - end - - usage << <<end_blurb - -You can also install additional generators for your own use: - 1. Download, for example, login_generator.zip - 2. Unzip to directory #{Dir.user_home}/.rails/generators/login - to use the generator with all your Rails apps -end_blurb - - if Object.const_defined?(:RAILS_ROOT) - usage << <<end_blurb - or to #{File.expand_path(RAILS_ROOT)}/lib/generators/login - to use with this app only. -end_blurb - end - - usage << <<end_blurb - 3. Run generate with no arguments for usage information - #{$0} login - -Generator gems are also available: - 1. gem search -r generator - 2. gem install login_generator - 3. #{$0} login - -end_blurb - return usage - end - end # Base - - end - end -end diff --git a/railties/lib/rails_generator/scripts/destroy.rb b/railties/lib/rails_generator/scripts/destroy.rb deleted file mode 100644 index a7c2a14751..0000000000 --- a/railties/lib/rails_generator/scripts/destroy.rb +++ /dev/null @@ -1,29 +0,0 @@ -require File.dirname(__FILE__) + '/../scripts' - -module Rails::Generator::Scripts - class Destroy < Base - mandatory_options :command => :destroy - - protected - def usage_message - usage = "\nInstalled Generators\n" - Rails::Generator::Base.sources.each do |source| - label = source.label.to_s.capitalize - names = source.names - usage << " #{label}: #{names.join(', ')}\n" unless names.empty? - end - - usage << <<end_blurb - -script/generate command. For instance, 'script/destroy migration CreatePost' -will delete the appropriate XXX_create_post.rb migration file in db/migrate, -while 'script/destroy scaffold Post' will delete the posts controller and -views, post model and migration, all associated tests, and the map.resources -:posts line in config/routes.rb. - -For instructions on finding new generators, run script/generate. -end_blurb - return usage - end - end -end diff --git a/railties/lib/rails_generator/scripts/generate.rb b/railties/lib/rails_generator/scripts/generate.rb deleted file mode 100644 index 1fe2f54ab3..0000000000 --- a/railties/lib/rails_generator/scripts/generate.rb +++ /dev/null @@ -1,7 +0,0 @@ -require File.dirname(__FILE__) + '/../scripts' - -module Rails::Generator::Scripts - class Generate < Base - mandatory_options :command => :create - end -end diff --git a/railties/lib/rails_generator/scripts/update.rb b/railties/lib/rails_generator/scripts/update.rb deleted file mode 100644 index 53a9faa366..0000000000 --- a/railties/lib/rails_generator/scripts/update.rb +++ /dev/null @@ -1,12 +0,0 @@ -require File.dirname(__FILE__) + '/../scripts' - -module Rails::Generator::Scripts - class Update < Base - mandatory_options :command => :update - - protected - def banner - "Usage: #{$0} [options] scaffold" - end - end -end diff --git a/railties/lib/rails_generator/secret_key_generator.rb b/railties/lib/rails_generator/secret_key_generator.rb deleted file mode 100644 index 7dd495a2f5..0000000000 --- a/railties/lib/rails_generator/secret_key_generator.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'active_support/deprecation' - -module Rails - # A class for creating random secret keys. This class will do its best to create a - # random secret key that's as secure as possible, using whatever methods are - # available on the current platform. For example: - # - # generator = Rails::SecretKeyGenerator("some unique identifier, such as the application name") - # generator.generate_secret # => "f3f1be90053fa851... (some long string)" - # - # This class is *deprecated* in Rails 2.2 in favor of ActiveSupport::SecureRandom. - # It is currently a wrapper around ActiveSupport::SecureRandom. - class SecretKeyGenerator - def initialize(identifier) - end - - # Generate a random secret key with the best possible method available on - # the current platform. - def generate_secret - ActiveSupport::SecureRandom.hex(64) - end - deprecate :generate_secret=>"You should use ActiveSupport::SecureRandom.hex(64)" - end -end diff --git a/railties/lib/rails_generator/simple_logger.rb b/railties/lib/rails_generator/simple_logger.rb deleted file mode 100644 index d750f07b84..0000000000 --- a/railties/lib/rails_generator/simple_logger.rb +++ /dev/null @@ -1,46 +0,0 @@ -module Rails - module Generator - class SimpleLogger # :nodoc: - attr_reader :out - attr_accessor :quiet - - def initialize(out = $stdout) - @out = out - @quiet = false - @level = 0 - end - - def log(status, message, &block) - @out.print("%12s %s%s\n" % [status, ' ' * @level, message]) unless quiet - indent(&block) if block_given? - end - - def indent(&block) - @level += 1 - if block_given? - begin - block.call - ensure - outdent - end - end - end - - def outdent - @level -= 1 - if block_given? - begin - block.call - ensure - indent - end - end - end - - private - def method_missing(method, *args, &block) - log(method.to_s, args.first, &block) - end - end - end -end diff --git a/railties/lib/rails_generator/spec.rb b/railties/lib/rails_generator/spec.rb deleted file mode 100644 index 9d780b7ac5..0000000000 --- a/railties/lib/rails_generator/spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -module Rails - module Generator - # A spec knows where a generator was found and how to instantiate it. - # Metadata include the generator's name, its base path, and the source - # which yielded it (PathSource, GemPathSource, etc.) - class Spec - attr_reader :name, :path, :source - - def initialize(name, path, source) - @name, @path, @source = name, path, source - end - - # Look up the generator class. Require its class file, find the class - # in ObjectSpace, tag it with this spec, and return. - def klass - unless @klass - require class_file - @klass = lookup_class - @klass.spec = self - end - @klass - end - - def class_file - "#{path}/#{name}_generator.rb" - end - - def class_name - "#{name.camelize}Generator" - end - - private - # Search for the first Class descending from Rails::Generator::Base - # whose name matches the requested class name. - def lookup_class - ObjectSpace.each_object(Class) do |obj| - return obj if obj.ancestors.include?(Rails::Generator::Base) and - obj.name.split('::').last == class_name - end - raise NameError, "Missing #{class_name} class in #{class_file}" - end - end - end -end diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake index 0e256737f9..0d4d658315 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/tasks/databases.rake @@ -411,9 +411,9 @@ namespace :db do desc "Creates a sessions migration for use with ActiveRecord::SessionStore" task :create => :environment do raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations? - require 'rails_generator' - require 'rails_generator/scripts/generate' - Rails::Generator::Scripts::Generate.new.run(["session_migration", ENV["MIGRATION"] || "CreateSessions"]) + require 'generators' + require 'generators/rails/session_migration/session_migration_generator' + Rails::Generators::SessionMigrationGenerator.start [ ENV["MIGRATION"] || "add_sessions_table" ] end desc "Clear the sessions table" diff --git a/railties/lib/tasks/documentation.rake b/railties/lib/tasks/documentation.rake index 8b41478a93..db1939c45f 100644 --- a/railties/lib/tasks/documentation.rake +++ b/railties/lib/tasks/documentation.rake @@ -21,7 +21,7 @@ namespace :doc do rdoc.rdoc_files.include('vendor/rails/railties/CHANGELOG') rdoc.rdoc_files.include('vendor/rails/railties/MIT-LICENSE') rdoc.rdoc_files.include('vendor/rails/railties/README') - rdoc.rdoc_files.include('vendor/rails/railties/lib/{*.rb,commands/*.rb,rails_generator/*.rb}') + rdoc.rdoc_files.include('vendor/rails/railties/lib/{*.rb,commands/*.rb,generators/*.rb}') rdoc.rdoc_files.include('vendor/rails/activerecord/README') rdoc.rdoc_files.include('vendor/rails/activerecord/CHANGELOG') rdoc.rdoc_files.include('vendor/rails/activerecord/lib/active_record/**/*.rb') diff --git a/railties/lib/tasks/framework.rake b/railties/lib/tasks/framework.rake index b6f2f2bc18..a084af7e11 100644 --- a/railties/lib/tasks/framework.rake +++ b/railties/lib/tasks/framework.rake @@ -78,51 +78,48 @@ namespace :rails do end desc "Update both configs, scripts and public/javascripts from Rails" - task :update => [ "update:scripts", "update:javascripts", "update:configs", "update:application_controller" ] + task :update => [ "update:configs", "update:javascripts", "update:scripts", "update:application_controller" ] desc "Applies the template supplied by LOCATION=/path/to/template" task :template do - require 'rails_generator/generators/applications/app/template_runner' template = ENV["LOCATION"] template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} - Rails::TemplateRunner.new(template) + + require 'generators' + generator = Rails::Generators::App.new [ RAILS_ROOT ], {}, :destination_root => RAILS_ROOT + generator.apply template, :verbose => false end namespace :update do - desc "Add new scripts to the application script/ directory" - task :scripts do - local_base = "script" - edge_base = "#{File.dirname(__FILE__)}/../../bin" - - local = Dir["#{local_base}/**/*"].reject { |path| File.directory?(path) } - edge = Dir["#{edge_base}/**/*"].reject { |path| File.directory?(path) } - - edge.each do |script| - base_name = script[(edge_base.length+1)..-1] - next if base_name == "rails" - next if local.detect { |path| base_name == path[(local_base.length+1)..-1] } - if !File.directory?("#{local_base}/#{File.dirname(base_name)}") - mkdir_p "#{local_base}/#{File.dirname(base_name)}" - end - install script, "#{local_base}/#{base_name}", :mode => 0755 - end - end + def invoke_from_app_generator(method) + require 'generators' + require 'generators/rails/app/app_generator' - desc "Update your javascripts from your current rails install" - task :javascripts do - require 'railties_path' - project_dir = RAILS_ROOT + '/public/javascripts/' - scripts = Dir[RAILTIES_PATH + '/html/javascripts/*.js'] - scripts.reject!{|s| File.basename(s) == 'application.js'} if File.exist?(project_dir + 'application.js') - FileUtils.cp(scripts, project_dir) + generator = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true }, + :destination_root => RAILS_ROOT + generator.invoke(method) end desc "Update config/boot.rb from your current rails install" task :configs do - require 'railties_path' - FileUtils.cp(RAILTIES_PATH + '/environments/boot.rb', RAILS_ROOT + '/config/boot.rb') + invoke_from_app_generator :create_boot_file + end + + desc "Update Prototype javascripts from your current rails install" + task :javascripts do + invoke_from_app_generator :create_prototype_files + end + + desc "Generate dispatcher files in RAILS_ROOT/public" + task :generate_dispatchers do + invoke_from_app_generator :create_dispatch_files end - + + desc "Add new scripts to the application script/ directory" + task :scripts do + invoke_from_app_generator :create_script_files + end + desc "Rename application.rb to application_controller.rb" task :application_controller do old_style = RAILS_ROOT + '/app/controllers/application.rb' @@ -132,14 +129,5 @@ namespace :rails do puts "#{old_style} has been renamed to #{new_style}, update your SCM as necessary" end end - - desc "Generate dispatcher files in RAILS_ROOT/public" - task :generate_dispatchers do - require 'railties_path' - FileUtils.cp(RAILTIES_PATH + '/dispatches/config.ru', RAILS_ROOT + '/config.ru') - FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.fcgi', RAILS_ROOT + '/public/dispatch.fcgi') - FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.rb') - FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.cgi') - end end end diff --git a/railties/lib/vendor/thor-0.11.3/CHANGELOG.rdoc b/railties/lib/vendor/thor-0.11.3/CHANGELOG.rdoc new file mode 100644 index 0000000000..544dde8c02 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/CHANGELOG.rdoc @@ -0,0 +1,75 @@ +== TODO + +* Improve spec coverage for Thor::Runner +* Improve help output to list shorthand switches, too + +== Current + +* BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore + since it wrong behavior to the invocation system. + +* thor help now show information about any class/task. All those calls are + possible: + + thor help describe + thor help describe:amazing + + Or even with default namespaces: + + thor help :spec + +* Thor::Runner now invokes the default task if none is supplied: + + thor describe # invokes the default task, usually help + +* Thor::Runner now works with mappings: + + thor describe -h + +* Added some documentation and code refactoring. + +== 0.9.8, released 2008-10-20 + +* Fixed some tiny issues that were introduced lately. + +== 0.9.7, released 2008-10-13 + +* Setting global method options on the initialize method works as expected: + All other tasks will accept these global options in addition to their own. +* Added 'group' notion to Thor task sets (class Thor); by default all tasks + are in the 'standard' group. Running 'thor -T' will only show the standard + tasks - adding --all will show all tasks. You can also filter on a specific + group using the --group option: thor -T --group advanced + +== 0.9.6, released 2008-09-13 + +* Generic improvements + +== 0.9.5, released 2008-08-27 + +* Improve Windows compatibility +* Update (incorrect) README and task.thor sample file +* Options hash is now frozen (once returned) +* Allow magic predicates on options object. For instance: `options.force?` +* Add support for :numeric type +* BACKWARDS INCOMPATIBLE: Refactor Thor::Options. You cannot access shorthand forms in options hash anymore (for instance, options[:f]) +* Allow specifying optional args with default values: method_options(:user => "mislav") +* Don't write options for nil or false values. This allows, for example, turning color off when running specs. +* Exit with the status of the spec command to help CI stuff out some. + +== 0.9.4, released 2008-08-13 + +* Try to add Windows compatibility. +* BACKWARDS INCOMPATIBLE: options hash is now accessed as a property in your class and is not passed as last argument anymore +* Allow options at the beginning of the argument list as well as the end. +* Make options available with symbol keys in addition to string keys. +* Allow true to be passed to Thor#method_options to denote a boolean option. +* If loading a thor file fails, don't give up, just print a warning and keep going. +* Make sure that we re-raise errors if they happened further down the pipe than we care about. +* Only delete the old file on updating when the installation of the new one is a success +* Make it Ruby 1.8.5 compatible. +* Don't raise an error if a boolean switch is defined multiple times. +* Thor::Options now doesn't parse through things that look like options but aren't. +* Add URI detection to install task, and make sure we don't append ".thor" to URIs +* Add rake2thor to the gem binfiles. +* Make sure local Thorfiles override system-wide ones. diff --git a/railties/lib/vendor/thor-0.11.3/LICENSE b/railties/lib/vendor/thor-0.11.3/LICENSE new file mode 100644 index 0000000000..98722da459 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008 Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file diff --git a/railties/lib/vendor/thor-0.11.3/README.markdown b/railties/lib/vendor/thor-0.11.3/README.markdown new file mode 100644 index 0000000000..a1d7259775 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/README.markdown @@ -0,0 +1,247 @@ +thor +==== + +Map options to a class. Simply create a class with the appropriate annotations +and have options automatically map to functions and parameters. + +Example: + + class App < Thor # [1] + map "-L" => :list # [2] + + desc "install APP_NAME", "install one of the available apps" # [3] + method_options :force => :boolean, :alias => :string # [4] + def install(name) + user_alias = options[:alias] + if options.force? + # do something + end + # other code + end + + desc "list [SEARCH]", "list all of the available apps, limited by SEARCH" + def list(search="") + # list everything + end + end + +Thor automatically maps commands as such: + + thor app:install myname --force + +That gets converted to: + + App.new.install("myname") + # with {'force' => true} as options hash + +1. Inherit from Thor to turn a class into an option mapper +2. Map additional non-valid identifiers to specific methods. In this case, convert -L to :list +3. Describe the method immediately below. The first parameter is the usage information, and the second parameter is the description +4. Provide any additional options that will be available the instance method options. + +Types for `method_options` +-------------------------- + +<dl> + <dt><code>:boolean</code></dt> + <dd>is parsed as --option or --option=true</dd> + <dt><code>:string</code></dt> + <dd>is parsed as --option=VALUE</dd> + <dt><code>:numeric</code></dt> + <dd>is parsed as --option=N</dd> + <dt><code>:array</code></dt> + <dd>is parsed as --option=one two three</dd> + <dt><code>:hash</code></dt> + <dd>is parsed as --option=key:value key:value key:value</dd> +</dl> + +Besides, method_option allows a default value to be given, examples: + + method_options :force => false + #=> Creates a boolean option with default value false + + method_options :alias => "bar" + #=> Creates a string option with default value "bar" + + method_options :threshold => 3.0 + #=> Creates a numeric option with default value 3.0 + +You can also supply :option => :required to mark an option as required. The +type is assumed to be string. If you want a required hash with default values +as option, you can use `method_option` which uses a more declarative style: + + method_option :attributes, :type => :hash, :default => {}, :required => true + +All arguments can be set to nil (except required arguments), by suppling a no or +skip variant. For example: + + thor app name --no-attributes + +In previous versions, aliases for options were created automatically, but now +they should be explicit. You can supply aliases in both short and declarative +styles: + + method_options %w( force -f ) => :boolean + +Or: + + method_option :force, :type => :boolean, :aliases => "-f" + +You can supply as many aliases as you want. + +NOTE: Type :optional available in Thor 0.9.0 was deprecated. Use :string or :boolean instead. + +Namespaces +---------- + +By default, your Thor tasks are invoked using Ruby namespace. In the example +above, tasks are invoked as: + + thor app:install name --force + +However, you could namespace your class as: + + module Sinatra + class App < Thor + # tasks + end + end + +And then you should invoke your tasks as: + + thor sinatra:app:install name --force + +If desired, you can change the namespace: + + module Sinatra + class App < Thor + namespace :myapp + # tasks + end + end + +And then your tasks hould be invoked as: + + thor myapp:install name --force + +Invocations +----------- + +Thor comes with a invocation-dependency system as well which allows a task to be +invoked only once. For example: + + class Counter < Thor + desc "one", "Prints 1, 2, 3" + def one + puts 1 + invoke :two + invoke :three + end + + desc "two", "Prints 2, 3" + def two + puts 2 + invoke :three + end + + desc "three", "Prints 3" + def three + puts 3 + end + end + +When invoking the task one: + + thor counter:one + +The output is "1 2 3", which means that the three task was invoked only once. +You can even invoke tasks from another class, so be sure to check the +documentation. + +Thor::Group +----------- + +Thor has a special class called Thor::Group. The main difference to Thor class +is that it invokes all tasks at once. The example above could be rewritten in +Thor::Group as this: + + class Counter < Thor::Group + desc "Prints 1, 2, 3" + + def one + puts 1 + end + + def two + puts 2 + end + + def three + puts 3 + end + end + +When invoked: + + thor counter + +It prints "1 2 3" as well. Notice you should described (desc) only the class +and not each task anymore. Thor::Group is a great tool to create generators, +since you can define several steps which are invoked in the order they are +defined (Thor::Group is the tool use in generators in Rails 3.0). + +Besides, Thor::Group can parse arguments and options as Thor tasks: + + class Counter < Thor::Group + # number will be available as attr_accessor + argument :number, :type => :numeric, :desc => "The number to start counting" + desc "Prints the 'number' given upto 'number+2'" + + def one + puts number + 0 + end + + def two + puts number + 1 + end + + def three + puts number + 2 + end + end + +The counter above expects one parameter and has the folling outputs: + + thor counter 5 + # Prints "5 6 7" + + thor counter 11 + # Prints "11 12 13" + +You can also give options to Thor::Group, but instead of using `method_option` and +`method_options`, you should use `class_option` and `class_options`. Both argument +and class_options methods are available to Thor class as well. + +Actions +------- + +Thor comes with several actions which helps with script and generator tasks. You +might be familiar with them since some came from Rails Templates. They are: `say`, +`ask`, `yes?`, `no?`, `add_file`, `remove_file`, `copy_file`, `template`, +`directory`, `inside`, `run`, `inject_into_file` and a couple more. + +To use them, you just need to include Thor::Actions in your Thor classes: + + class App < Thor + include Thor::Actions + # tasks + end + +Some actions like copy file requires that a class method called source_root is +defined in your class. This is the directory where your templates should be +placed. Be sure to check the documentation. + +License +------- + +See MIT LICENSE. diff --git a/railties/lib/vendor/thor-0.11.3/bin/rake2thor b/railties/lib/vendor/thor-0.11.3/bin/rake2thor new file mode 100755 index 0000000000..50c7410d80 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/bin/rake2thor @@ -0,0 +1,87 @@ +#!/usr/bin/env ruby + +require 'rubygems' +require 'ruby2ruby' +require 'parse_tree' +if Ruby2Ruby::VERSION >= "1.2.0" + require 'parse_tree_extensions' +end +require 'rake' + +input = ARGV[0] || 'Rakefile' +output = ARGV[1] || 'Thorfile' + +$requires = [] + +module Kernel + def require_with_record(file) + $requires << file if caller[1] =~ /rake2thor:/ + require_without_record file + end + alias_method :require_without_record, :require + alias_method :require, :require_with_record +end + +load input + +@private_methods = [] + +def file_task_name(name) + "compile_" + name.gsub('/', '_slash_').gsub('.', '_dot_').gsub(/\W/, '_') +end + +def method_for_task(task) + file_task = task.is_a?(Rake::FileTask) + comment = task.instance_variable_get('@comment') + prereqs = task.instance_variable_get('@prerequisites').select(&Rake::Task.method(:task_defined?)) + actions = task.instance_variable_get('@actions') + name = task.name.gsub(/^([^:]+:)+/, '') + name = file_task_name(name) if file_task + meth = '' + + meth << "desc #{name.inspect}, #{comment.inspect}\n" if comment + meth << "def #{name}\n" + + meth << prereqs.map do |pre| + pre = pre.to_s + pre = file_task_name(pre) if Rake::Task[pre].is_a?(Rake::FileTask) + ' ' + pre + end.join("\n") + + meth << "\n\n" unless prereqs.empty? || actions.empty? + + meth << actions.map do |act| + act = act.to_ruby + unless act.gsub!(/^proc \{ \|(\w+)\|\n/, + " \\1 = Struct.new(:name).new(#{name.inspect}) # A crude mock Rake::Task object\n") + act.gsub!(/^proc \{\n/, '') + end + act.gsub(/\n\}$/, '') + end.join("\n") + + meth << "\nend" + + if file_task + @private_methods << meth + return + end + + meth +end + +body = Rake::Task.tasks.map(&method(:method_for_task)).compact.map { |meth| meth.gsub(/^/, ' ') }.join("\n\n") + +unless @private_methods.empty? + body << "\n\n private\n\n" + body << @private_methods.map { |meth| meth.gsub(/^/, ' ') }.join("\n\n") +end + +requires = $requires.map { |r| "require #{r.inspect}" }.join("\n") + +File.open(output, 'w') { |f| f.write(<<END.lstrip) } +#{requires} + +class Default < Thor +#{body} +end +END diff --git a/railties/lib/vendor/thor-0.11.3/bin/thor b/railties/lib/vendor/thor-0.11.3/bin/thor new file mode 100755 index 0000000000..eaf849fb4a --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/bin/thor @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +# -*- mode: ruby -*- + +require File.join(File.dirname(__FILE__), '..', 'lib', 'thor') +require 'thor/runner' + +Thor::Runner.start diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor.rb b/railties/lib/vendor/thor-0.11.3/lib/thor.rb new file mode 100644 index 0000000000..f65455cdda --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor.rb @@ -0,0 +1,240 @@ +$:.unshift File.expand_path(File.dirname(__FILE__)) +require 'thor/base' +require 'thor/group' +require 'thor/actions' + +class Thor + class << self + # Sets the default task when thor is executed without an explicit task to be called. + # + # ==== Parameters + # meth<Symbol>:: name of the defaut task + # + def default_task(meth=nil) + case meth + when :none + @default_task = 'help' + when nil + @default_task ||= from_superclass(:default_task, 'help') + else + @default_task = meth.to_s + end + end + + # Defines the usage and the description of the next task. + # + # ==== Parameters + # usage<String> + # description<String> + # + def desc(usage, description, options={}) + if options[:for] + task = find_and_refresh_task(options[:for]) + task.usage = usage if usage + task.description = description if description + else + @usage, @desc = usage, description + end + end + + # Maps an input to a task. If you define: + # + # map "-T" => "list" + # + # Running: + # + # thor -T + # + # Will invoke the list task. + # + # ==== Parameters + # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given task. + # + def map(mappings=nil) + @map ||= from_superclass(:map, {}) + + if mappings + mappings.each do |key, value| + if key.respond_to?(:each) + key.each {|subkey| @map[subkey] = value} + else + @map[key] = value + end + end + end + + @map + end + + # Declares the options for the next task to be declared. + # + # ==== Parameters + # Hash[Symbol => Object]:: The hash key is the name of the option and the value + # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric + # or :required (string). If you give a value, the type of the value is used. + # + def method_options(options=nil) + @method_options ||= {} + build_options(options, @method_options) if options + @method_options + end + + # Adds an option to the set of class options. If :for is given as option, + # it allows you to change the options from a previous defined task. + # + # def previous_task + # # magic + # end + # + # method_options :foo => :bar, :for => :previous_task + # + # def next_task + # # magic + # end + # + # ==== Parameters + # name<Symbol>:: The name of the argument. + # options<Hash>:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :default - Default value for this argument. It cannot be required and have default values. + # :aliases - Aliases for this option. + # :type - The type of the argument, can be :string, :hash, :array, :numeric, :boolean or :default. + # Default accepts arguments as booleans (--switch) or as strings (--switch=VALUE). + # :group - The group for this options. Use by class options to output options in different levels. + # :banner - String to show on usage notes. + # + def method_option(name, options={}) + scope = if options[:for] + find_and_refresh_task(options[:for]).options + else + method_options + end + + build_option(name, options, scope) + end + + # Parses the task and options from the given args, instantiate the class + # and invoke the task. This method is used when the arguments must be parsed + # from an array. If you are inside Ruby and want to use a Thor class, you + # can simply initialize it: + # + # script = MyScript.new(args, options, config) + # script.invoke(:task, first_arg, second_arg, third_arg) + # + def start(given_args=ARGV, config={}) + super do + meth = normalize_task_name(given_args.shift) + task = all_tasks[meth] + + if task + args, opts = Thor::Options.split(given_args) + config.merge!(:task_options => task.options) + else + args, opts = given_args, {} + end + + task ||= Task.dynamic(meth) + trailing = args[Range.new(arguments.size, -1)] + new(args, opts, config).invoke(task, trailing || []) + end + end + + # Prints help information. If a task name is given, it shows information + # only about the specific task. + # + # ==== Parameters + # meth<String>:: An optional task name to print usage information about. + # + # ==== Options + # namespace:: When true, shows the namespace in the output before the usage. + # skip_inherited:: When true, does not show tasks from superclass. + # + def help(shell, meth=nil, options={}) + meth, options = nil, meth if meth.is_a?(Hash) + + if meth + task = all_tasks[meth] + raise UndefinedTaskError, "task '#{meth}' could not be found in namespace '#{self.namespace}'" unless task + + shell.say "Usage:" + shell.say " #{banner(task, options[:namespace])}" + shell.say + class_options_help(shell, "Class") + shell.say task.description + else + list = (options[:short] ? tasks : all_tasks).map do |_, task| + item = [ " " + banner(task, options[:namespace]) ] + item << if task.short_description + "\n # #{task.short_description}\n" + else + "\n" + end + end + + if options[:short] + shell.print_table(list) + else + shell.say "Tasks:" + shell.print_table(list) + class_options_help(shell, "Class") + end + end + end + + protected + + # The banner for this class. You can customize it if you are invoking the + # thor class by another means which is not the Thor::Runner. It receives + # the task that is going to be invoked and if the namespace should be + # displayed. + # + def banner(task, namespace=true) #:nodoc: + task.formatted_usage(self, namespace) + end + + def baseclass #:nodoc: + Thor + end + + def create_task(meth) #:nodoc: + if @usage && @desc + tasks[meth.to_s] = Thor::Task.new(meth, @desc, @usage, method_options) + @usage, @desc, @method_options = nil + true + elsif self.all_tasks[meth.to_s] || meth.to_sym == :method_missing + true + else + puts "[WARNING] Attempted to create task #{meth.inspect} without usage or description. " << + "Call desc if you want this method to be available as task or declare it inside a " << + "no_tasks{} block. Invoked from #{caller[1].inspect}." + false + end + end + + def initialize_added #:nodoc: + class_options.merge!(method_options) + @method_options = nil + end + + # Receives a task name (can be nil), and try to get a map from it. + # If a map can't be found use the sent name or the default task. + # + def normalize_task_name(meth) #:nodoc: + mapping = map[meth.to_s] + meth = mapping || meth || default_task + meth.to_s.gsub('-','_') # treat foo-bar > foo_bar + end + end + + include Thor::Base + + map HELP_MAPPINGS => :help + + desc "help [TASK]", "Describe available tasks or one specific task" + def help(task=nil) + self.class.help(shell, task, :namespace => task && task.include?(?:)) + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions.rb new file mode 100644 index 0000000000..b8cfde1940 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions.rb @@ -0,0 +1,270 @@ +require 'fileutils' + +Dir[File.join(File.dirname(__FILE__), "actions", "*.rb")].each do |action| + require action +end + +class Thor + module Actions + attr_accessor :behavior + + # On inclusion, add some options to base. + # + def self.included(base) #:nodoc: + base.extend ClassMethods + return unless base.respond_to?(:class_option) + + base.class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime, + :desc => "Run but do not make any changes" + + base.class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime, + :desc => "Overwrite files that already exist" + + base.class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime, + :desc => "Skip files that already exist" + + base.class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime, + :desc => "Supress status output" + end + + module ClassMethods + # Hold source paths for one Thor instance. source_paths_for_search is the + # method responsible to gather source_paths from this current class, + # inherited paths and the source root. + # + def source_paths + @source_paths ||= [] + end + + # Returns the source paths in the following order: + # + # 1) This class source paths + # 2) Source root + # 3) Parents source paths + # + def source_paths_for_search + @source_paths_for_search ||= begin + paths = [] + paths += self.source_paths + paths << self.source_root if self.respond_to?(:source_root) + paths += from_superclass(:source_paths, []) + paths + end + end + end + + # Extends initializer to add more configuration options. + # + # ==== Configuration + # behavior<Symbol>:: The actions default behavior. Can be :invoke or :revoke. + # It also accepts :force, :skip and :pretend to set the behavior + # and the respective option. + # + # destination_root<String>:: The root directory needed for some actions. It's also known + # as destination root. + # + def initialize(args=[], options={}, config={}) + self.behavior = case config[:behavior].to_s + when "force", "skip" + _cleanup_options_and_set(options, config[:behavior]) + :invoke + when "revoke" + :revoke + else + :invoke + end + + super + self.destination_root = config[:destination_root] + end + + # Wraps an action object and call it accordingly to the thor class behavior. + # + def action(instance) + if behavior == :revoke + instance.revoke! + else + instance.invoke! + end + end + + # Returns the root for this thor class (also aliased as destination root). + # + def destination_root + @destination_stack.last + end + + # Sets the root for this thor class. Relatives path are added to the + # directory where the script was invoked and expanded. + # + def destination_root=(root) + @destination_stack ||= [] + @destination_stack[0] = File.expand_path(root || '') + end + + # Returns the given path relative to the absolute root (ie, root where + # the script started). + # + def relative_to_original_destination_root(path, remove_dot=true) + path = path.gsub(@destination_stack[0], '.') + remove_dot ? (path[2..-1] || '') : path + end + + # Receives a file or directory and search for it in the source paths. + # + def find_in_source_paths(file) + relative_root = relative_to_original_destination_root(destination_root, false) + paths = self.class.source_paths_for_search + + paths.each do |source| + source_file = File.expand_path(file, File.join(source, relative_root)) + return source_file if File.exists?(source_file) + end + + if paths.empty? + raise Error, "You don't have any source path defined for class #{self.class.name}. To fix this, " << + "you can define a source_root in your class." + else + raise Error, "Could not find #{file.inspect} in source paths." + end + end + + # Do something in the root or on a provided subfolder. If a relative path + # is given it's referenced from the current root. The full path is yielded + # to the block you provide. The path is set back to the previous path when + # the method exits. + # + # ==== Parameters + # dir<String>:: the directory to move to. + # config<Hash>:: give :verbose => true to log and use padding. + # + def inside(dir='', config={}, &block) + verbose = config.fetch(:verbose, false) + + say_status :inside, dir, verbose + shell.padding += 1 if verbose + @destination_stack.push File.expand_path(dir, destination_root) + + FileUtils.mkdir_p(destination_root) unless File.exist?(destination_root) + FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield } + + @destination_stack.pop + shell.padding -= 1 if verbose + end + + # Goes to the root and execute the given block. + # + def in_root + inside(@destination_stack.first) { yield } + end + + # Loads an external file and execute it in the instance binding. + # + # ==== Parameters + # path<String>:: The path to the file to execute. Can be a web address or + # a relative path from the source root. + # + # ==== Examples + # + # apply "http://gist.github.com/103208" + # + # apply "recipes/jquery.rb" + # + def apply(path, config={}) + verbose = config.fetch(:verbose, true) + path = find_in_source_paths(path) unless path =~ /^http\:\/\// + + say_status :apply, path, verbose + shell.padding += 1 if verbose + instance_eval(open(path).read) + shell.padding -= 1 if verbose + end + + # Executes a command. + # + # ==== Parameters + # command<String>:: the command to be executed. + # config<Hash>:: give :verbose => false to not log the status. Specify :with + # to append an executable to command executation. + # + # ==== Example + # + # inside('vendor') do + # run('ln -s ~/edge rails') + # end + # + def run(command, config={}) + return unless behavior == :invoke + + destination = relative_to_original_destination_root(destination_root, false) + desc = "#{command} from #{destination.inspect}" + + if config[:with] + desc = "#{File.basename(config[:with].to_s)} #{desc}" + command = "#{config[:with]} #{command}" + end + + say_status :run, desc, config.fetch(:verbose, true) + `#{command}` unless options[:pretend] + end + + # Executes a ruby script (taking into account WIN32 platform quirks). + # + # ==== Parameters + # command<String>:: the command to be executed. + # config<Hash>:: give :verbose => false to not log the status. + # + def run_ruby_script(command, config={}) + return unless behavior == :invoke + run "#{command}", config.merge(:with => Thor::Util.ruby_command) + end + + # Run a thor command. A hash of options can be given and it's converted to + # switches. + # + # ==== Parameters + # task<String>:: the task to be invoked + # args<Array>:: arguments to the task + # config<Hash>:: give :verbose => false to not log the status. Other options + # are given as parameter to Thor. + # + # ==== Examples + # + # thor :install, "http://gist.github.com/103208" + # #=> thor install http://gist.github.com/103208 + # + # thor :list, :all => true, :substring => 'rails' + # #=> thor list --all --substring=rails + # + def thor(task, *args) + config = args.last.is_a?(Hash) ? args.pop : {} + verbose = config.key?(:verbose) ? config.delete(:verbose) : true + + args.unshift task + args.push Thor::Options.to_switches(config) + command = args.join(' ').strip + + run command, :with => :thor, :verbose => verbose + end + + protected + + # Allow current root to be shared between invocations. + # + def _shared_configuration #:nodoc: + super.merge!(:destination_root => self.destination_root) + end + + def _cleanup_options_and_set(options, key) #:nodoc: + case options + when Array + %w(--force -f --skip -s).each { |i| options.delete(i) } + options << "--#{key}" + when Hash + [:force, :skip, "force", "skip"].each { |i| options.delete(i) } + options.merge!(key => true) + end + end + + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions/create_file.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/create_file.rb new file mode 100644 index 0000000000..8f6badee27 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/create_file.rb @@ -0,0 +1,102 @@ +require 'thor/actions/empty_directory' + +class Thor + module Actions + + # Create a new file relative to the destination root with the given data, + # which is the return value of a block or a data string. + # + # ==== Parameters + # destination<String>:: the relative path to the destination root. + # data<String|NilClass>:: the data to append to the file. + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Examples + # + # create_file "lib/fun_party.rb" do + # hostname = ask("What is the virtual hostname I should use?") + # "vhost.name = #{hostname}" + # end + # + # create_file "config/apach.conf", "your apache config" + # + def create_file(destination, data=nil, config={}, &block) + action CreateFile.new(self, destination, block || data.to_s, config) + end + alias :add_file :create_file + + # AddFile is a subset of Template, which instead of rendering a file with + # ERB, it gets the content from the user. + # + class CreateFile < EmptyDirectory #:nodoc: + attr_reader :data + + def initialize(base, destination, data, config={}) + @data = data + super(base, destination, config) + end + + # Checks if the content of the file at the destination is identical to the rendered result. + # + # ==== Returns + # Boolean:: true if it is identical, false otherwise. + # + def identical? + exists? && File.read(destination) == render + end + + # Holds the content to be added to the file. + # + def render + @render ||= if data.is_a?(Proc) + data.call + else + data + end + end + + def invoke! + invoke_with_conflict_check do + FileUtils.mkdir_p(File.dirname(destination)) + File.open(destination, 'w'){ |f| f.write render } + end + end + + protected + + # Now on conflict we check if the file is identical or not. + # + def on_conflict_behavior(&block) + if identical? + say_status :identical, :blue + else + options = base.options.merge(config) + force_or_skip_or_conflict(options[:force], options[:skip], &block) + end + end + + # If force is true, run the action, otherwise check if it's not being + # skipped. If both are false, show the file_collision menu, if the menu + # returns true, force it, otherwise skip. + # + def force_or_skip_or_conflict(force, skip, &block) + if force + say_status :force, :yellow + block.call unless pretend? + elsif skip + say_status :skip, :yellow + else + say_status :conflict, :red + force_or_skip_or_conflict(force_on_collision?, true, &block) + end + end + + # Shows the file collision menu to the user and gets the result. + # + def force_on_collision? + base.shell.file_collision(destination){ render } + end + + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions/directory.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/directory.rb new file mode 100644 index 0000000000..e33639f4e5 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/directory.rb @@ -0,0 +1,87 @@ +require 'thor/actions/empty_directory' + +class Thor + module Actions + + # Copies interactively the files from source directory to root directory. + # If any of the files finishes with .tt, it's considered to be a template + # and is placed in the destination without the extension .tt. If any + # empty directory is found, it's copied and all .empty_directory files are + # ignored. Remember that file paths can also be encoded, let's suppose a doc + # directory with the following files: + # + # doc/ + # components/.empty_directory + # README + # rdoc.rb.tt + # %app_name%.rb + # + # When invoked as: + # + # directory "doc" + # + # It will create a doc directory in the destination with the following + # files (assuming that the app_name is "blog"): + # + # doc/ + # components/ + # README + # rdoc.rb + # blog.rb + # + # ==== Parameters + # source<String>:: the relative path to the source root. + # destination<String>:: the relative path to the destination root. + # config<Hash>:: give :verbose => false to not log the status. + # If :recursive => false, does not look for paths recursively. + # + # ==== Examples + # + # directory "doc" + # directory "doc", "docs", :recursive => false + # + def directory(source, destination=nil, config={}) + action Directory.new(self, source, destination || source, config) + end + + class Directory < EmptyDirectory #:nodoc: + attr_reader :source + + def initialize(base, source, destination=nil, config={}) + @source = File.expand_path(base.find_in_source_paths(source.to_s)) + super(base, destination, { :recursive => true }.merge(config)) + end + + def invoke! + base.empty_directory given_destination, config + execute! + end + + def revoke! + execute! + end + + protected + + def execute! + lookup = config[:recursive] ? File.join(source, '**') : source + lookup = File.join(lookup, '{*,.[a-z]*}') + + Dir[lookup].each do |file_source| + next if File.directory?(file_source) + file_destination = File.join(given_destination, file_source.gsub(source, '.')) + + case file_source + when /\.empty_directory$/ + base.empty_directory(File.dirname(file_destination), config) + when /\.tt$/ + base.template(file_source, file_destination[0..-4], config) + else + base.copy_file(file_source, file_destination, config) + end + end + end + + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions/empty_directory.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/empty_directory.rb new file mode 100644 index 0000000000..03c1fe4af1 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/empty_directory.rb @@ -0,0 +1,133 @@ +class Thor + module Actions + + # Creates an empty directory. + # + # ==== Parameters + # destination<String>:: the relative path to the destination root. + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Examples + # + # empty_directory "doc" + # + def empty_directory(destination, config={}) + action EmptyDirectory.new(self, destination, config) + end + + # Class which holds create directory logic. This is the base class for + # other actions like create_file and directory. + # + # This implementation is based in Templater actions, created by Jonas Nicklas + # and Michael S. Klishin under MIT LICENSE. + # + class EmptyDirectory #:nodoc: + attr_reader :base, :destination, :given_destination, :relative_destination, :config + + # Initializes given the source and destination. + # + # ==== Parameters + # base<Thor::Base>:: A Thor::Base instance + # source<String>:: Relative path to the source of this file + # destination<String>:: Relative path to the destination of this file + # config<Hash>:: give :verbose => false to not log the status. + # + def initialize(base, destination, config={}) + @base, @config = base, { :verbose => true }.merge(config) + self.destination = destination + end + + # Checks if the destination file already exists. + # + # ==== Returns + # Boolean:: true if the file exists, false otherwise. + # + def exists? + ::File.exists?(destination) + end + + def invoke! + invoke_with_conflict_check do + ::FileUtils.mkdir_p(destination) + end + end + + def revoke! + say_status :remove, :red + ::FileUtils.rm_rf(destination) if !pretend? && exists? + end + + protected + + # Shortcut for pretend. + # + def pretend? + base.options[:pretend] + end + + # Sets the absolute destination value from a relative destination value. + # It also stores the given and relative destination. Let's suppose our + # script is being executed on "dest", it sets the destination root to + # "dest". The destination, given_destination and relative_destination + # are related in the following way: + # + # inside "bar" do + # empty_directory "baz" + # end + # + # destination #=> dest/bar/baz + # relative_destination #=> bar/baz + # given_destination #=> baz + # + def destination=(destination) + if destination + @given_destination = convert_encoded_instructions(destination.to_s) + @destination = ::File.expand_path(@given_destination, base.destination_root) + @relative_destination = base.relative_to_original_destination_root(@destination) + end + end + + # Filenames in the encoded form are converted. If you have a file: + # + # %class_name%.rb + # + # It gets the class name from the base and replace it: + # + # user.rb + # + def convert_encoded_instructions(filename) + filename.gsub(/%(.*?)%/) do |string| + instruction = $1.strip + base.respond_to?(instruction) ? base.send(instruction) : string + end + end + + # Receives a hash of options and just execute the block if some + # conditions are met. + # + def invoke_with_conflict_check(&block) + if exists? + on_conflict_behavior(&block) + else + say_status :create, :green + block.call unless pretend? + end + + destination + end + + # What to do when the destination file already exists. + # + def on_conflict_behavior(&block) + say_status :exist, :blue + end + + # Shortcut to say_status shell method. + # + def say_status(status, color) + base.shell.say_status status, relative_destination, color if config[:verbose] + end + + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions/file_manipulation.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/file_manipulation.rb new file mode 100644 index 0000000000..74c157ba8c --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/file_manipulation.rb @@ -0,0 +1,195 @@ +require 'erb' +require 'open-uri' + +class Thor + module Actions + + # Copies the file from the relative source to the relative destination. If + # the destination is not given it's assumed to be equal to the source. + # + # ==== Parameters + # source<String>:: the relative path to the source root. + # destination<String>:: the relative path to the destination root. + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Examples + # + # copy_file "README", "doc/README" + # + # copy_file "doc/README" + # + def copy_file(source, destination=nil, config={}) + destination ||= source + source = File.expand_path(find_in_source_paths(source.to_s)) + + create_file destination, nil, config do + File.read(source) + end + end + + # Gets the content at the given address and places it at the given relative + # destination. If a block is given instead of destination, the content of + # the url is yielded and used as location. + # + # ==== Parameters + # source<String>:: the address of the given content. + # destination<String>:: the relative path to the destination root. + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Examples + # + # get "http://gist.github.com/103208", "doc/README" + # + # get "http://gist.github.com/103208" do |content| + # content.split("\n").first + # end + # + def get(source, destination=nil, config={}, &block) + source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^http\:\/\// + render = open(source).read + + destination ||= if block_given? + block.arity == 1 ? block.call(render) : block.call + else + File.basename(source) + end + + create_file destination, render, config + end + + # Gets an ERB template at the relative source, executes it and makes a copy + # at the relative destination. If the destination is not given it's assumed + # to be equal to the source removing .tt from the filename. + # + # ==== Parameters + # source<String>:: the relative path to the source root. + # destination<String>:: the relative path to the destination root. + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Examples + # + # template "README", "doc/README" + # + # template "doc/README" + # + def template(source, destination=nil, config={}) + destination ||= source + source = File.expand_path(find_in_source_paths(source.to_s)) + context = instance_eval('binding') + + create_file destination, nil, config do + ERB.new(::File.read(source), nil, '-').result(context) + end + end + + # Changes the mode of the given file or directory. + # + # ==== Parameters + # mode<Integer>:: the file mode + # path<String>:: the name of the file to change mode + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Example + # + # chmod "script/*", 0755 + # + def chmod(path, mode, config={}) + return unless behavior == :invoke + path = File.expand_path(path, destination_root) + say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true) + FileUtils.chmod_R(mode, path) unless options[:pretend] + end + + # Prepend text to a file. + # + # ==== Parameters + # path<String>:: path of the file to be changed + # data<String>:: the data to prepend to the file, can be also given as a block. + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Example + # + # prepend_file 'config/environments/test.rb', 'config.gem "rspec"' + # + def prepend_file(path, data=nil, config={}, &block) + return unless behavior == :invoke + path = File.expand_path(path, destination_root) + say_status :prepend, relative_to_original_destination_root(path), config.fetch(:verbose, true) + + unless options[:pretend] + content = data || block.call + content << File.read(path) + File.open(path, 'wb') { |file| file.write(content) } + end + end + + # Append text to a file. + # + # ==== Parameters + # path<String>:: path of the file to be changed + # data<String>:: the data to append to the file, can be also given as a block. + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Example + # + # append_file 'config/environments/test.rb', 'config.gem "rspec"' + # + def append_file(path, data=nil, config={}, &block) + return unless behavior == :invoke + path = File.expand_path(path, destination_root) + say_status :append, relative_to_original_destination_root(path), config.fetch(:verbose, true) + File.open(path, 'ab') { |file| file.write(data || block.call) } unless options[:pretend] + end + + # Run a regular expression replacement on a file. + # + # ==== Parameters + # path<String>:: path of the file to be changed + # flag<Regexp|String>:: the regexp or string to be replaced + # replacement<String>:: the replacement, can be also given as a block + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Example + # + # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' + # + # gsub_file 'README', /rake/, :green do |match| + # match << " no more. Use thor!" + # end + # + def gsub_file(path, flag, *args, &block) + return unless behavior == :invoke + config = args.last.is_a?(Hash) ? args.pop : {} + + path = File.expand_path(path, destination_root) + say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true) + + unless options[:pretend] + content = File.read(path) + content.gsub!(flag, *args, &block) + File.open(path, 'wb') { |file| file.write(content) } + end + end + + # Removes a file at the given location. + # + # ==== Parameters + # path<String>:: path of the file to be changed + # config<Hash>:: give :verbose => false to not log the status. + # + # ==== Example + # + # remove_file 'README' + # remove_file 'app/controllers/application_controller.rb' + # + def remove_file(path, config={}) + return unless behavior == :invoke + path = File.expand_path(path, destination_root) + + say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true) + ::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path) + end + alias :remove_dir :remove_file + + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/actions/inject_into_file.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/inject_into_file.rb new file mode 100644 index 0000000000..089bd894e4 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/actions/inject_into_file.rb @@ -0,0 +1,78 @@ +require 'thor/actions/empty_directory' + +class Thor + module Actions + + # Injects the given content into a file. Different from append_file, + # prepend_file and gsub_file, this method is reversible. By this reason, + # the flag can only be strings. gsub_file is your friend if you need to + # deal with more complex cases. + # + # ==== Parameters + # destination<String>:: Relative path to the destination root + # data<String>:: Data to add to the file. Can be given as a block. + # config<Hash>:: give :verbose => false to not log the status and the flag + # for injection (:after or :before). + # + # ==== Examples + # + # inject_into_file "config/environment.rb", "config.gem thor", :after => "Rails::Initializer.run do |config|\n" + # + # inject_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do + # gems = ask "Which gems would you like to add?" + # gems.split(" ").map{ |gem| " config.gem #{gem}" }.join("\n") + # end + # + def inject_into_file(destination, *args, &block) + if block_given? + data, config = block, args.shift + else + data, config = args.shift, args.shift + end + + log_status = args.empty? || args.pop + action InjectIntoFile.new(self, destination, data, config) + end + + class InjectIntoFile < EmptyDirectory + attr_reader :flag, :replacement + + def initialize(base, destination, data, config) + super(base, destination, { :verbose => true }.merge(config)) + + data = data.call if data.is_a?(Proc) + + @replacement = if @config.key?(:after) + @flag = @config.delete(:after) + @flag + data + else + @flag = @config.delete(:before) + data + @flag + end + end + + def invoke! + say_status :inject, config[:verbose] + replace!(flag, replacement) + end + + def revoke! + say_status :deinject, config[:verbose] + replace!(replacement, flag) + end + + protected + + # Adds the content to the file. + # + def replace!(regexp, string) + unless base.options[:pretend] + content = File.read(destination) + content.gsub!(regexp, string) + File.open(destination, 'wb') { |file| file.write(content) } + end + end + + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/base.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/base.rb new file mode 100644 index 0000000000..0bdcc1f4d5 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/base.rb @@ -0,0 +1,516 @@ +require 'thor/core_ext/hash_with_indifferent_access' +require 'thor/core_ext/ordered_hash' +require 'thor/error' +require 'thor/shell' +require 'thor/invocation' +require 'thor/parser' +require 'thor/task' +require 'thor/util' + +class Thor + HELP_MAPPINGS = %w(-h -? --help -D) + THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root + action add_file create_file in_root inside run run_ruby_script) + + module Base + attr_accessor :options + + # It receives arguments in an Array and two hashes, one for options and + # other for configuration. + # + # Notice that it does not check if all required arguments were supplied. + # It should be done by the parser. + # + # ==== Parameters + # args<Array[Object]>:: An array of objects. The objects are applied to their + # respective accessors declared with <tt>argument</tt>. + # + # options<Hash>:: An options hash that will be available as self.options. + # The hash given is converted to a hash with indifferent + # access, magic predicates (options.skip?) and then frozen. + # + # config<Hash>:: Configuration for this Thor class. + # + def initialize(args=[], options={}, config={}) + Thor::Arguments.parse(self.class.arguments, args).each do |key, value| + send("#{key}=", value) + end + + parse_options = self.class.class_options + + if options.is_a?(Array) + task_options = config.delete(:task_options) # hook for start + parse_options = parse_options.merge(task_options) if task_options + array_options, hash_options = options, {} + else + array_options, hash_options = [], options + end + + options = Thor::Options.parse(parse_options, array_options) + self.options = Thor::CoreExt::HashWithIndifferentAccess.new(options).merge!(hash_options) + self.options.freeze + end + + class << self + def included(base) #:nodoc: + base.send :extend, ClassMethods + base.send :include, Invocation + base.send :include, Shell + end + + # Returns the classes that inherits from Thor or Thor::Group. + # + # ==== Returns + # Array[Class] + # + def subclasses + @subclasses ||= [] + end + + # Returns the files where the subclasses are kept. + # + # ==== Returns + # Hash[path<String> => Class] + # + def subclass_files + @subclass_files ||= Hash.new{ |h,k| h[k] = [] } + end + + # Whenever a class inherits from Thor or Thor::Group, we should track the + # class and the file on Thor::Base. This is the method responsable for it. + # Also adds the source root to the source paths if the klass respond to it. + # + def register_klass_file(klass) #:nodoc: + file = caller[1].match(/(.*):\d+/)[1] + Thor::Base.subclasses << klass unless Thor::Base.subclasses.include?(klass) + + file_subclasses = Thor::Base.subclass_files[File.expand_path(file)] + file_subclasses << klass unless file_subclasses.include?(klass) + end + end + + module ClassMethods + # Adds an argument to the class and creates an attr_accessor for it. + # + # Arguments are different from options in several aspects. The first one + # is how they are parsed from the command line, arguments are retrieved + # from position: + # + # thor task NAME + # + # Instead of: + # + # thor task --name=NAME + # + # Besides, arguments are used inside your code as an accessor (self.argument), + # while options are all kept in a hash (self.options). + # + # Finally, arguments cannot have type :default or :boolean but can be + # optional (supplying :optional => :true or :required => false), although + # you cannot have a required argument after a non-required argument. If you + # try it, an error is raised. + # + # ==== Parameters + # name<Symbol>:: The name of the argument. + # options<Hash>:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :optional - If the argument is optional or not. + # :type - The type of the argument, can be :string, :hash, :array, :numeric. + # :default - Default value for this argument. It cannot be required and have default values. + # :banner - String to show on usage notes. + # + # ==== Errors + # ArgumentError:: Raised if you supply a required argument after a non required one. + # + def argument(name, options={}) + is_thor_reserved_word?(name, :argument) + no_tasks { attr_accessor name } + + required = if options.key?(:optional) + !options[:optional] + elsif options.key?(:required) + options[:required] + else + options[:default].nil? + end + + remove_argument name + + arguments.each do |argument| + next if argument.required? + raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " << + "the non-required argument #{argument.human_name.inspect}." + end if required + + arguments << Thor::Argument.new(name, options[:desc], required, options[:type], + options[:default], options[:banner]) + end + + # Returns this class arguments, looking up in the ancestors chain. + # + # ==== Returns + # Array[Thor::Argument] + # + def arguments + @arguments ||= from_superclass(:arguments, []) + end + + # Adds a bunch of options to the set of class options. + # + # class_options :foo => false, :bar => :required, :baz => :string + # + # If you prefer more detailed declaration, check class_option. + # + # ==== Parameters + # Hash[Symbol => Object] + # + def class_options(options=nil) + @class_options ||= from_superclass(:class_options, {}) + build_options(options, @class_options) if options + @class_options + end + + # Adds an option to the set of class options + # + # ==== Parameters + # name<Symbol>:: The name of the argument. + # options<Hash>:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :default - Default value for this argument. + # :group - The group for this options. Use by class options to output options in different levels. + # :aliases - Aliases for this option. + # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. + # :banner - String to show on usage notes. + # + def class_option(name, options={}) + build_option(name, options, class_options) + end + + # Removes a previous defined argument. If :undefine is given, undefine + # accessors as well. + # + # ==== Paremeters + # names<Array>:: Arguments to be removed + # + # ==== Examples + # + # remove_argument :foo + # remove_argument :foo, :bar, :baz, :undefine => true + # + def remove_argument(*names) + options = names.last.is_a?(Hash) ? names.pop : {} + + names.each do |name| + arguments.delete_if { |a| a.name == name.to_s } + undef_method name, "#{name}=" if options[:undefine] + end + end + + # Removes a previous defined class option. + # + # ==== Paremeters + # names<Array>:: Class options to be removed + # + # ==== Examples + # + # remove_class_option :foo + # remove_class_option :foo, :bar, :baz + # + def remove_class_option(*names) + names.each do |name| + class_options.delete(name) + end + end + + # Defines the group. This is used when thor list is invoked so you can specify + # that only tasks from a pre-defined group will be shown. Defaults to standard. + # + # ==== Parameters + # name<String|Symbol> + # + def group(name=nil) + case name + when nil + @group ||= from_superclass(:group, 'standard') + else + @group = name.to_s + end + end + + # Returns the tasks for this Thor class. + # + # ==== Returns + # OrderedHash:: An ordered hash with this class tasks. + # + def tasks + @tasks ||= Thor::CoreExt::OrderedHash.new + end + + # Returns the tasks for this Thor class and all subclasses. + # + # ==== Returns + # OrderedHash + # + def all_tasks + @all_tasks ||= from_superclass(:all_tasks, Thor::CoreExt::OrderedHash.new) + @all_tasks.merge(tasks) + end + + # Removes a given task from this Thor class. This is usually done if you + # are inheriting from another class and don't want it to be available + # anymore. + # + # By default it only remove the mapping to the task. But you can supply + # :undefine => true to undefine the method from the class as well. + # + # ==== Parameters + # name<Symbol|String>:: The name of the task to be removed + # options<Hash>:: You can give :undefine => true if you want tasks the method + # to be undefined from the class as well. + # + def remove_task(*names) + options = names.last.is_a?(Hash) ? names.pop : {} + + names.each do |name| + tasks.delete(name.to_s) + all_tasks.delete(name.to_s) + undef_method name if options[:undefine] + end + end + + # All methods defined inside the given block are not added as tasks. + # + # So you can do: + # + # class MyScript < Thor + # no_tasks do + # def this_is_not_a_task + # end + # end + # end + # + # You can also add the method and remove it from the task list: + # + # class MyScript < Thor + # def this_is_not_a_task + # end + # remove_task :this_is_not_a_task + # end + # + def no_tasks + @no_tasks = true + yield + @no_tasks = false + end + + # Sets the namespace for the Thor or Thor::Group class. By default the + # namespace is retrieved from the class name. If your Thor class is named + # Scripts::MyScript, the help method, for example, will be called as: + # + # thor scripts:my_script -h + # + # If you change the namespace: + # + # namespace :my_scripts + # + # You change how your tasks are invoked: + # + # thor my_scripts -h + # + # Finally, if you change your namespace to default: + # + # namespace :default + # + # Your tasks can be invoked with a shortcut. Instead of: + # + # thor :my_task + # + def namespace(name=nil) + case name + when nil + @namespace ||= Thor::Util.constant_to_namespace(self, false) + else + @namespace = name.to_s + end + end + + # Default way to start generators from the command line. + # + def start(given_args=ARGV, config={}) #:nodoc: + config[:shell] ||= Thor::Base.shell.new + yield + rescue Thor::Error => e + if given_args.include?("--debug") + raise e + else + config[:shell].error e.message + end + end + + protected + + # Prints the class options per group. If an option does not belong to + # any group, it uses the ungrouped name value. This method provide to + # hooks to add extra options, one of them if the third argument called + # extra_group that should be a hash in the format :group => Array[Options]. + # + # The second is by returning a lamda used to print values. The lambda + # requires two options: the group name and the array of options. + # + def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc: + groups = {} + + class_options.each do |_, value| + groups[value.group] ||= [] + groups[value.group] << value + end + + printer = proc do |group_name, options| + list = [] + padding = options.collect{ |o| o.aliases.size }.max.to_i * 4 + + options.each do |option| + item = [ option.usage(padding) ] + + item << if option.description + "# #{option.description}" + else + "" + end + + list << item + list << [ "", "# Default: #{option.default}" ] if option.show_default? + end + + unless list.empty? + if group_name + shell.say "#{group_name} options:" + else + shell.say "Options:" + end + + shell.print_table(list, :ident => 2) + shell.say "" + end + end + + # Deal with default group + global_options = groups.delete(nil) || [] + printer.call(ungrouped_name, global_options) if global_options + + # Print all others + groups = extra_group.merge(groups) if extra_group + groups.each(&printer) + printer + end + + # Raises an error if the word given is a Thor reserved word. + # + def is_thor_reserved_word?(word, type) + return false unless THOR_RESERVED_WORDS.include?(word.to_s) + raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}" + end + + # Build an option and adds it to the given scope. + # + # ==== Parameters + # name<Symbol>:: The name of the argument. + # options<Hash>:: Described in both class_option and method_option. + # + def build_option(name, options, scope) + scope[name] = Thor::Option.new(name, options[:desc], options[:required], + options[:type], options[:default], options[:banner], + options[:group], options[:aliases]) + end + + # Receives a hash of options, parse them and add to the scope. This is a + # fast way to set a bunch of options: + # + # build_options :foo => true, :bar => :required, :baz => :string + # + # ==== Parameters + # Hash[Symbol => Object] + # + def build_options(options, scope) + options.each do |key, value| + scope[key] = Thor::Option.parse(key, value) + end + end + + # Finds a task with the given name. If the task belongs to the current + # class, just return it, otherwise dup it and add the fresh copy to the + # current task hash. + # + def find_and_refresh_task(name) + task = if task = tasks[name.to_s] + task + elsif task = all_tasks[name.to_s] + tasks[name.to_s] = task.clone + else + raise ArgumentError, "You supplied :for => #{name.inspect}, but the task #{name.inspect} could not be found." + end + end + + # Everytime someone inherits from a Thor class, register the klass + # and file into baseclass. + # + def inherited(klass) + Thor::Base.register_klass_file(klass) + end + + # Fire this callback whenever a method is added. Added methods are + # tracked as tasks if the requirements set by valid_task? are valid. + # + def method_added(meth) + meth = meth.to_s + + if meth == "initialize" + initialize_added + return + end + + # Return if it's not a public instance method + return unless public_instance_methods.include?(meth) || + public_instance_methods.include?(meth.to_sym) + + return if @no_tasks || !create_task(meth) + + is_thor_reserved_word?(meth, :task) + Thor::Base.register_klass_file(self) + end + + # Retrieves a value from superclass. If it reaches the baseclass, + # returns nil. + # + def from_superclass(method, default=nil) + if self == baseclass || !superclass.respond_to?(method, true) + default + else + value = superclass.send(method) + value.dup if value + end + end + + # SIGNATURE: Sets the baseclass. This is where the superclass lookup + # finishes. + def baseclass #:nodoc: + end + + # SIGNATURE: Creates a new task if valid_task? is true. This method is + # called when a new method is added to the class. + def create_task(meth) #:nodoc: + end + + # SIGNATURE: Defines behavior when the initialize method is added to the + # class. + def initialize_added #:nodoc: + end + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/hash_with_indifferent_access.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/hash_with_indifferent_access.rb new file mode 100644 index 0000000000..3213961fe4 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/hash_with_indifferent_access.rb @@ -0,0 +1,75 @@ +class Thor + module CoreExt + + # A hash with indifferent access and magic predicates. + # + # hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true + # + # hash[:foo] #=> 'bar' + # hash['foo'] #=> 'bar' + # hash.foo? #=> true + # + class HashWithIndifferentAccess < ::Hash + + def initialize(hash={}) + super() + hash.each do |key, value| + self[convert_key(key)] = value + end + end + + def [](key) + super(convert_key(key)) + end + + def []=(key, value) + super(convert_key(key), value) + end + + def delete(key) + super(convert_key(key)) + end + + def values_at(*indices) + indices.collect { |key| self[convert_key(key)] } + end + + def merge(other) + dup.merge!(other) + end + + def merge!(other) + other.each do |key, value| + self[convert_key(key)] = value + end + self + end + + protected + + def convert_key(key) + key.is_a?(Symbol) ? key.to_s : key + end + + # Magic predicates. For instance: + # + # options.force? # => !!options['force'] + # options.shebang # => "/usr/lib/local/ruby" + # options.test_framework?(:rspec) # => options[:test_framework] == :rspec + # + def method_missing(method, *args, &block) + method = method.to_s + if method =~ /^(\w+)\?$/ + if args.empty? + !!self[$1] + else + self[$1] == args.first + end + else + self[method] + end + end + + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/ordered_hash.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/ordered_hash.rb new file mode 100644 index 0000000000..5e4ad5609f --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/core_ext/ordered_hash.rb @@ -0,0 +1,102 @@ +require 'forwardable' + +class Thor #:nodoc: + module CoreExt #:nodoc: + + if RUBY_VERSION >= '1.9' + class OrderedHash < ::Hash + end + else + # This class is based on the Ruby 1.9 ordered hashes. + # + # It keeps the semantics and most of the efficiency of normal hashes + # while also keeping track of the order in which elements were set. + # + class OrderedHash #:nodoc: + include Enumerable + + Node = Struct.new(:key, :value, :next, :prev) + + def initialize + @hash = {} + end + + def [](key) + @hash[key] && @hash[key].value + end + + def []=(key, value) + if node = @hash[key] + node.value = value + else + node = Node.new(key, value) + + if @first.nil? + @first = @last = node + else + node.prev = @last + @last.next = node + @last = node + end + end + + @hash[key] = node + value + end + + def delete(key) + if node = @hash[key] + prev_node = node.prev + next_node = node.next + + next_node.prev = prev_node if next_node + prev_node.next = next_node if prev_node + + @first = next_node if @first == node + @last = prev_node if @last == node + + value = node.value + end + + @hash.delete(key) + value + end + + def keys + self.map { |k, v| k } + end + + def values + self.map { |k, v| v } + end + + def each + return unless @first + yield [@first.key, @first.value] + node = @first + yield [node.key, node.value] while node = node.next + self + end + + def merge(other) + hash = self.class.new + + self.each do |key, value| + hash[key] = value + end + + other.each do |key, value| + hash[key] = value + end + + hash + end + + def empty? + @hash.empty? + end + end + end + + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/error.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/error.rb new file mode 100644 index 0000000000..c846e9ce74 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/error.rb @@ -0,0 +1,27 @@ +class Thor + # Thor::Error is raised when it's caused by the user invoking the task and + # only errors that inherit from it are rescued. + # + # So, for example, if the developer declares a required argument after an + # option, it should raise an ::ArgumentError and not ::Thor::ArgumentError, + # because it was caused by the developer and not the "final user". + # + class Error < StandardError #:nodoc: + end + + # Raised when a task was not found. + # + class UndefinedTaskError < Error #:nodoc: + end + + # Raised when a task was found, but not invoked properly. + # + class InvocationError < Error #:nodoc: + end + + class RequiredArgumentMissingError < InvocationError #:nodoc: + end + + class MalformattedArgumentError < InvocationError #:nodoc: + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/group.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/group.rb new file mode 100644 index 0000000000..1be1c35ba5 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/group.rb @@ -0,0 +1,263 @@ +# Thor has a special class called Thor::Group. The main difference to Thor class +# is that it invokes all tasks at once. It also include some methods that allows +# invocations to be done at the class method, which are not available to Thor +# tasks. +# +class Thor::Group + class << self + # The descrition for this Thor::Group. If none is provided, but a source root + # exists, tries to find the USAGE one folder above it, otherwise searches + # in the superclass. + # + # ==== Parameters + # description<String>:: The description for this Thor::Group. + # + def desc(description=nil) + case description + when nil + @desc ||= from_superclass(:desc, nil) + else + @desc = description + end + end + + # Start works differently in Thor::Group, it simply invokes all tasks + # inside the class. + # + def start(given_args=ARGV, config={}) + super do + if Thor::HELP_MAPPINGS.include?(given_args.first) + help(config[:shell]) + return + end + + args, opts = Thor::Options.split(given_args) + new(args, opts, config).invoke + end + end + + # Prints help information. + # + # ==== Options + # short:: When true, shows only usage. + # + def help(shell, options={}) + if options[:short] + shell.say banner + else + shell.say "Usage:" + shell.say " #{banner}" + shell.say + class_options_help(shell) + shell.say self.desc if self.desc + end + end + + # Stores invocations for this class merging with superclass values. + # + def invocations #:nodoc: + @invocations ||= from_superclass(:invocations, {}) + end + + # Stores invocation blocks used on invoke_from_option. + # + def invocation_blocks #:nodoc: + @invocation_blocks ||= from_superclass(:invocation_blocks, {}) + end + + # Invoke the given namespace or class given. It adds an instance + # method that will invoke the klass and task. You can give a block to + # configure how it will be invoked. + # + # The namespace/class given will have its options showed on the help + # usage. Check invoke_from_option for more information. + # + def invoke(*names, &block) + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, :white) + + names.each do |name| + invocations[name] = false + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_#{name.to_s.gsub(/\W/, '_')} + klass, task = self.class.prepare_for_invocation(nil, #{name.inspect}) + + if klass + say_status :invoke, #{name.inspect}, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, task, &block + else + say_status :error, %(#{name.inspect} [not found]), :red + end + end + METHOD + end + end + + # Invoke a thor class based on the value supplied by the user to the + # given option named "name". A class option must be created before this + # method is invoked for each name given. + # + # ==== Examples + # + # class GemGenerator < Thor::Group + # class_option :test_framework, :type => :string + # invoke_from_option :test_framework + # end + # + # ==== Boolean options + # + # In some cases, you want to invoke a thor class if some option is true or + # false. This is automatically handled by invoke_from_option. Then the + # option name is used to invoke the generator. + # + # ==== Preparing for invocation + # + # In some cases you want to customize how a specified hook is going to be + # invoked. You can do that by overwriting the class method + # prepare_for_invocation. The class method must necessarily return a klass + # and an optional task. + # + # ==== Custom invocations + # + # You can also supply a block to customize how the option is giong to be + # invoked. The block receives two parameters, an instance of the current + # class and the klass to be invoked. + # + def invoke_from_option(*names, &block) + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, :white) + + names.each do |name| + unless class_options.key?(name) + raise ArgumentError, "You have to define the option #{name.inspect} " << + "before setting invoke_from_option." + end + + invocations[name] = true + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')} + return unless options[#{name.inspect}] + + value = options[#{name.inspect}] + value = #{name.inspect} if TrueClass === value + klass, task = self.class.prepare_for_invocation(#{name.inspect}, value) + + if klass + say_status :invoke, value, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, task, &block + else + say_status :error, %(\#{value} [not found]), :red + end + end + METHOD + end + end + + # Remove a previously added invocation. + # + # ==== Examples + # + # remove_invocation :test_framework + # + def remove_invocation(*names) + names.each do |name| + remove_task(name) + remove_class_option(name) + invocations.delete(name) + invocation_blocks.delete(name) + end + end + + # Overwrite class options help to allow invoked generators options to be + # shown recursively when invoking a generator. + # + def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc: + group_options = {} + + get_options_from_invocations(group_options, class_options) do |klass| + klass.send(:get_options_from_invocations, group_options, class_options) + end + + group_options.merge!(extra_group) if extra_group + super(shell, ungrouped_name, group_options) + end + + # Get invocations array and merge options from invocations. Those + # options are added to group_options hash. Options that already exists + # in base_options are not added twice. + # + def get_options_from_invocations(group_options, base_options) #:nodoc: + invocations.each do |name, from_option| + value = if from_option + option = class_options[name] + option.type == :boolean ? name : option.default + else + name + end + next unless value + + klass, task = prepare_for_invocation(name, value) + next unless klass && klass.respond_to?(:class_options) + + value = value.to_s + human_name = value.respond_to?(:classify) ? value.classify : value + + group_options[human_name] ||= [] + group_options[human_name] += klass.class_options.values.select do |option| + base_options[option.name.to_sym].nil? && option.group.nil? && + !group_options.values.flatten.any? { |i| i.name == option.name } + end + + yield klass if block_given? + end + end + + protected + + # The banner for this class. You can customize it if you are invoking the + # thor class by another means which is not the Thor::Runner. + # + def banner #:nodoc: + "#{self.namespace} #{self.arguments.map {|a| a.usage }.join(' ')}" + end + + def baseclass #:nodoc: + Thor::Group + end + + def create_task(meth) #:nodoc: + tasks[meth.to_s] = Thor::Task.new(meth, nil, nil, nil) + true + end + end + + include Thor::Base + + protected + + # Shortcut to invoke with padding and block handling. Use internally by + # invoke and invoke_from_option class methods. + # + def _invoke_for_class_method(klass, task=nil, *args, &block) + shell.padding += 1 + + result = if block_given? + if block.arity == 2 + block.call(self, klass) + else + block.call(self, klass, task) + end + else + invoke klass, task, *args + end + + shell.padding -= 1 + result + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/invocation.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/invocation.rb new file mode 100644 index 0000000000..34e7a4b911 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/invocation.rb @@ -0,0 +1,172 @@ +class Thor + module Invocation + def self.included(base) #:nodoc: + base.extend ClassMethods + end + + module ClassMethods + # Prepare for class methods invocations. This method must return a klass to + # have the invoked class options showed in help messages in generators. + # + def prepare_for_invocation(key, name) #:nodoc: + case name + when Symbol, String + Thor::Util.namespace_to_thor_class(name.to_s, false) + else + name + end + end + end + + # Make initializer aware of invocations and the initializer proc. + # + def initialize(args=[], options={}, config={}, &block) #:nodoc: + @_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] } + @_initializer = [ args, options, config ] + super + end + + # Receives a name and invokes it. The name can be a string (either "task" or + # "namespace:task"), a Thor::Task, a Class or a Thor instance. If the task + # cannot be guessed by name, it can also be supplied as second argument. + # + # You can also supply the arguments, options and configuration values for + # the task to be invoked, if none is given, the same values used to + # initialize the invoker are used to initialize the invoked. + # + # ==== Examples + # + # class A < Thor + # def foo + # invoke :bar + # invoke "b:hello", ["José"] + # end + # + # def bar + # invoke "b:hello", ["José"] + # end + # end + # + # class B < Thor + # def hello(name) + # puts "hello #{name}" + # end + # end + # + # You can notice that the method "foo" above invokes two tasks: "bar", + # which belongs to the same class and "hello" which belongs to the class B. + # + # By using an invocation system you ensure that a task is invoked only once. + # In the example above, invoking "foo" will invoke "b:hello" just once, even + # if it's invoked later by "bar" method. + # + # When class A invokes class B, all arguments used on A initialization are + # supplied to B. This allows lazy parse of options. Let's suppose you have + # some rspec tasks: + # + # class Rspec < Thor::Group + # class_option :mock_framework, :type => :string, :default => :rr + # + # def invoke_mock_framework + # invoke "rspec:#{options[:mock_framework]}" + # end + # end + # + # As you noticed, it invokes the given mock framework, which might have its + # own options: + # + # class Rspec::RR < Thor::Group + # class_option :style, :type => :string, :default => :mock + # end + # + # Since it's not rspec concern to parse mock framework options, when RR + # is invoked all options are parsed again, so RR can extract only the options + # that it's going to use. + # + # If you want Rspec::RR to be initialized with its own set of options, you + # have to do that explicitely: + # + # invoke "rspec:rr", [], :style => :foo + # + # Besides giving an instance, you can also give a class to invoke: + # + # invoke Rspec::RR, [], :style => :foo + # + def invoke(name=nil, task=nil, args=nil, opts=nil, config=nil) + task, args, opts, config = nil, task, args, opts if task.nil? || task.is_a?(Array) + args, opts, config = nil, args, opts if args.is_a?(Hash) + + object, task = _prepare_for_invocation(name, task) + if object.is_a?(Class) + klass = object + + stored_args, stored_opts, stored_config = @_initializer + args ||= stored_args.dup + opts ||= stored_opts.dup + + config ||= {} + config = stored_config.merge(_shared_configuration).merge!(config) + instance = klass.new(args, opts, config) + else + klass, instance = object.class, object + end + + method_args = [] + current = @_invocations[klass] + + iterator = proc do |_, task| + unless current.include?(task.name) + current << task.name + task.run(instance, method_args) + end + end + + if task + args ||= [] + method_args = args[Range.new(klass.arguments.size, -1)] || [] + iterator.call(nil, task) + else + klass.all_tasks.map(&iterator) + end + end + + protected + + # Configuration values that are shared between invocations. + # + def _shared_configuration + { :invocations => @_invocations } + end + + # Prepare for invocation in the instance level. In this case, we have to + # take into account that a just a task name from the current class was + # given or even a Thor::Task object. + # + def _prepare_for_invocation(name, sent_task=nil) #:nodoc: + if name.is_a?(Thor::Task) + task = name + elsif task = self.class.all_tasks[name.to_s] + object = self + else + object, task = self.class.prepare_for_invocation(nil, name) + task ||= sent_task + end + + # If the object was not set, use self and use the name as task. + object, task = self, name unless object + return object, _validate_klass_and_task(object, task) + end + + # Check if the object given is a Thor class object and get a task object + # for it. + # + def _validate_klass_and_task(object, task) #:nodoc: + klass = object.is_a?(Class) ? object : object.class + raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base + + task ||= klass.default_task if klass <= Thor + task = klass.all_tasks[task.to_s] || Task.dynamic(task) if task && !task.is_a?(Thor::Task) + task + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/parser.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/parser.rb new file mode 100644 index 0000000000..57a3f6e1a5 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/parser.rb @@ -0,0 +1,4 @@ +require 'thor/parser/argument' +require 'thor/parser/arguments' +require 'thor/parser/option' +require 'thor/parser/options' diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/parser/argument.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/argument.rb new file mode 100644 index 0000000000..2d7f4dbafb --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/argument.rb @@ -0,0 +1,67 @@ +class Thor + class Argument + VALID_TYPES = [ :numeric, :hash, :array, :string ] + + attr_reader :name, :description, :required, :type, :default, :banner + alias :human_name :name + + def initialize(name, description=nil, required=true, type=:string, default=nil, banner=nil) + class_name = self.class.name.split("::").last + + raise ArgumentError, "#{class_name} name can't be nil." if name.nil? + raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type) + + @name = name.to_s + @description = description + @required = required || false + @type = (type || :string).to_sym + @default = default + @banner = banner || default_banner + + validate! # Trigger specific validations + end + + def usage + required? ? banner : "[#{banner}]" + end + + def required? + required + end + + def show_default? + case default + when Array, String, Hash + !default.empty? + else + default + end + end + + protected + + def validate! + raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil? + end + + def valid_type?(type) + VALID_TYPES.include?(type.to_sym) + end + + def default_banner + case type + when :boolean + nil + when :string, :default + human_name.upcase + when :numeric + "N" + when :hash + "key:value" + when :array + "one two three" + end + end + + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/parser/arguments.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/arguments.rb new file mode 100644 index 0000000000..9a2262d6f7 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/arguments.rb @@ -0,0 +1,145 @@ +class Thor + class Arguments + NUMERIC = /(\d*\.\d+|\d+)/ + + # Receives an array of args and returns two arrays, one with arguments + # and one with switches. + # + def self.split(args) + arguments = [] + + args.each do |item| + break if item =~ /^-/ + arguments << item + end + + return arguments, args[Range.new(arguments.size, -1)] + end + + def self.parse(base, args) + new(base).parse(args) + end + + # Takes an array of Thor::Argument objects. + # + def initialize(arguments=[]) + @assigns, @non_assigned_required = {}, [] + @switches = arguments + + arguments.each do |argument| + if argument.default + @assigns[argument.human_name] = argument.default + elsif argument.required? + @non_assigned_required << argument + end + end + end + + def parse(args) + @pile = args.dup + + @switches.each do |argument| + break unless peek + @non_assigned_required.delete(argument) + @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name) + end + + check_requirement! + @assigns + end + + private + + def peek + @pile.first + end + + def shift + @pile.shift + end + + def unshift(arg) + unless arg.kind_of?(Array) + @pile.unshift(arg) + else + @pile = arg + @pile + end + end + + def current_is_value? + peek && peek.to_s !~ /^-/ + end + + # Runs through the argument array getting strings that contains ":" and + # mark it as a hash: + # + # [ "name:string", "age:integer" ] + # + # Becomes: + # + # { "name" => "string", "age" => "integer" } + # + def parse_hash(name) + return shift if peek.is_a?(Hash) + hash = {} + + while current_is_value? && peek.include?(?:) + key, value = shift.split(':') + hash[key] = value + end + hash + end + + # Runs through the argument array getting all strings until no string is + # found or a switch is found. + # + # ["a", "b", "c"] + # + # And returns it as an array: + # + # ["a", "b", "c"] + # + def parse_array(name) + return shift if peek.is_a?(Array) + array = [] + + while current_is_value? + array << shift + end + array + end + + # Check if the peel is numeric ofrmat and return a Float or Integer. + # Otherwise raises an error. + # + def parse_numeric(name) + return shift if peek.is_a?(Numeric) + + unless peek =~ NUMERIC && $& == peek + raise MalformattedArgumentError, "expected numeric value for '#{name}'; got #{peek.inspect}" + end + + $&.index('.') ? shift.to_f : shift.to_i + end + + # Parse string, i.e., just return the current value in the pile. + # + def parse_string(name) + shift + end + + # Raises an error if @non_assigned_required array is not empty. + # + def check_requirement! + unless @non_assigned_required.empty? + names = @non_assigned_required.map do |o| + o.respond_to?(:switch_name) ? o.switch_name : o.human_name + end.join("', '") + + class_name = self.class.name.split('::').last.downcase + raise RequiredArgumentMissingError, "no value provided for required #{class_name} '#{names}'" + end + end + + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/parser/option.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/option.rb new file mode 100644 index 0000000000..5c43f6b18f --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/option.rb @@ -0,0 +1,132 @@ +class Thor + class Option < Argument + attr_reader :aliases, :group + + VALID_TYPES = [:boolean, :numeric, :hash, :array, :string] + + def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, group=nil, aliases=nil) + super(name, description, required, type, default, banner) + @aliases = [*aliases].compact + @group = group.to_s.capitalize if group + end + + # This parse quick options given as method_options. It makes several + # assumptions, but you can be more specific using the option method. + # + # parse :foo => "bar" + # #=> Option foo with default value bar + # + # parse [:foo, :baz] => "bar" + # #=> Option foo with default value bar and alias :baz + # + # parse :foo => :required + # #=> Required option foo without default value + # + # parse :foo => 2 + # #=> Option foo with default value 2 and type numeric + # + # parse :foo => :numeric + # #=> Option foo without default value and type numeric + # + # parse :foo => true + # #=> Option foo with default value true and type boolean + # + # The valid types are :boolean, :numeric, :hash, :array and :string. If none + # is given a default type is assumed. This default type accepts arguments as + # string (--foo=value) or booleans (just --foo). + # + # By default all options are optional, unless :required is given. + # + def self.parse(key, value) + if key.is_a?(Array) + name, *aliases = key + else + name, aliases = key, [] + end + + name = name.to_s + default = value + + type = case value + when Symbol + default = nil + + if VALID_TYPES.include?(value) + value + elsif required = (value == :required) + :string + elsif value == :optional + # TODO Remove this warning in the future. + warn "Optional type is deprecated. Choose :boolean or :string instead. Assumed to be :boolean." + :boolean + end + when TrueClass, FalseClass + :boolean + when Numeric + :numeric + when Hash, Array, String + value.class.name.downcase.to_sym + end + + self.new(name.to_s, nil, required, type, default, nil, nil, aliases) + end + + def switch_name + @switch_name ||= dasherized? ? name : dasherize(name) + end + + def human_name + @human_name ||= dasherized? ? undasherize(name) : name + end + + def usage(padding=0) + sample = if banner && !banner.to_s.empty? + "#{switch_name}=#{banner}" + else + switch_name + end + + sample = "[#{sample}]" unless required? + + if aliases.empty? + (" " * padding) << sample + else + "#{aliases.join(', ')}, #{sample}" + end + end + + # Allow some type predicates as: boolean?, string? and etc. + # + def method_missing(method, *args, &block) + given = method.to_s.sub(/\?$/, '').to_sym + if valid_type?(given) + self.type == given + else + super + end + end + + protected + + def validate! + raise ArgumentError, "An option cannot be boolean and required." if boolean? && required? + end + + def valid_type?(type) + VALID_TYPES.include?(type.to_sym) + end + + def dasherized? + name.index('-') == 0 + end + + def undasherize(str) + str.sub(/^-{1,2}/, '') + end + + def dasherize(str) + (str.length > 1 ? "--" : "-") + str.gsub('_', '-') + end + + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/parser/options.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/options.rb new file mode 100644 index 0000000000..01c86b7b27 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/parser/options.rb @@ -0,0 +1,142 @@ +class Thor + # This is a modified version of Daniel Berger's Getopt::Long class, licensed + # under Ruby's license. + # + class Options < Arguments + LONG_RE = /^(--\w+[-\w+]*)$/ + SHORT_RE = /^(-[a-z])$/i + EQ_RE = /^(--\w+[-\w+]*|-[a-z])=(.*)$/i + SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args + SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i + + # Receives a hash and makes it switches. + # + def self.to_switches(options) + options.map do |key, value| + case value + when true + "--#{key}" + when Array + "--#{key} #{value.map{ |v| v.inspect }.join(' ')}" + when Hash + "--#{key} #{value.map{ |k,v| "#{k}:#{v}" }.join(' ')}" + when nil, false + "" + else + "--#{key} #{value.inspect}" + end + end.join(" ") + end + + # Takes a hash of Thor::Option objects. + # + def initialize(options={}) + options = options.values + super(options) + @shorts, @switches = {}, {} + + options.each do |option| + @switches[option.switch_name] = option + + option.aliases.each do |short| + @shorts[short.to_s] ||= option.switch_name + end + end + end + + def parse(args) + @pile = args.dup + + while peek + if current_is_switch? + case shift + when SHORT_SQ_RE + unshift($1.split('').map { |f| "-#{f}" }) + next + when EQ_RE, SHORT_NUM + unshift($2) + switch = $1 + when LONG_RE, SHORT_RE + switch = $1 + end + + switch = normalize_switch(switch) + next unless option = switch_option(switch) + + @assigns[option.human_name] = parse_peek(switch, option) + else + shift + end + end + + check_requirement! + @assigns + end + + protected + + # Returns true if the current value in peek is a registered switch. + # + def current_is_switch? + case peek + when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM + switch?($1) + when SHORT_SQ_RE + $1.split('').any? { |f| switch?("-#{f}") } + end + end + + def switch?(arg) + switch_option(arg) || @shorts.key?(arg) + end + + def switch_option(arg) + if match = no_or_skip?(arg) + @switches[arg] || @switches["--#{match}"] + else + @switches[arg] + end + end + + def no_or_skip?(arg) + arg =~ /^--(no|skip)-([-\w]+)$/ + $2 + end + + # Check if the given argument is actually a shortcut. + # + def normalize_switch(arg) + @shorts.key?(arg) ? @shorts[arg] : arg + end + + # Parse boolean values which can be given as --foo=true, --foo or --no-foo. + # + def parse_boolean(switch) + if current_is_value? + ["true", "TRUE", "t", "T", true].include?(shift) + else + @switches.key?(switch) || !no_or_skip?(switch) + end + end + + # Parse the value at the peek analyzing if it requires an input or not. + # + def parse_peek(switch, option) + unless current_is_value? + if option.boolean? + # No problem for boolean types + elsif no_or_skip?(switch) + return nil # User set value to nil + elsif option.string? && !option.required? + return option.human_name # Return the option name + else + raise MalformattedArgumentError, "no value provided for option '#{switch}'" + end + end + + @non_assigned_required.delete(option) + send(:"parse_#{option.type}", switch) + end + + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/runner.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/runner.rb new file mode 100644 index 0000000000..6782c61dec --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/runner.rb @@ -0,0 +1,291 @@ +require 'fileutils' +require 'open-uri' +require 'yaml' +require 'digest/md5' +require 'pathname' + +class Thor::Runner < Thor + map "-T" => :list, "-i" => :install, "-u" => :update + + # Override Thor#help so it can give information about any class and any method. + # + def help(meth=nil) + if meth && !self.respond_to?(meth) + initialize_thorfiles(meth) + klass, task = Thor::Util.namespace_to_thor_class(meth) + klass.start(["-h", task].compact, :shell => self.shell) # send mapping -h because it works with Thor::Group too + else + super + end + end + + # If a task is not found on Thor::Runner, method missing is invoked and + # Thor::Runner is then responsable for finding the task in all classes. + # + def method_missing(meth, *args) + meth = meth.to_s + initialize_thorfiles(meth) + klass, task = Thor::Util.namespace_to_thor_class(meth) + args.unshift(task) if task + klass.start(args, :shell => shell) + end + + desc "install NAME", "Install a Thor file into your system tasks, optionally named for future updates" + method_options :as => :string, :relative => :boolean + def install(name) + initialize_thorfiles + + # If a directory name is provided as the argument, look for a 'main.thor' + # task in said directory. + begin + if File.directory?(File.expand_path(name)) + base, package = File.join(name, "main.thor"), :directory + contents = open(base).read + else + base, package = name, :file + contents = open(name).read + end + rescue OpenURI::HTTPError + raise Error, "Error opening URI '#{name}'" + rescue Errno::ENOENT + raise Error, "Error opening file '#{name}'" + end + + say "Your Thorfile contains:" + say contents + + return false if no?("Do you wish to continue [y/N]?") + + as = options["as"] || begin + first_line = contents.split("\n")[0] + (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil + end + + unless as + basename = File.basename(name) + as = ask("Please specify a name for #{name} in the system repository [#{basename}]:") + as = basename if as.empty? + end + + location = if options[:relative] || name =~ /^http:\/\// + name + else + File.expand_path(name) + end + + thor_yaml[as] = { + :filename => Digest::MD5.hexdigest(name + as), + :location => location, + :namespaces => Thor::Util.namespaces_in_contents(contents, base) + } + + save_yaml(thor_yaml) + say "Storing thor file in your system repository" + destination = File.join(thor_root, thor_yaml[as][:filename]) + + if package == :file + File.open(destination, "w") { |f| f.puts contents } + else + FileUtils.cp_r(name, destination) + end + + thor_yaml[as][:filename] # Indicate success + end + + desc "uninstall NAME", "Uninstall a named Thor module" + def uninstall(name) + raise Error, "Can't find module '#{name}'" unless thor_yaml[name] + say "Uninstalling #{name}." + FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}")) + + thor_yaml.delete(name) + save_yaml(thor_yaml) + + puts "Done." + end + + desc "update NAME", "Update a Thor file from its original location" + def update(name) + raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location] + + say "Updating '#{name}' from #{thor_yaml[name][:location]}" + + old_filename = thor_yaml[name][:filename] + self.options = self.options.merge("as" => name) + filename = install(thor_yaml[name][:location]) + + unless filename == old_filename + File.delete(File.join(thor_root, old_filename)) + end + end + + desc "installed", "List the installed Thor modules and tasks" + method_options :internal => :boolean + def installed + initialize_thorfiles(nil, true) + + klasses = Thor::Base.subclasses + klasses -= [Thor, Thor::Runner] unless options["internal"] + + display_klasses(true, klasses) + end + + desc "list [SEARCH]", + "List the available thor tasks (--substring means SEARCH anywhere in the namespace)" + method_options :substring => :boolean, :group => :string, :all => :boolean + def list(search="") + initialize_thorfiles + + search = ".*#{search}" if options["substring"] + search = /^#{search}.*/i + group = options[:group] || "standard" + + klasses = Thor::Base.subclasses.select do |k| + (options[:all] || k.group == group) && k.namespace =~ search + end + + display_klasses(false, klasses) + end + + private + + def thor_root + Thor::Util.thor_root + end + + def thor_yaml + @thor_yaml ||= begin + yaml_file = File.join(thor_root, "thor.yml") + yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file) + yaml || {} + end + end + + # Save the yaml file. If none exists in thor root, creates one. + # + def save_yaml(yaml) + yaml_file = File.join(thor_root, "thor.yml") + + unless File.exists?(yaml_file) + FileUtils.mkdir_p(thor_root) + yaml_file = File.join(thor_root, "thor.yml") + FileUtils.touch(yaml_file) + end + + File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml } + end + + # Load the thorfiles. If relevant_to is supplied, looks for specific files + # in the thor_root instead of loading them all. + # + # By default, it also traverses the current path until find Thor files, as + # described in thorfiles. This look up can be skipped by suppliying + # skip_lookup true. + # + def initialize_thorfiles(relevant_to=nil, skip_lookup=false) + thorfiles(relevant_to, skip_lookup).each do |f| + Thor::Util.load_thorfile(f) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f)) + end + end + + # Finds Thorfiles by traversing from your current directory down to the root + # directory of your system. If at any time we find a Thor file, we stop. + # + # We also ensure that system-wide Thorfiles are loaded first, so local + # Thorfiles can override them. + # + # ==== Example + # + # If we start at /Users/wycats/dev/thor ... + # + # 1. /Users/wycats/dev/thor + # 2. /Users/wycats/dev + # 3. /Users/wycats <-- we find a Thorfile here, so we stop + # + # Suppose we start at c:\Documents and Settings\james\dev\thor ... + # + # 1. c:\Documents and Settings\james\dev\thor + # 2. c:\Documents and Settings\james\dev + # 3. c:\Documents and Settings\james + # 4. c:\Documents and Settings + # 5. c:\ <-- no Thorfiles found! + # + def thorfiles(relevant_to=nil, skip_lookup=false) + # Deal with deprecated thor when :namespaces: is available as constants + save_yaml(thor_yaml) if Thor::Util.convert_constants_to_namespaces(thor_yaml) + + thorfiles = [] + + unless skip_lookup + Pathname.pwd.ascend do |path| + thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten + break unless thorfiles.empty? + end + end + + files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Thor::Util.thor_root_glob) + files += thorfiles + files -= ["#{thor_root}/thor.yml"] + + files.map! do |file| + File.directory?(file) ? File.join(file, "main.thor") : file + end + end + + # Load thorfiles relevant to the given method. If you provide "foo:bar" it + # will load all thor files in the thor.yaml that has "foo" e "foo:bar" + # namespaces registered. + # + def thorfiles_relevant_to(meth) + lookup = [ meth, meth.split(":")[0...-1].join(":") ] + + files = thor_yaml.select do |k, v| + v[:namespaces] && !(v[:namespaces] & lookup).empty? + end + + files.map { |k, v| File.join(thor_root, "#{v[:filename]}") } + end + + # Display information about the given klasses. If with_module is given, + # it shows a table with information extracted from the yaml file. + # + def display_klasses(with_modules=false, klasses=Thor.subclasses) + klasses -= [Thor, Thor::Runner] unless with_modules + raise Error, "No Thor tasks available" if klasses.empty? + + if with_modules && !thor_yaml.empty? + info = [] + labels = ["Modules", "Namespaces"] + + info << labels + info << [ "-" * labels[0].size, "-" * labels[1].size ] + + thor_yaml.each do |name, hash| + info << [ name, hash[:namespaces].join(", ") ] + end + + print_table info + say "" + end + + unless klasses.empty? + klasses.each { |k| display_tasks(k) } + else + say "\033[1;34mNo Thor tasks available\033[0m" + end + end + + # Display tasks from the given Thor class. + # + def display_tasks(klass) + unless klass.tasks.empty? + base = klass.namespace + + color = base == "default" ? :magenta : :blue + say shell.set_color(base, color, true) + say "-" * base.length + + klass.help(shell, :short => true, :namespace => true) + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/shell.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/shell.rb new file mode 100644 index 0000000000..7ed4a24bfb --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/shell.rb @@ -0,0 +1,72 @@ +require 'thor/shell/color' + +class Thor + module Base + # Returns the shell used in all Thor classes. Default to color one. + # + def self.shell + @shell ||= Thor::Shell::Color + end + + # Sets the shell used in all Thor classes. + # + def self.shell=(klass) + @shell = klass + end + end + + module Shell + SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_list, :print_table] + + # Add shell to initialize config values. + # + # ==== Configuration + # shell<Object>:: An instance of the shell to be used. + # + # ==== Examples + # + # class MyScript < Thor + # argument :first, :type => :numeric + # end + # + # MyScript.new [1.0], { :foo => :bar }, :shell => Thor::Shell::Basic.new + # + def initialize(args=[], options={}, config={}) + super + self.shell = config[:shell] + self.shell.base ||= self if self.shell.respond_to?(:base) + end + + # Holds the shell for the given Thor instance. If no shell is given, + # it gets a default shell from Thor::Base.shell. + # + def shell + @shell ||= Thor::Base.shell.new + end + + # Sets the shell for this thor class. + # + def shell=(shell) + @shell = shell + end + + # Common methods that are delegated to the shell. + # + SHELL_DELEGATED_METHODS.each do |method| + module_eval <<-METHOD, __FILE__, __LINE__ + def #{method}(*args) + shell.#{method}(*args) + end + METHOD + end + + protected + + # Allow shell to be shared between invocations. + # + def _shared_configuration + super.merge!(:shell => self.shell) + end + + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/shell/basic.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/shell/basic.rb new file mode 100644 index 0000000000..e294c87567 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/shell/basic.rb @@ -0,0 +1,216 @@ +require 'tempfile' + +class Thor + module Shell + class Basic + attr_accessor :base, :padding + + # Initialize base and padding to nil. + # + def initialize #:nodoc: + @base, @padding = nil, 0 + end + + # Do not allow padding to be less than zero. + # + def padding=(value) #:nodoc: + @padding = [0, value].max + end + + # Ask something to the user and receives a response. + # + # ==== Example + # ask("What is your name?") + # + def ask(statement, color=nil) + say("#{statement} ", color) + $stdin.gets.strip + end + + # Say (print) something to the user. If the sentence ends with a whitespace + # or tab character, a new line is not appended (print + flush). Otherwise + # are passed straight to puts (behavior got from Highline). + # + # ==== Example + # say("I know you knew that.") + # + def say(message="", color=nil, force_new_line=false) + message = message.to_s + new_line = force_new_line || !(message[-1, 1] == " " || message[-1, 1] == "\t") + message = set_color(message, color) if color + + if new_line + $stdout.puts(message) + else + $stdout.print(message) + $stdout.flush + end + end + + # Say a status with the given color and appends the message. Since this + # method is used frequently by actions, it allows nil or false to be given + # in log_status, avoiding the message from being shown. If a Symbol is + # given in log_status, it's used as the color. + # + def say_status(status, message, log_status=true) #:nodoc: + return if quiet? || log_status == false + spaces = " " * (padding + 1) + color = log_status.is_a?(Symbol) ? log_status : :green + + status = status.to_s.rjust(12) + status = set_color status, color, true if color + say "#{status}#{spaces}#{message}", nil, true + end + + # Make a question the to user and returns true if the user replies "y" or + # "yes". + # + def yes?(statement, color=nil) + ask(statement, color) =~ is?(:yes) + end + + # Make a question the to user and returns true if the user replies "n" or + # "no". + # + def no?(statement, color=nil) + !yes?(statement, color) + end + + # Prints a list of items. + # + # ==== Parameters + # list<Array[String, String, ...]> + # mode<Symbol>:: Can be :rows or :inline. Defaults to :rows. + # + def print_list(list, mode=:rows) + return if list.empty? + + content = case mode + when :inline + last = list.pop + "#{list.join(", ")}, and #{last}" + else # rows + list.join("\n") + end + + $stdout.puts content + end + + # Prints a table. + # + # ==== Parameters + # Array[Array[String, String, ...]] + # + # ==== Options + # ident<Integer>:: Ident the first column by ident value. + # emphasize_last<Boolean>:: When true, add a different behavior to the last column. + # + def print_table(table, options={}) + return if table.empty? + + formats = [] + 0.upto(table.first.length - 2) do |i| + maxima = table.max{ |a,b| a[i].size <=> b[i].size }[i].size + formats << "%-#{maxima + 2}s" + end + + formats[0] = formats[0].insert(0, " " * options[:ident]) if options[:ident] + formats << "%s" + + table.each do |row| + row.each_with_index do |column, i| + $stdout.print formats[i] % column.to_s + end + $stdout.puts + end + end + + # Deals with file collision and returns true if the file should be + # overwriten and false otherwise. If a block is given, it uses the block + # response as the content for the diff. + # + # ==== Parameters + # destination<String>:: the destination file to solve conflicts + # block<Proc>:: an optional proc that returns the value to be used in diff + # + def file_collision(destination) + return true if @always_force + options = block_given? ? "[Ynaqdh]" : "[Ynaqh]" + + while true + answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}] + + case answer + when is?(:yes), is?(:force) + return true + when is?(:no), is?(:skip) + return false + when is?(:always) + return @always_force = true + when is?(:quit) + say 'Aborting...' + raise SystemExit + when is?(:diff) + show_diff(destination, yield) if block_given? + say 'Retrying...' + else + say file_collision_help + end + end + end + + # Called if something goes wrong during the execution. This is used by Thor + # internally and should not be used inside your scripts. If someone went + # wrong, you can always raise an exception. If you raise a Thor::Error, it + # will be rescued and wrapped in the method below. + # + def error(statement) #:nodoc: + $stderr.puts statement + end + + # Apply color to the given string with optional bold. + # + def set_color(string, color, bold=false) + string + end + + protected + + def is?(value) + value = value.to_s + + if value.size == 1 + /\A#{value}\z/i + else + /\A(#{value}|#{value[0,1]})\z/i + end + end + + def file_collision_help +<<HELP +Y - yes, overwrite +n - no, do not overwrite +a - all, overwrite this and all others +q - quit, abort +d - diff, show the differences between the old and the new +h - help, show this help +HELP + end + + def show_diff(destination, content) + diff_cmd = ENV['THOR_DIFF'] || ENV['RAILS_DIFF'] || 'diff -u' + + Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp| + temp.write content + temp.rewind + say `#{diff_cmd} "#{destination}" "#{temp.path}"` + end + end + + def quiet? + base && base.options[:quiet] + end + + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/shell/color.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/shell/color.rb new file mode 100644 index 0000000000..be7995146a --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/shell/color.rb @@ -0,0 +1,106 @@ +require 'thor/shell/basic' + +class Thor + module Shell + # Set color in the output. Got color values from HighLine. + # + class Color < Basic + # Embed in a String to clear all previous ANSI sequences. + CLEAR = "\e[0m" + # The start of an ANSI bold sequence. + BOLD = "\e[1m" + + # Set the terminal's foreground ANSI color to black. + BLACK = "\e[30m" + # Set the terminal's foreground ANSI color to red. + RED = "\e[31m" + # Set the terminal's foreground ANSI color to green. + GREEN = "\e[32m" + # Set the terminal's foreground ANSI color to yellow. + YELLOW = "\e[33m" + # Set the terminal's foreground ANSI color to blue. + BLUE = "\e[34m" + # Set the terminal's foreground ANSI color to magenta. + MAGENTA = "\e[35m" + # Set the terminal's foreground ANSI color to cyan. + CYAN = "\e[36m" + # Set the terminal's foreground ANSI color to white. + WHITE = "\e[37m" + + # Set the terminal's background ANSI color to black. + ON_BLACK = "\e[40m" + # Set the terminal's background ANSI color to red. + ON_RED = "\e[41m" + # Set the terminal's background ANSI color to green. + ON_GREEN = "\e[42m" + # Set the terminal's background ANSI color to yellow. + ON_YELLOW = "\e[43m" + # Set the terminal's background ANSI color to blue. + ON_BLUE = "\e[44m" + # Set the terminal's background ANSI color to magenta. + ON_MAGENTA = "\e[45m" + # Set the terminal's background ANSI color to cyan. + ON_CYAN = "\e[46m" + # Set the terminal's background ANSI color to white. + ON_WHITE = "\e[47m" + + # Set color by using a string or one of the defined constants. Based + # on Highline implementation. CLEAR is automatically be embedded to + # the end of the returned String. + # + def set_color(string, color, bold=false) + color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol) + bold = bold ? BOLD : "" + "#{bold}#{color}#{string}#{CLEAR}" + end + + protected + + # Overwrite show_diff to show diff with colors if Diff::LCS is + # available. + # + def show_diff(destination, content) + if diff_lcs_loaded? && ENV['THOR_DIFF'].nil? && ENV['RAILS_DIFF'].nil? + actual = File.read(destination).to_s.split("\n") + content = content.to_s.split("\n") + + Diff::LCS.sdiff(actual, content).each do |diff| + output_diff_line(diff) + end + else + super + end + end + + def output_diff_line(diff) + case diff.action + when '-' + say "- #{diff.old_element.chomp}", :red, true + when '+' + say "+ #{diff.new_element.chomp}", :green, true + when '!' + say "- #{diff.old_element.chomp}", :red, true + say "+ #{diff.new_element.chomp}", :green, true + else + say " #{diff.old_element.chomp}", nil, true + end + end + + # Check if Diff::LCS is loaded. If it is, use it to create pretty output + # for diff. + # + def diff_lcs_loaded? + return true if defined?(Diff::LCS) + return @diff_lcs_loaded unless @diff_lcs_loaded.nil? + + @diff_lcs_loaded = begin + require 'diff/lcs' + true + rescue LoadError + false + end + end + + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/task.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/task.rb new file mode 100644 index 0000000000..92c0776c04 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/task.rb @@ -0,0 +1,108 @@ +class Thor + class Task < Struct.new(:name, :description, :usage, :options) + + # Creates a dynamic task. Dynamic tasks are created on demand to allow method + # missing calls (since a method missing does not have a task object for it). + # + def self.dynamic(name) + new(name, "A dynamically-generated task", name.to_s) + end + + def initialize(name, description, usage, options=nil) + super(name.to_s, description, usage, options || {}) + end + + def initialize_copy(other) + super(other) + self.options = other.options.dup if other.options + end + + def short_description + description.split("\n").first if description + end + + # By default, a task invokes a method in the thor class. You can change this + # implementation to create custom tasks. + # + def run(instance, args=[]) + raise UndefinedTaskError, "the '#{name}' task of #{instance.class} is private" unless public_method?(instance) + instance.send(name, *args) + rescue ArgumentError => e + parse_argument_error(instance, e, caller) + rescue NoMethodError => e + parse_no_method_error(instance, e) + end + + # Returns the formatted usage. If a class is given, the class arguments are + # injected in the usage. + # + def formatted_usage(klass=nil, namespace=false) + formatted = '' + formatted << "#{klass.namespace.gsub(/^default/,'')}:" if klass && namespace + formatted << formatted_arguments(klass) + formatted << " #{formatted_options}" + formatted.strip! + formatted + end + + # Injects the class arguments into the task usage. + # + def formatted_arguments(klass) + if klass && !klass.arguments.empty? + usage.to_s.gsub(/^#{name}/) do |match| + match << " " << klass.arguments.map{ |a| a.usage }.join(' ') + end + else + usage.to_s + end + end + + # Returns the options usage for this task. + # + def formatted_options + @formatted_options ||= options.map{ |_, o| o.usage }.sort.join(" ") + end + + protected + + # Given a target, checks if this class name is not a private/protected method. + # + def public_method?(instance) + collection = instance.private_methods + instance.protected_methods + !(collection).include?(name.to_s) && !(collection).include?(name.to_sym) # For Ruby 1.9 + end + + # Clean everything that comes from the Thor gempath and remove the caller. + # + def sans_backtrace(backtrace, caller) + dirname = /^#{Regexp.escape(File.dirname(__FILE__))}/ + saned = backtrace.reject { |frame| frame =~ dirname } + saned -= caller + end + + def parse_argument_error(instance, e, caller) + backtrace = sans_backtrace(e.backtrace, caller) + + if backtrace.empty? && e.message =~ /wrong number of arguments/ + if instance.is_a?(Thor::Group) + raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?" + else + raise InvocationError, "'#{name}' was called incorrectly. Call as " << + "'#{formatted_usage(instance.class, true)}'" + end + else + raise e + end + end + + def parse_no_method_error(instance, e) + if e.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/ + raise UndefinedTaskError, "The #{instance.class.namespace} namespace " << + "doesn't have a '#{name}' task" + else + raise e + end + end + + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/tasks.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks.rb new file mode 100644 index 0000000000..d1a7b1c673 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks.rb @@ -0,0 +1,4 @@ +# This only loads all tasks inside tasks. +Dir[File.join(File.dirname(__FILE__), "tasks", "*.rb")].each do |task| + require task +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/install.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/install.rb new file mode 100644 index 0000000000..6b20ff1634 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/install.rb @@ -0,0 +1,35 @@ +class Thor + # Creates an install task. + # + # ==== Parameters + # spec<Gem::Specification> + # + # ==== Options + # :dir - The directory where the package is hold before installation. Defaults to ./pkg. + # + def self.install_task(spec, options={}) + package_task(spec, options) + tasks['install'] = Thor::InstallTask.new(spec, options) + end + + class InstallTask < Task + attr_accessor :spec, :config + + def initialize(gemspec, config={}) + super(:install, "Install the gem", "install", {}) + @spec = gemspec + @config = { :dir => File.join(Dir.pwd, "pkg") }.merge(config) + end + + def run(instance, args=[]) + null, sudo, gem = RUBY_PLATFORM =~ /mswin|mingw/ ? ['NUL', '', 'gem.bat'] : + ['/dev/null', 'sudo', 'gem'] + + old_stderr, $stderr = $stderr.dup, File.open(null, "w") + instance.invoke(:package) + $stderr = old_stderr + + system %{#{sudo} #{Gem.ruby} -S #{gem} install #{config[:dir]}/#{spec.name}-#{spec.version} --no-rdoc --no-ri --no-update-sources} + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/package.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/package.rb new file mode 100644 index 0000000000..603d61b4ab --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/package.rb @@ -0,0 +1,31 @@ +require "fileutils" + +class Thor + # Creates a package task. + # + # ==== Parameters + # spec<Gem::Specification> + # + # ==== Options + # :dir - The package directory. Defaults to ./pkg. + # + def self.package_task(spec, options={}) + tasks['package'] = Thor::PackageTask.new(spec, options) + end + + class PackageTask < Task + attr_accessor :spec, :config + + def initialize(gemspec, config={}) + super(:package, "Build a gem package", "package", {}) + @spec = gemspec + @config = {:dir => File.join(Dir.pwd, "pkg")}.merge(config) + end + + def run(instance, args=[]) + FileUtils.mkdir_p(config[:dir]) + Gem::Builder.new(spec).build + FileUtils.mv(spec.file_name, File.join(config[:dir], spec.file_name)) + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/spec.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/spec.rb new file mode 100644 index 0000000000..c7d00968e8 --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/tasks/spec.rb @@ -0,0 +1,70 @@ +require "fileutils" + +class Thor + # Creates a spec task. + # + # ==== Parameters + # files<Array> - Array of files to spec + # + # ==== Options + # :name - The name of the task. It can be rcov or spec. Spec is the default. + # :rcov - A hash with rcov specific options. + # :rcov_dir - Where rcov reports should be printed. + # :verbose - Sets the default value for verbose, although it can be specified + # also through the command line. + # + # All other options are added to rspec. + # + def self.spec_task(files, options={}) + name = (options.delete(:name) || 'spec').to_s + tasks[name] = Thor::SpecTask.new(name, files, options) + end + + class SpecTask < Task + attr_accessor :name, :files, :rcov_dir, :rcov_config, :spec_config + + def initialize(name, files, config={}) + options = { :verbose => Thor::Option.parse(:verbose, config.delete(:verbose) || false) } + super(name, "#{name.capitalize} task", name, options) + + @name = name + @files = files.map{ |f| %["#{f}"] }.join(" ") + @rcov_dir = config.delete(:rdoc_dir) || File.join(Dir.pwd, 'coverage') + @rcov_config = config.delete(:rcov) || {} + @spec_config = { :format => 'specdoc', :color => true }.merge(config) + end + + def run(instance, args=[]) + rcov_opts = Thor::Options.to_switches(rcov_config) + spec_opts = Thor::Options.to_switches(spec_config) + + require 'rbconfig' + cmd = RbConfig::CONFIG['ruby_install_name'] << " " + + if rcov? + FileUtils.rm_rf(rcov_dir) + cmd << "-S #{where('rcov')} -o #{rcov_dir} #{rcov_opts} " + end + + cmd << [where('spec'), rcov? ? " -- " : nil, files, spec_opts].join(" ") + + puts cmd if instance.options.verbose? + system(cmd) + exit($?.exitstatus) + end + + private + + def rcov? + name == "rcov" + end + + def where(file) + ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + file_with_path = File.join(path, file) + next unless File.exist?(file_with_path) && File.executable?(file_with_path) + return File.expand_path(file_with_path) + end + end + end +end diff --git a/railties/lib/vendor/thor-0.11.3/lib/thor/util.rb b/railties/lib/vendor/thor-0.11.3/lib/thor/util.rb new file mode 100644 index 0000000000..26db24aadb --- /dev/null +++ b/railties/lib/vendor/thor-0.11.3/lib/thor/util.rb @@ -0,0 +1,229 @@ +require 'rbconfig' + +class Thor + module Sandbox; end + + # This module holds several utilities: + # + # 1) Methods to convert thor namespaces to constants and vice-versa. + # + # Thor::Utils.constant_to_namespace(Foo::Bar::Baz) #=> "foo:bar:baz" + # Thor::Utils.namespace_to_constant("foo:bar:baz") #=> Foo::Bar::Baz + # + # 2) Loading thor files and sandboxing: + # + # Thor::Utils.load_thorfile("~/.thor/foo") + # + module Util + + # Receives a namespace and search for it in the Thor::Base subclasses. + # + # ==== Parameters + # namespace<String>:: The namespace to search for. + # + def self.find_by_namespace(namespace) + namespace = 'default' if namespace.empty? + + Thor::Base.subclasses.find do |klass| + klass.namespace == namespace + end + end + + # Receives a constant and converts it to a Thor namespace. Since Thor tasks + # can be added to a sandbox, this method is also responsable for removing + # the sandbox namespace. + # + # This method should not be used in general because it's used to deal with + # older versions of Thor. On current versions, if you need to get the + # namespace from a class, just call namespace on it. + # + # ==== Parameters + # constant<Object>:: The constant to be converted to the thor path. + # + # ==== Returns + # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz" + # + def self.constant_to_namespace(constant, remove_default=true) + constant = constant.to_s.gsub(/^Thor::Sandbox::/, "") + constant = snake_case(constant).squeeze(":") + constant.gsub!(/^default/, '') if remove_default + constant + end + + # Given the contents, evaluate it inside the sandbox and returns the thor + # classes defined in the sandbox. + # + # ==== Parameters + # contents<String> + # + # ==== Returns + # Array[Object] + # + def self.namespaces_in_contents(contents, file=__FILE__) + old_constants = Thor::Base.subclasses.dup + Thor::Base.subclasses.clear + + load_thorfile(file, contents) + + new_constants = Thor::Base.subclasses.dup + Thor::Base.subclasses.replace(old_constants) + + new_constants.map!{ |c| c.namespace } + new_constants.compact! + new_constants + end + + # Receives a string and convert it to snake case. SnakeCase returns snake_case. + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def self.snake_case(str) + return str.downcase if str =~ /^[A-Z_]+$/ + str.gsub(/\B[A-Z]/, '_\&').squeeze('_') =~ /_*(.*)/ + return $+.downcase + end + + # Receives a namespace and tries to retrieve a Thor or Thor::Group class + # from it. It first searches for a class using the all the given namespace, + # if it's not found, removes the highest entry and searches for the class + # again. If found, returns the highest entry as the class name. + # + # ==== Examples + # + # class Foo::Bar < Thor + # def baz + # end + # end + # + # class Baz::Foo < Thor::Group + # end + # + # Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default task + # Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil + # Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz" + # + # ==== Parameters + # namespace<String> + # + # ==== Errors + # Thor::Error:: raised if the namespace cannot be found. + # + # Thor::Error:: raised if the namespace evals to a class which does not + # inherit from Thor or Thor::Group. + # + def self.namespace_to_thor_class(namespace, raise_if_nil=true) + klass, task_name = Thor::Util.find_by_namespace(namespace), nil + + if klass.nil? && namespace.include?(?:) + namespace = namespace.split(":") + task_name = namespace.pop + klass = Thor::Util.find_by_namespace(namespace.join(":")) + end + + raise Error, "could not find Thor class or task '#{namespace}'" if raise_if_nil && klass.nil? + + return klass, task_name + end + + # Receives a path and load the thor file in the path. The file is evaluated + # inside the sandbox to avoid namespacing conflicts. + # + def self.load_thorfile(path, content=nil) + content ||= File.read(path) + + begin + Thor::Sandbox.class_eval(content, path) + rescue Exception => e + $stderr.puts "WARNING: unable to load thorfile #{path.inspect}: #{e.message}" + end + end + + # Receives a yaml (hash) and updates all constants entries to namespace. + # This was added to deal with deprecated versions of Thor. + # + # TODO Deprecate this method in the future. + # + # ==== Returns + # TrueClass|FalseClass:: Returns true if any change to the yaml file was made. + # + def self.convert_constants_to_namespaces(yaml) + yaml_changed = false + + yaml.each do |k, v| + next unless v[:constants] && v[:namespaces].nil? + yaml_changed = true + yaml[k][:namespaces] = v[:constants].map{|c| Thor::Util.constant_to_namespace(c)} + end + + yaml_changed + end + + def self.user_home + @@user_home ||= if ENV["HOME"] + ENV["HOME"] + elsif ENV["USERPROFILE"] + ENV["USERPROFILE"] + elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"] + File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"]) + elsif ENV["APPDATA"] + ENV["APPDATA"] + else + begin + File.expand_path("~") + rescue + if File::ALT_SEPARATOR + "C:/" + else + "/" + end + end + end + end + + # Returns the root where thor files are located, dependending on the OS. + # + def self.thor_root + File.join(user_home, ".thor") + end + + # Returns the files in the thor root. On Windows thor_root will be something + # like this: + # + # C:\Documents and Settings\james\.thor + # + # If we don't #gsub the \ character, Dir.glob will fail. + # + def self.thor_root_glob + files = Dir["#{thor_root.gsub(/\\/, '/')}/*"] + + files.map! do |file| + File.directory?(file) ? File.join(file, "main.thor") : file + end + end + + # Where to look for Thor files. + # + def self.globs_for(path) + ["#{path}/Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"] + end + + # Return the path to the ruby interpreter taking into account multiple + # installations and windows extensions. + # + def self.ruby_command #:nodoc: + @ruby_command ||= begin + ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) + ruby << Config::CONFIG['EXEEXT'] + + # escape string in case path to ruby executable contain spaces. + ruby.sub!(/.*\s.*/m, '"\&"') + ruby + end + end + + end +end diff --git a/railties/test/boot_test.rb b/railties/test/boot_test.rb index 2dd68857c3..7bf420d4f2 100644 --- a/railties/test/boot_test.rb +++ b/railties/test/boot_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'initializer' -require "#{File.dirname(__FILE__)}/../environments/boot" +require "#{File.dirname(__FILE__)}/../lib/generators/rails/app/templates/config/boot" require 'rails/gem_dependency' class BootTest < Test::Unit::TestCase diff --git a/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb b/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb new file mode 100644 index 0000000000..7a4edb8bcb --- /dev/null +++ b/railties/test/fixtures/lib/generators/active_record/fixjour/fixjour_generator.rb @@ -0,0 +1,8 @@ +require 'generators/active_record' + +module ActiveRecord + module Generators + class FixjourGenerator < Base + end + end +end diff --git a/railties/test/fixtures/lib/generators/fixjour_generator.rb b/railties/test/fixtures/lib/generators/fixjour_generator.rb new file mode 100644 index 0000000000..ef3e9edbed --- /dev/null +++ b/railties/test/fixtures/lib/generators/fixjour_generator.rb @@ -0,0 +1,2 @@ +class FixjourGenerator < Rails::Generators::NamedBase +end diff --git a/railties/test/fixtures/lib/generators/rails/javascripts_generator.rb b/railties/test/fixtures/lib/generators/rails/javascripts_generator.rb new file mode 100644 index 0000000000..cad5e96784 --- /dev/null +++ b/railties/test/fixtures/lib/generators/rails/javascripts_generator.rb @@ -0,0 +1,4 @@ +module Rails::Generators + class JavascriptsGenerator < Rails::Generators::NamedBase + end +end diff --git a/railties/test/fixtures/lib/generators/working/working_generator.rb b/railties/test/fixtures/lib/generators/working/working_generator.rb deleted file mode 100644 index 465b34319a..0000000000 --- a/railties/test/fixtures/lib/generators/working/working_generator.rb +++ /dev/null @@ -1,2 +0,0 @@ -class WorkingGenerator < Rails::Generator::NamedBase -end diff --git a/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb b/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb deleted file mode 100644 index 8fda8197d1..0000000000 --- a/railties/test/fixtures/plugins/default/stubby/generators/stubby_generator/stubby_generator.rb +++ /dev/null @@ -1,4 +0,0 @@ -class StubbyGenerator < Rails::Generator::Base - def manifest - end -end diff --git a/railties/test/fixtures/vendor/gems/mspec/lib/generators/mspec_generator.rb b/railties/test/fixtures/vendor/gems/mspec/lib/generators/mspec_generator.rb new file mode 100644 index 0000000000..191bdbf2fc --- /dev/null +++ b/railties/test/fixtures/vendor/gems/mspec/lib/generators/mspec_generator.rb @@ -0,0 +1,2 @@ +class MspecGenerator < Rails::Generators::NamedBase +end diff --git a/railties/test/fixtures/vendor/gems/wrong/lib/generators/wrong_generator.rb b/railties/test/fixtures/vendor/gems/wrong/lib/generators/wrong_generator.rb new file mode 100644 index 0000000000..6aa7cb052e --- /dev/null +++ b/railties/test/fixtures/vendor/gems/wrong/lib/generators/wrong_generator.rb @@ -0,0 +1,3 @@ +# Old generator version +class WrongGenerator < Rails::Generator::NamedBase +end diff --git a/railties/test/generator_lookup_test.rb b/railties/test/generator_lookup_test.rb deleted file mode 100644 index b67087e5ea..0000000000 --- a/railties/test/generator_lookup_test.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'plugin_test_helper' - -class GeneratorLookupTest < Test::Unit::TestCase - def setup - @fixture_dirs = %w{alternate default} - @configuration = Rails.configuration = Rails::Configuration.new - # We need to add our testing plugin directory to the plugin paths so - # the locator knows where to look for our plugins - @configuration.plugin_paths += @fixture_dirs.map{|fd| plugin_fixture_path(fd)} - @initializer = Rails::Initializer.default - @initializer.config = @configuration - @initializer.run(:add_plugin_load_paths) - @initializer.run(:load_plugins) - @initializer.run(:set_root_path) - load 'rails_generator.rb' - require 'rails_generator/scripts' - end - - def test_should_load_from_all_plugin_paths - assert Rails::Generator::Base.lookup('a_generator') - assert Rails::Generator::Base.lookup('stubby_generator') - end - - def test_should_create_generator_source_for_each_directory_in_plugin_paths - sources = Rails::Generator::Base.sources - @fixture_dirs.each do |gen_dir| - expected_label = "plugins (fixtures/plugins/#{gen_dir})".to_sym - assert sources.any? {|source| source.label == expected_label } - end - end - - def test_should_preserve_order_in_usage_message - msg = Rails::Generator::Scripts::Base.new.send(:usage_message) - positions = @fixture_dirs.map do |gen_dir| - pos = msg.index("Plugins (fixtures/plugins/#{gen_dir})") - assert_not_nil pos - pos - end - assert_equal positions.sort, positions - end - -end diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb new file mode 100644 index 0000000000..0cda49702b --- /dev/null +++ b/railties/test/generators/actions_test.rb @@ -0,0 +1,177 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/app/app_generator' + +class ActionsTest < GeneratorsTestCase + def setup + super + @git_plugin_uri = 'git://github.com/technoweenie/restful-authentication.git' + @svn_plugin_uri = 'svn://svnhub.com/technoweenie/restful-authentication/trunk' + end + + def test_create_file_should_write_data_to_file_path + action :create_file, 'lib/test_file.rb', 'heres test data' + assert_file 'lib/test_file.rb', 'heres test data' + end + + def test_create_file_should_write_block_contents_to_file_path + action(:create_file, 'lib/test_file.rb'){ 'heres block data' } + assert_file 'lib/test_file.rb', 'heres block data' + end + + def test_plugin_with_git_option_should_run_plugin_install + generator.expects(:run_ruby_script).once.with("script/plugin install #{@git_plugin_uri}", :verbose => false) + action :plugin, 'restful-authentication', :git => @git_plugin_uri + end + + def test_plugin_with_svn_option_should_run_plugin_install + generator.expects(:run_ruby_script).once.with("script/plugin install #{@svn_plugin_uri}", :verbose => false) + action :plugin, 'restful-authentication', :svn => @svn_plugin_uri + end + + def test_plugin_with_git_option_and_submodule_should_use_git_scm + generator.expects(:run).with("git submodule add #{@git_plugin_uri} vendor/plugins/rest_auth", :verbose => false) + action :plugin, 'rest_auth', :git => @git_plugin_uri, :submodule => true + end + + def test_plugin_with_no_options_should_skip_method + generator.expects(:run).never + action :plugin, 'rest_auth', {} + end + + def test_gem_should_put_gem_dependency_in_enviroment + run_generator + action :gem, 'will-paginate' + assert_file 'config/environment.rb', /config\.gem 'will\-paginate'/ + end + + def test_gem_with_options_should_include_options_in_gem_dependency_in_environment + run_generator + action :gem, 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com' + + regexp = /#{Regexp.escape("config.gem 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com'")}/ + assert_file 'config/environment.rb', regexp + end + + def test_gem_with_env_string_should_put_gem_dependency_in_specified_environment + run_generator + action :gem, 'rspec', :env => 'test' + assert_file 'config/environments/test.rb', /config\.gem 'rspec'/ + end + + def test_gem_with_env_array_should_put_gem_dependency_in_specified_environments + run_generator + action :gem, 'quietbacktrace', :env => %w[ development test ] + assert_file 'config/environments/development.rb', /config\.gem 'quietbacktrace'/ + assert_file 'config/environments/test.rb', /config\.gem 'quietbacktrace'/ + end + + def test_gem_with_lib_option_set_to_false_should_put_gem_dependency_in_enviroment_correctly + run_generator + action :gem, 'mislav-will-paginate', :lib => false + assert_file 'config/environment.rb', /config\.gem 'mislav\-will\-paginate'\, :lib => false/ + end + + def test_environment_should_include_data_in_environment_initializer_block + run_generator + load_paths = 'config.load_paths += %w["#{RAILS_ROOT}/app/extras"]' + action :environment, load_paths + assert_file 'config/environment.rb', /#{Regexp.escape(load_paths)}/ + end + + def test_environment_with_block_should_include_block_contents_in_environment_initializer_block + run_generator + + action :environment do + '# This wont be added' + '# This will be added' + end + + assert_file 'config/environment.rb' do |content| + assert_match /# This will be added/, content + assert_no_match /# This wont be added/, content + end + end + + def test_git_with_symbol_should_run_command_using_git_scm + generator.expects(:run).once.with('git init') + action :git, :init + end + + def test_git_with_hash_should_run_each_command_using_git_scm + generator.expects(:run).times(2) + action :git, :rm => 'README', :add => '.' + end + + def test_vendor_should_write_data_to_file_in_vendor + action :vendor, 'vendor_file.rb', '# vendor data' + assert_file 'vendor/vendor_file.rb', '# vendor data' + end + + def test_lib_should_write_data_to_file_in_lib + action :lib, 'my_library.rb', 'class MyLibrary' + assert_file 'lib/my_library.rb', 'class MyLibrary' + end + + def test_rakefile_should_write_date_to_file_in_lib_tasks + action :rakefile, 'myapp.rake', 'task :run => [:environment]' + assert_file 'lib/tasks/myapp.rake', 'task :run => [:environment]' + end + + def test_initializer_should_write_date_to_file_in_config_initializers + action :initializer, 'constants.rb', 'MY_CONSTANT = 42' + assert_file 'config/initializers/constants.rb', 'MY_CONSTANT = 42' + end + + def test_generate_should_run_script_generate_with_argument_and_options + generator.expects(:run_ruby_script).once.with('script/generate model MyModel', :verbose => false) + action :generate, 'model', 'MyModel' + end + + def test_rake_should_run_rake_command_with_development_env + generator.expects(:run).once.with('rake log:clear RAILS_ENV=development', :verbose => false) + action :rake, 'log:clear' + end + + def test_rake_with_env_option_should_run_rake_command_in_env + generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', :verbose => false) + action :rake, 'log:clear', :env => 'production' + end + + def test_rake_with_sudo_option_should_run_rake_command_with_sudo + generator.expects(:run).once.with('sudo rake log:clear RAILS_ENV=development', :verbose => false) + action :rake, 'log:clear', :sudo => true + end + + def test_capify_should_run_the_capify_command + generator.expects(:run).once.with('capify .', :verbose => false) + action :capify! + end + + def test_freeze_should_freeze_rails_edge + generator.expects(:run).once.with('rake rails:freeze:edge', :verbose => false) + action :freeze! + end + + def test_route_should_add_data_to_the_routes_block_in_config_routes + run_generator + route_command = "map.route '/login', :controller => 'sessions', :action => 'new'" + action :route, route_command + assert_file 'config/routes.rb', /#{Regexp.escape(route_command)}/ + end + + protected + + def run_generator + silence(:stdout) { Rails::Generators::AppGenerator.start [destination_root] } + end + + def generator(config={}) + @generator ||= Rails::Generators::Base.new([], {}, { :destination_root => destination_root }.merge!(config)) + end + + def action(*args, &block) + silence(:stdout){ generator.send(*args, &block) } + end + +end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb new file mode 100644 index 0000000000..c794a2ade6 --- /dev/null +++ b/railties/test/generators/app_generator_test.rb @@ -0,0 +1,182 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/app/app_generator' + +class AppGeneratorTest < GeneratorsTestCase + + def setup + super + Rails::Generators::AppGenerator.instance_variable_set('@desc', nil) + end + + def teardown + super + Rails::Generators::AppGenerator.instance_variable_set('@desc', nil) + end + + def test_application_skeleton_is_created + run_generator + + %w( + 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/performance + 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 } + end + + def test_invalid_database_option_raises_an_error + content = capture(:stderr){ run_generator(["-d", "unknown"]) } + assert_match /Invalid value for \-\-database option/, content + end + + def test_dispatchers_are_not_added_by_default + run_generator + assert_no_file "config.ru" + assert_no_file "public/dispatch.cgi" + assert_no_file "public/dispatch.fcgi" + end + + def test_dispatchers_are_added_if_required + run_generator ["--with-dispatchers"] + assert_file "config.ru" + assert_file "public/dispatch.cgi" + assert_file "public/dispatch.fcgi" + end + + def test_config_database_is_added_by_default + run_generator + assert_file "config/database.yml", /sqlite3/ + end + + def test_config_database_is_not_added_if_skip_activerecord_is_given + run_generator ["--skip-activerecord"] + assert_no_file "config/database.yml" + end + + def test_activerecord_is_removed_from_frameworks_if_skip_activerecord_is_given + run_generator ["--skip-activerecord"] + assert_file "config/environment.rb", /config\.frameworks \-= \[ :active_record \]/ + end + + def test_prototype_and_test_unit_are_added_by_default + run_generator + assert_file "public/javascripts/prototype.js" + assert_file "test" + end + + def test_prototype_and_test_unit_are_skipped_if_required + run_generator ["--skip-prototype", "--skip-testunit"] + assert_no_file "public/javascripts/prototype.js" + assert_no_file "test" + end + + def test_shebang_is_added_to_files + run_generator ["--ruby", "foo/bar/baz"] + + %w( + about + console + dbconsole + destroy + generate + plugin + runner + server + ).each { |path| assert_file "script/#{path}", /#!foo\/bar\/baz/ } + end + + def test_shebang_when_is_the_same_as_default_use_env + run_generator ["--ruby", Thor::Util.ruby_command] + + %w( + about + console + dbconsole + destroy + generate + plugin + runner + server + ).each { |path| assert_file "script/#{path}", /#!\/usr\/bin\/env/ } + end + + def test_rails_is_frozen + generator(:freeze => true, :database => "sqlite3").expects(:run). + with("rake rails:freeze:edge", :verbose => false) + silence(:stdout){ generator.invoke } + assert_file 'config/environment.rb', /# RAILS_GEM_VERSION/ + end + + def test_template_raises_an_error_with_invalid_path + content = capture(:stderr){ run_generator(["-m", "non/existant/path"]) } + assert_match /The template \[.*\] could not be loaded/, content + assert_match /non\/existant\/path/, content + end + + def test_template_is_executed_when_supplied + path = "http://gist.github.com/103208.txt" + template = %{ say "It works!" } + template.instance_eval "def read; self; end" # Make the string respond to read + + generator(:template => path, :database => "sqlite3").expects(:open).with(path).returns(template) + assert_match /It works!/, silence(:stdout){ generator.invoke } + end + + def test_usage_read_from_file + File.expects(:read).returns("USAGE FROM FILE") + assert_equal "USAGE FROM FILE", Rails::Generators::AppGenerator.desc + end + + def test_default_usage + File.expects(:exist?).returns(false) + assert_match /Create rails files for app generator/, Rails::Generators::AppGenerator.desc + end + + def test_default_namespace + assert_match "rails:generators:app", Rails::Generators::AppGenerator.namespace + end + + def test_file_is_added_for_backwards_compatibility + action :file, 'lib/test_file.rb', 'heres test data' + assert_file 'lib/test_file.rb', 'heres test data' + end + + protected + + def run_generator(args=[]) + silence(:stdout) { Rails::Generators::AppGenerator.start [destination_root].concat(args) } + end + + def generator(options={}) + @generator ||= Rails::Generators::AppGenerator.new([destination_root], options, :destination_root => destination_root) + end + + def action(*args, &block) + silence(:stdout){ generator.send(*args, &block) } + end + +end diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb new file mode 100644 index 0000000000..850b9e632d --- /dev/null +++ b/railties/test/generators/controller_generator_test.rb @@ -0,0 +1,80 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/controller/controller_generator' + +class ControllerGeneratorTest < GeneratorsTestCase + + def test_help_does_not_show_invoked_generators_options_if_they_already_exist + content = run_generator ["--help"] + assert_no_match /Helper options:/, content + end + + def test_controller_skeleton_is_created + run_generator + assert_file "app/controllers/account_controller.rb", /class AccountController < ApplicationController/ + end + + def test_check_class_collision + Object.send :const_set, :ObjectController, Class.new + content = capture(:stderr){ run_generator ["object"] } + assert_match /The name 'ObjectController' is either already used in your application or reserved/, content + ensure + Object.send :remove_const, :ObjectController + end + + # No need to spec content since it's already spec'ed on helper generator. + # + def test_invokes_helper + run_generator + assert_file "app/helpers/account_helper.rb" + assert_file "test/unit/helpers/account_helper_test.rb" + end + + def test_does_not_invoke_helper_if_required + run_generator ["account", "--skip-helper"] + assert_no_file "app/helpers/account_helper.rb" + assert_no_file "test/unit/helpers/account_helper_test.rb" + end + + def test_invokes_default_test_framework + run_generator + assert_file "test/functional/account_controller_test.rb" + end + + def test_does_not_invoke_test_framework_if_required + run_generator ["account", "--no-test-framework"] + assert_no_file "test/functional/account_controller_test.rb" + end + + def test_invokes_default_template_engine + run_generator + assert_file "app/views/account/foo.html.erb", /app\/views\/account\/foo/ + assert_file "app/views/account/bar.html.erb", /app\/views\/account\/bar/ + end + + def test_invokes_default_template_engine_even_with_no_action + run_generator ["account"] + assert_file "app/views/account" + end + + def test_template_engine_with_class_path + run_generator ["admin/account"] + assert_file "app/views/admin/account" + end + + def test_actions_are_turned_into_methods + run_generator + + assert_file "app/controllers/account_controller.rb" do |controller| + assert_instance_method controller, :foo + assert_instance_method controller, :bar + end + end + + protected + + def run_generator(args=["Account", "foo", "bar"]) + silence(:stdout) { Rails::Generators::ControllerGenerator.start args, :destination_root => destination_root } + end + +end diff --git a/railties/test/generators/generator_generator_test.rb b/railties/test/generators/generator_generator_test.rb new file mode 100644 index 0000000000..b1ebe5f1b5 --- /dev/null +++ b/railties/test/generators/generator_generator_test.rb @@ -0,0 +1,26 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/generator/generator_generator' + +class GeneratorGeneratorTest < GeneratorsTestCase + + def test_generator_skeleton_is_created + run_generator + + %w( + lib/generators/awesome + lib/generators/awesome/USAGE + lib/generators/awesome/templates + ).each{ |path| assert_file path } + + assert_file "lib/generators/awesome/awesome_generator.rb", + /class AwesomeGenerator < Rails::Generators::NamedBase/ + end + + protected + + def run_generator(args=["awesome"], config={}) + silence(:stdout) { Rails::Generators::GeneratorGenerator.start args, config.merge(:destination_root => destination_root) } + end + +end diff --git a/railties/test/generators/generator_test_helper.rb b/railties/test/generators/generator_test_helper.rb deleted file mode 100644 index 01bf1c90bd..0000000000 --- a/railties/test/generators/generator_test_helper.rb +++ /dev/null @@ -1,303 +0,0 @@ -require 'test/unit' -require 'fileutils' - -# Mock out what we need from AR::Base -module ActiveRecord - class Base - class << self - attr_accessor :pluralize_table_names, :timestamped_migrations - end - self.pluralize_table_names = true - self.timestamped_migrations = true - end - - module ConnectionAdapters - class Column - attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale - - def initialize(name, default, sql_type = nil) - @name = name - @default = default - @type = @sql_type = sql_type - end - - def human_name - @name.humanize - end - end - end -end - -# Mock up necessities from ActionView -module ActionView - module Helpers - module ActionRecordHelper; end - class InstanceTag; end - end -end - -# Set RAILS_ROOT appropriately fixture generation -tmp_dir = "#{File.dirname(__FILE__)}/../fixtures/tmp" - -if defined? RAILS_ROOT - RAILS_ROOT.replace tmp_dir -else - RAILS_ROOT = tmp_dir -end -FileUtils.mkdir_p RAILS_ROOT - -$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib" -require 'initializer' - -# Mocks out the configuration -module Rails - def self.configuration - Rails::Configuration.new - end -end - -require 'rails_generator' - -class GeneratorTestCase < Test::Unit::TestCase - include FileUtils - - def setup - ActiveRecord::Base.pluralize_table_names = true - - mkdir_p "#{RAILS_ROOT}/app/views/layouts" - mkdir_p "#{RAILS_ROOT}/config" - mkdir_p "#{RAILS_ROOT}/db" - mkdir_p "#{RAILS_ROOT}/test/fixtures" - mkdir_p "#{RAILS_ROOT}/public/stylesheets" - - File.open("#{RAILS_ROOT}/config/routes.rb", 'w') do |f| - f << "ActionController::Routing::Routes.draw do |map|\n\nend" - end - end - - def teardown - rm_rf "#{RAILS_ROOT}/app" - rm_rf "#{RAILS_ROOT}/test" - rm_rf "#{RAILS_ROOT}/config" - rm_rf "#{RAILS_ROOT}/db" - rm_rf "#{RAILS_ROOT}/public" - end - - def test_truth - # don't complain, test/unit - end - - # Instantiates the Generator. - def build_generator(name, params) - Rails::Generator::Base.instance(name, params) - end - - # Runs the +create+ command (like the command line does). - def run_generator(name, params) - silence_generator do - build_generator(name, params).command(:create).invoke! - end - end - - # Silences the logger temporarily and returns the output as a String. - def silence_generator - logger_original = Rails::Generator::Base.logger - myout = StringIO.new - Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(myout) - yield if block_given? - Rails::Generator::Base.logger = logger_original - myout.string - end - - # Asserts that the given controller was generated. - # It takes a name or symbol without the <tt>_controller</tt> part and an optional super class. - # The contents of the class source file is passed to a block. - def assert_generated_controller_for(name, parent = "ApplicationController") - assert_generated_class "app/controllers/#{name.to_s.underscore}_controller", parent do |body| - yield body if block_given? - end - end - - # Asserts that the given model was generated. - # It takes a name or symbol and an optional super class. - # The contents of the class source file is passed to a block. - def assert_generated_model_for(name, parent = "ActiveRecord::Base") - assert_generated_class "app/models/#{name.to_s.underscore}", parent do |body| - yield body if block_given? - end - end - - # Asserts that the given helper was generated. - # It takes a name or symbol without the <tt>_helper</tt> part. - # The contents of the module source file is passed to a block. - def assert_generated_helper_for(name) - assert_generated_module "app/helpers/#{name.to_s.underscore}_helper" do |body| - yield body if block_given? - end - end - - # Asserts that the given functional test was generated. - # It takes a name or symbol without the <tt>_controller_test</tt> part and an optional super class. - # The contents of the class source file is passed to a block. - def assert_generated_functional_test_for(name, parent = "ActionController::TestCase") - assert_generated_class "test/functional/#{name.to_s.underscore}_controller_test",parent do |body| - yield body if block_given? - end - end - - # Asserts that the given helper test test was generated. - # It takes a name or symbol without the <tt>_helper_test</tt> part and an optional super class. - # The contents of the class source file is passed to a block. - def assert_generated_helper_test_for(name, parent = "ActionView::TestCase") - path = "test/unit/helpers/#{name.to_s.underscore}_helper_test" - # Have to pass the path without the "test/" part so that class_name_from_path will return a correct result - class_name = class_name_from_path(path.gsub(/^test\//, '')) - - assert_generated_class path,parent,class_name do |body| - yield body if block_given? - end - end - - # Asserts that the given unit test was generated. - # It takes a name or symbol without the <tt>_test</tt> part and an optional super class. - # The contents of the class source file is passed to a block. - def assert_generated_unit_test_for(name, parent = "ActiveSupport::TestCase") - assert_generated_class "test/unit/#{name.to_s.underscore}_test", parent do |body| - yield body if block_given? - end - end - - # Asserts that the given file was generated. - # The contents of the file is passed to a block. - def assert_generated_file(path) - assert_file_exists(path) - File.open("#{RAILS_ROOT}/#{path}") do |f| - yield f.read if block_given? - end - end - - # asserts that the given file exists - def assert_file_exists(path) - assert File.exist?("#{RAILS_ROOT}/#{path}"), - "The file '#{RAILS_ROOT}/#{path}' should exist" - end - - # Asserts that the given class source file was generated. - # It takes a path without the <tt>.rb</tt> part and an optional super class. - # The contents of the class source file is passed to a block. - def assert_generated_class(path, parent = nil, class_name = class_name_from_path(path)) - assert_generated_file("#{path}.rb") do |body| - assert_match /class #{class_name}#{parent.nil? ? '':" < #{parent}"}/, body, "the file '#{path}.rb' should be a class" - yield body if block_given? - end - end - - def class_name_from_path(path) - # FIXME: Sucky way to detect namespaced classes - if path.split('/').size > 3 - path =~ /\/?(\d+_)?(\w+)\/(\w+)$/ - "#{$2.camelize}::#{$3.camelize}" - else - path =~ /\/?(\d+_)?(\w+)$/ - $2.camelize - end - end - - # Asserts that the given module source file was generated. - # It takes a path without the <tt>.rb</tt> part. - # The contents of the class source file is passed to a block. - def assert_generated_module(path) - # FIXME: Sucky way to detect namespaced modules - if path.split('/').size > 3 - path =~ /\/?(\w+)\/(\w+)$/ - module_name = "#{$1.camelize}::#{$2.camelize}" - else - path =~ /\/?(\w+)$/ - module_name = $1.camelize - end - - assert_generated_file("#{path}.rb") do |body| - assert_match /module #{module_name}/, body, "the file '#{path}.rb' should be a module" - yield body if block_given? - end - end - - # Asserts that the given CSS stylesheet file was generated. - # It takes a path without the <tt>.css</tt> part. - # The contents of the stylesheet source file is passed to a block. - def assert_generated_stylesheet(path) - assert_generated_file("public/stylesheets/#{path}.css") do |body| - yield body if block_given? - end - end - - # Asserts that the given YAML file was generated. - # It takes a path without the <tt>.yml</tt> part. - # The parsed YAML tree is passed to a block. - def assert_generated_yaml(path) - assert_generated_file("#{path}.yml") do |body| - yaml = YAML.load(body) - assert yaml, 'YAML data missing' - yield yaml if block_given? - end - end - - # Asserts that the given fixtures YAML file was generated. - # It takes a fixture name without the <tt>.yml</tt> part. - # The parsed YAML tree is passed to a block. - def assert_generated_fixtures_for(name) - assert_generated_yaml "test/fixtures/#{name.to_s.underscore}" do |yaml| - yield yaml if block_given? - end - end - - # Asserts that the given views were generated. - # It takes a controller name and a list of views (including extensions). - # The body of each view is passed to a block. - def assert_generated_views_for(name, *actions) - actions.each do |action| - assert_generated_file("app/views/#{name.to_s.underscore}/#{action}") do |body| - yield body if block_given? - end - end - end - - def assert_generated_migration(name, parent = "ActiveRecord::Migration") - file = Dir.glob("#{RAILS_ROOT}/db/migrate/*_#{name.to_s.underscore}.rb").first - file = file.match(/db\/migrate\/[0-9]+_\w+/).to_s - assert_generated_class file, parent do |body| - assert_match /timestamps/, body, "should have timestamps defined" - yield body if block_given? - end - end - - # Asserts that the given migration file was not generated. - # It takes the name of the migration as a parameter. - def assert_skipped_migration(name) - migration_file = "#{RAILS_ROOT}/db/migrate/001_#{name.to_s.underscore}.rb" - assert !File.exist?(migration_file), "should not create migration #{migration_file}" - end - - # Asserts that the given resource was added to the routes. - def assert_added_route_for(name) - assert_generated_file("config/routes.rb") do |body| - assert_match /map.resources :#{name.to_s.underscore}/, body, - "should add route for :#{name.to_s.underscore}" - end - end - - # Asserts that the given methods are defined in the body. - # This does assume standard rails code conventions with regards to the source code. - # The body of each individual method is passed to a block. - def assert_has_method(body, *methods) - methods.each do |name| - assert body =~ /^ def #{name}(\(.+\))?\n((\n| .*\n)*) end/, "should have method #{name}" - yield(name, $2) if block_given? - end - end - - # Asserts that the given column is defined in the migration. - def assert_generated_column(body, name, type) - assert_match /t\.#{type.to_s} :#{name.to_s}/, body, "should have column #{name.to_s} defined" - end -end diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb new file mode 100644 index 0000000000..011bd518f8 --- /dev/null +++ b/railties/test/generators/generators_test_helper.rb @@ -0,0 +1,101 @@ +require 'test/unit' +require 'fileutils' + +fixtures = File.expand_path(File.join(File.dirname(__FILE__), '..', 'fixtures')) +if defined?(RAILS_ROOT) + RAILS_ROOT.replace fixtures +else + RAILS_ROOT = fixtures +end + +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../activerecord/lib" +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib" +require 'generators' + +CURRENT_PATH = File.expand_path(Dir.pwd) +Rails::Generators.no_color! + +class GeneratorsTestCase < Test::Unit::TestCase + include FileUtils + + def destination_root + @destination_root ||= File.expand_path(File.join(File.dirname(__FILE__), + '..', '..', 'fixtures', 'tmp')) + end + + def setup + cd CURRENT_PATH + rm_rf(destination_root) + mkdir_p(destination_root) + end + + def test_truth + # don't complain, test/unit + end + + def capture(stream) + begin + stream = stream.to_s + eval "$#{stream} = StringIO.new" + yield + result = eval("$#{stream}").string + ensure + eval("$#{stream} = #{stream.upcase}") + end + + result + end + alias :silence :capture + + def assert_file(relative, *contents) + absolute = File.join(destination_root, relative) + assert File.exists?(absolute), "Expected file #{relative.inspect} to exist, but does not" + + read = File.read(absolute) if block_given? || !contents.empty? + yield read if block_given? + + contents.each do |content| + case content + when String + assert_equal content, read + when Regexp + assert_match content, read + end + end + end + + def assert_no_file(relative) + absolute = File.join(destination_root, relative) + assert !File.exists?(absolute), "Expected file #{relative.inspect} to not exist, but does" + end + + def assert_migration(relative, *contents, &block) + file_name = migration_file_name(relative) + assert file_name, "Expected migration #{relative} to exist, but was not found" + assert_file File.join(File.dirname(relative), file_name), *contents, &block + end + + def assert_no_migration(relative) + file_name = migration_file_name(relative) + assert_nil file_name, "Expected migration #{relative} to not exist, but found #{file_name}" + end + + def assert_class_method(content, method, &block) + assert_instance_method content, "self.#{method}", &block + end + + def assert_instance_method(content, method) + assert content =~ /def #{method}(\(.+\))?(.*?)\n end/m, "Expected to have method #{method}" + yield $2.strip if block_given? + end + + protected + + def migration_file_name(relative) + absolute = File.join(destination_root, relative) + dirname, file_name = File.dirname(absolute), File.basename(absolute).sub(/\.rb$/, '') + + migration = Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first + File.basename(migration) if migration + end +end diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb new file mode 100644 index 0000000000..3d655016f1 --- /dev/null +++ b/railties/test/generators/helper_generator_test.rb @@ -0,0 +1,60 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/helper/helper_generator' + +ObjectHelper = Class.new +AnotherObjectHelperTest = Class.new + +class HelperGeneratorTest < GeneratorsTestCase + + def test_helper_skeleton_is_created + run_generator + assert_file "app/helpers/admin_helper.rb", /module AdminHelper/ + end + + def test_invokes_default_test_framework + run_generator + assert_file "test/unit/helpers/admin_helper_test.rb", /class AdminHelperTest < ActionView::TestCase/ + end + + def test_logs_if_the_test_framework_cannot_be_found + content = run_generator ["admin", "--test-framework=rspec"] + assert_match /rspec \[not found\]/, content + end + + def test_check_class_collision + content = capture(:stderr){ run_generator ["object"] } + assert_match /The name 'ObjectHelper' is either already used in your application or reserved/, content + end + + def test_check_class_collision_on_tests + content = capture(:stderr){ run_generator ["another_object"] } + assert_match /The name 'AnotherObjectHelperTest' is either already used in your application or reserved/, content + end + + def test_namespaced_and_not_namespaced_helpers + run_generator ["products"] + + # We have to require the generated helper to show the problem because + # the test helpers just check for generated files and contents but + # do not actually load them. But they have to be loaded (as in a real environment) + # to make the second generator run fail + require "#{destination_root}/app/helpers/products_helper" + + assert_nothing_raised do + begin + run_generator ["admin::products"] + ensure + # cleanup + Object.send(:remove_const, :ProductsHelper) + end + end + end + + protected + + def run_generator(args=["admin"]) + silence(:stdout) { Rails::Generators::HelperGenerator.start args, :destination_root => destination_root } + end + +end diff --git a/railties/test/generators/integration_test_generator_test.rb b/railties/test/generators/integration_test_generator_test.rb new file mode 100644 index 0000000000..190b28012c --- /dev/null +++ b/railties/test/generators/integration_test_generator_test.rb @@ -0,0 +1,18 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/integration_test/integration_test_generator' + +class IntegrationTestGeneratorTest < GeneratorsTestCase + + def test_integration_test_skeleton_is_created + run_generator + assert_file "test/integration/integration_test.rb", /class IntegrationTest < ActionController::IntegrationTest/ + end + + protected + + def run_generator(args=["integration"]) + silence(:stdout) { Rails::Generators::IntegrationTestGenerator.start args, :destination_root => destination_root } + end + +end diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb new file mode 100644 index 0000000000..9e74859c3a --- /dev/null +++ b/railties/test/generators/mailer_generator_test.rb @@ -0,0 +1,52 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/mailer/mailer_generator' + +class MailerGeneratorTest < GeneratorsTestCase + + def test_mailer_skeleton_is_created + run_generator + assert_file "app/models/notifier.rb", /class Notifier < ActionMailer::Base/ + end + + def test_check_class_collision + content = capture(:stderr){ run_generator ["object"] } + assert_match /The name 'Object' is either already used in your application or reserved/, content + end + + def test_invokes_default_test_framework + run_generator + assert_file "test/unit/notifier_test.rb", /class NotifierTest < ActionMailer::TestCase/ + assert_file "test/fixtures/notifier/foo", /app\/views\/notifier\/foo/ + assert_file "test/fixtures/notifier/bar", /app\/views\/notifier\/bar/ + end + + def test_invokes_default_template_engine + run_generator + assert_file "app/views/notifier/foo.erb", /app\/views\/notifier\/foo/ + assert_file "app/views/notifier/bar.erb", /app\/views\/notifier\/bar/ + end + + def test_invokes_default_template_engine_even_with_no_action + run_generator ["notifier"] + assert_file "app/views/notifier" + end + + def test_logs_if_the_template_engine_cannot_be_found + content = run_generator ["notifier", "foo", "bar", "--template-engine=haml"] + assert_match /haml \[not found\]/, content + end + + def test_actions_are_turned_into_methods + run_generator + assert_file "app/models/notifier.rb", /def foo/ + assert_file "app/models/notifier.rb", /def bar/ + end + + protected + + def run_generator(args=["notifier", "foo", "bar"]) + silence(:stdout) { Rails::Generators::MailerGenerator.start args, :destination_root => destination_root } + end + +end diff --git a/railties/test/generators/metal_generator_test.rb b/railties/test/generators/metal_generator_test.rb new file mode 100644 index 0000000000..97e892cefb --- /dev/null +++ b/railties/test/generators/metal_generator_test.rb @@ -0,0 +1,23 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/metal/metal_generator' + +class MetalGeneratorTest < GeneratorsTestCase + + def test_metal_skeleton_is_created + run_generator + assert_file "app/metal/foo.rb", /class Foo/ + end + + def test_check_class_collision + content = capture(:stderr){ run_generator ["object"] } + assert_match /The name 'Object' is either already used in your application or reserved/, content + end + + protected + + def run_generator(args=["foo"]) + silence(:stdout) { Rails::Generators::MetalGenerator.start args, :destination_root => destination_root } + end + +end diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb new file mode 100644 index 0000000000..a4335068e6 --- /dev/null +++ b/railties/test/generators/migration_generator_test.rb @@ -0,0 +1,59 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/migration/migration_generator' + +class MigrationGeneratorTest < GeneratorsTestCase + + def test_migration + @migration = "change_title_body_from_posts" + run_generator + assert_migration "db/migrate/#{@migration}.rb", /class ChangeTitleBodyFromPosts < ActiveRecord::Migration/ + end + + def test_migration_with_class_name + @migration = "ChangeTitleBodyFromPosts" + run_generator + assert_migration "db/migrate/change_title_body_from_posts.rb", /class #{@migration} < ActiveRecord::Migration/ + end + + def test_add_migration_with_attributes + @migration = "add_title_body_to_posts" + run_generator [@migration, "title:string", "body:text"] + + assert_migration "db/migrate/#{@migration}.rb" do |content| + assert_class_method content, :up do |up| + assert_match /add_column :posts, :title, :string/, up + assert_match /add_column :posts, :body, :text/, up + end + + assert_class_method content, :down do |down| + assert_match /remove_column :posts, :title/, down + assert_match /remove_column :posts, :body/, down + end + end + end + + def test_remove_migration_with_attributes + @migration = "remove_title_body_from_posts" + run_generator [@migration, "title:string", "body:text"] + + assert_migration "db/migrate/#{@migration}.rb" do |content| + assert_class_method content, :up do |up| + assert_match /remove_column :posts, :title/, up + assert_match /remove_column :posts, :body/, up + end + + assert_class_method content, :down do |down| + assert_match /add_column :posts, :title, :string/, down + assert_match /add_column :posts, :body, :text/, down + end + end + end + + protected + + def run_generator(args=[@migration]) + silence(:stdout) { Rails::Generators::MigrationGenerator.start args, :destination_root => destination_root } + end + +end diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb new file mode 100644 index 0000000000..a9b772d67b --- /dev/null +++ b/railties/test/generators/model_generator_test.rb @@ -0,0 +1,137 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/model/model_generator' + +class ModelGeneratorTest < GeneratorsTestCase + + def test_help_shows_invoked_generators_options + content = run_generator ["--help"] + assert_match /ActiveRecord options:/, content + assert_match /TestUnit options:/, content + end + + def test_invokes_default_orm + run_generator + assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/ + end + + def test_model_with_parent_option + run_generator ["account", "--parent", "Admin::Account"] + assert_file "app/models/account.rb", /class Account < Admin::Account/ + assert_no_migration "db/migrate/create_accounts.rb" + end + + def test_model_with_underscored_parent_option + run_generator ["account", "--parent", "admin/account"] + assert_file "app/models/account.rb", /class Account < Admin::Account/ + end + + def test_migration + run_generator + assert_migration "db/migrate/create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration/ + end + + def test_migration_is_skipped + run_generator ["account", "--no-migration"] + assert_no_migration "db/migrate/create_accounts.rb" + end + + def test_migration_with_attributes + run_generator ["product", "name:string", "supplier_id:integer"] + + assert_migration "db/migrate/create_products.rb" do |m| + assert_class_method m, :up do |up| + assert_match /create_table :products/, up + assert_match /t\.string :name/, up + assert_match /t\.integer :supplier_id/, up + end + + assert_class_method m, :down do |down| + assert_match /drop_table :products/, down + end + end + end + + def test_migration_without_timestamps + ActiveRecord::Base.timestamped_migrations = false + run_generator ["account"] + assert_file "db/migrate/001_create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration/ + + run_generator ["project"] + assert_file "db/migrate/002_create_projects.rb", /class CreateProjects < ActiveRecord::Migration/ + ensure + ActiveRecord::Base.timestamped_migrations = true + end + + def test_model_with_references_attribute_generates_belongs_to_associations + run_generator ["product", "name:string", "supplier_id:references"] + assert_file "app/models/product.rb", /belongs_to :supplier/ + end + + def test_model_with_belongs_to_attribute_generates_belongs_to_associations + run_generator ["product", "name:string", "supplier_id:belongs_to"] + assert_file "app/models/product.rb", /belongs_to :supplier/ + end + + def test_migration_with_timestamps + run_generator + assert_migration "db/migrate/create_accounts.rb", /t.timestamps/ + end + + def test_migration_timestamps_are_skipped + run_generator ["account", "--no-timestamps"] + + assert_migration "db/migrate/create_accounts.rb" do |m| + assert_class_method m, :up do |up| + assert_no_match /t.timestamps/, up + end + end + end + + def test_migration_already_exists_error_message + run_generator + error = capture(:stderr){ run_generator ["Account"], :behavior => :skip } + assert_match /Another migration is already named create_accounts/, error + end + + def test_migration_error_is_not_shown_on_revoke + run_generator + error = capture(:stderr){ run_generator ["Account"], :behavior => :revoke } + assert_no_match /Another migration is already named create_accounts/, error + end + + def test_migration_is_removed_on_revoke + run_generator + run_generator ["Account"], :behavior => :revoke + assert_no_migration "db/migrate/create_accounts.rb" + end + + def test_invokes_default_test_framework + run_generator + assert_file "test/unit/account_test.rb", /class AccountTest < ActiveSupport::TestCase/ + assert_file "test/fixtures/accounts.yml", /name: MyString/, /age: 1/ + end + + def test_fixture_is_skipped + run_generator ["account", "--skip-fixture"] + assert_no_file "test/fixtures/accounts.yml" + end + + def test_fixture_is_skipped_if_fixture_replacement_is_given + content = run_generator ["account", "-r", "factory_girl"] + assert_match /factory_girl \[not found\]/, content + assert_no_file "test/fixtures/accounts.yml" + end + + def test_check_class_collision + content = capture(:stderr){ run_generator ["object"] } + assert_match /The name 'Object' is either already used in your application or reserved/, content + end + + protected + + def run_generator(args=["Account", "name:string", "age:integer"], config={}) + silence(:stdout) { Rails::Generators::ModelGenerator.start args, config.merge(:destination_root => destination_root) } + end + +end diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb new file mode 100644 index 0000000000..3a5c58ab32 --- /dev/null +++ b/railties/test/generators/named_base_test.rb @@ -0,0 +1,42 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/scaffold_controller/scaffold_controller_generator' + +# Mock out what we need from AR::Base. +module ActiveRecord + class Base + class << self + attr_accessor :pluralize_table_names + end + self.pluralize_table_names = true + end +end + +class NamedBaseTest < GeneratorsTestCase + + def test_named_generator_attributes + g = Rails::Generators::ScaffoldControllerGenerator.new ["admin/foo"] + assert_equal 'admin/foo', g.name + assert_equal %w(admin), g.class_path + assert_equal 1, g.class_nesting_depth + assert_equal 'Admin::Foo', g.class_name + assert_equal 'foo', g.singular_name + assert_equal 'foos', g.plural_name + assert_equal g.singular_name, g.file_name + assert_equal "admin_#{g.plural_name}", g.table_name + end + + def test_named_generator_attributes_without_pluralized + ActiveRecord::Base.pluralize_table_names = false + g = Rails::Generators::ScaffoldControllerGenerator.new ["admin/foo"] + assert_equal "admin_#{g.singular_name}", g.table_name + end + + def test_scaffold_plural_names + g = Rails::Generators::ScaffoldControllerGenerator.new ["ProductLine"] + assert_equal "ProductLines", g.controller_name + assert_equal "ProductLines", g.controller_class_name + assert_equal "product_lines", g.controller_file_name + end + +end diff --git a/railties/test/generators/observer_generator_test.rb b/railties/test/generators/observer_generator_test.rb new file mode 100644 index 0000000000..a556731e16 --- /dev/null +++ b/railties/test/generators/observer_generator_test.rb @@ -0,0 +1,33 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/observer/observer_generator' + +class ObserverGeneratorTest < GeneratorsTestCase + + def test_invokes_default_orm + run_generator + assert_file "app/models/account_observer.rb", /class AccountObserver < ActiveRecord::Observer/ + end + + def test_invokes_default_orm_with_class_path + run_generator ["admin/account"] + assert_file "app/models/admin/account_observer.rb", /class Admin::AccountObserver < ActiveRecord::Observer/ + end + + def test_invokes_default_test_framework + run_generator + assert_file "test/unit/account_observer_test.rb", /class AccountObserverTest < ActiveSupport::TestCase/ + end + + def test_logs_if_the_test_framework_cannot_be_found + content = run_generator ["account", "--test-framework=rspec"] + assert_match /rspec \[not found\]/, content + end + + protected + + def run_generator(args=["account"]) + silence(:stdout) { Rails::Generators::ObserverGenerator.start args, :destination_root => destination_root } + end + +end diff --git a/railties/test/generators/performance_test_generator_test.rb b/railties/test/generators/performance_test_generator_test.rb new file mode 100644 index 0000000000..74c9c20c4c --- /dev/null +++ b/railties/test/generators/performance_test_generator_test.rb @@ -0,0 +1,18 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/performance_test/performance_test_generator' + +class PerformanceTestGeneratorTest < GeneratorsTestCase + + def test_performance_test_skeleton_is_created + run_generator + assert_file "test/performance/performance_test.rb", /class PerformanceTest < ActionController::PerformanceTest/ + end + + protected + + def run_generator(args=["performance"]) + silence(:stdout) { Rails::Generators::PerformanceTestGenerator.start args, :destination_root => destination_root } + end + +end diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb new file mode 100644 index 0000000000..3982586bc3 --- /dev/null +++ b/railties/test/generators/plugin_generator_test.rb @@ -0,0 +1,56 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/plugin/plugin_generator' + +class PluginGeneratorTest < GeneratorsTestCase + + def test_plugin_skeleton_is_created + run_generator + + %w( + vendor/plugins + vendor/plugins/plugin_fu + vendor/plugins/plugin_fu/lib + ).each{ |path| assert_file path } + end + + def test_check_class_collision + content = capture(:stderr){ run_generator ["object"] } + assert_match /The name 'Object' is either already used in your application or reserved/, content + end + + def test_invokes_default_test_framework + run_generator + assert_file "vendor/plugins/plugin_fu/test/plugin_fu_test.rb", /class PluginFuTest < ActiveSupport::TestCase/ + assert_file "vendor/plugins/plugin_fu/test/test_helper.rb" + end + + def test_logs_if_the_test_framework_cannot_be_found + content = run_generator ["plugin_fu", "--test-framework=rspec"] + assert_match /rspec \[not found\]/, content + end + + def test_creates_tasks_if_required + run_generator ["plugin_fu", "--tasks"] + assert_file "vendor/plugins/plugin_fu/tasks/plugin_fu_tasks.rake" + end + + def test_creates_generator_if_required + run_generator ["plugin_fu", "--generator"] + assert_file "vendor/plugins/plugin_fu/lib/generators/templates" + assert_file "vendor/plugins/plugin_fu/lib/generators/plugin_fu_generator.rb", + /class PluginFuGenerator < Rails::Generators::NamedBase/ + end + + def test_plugin_generator_on_revoke + run_generator + run_generator ["plugin_fu"], :behavior => :revoke + end + + protected + + def run_generator(args=["plugin_fu"], config={}) + silence(:stdout) { Rails::Generators::PluginGenerator.start args, config.merge(:destination_root => destination_root) } + end + +end diff --git a/railties/test/generators/rails_controller_generator_test.rb b/railties/test/generators/rails_controller_generator_test.rb deleted file mode 100644 index 43fbe972e2..0000000000 --- a/railties/test/generators/rails_controller_generator_test.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'generators/generator_test_helper' - -module Admin -end - -class RailsControllerGeneratorTest < GeneratorTestCase - - def test_controller_generates_controller - run_generator('controller', %w(products)) - - assert_generated_controller_for :products - assert_generated_functional_test_for :products - assert_generated_helper_for :products - assert_generated_helper_test_for :products - end - - def test_controller_generates_namespaced_controller - run_generator('controller', %w(admin::products)) - - assert_generated_controller_for "admin::products" - assert_generated_functional_test_for "admin::products" - assert_generated_helper_for "admin::products" - assert_generated_helper_test_for "admin::products" - end - - def test_controller_generates_namespaced_and_not_namespaced_controllers - run_generator('controller', %w(products)) - - # We have to require the generated helper to show the problem because - # the test helpers just check for generated files and contents but - # do not actually load them. But they have to be loaded (as in a real environment) - # to make the second generator run fail - require "#{RAILS_ROOT}/app/helpers/products_helper" - - assert_nothing_raised do - begin - run_generator('controller', %w(admin::products)) - ensure - # cleanup - Object.send(:remove_const, :ProductsHelper) - end - end - end -end diff --git a/railties/test/generators/rails_helper_generator_test.rb b/railties/test/generators/rails_helper_generator_test.rb deleted file mode 100644 index 8d05f555e6..0000000000 --- a/railties/test/generators/rails_helper_generator_test.rb +++ /dev/null @@ -1,36 +0,0 @@ -require File.dirname(__FILE__) + '/generator_test_helper' - -class RailsHelperGeneratorTest < GeneratorTestCase - def test_helper_generates_helper - run_generator('helper', %w(products)) - - assert_generated_helper_for :products - assert_generated_helper_test_for :products - end - - def test_helper_generates_namespaced_helper - run_generator('helper', %w(admin::products)) - - assert_generated_helper_for "admin::products" - assert_generated_helper_test_for "admin::products" - end - - def test_helper_generates_namespaced_and_not_namespaced_helpers - run_generator('helper', %w(products)) - - # We have to require the generated helper to show the problem because - # the test helpers just check for generated files and contents but - # do not actually load them. But they have to be loaded (as in a real environment) - # to make the second generator run fail - require "#{RAILS_ROOT}/app/helpers/products_helper" - - assert_nothing_raised do - begin - run_generator('helper', %w(admin::products)) - ensure - # cleanup - Object.send(:remove_const, :ProductsHelper) - end - end - end -end diff --git a/railties/test/generators/rails_mailer_generator_test.rb b/railties/test/generators/rails_mailer_generator_test.rb deleted file mode 100644 index de61e6736d..0000000000 --- a/railties/test/generators/rails_mailer_generator_test.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'generators/generator_test_helper' - -class RailsMailerGeneratorTest < GeneratorTestCase - - def test_generates_mailer - run_generator('mailer', %w(Notifier reset_password)) - - assert_generated_model_for :notifier, 'ActionMailer::Base' do |model| - assert_has_method model, :reset_password do |name, body| - assert_equal [ - "subject 'Notifier#reset_password'", - "recipients ''", - "from ''", - "sent_on sent_at", - "", - "body :greeting => 'Hi,'" - ], - body.split("\n").map{|line| line.sub(' '*4, '') } - end - - assert_no_match /(self.default_url_options =|default_url_options\[.*\] =)/, model, - 'individual mailer models should not set default_url_options because the options are shared by all mailers' - end - - assert_generated_views_for :notifier, 'reset_password.erb' - assert_generated_unit_test_for :notifier, 'ActionMailer::TestCase' - assert_generated_file "test/fixtures/notifier/reset_password" - end -end diff --git a/railties/test/generators/rails_model_generator_test.rb b/railties/test/generators/rails_model_generator_test.rb deleted file mode 100644 index aea2abafba..0000000000 --- a/railties/test/generators/rails_model_generator_test.rb +++ /dev/null @@ -1,48 +0,0 @@ -require 'generators/generator_test_helper' - -class RailsModelGeneratorTest < GeneratorTestCase - - def test_model_generates_resources - run_generator('model', %w(Product name:string)) - - assert_generated_model_for :product - assert_generated_fixtures_for :products - assert_generated_migration :create_products - end - - def test_model_skip_migration_skips_migration - run_generator('model', %w(Product name:string --skip-migration)) - - assert_generated_model_for :product - assert_generated_fixtures_for :products - assert_skipped_migration :create_products - end - - def test_model_with_attributes_generates_resources_with_attributes - run_generator('model', %w(Product name:string supplier_id:integer created_at:timestamp)) - - assert_generated_model_for :product - assert_generated_fixtures_for :products - assert_generated_migration :create_products do |t| - assert_generated_column t, :name, :string - assert_generated_column t, :supplier_id, :integer - assert_generated_column t, :created_at, :timestamp - end - end - - def test_model_with_reference_attributes_generates_belongs_to_associations - run_generator('model', %w(Product name:string supplier:references)) - - assert_generated_model_for :product do |body| - assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'" - end - end - - def test_model_with_belongs_to_attributes_generates_belongs_to_associations - run_generator('model', %w(Product name:string supplier:belongs_to)) - - assert_generated_model_for :product do |body| - assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'" - end - end -end diff --git a/railties/test/generators/rails_model_subclass_generator_test.rb b/railties/test/generators/rails_model_subclass_generator_test.rb deleted file mode 100644 index 30066b5a3c..0000000000 --- a/railties/test/generators/rails_model_subclass_generator_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'generators/generator_test_helper' - -class RailsModelSubclassGeneratorTest < GeneratorTestCase - - def test_model_subclass_generates_resources - run_generator('model_subclass', %w(Car Product)) - - assert_generated_model_for :car, "Product" - assert_generated_unit_test_for :car - end - - def test_model_subclass_must_have_a_parent_class_name - assert_raise(Rails::Generator::UsageError) { run_generator('model_subclass', %w(Car)) } - end -end
\ No newline at end of file diff --git a/railties/test/generators/rails_resource_generator_test.rb b/railties/test/generators/rails_resource_generator_test.rb deleted file mode 100644 index 1f5bd0ef1e..0000000000 --- a/railties/test/generators/rails_resource_generator_test.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'generators/generator_test_helper' - -class RailsResourceGeneratorTest < GeneratorTestCase - def test_resource_generates_resources - run_generator('resource', %w(Product name:string)) - - assert_generated_controller_for :products - assert_generated_model_for :product - assert_generated_fixtures_for :products - assert_generated_functional_test_for :products - assert_generated_helper_for :products - assert_generated_helper_test_for :products - assert_generated_migration :create_products - assert_added_route_for :products - end - - def test_resource_skip_migration_skips_migration - run_generator('resource', %w(Product name:string --skip-migration)) - - assert_generated_controller_for :products - assert_generated_model_for :product - assert_generated_fixtures_for :products - assert_generated_functional_test_for :products - assert_generated_helper_for :products - assert_generated_helper_test_for :products - assert_skipped_migration :create_products - assert_added_route_for :products - end -end diff --git a/railties/test/generators/rails_scaffold_generator_test.rb b/railties/test/generators/rails_scaffold_generator_test.rb deleted file mode 100644 index 70829a77fd..0000000000 --- a/railties/test/generators/rails_scaffold_generator_test.rb +++ /dev/null @@ -1,150 +0,0 @@ -require 'generators/generator_test_helper' -require 'abstract_unit' - -class RailsScaffoldGeneratorTest < GeneratorTestCase - def test_scaffolded_names - g = Rails::Generator::Base.instance('scaffold', %w(ProductLine)) - assert_equal "ProductLines", g.controller_name - assert_equal "ProductLines", g.controller_class_name - assert_equal "ProductLine", g.controller_singular_name - assert_equal "product_lines", g.controller_plural_name - assert_equal "product_lines", g.controller_file_name - assert_equal "product_lines", g.controller_table_name - end - - def test_scaffold_generates_resources - - run_generator('scaffold', %w(Product name:string)) - - assert_generated_controller_for :products do |f| - - assert_has_method f, :index do |name, m| - assert_match /@products = Product\.all/, m, "#{name} should query products table" - end - - assert_has_method f, :show, :edit, :update, :destroy do |name, m| - assert_match /@product = Product\.find\(params\[:id\]\)/, m, "#{name.to_s} should query products table" - end - - assert_has_method f, :new do |name, m| - assert_match /@product = Product\.new/, m, "#{name.to_s} should instantiate a product" - end - - assert_has_method f, :create do |name, m| - assert_match /@product = Product\.new\(params\[:product\]\)/, m, "#{name.to_s} should instantiate a product" - assert_match /format.xml \{ render :xml => @product.errors, :status => :unprocessable_entity \}/, m, "#{name.to_s} should set status to :unprocessable_entity code for xml" - end - - end - - assert_generated_model_for :product - assert_generated_functional_test_for :products - assert_generated_unit_test_for :product - assert_generated_fixtures_for :products - assert_generated_helper_for :products - assert_generated_helper_test_for :products - assert_generated_stylesheet :scaffold - assert_generated_views_for :products, "index.html.erb", "new.html.erb", "edit.html.erb", "show.html.erb" - - assert_generated_migration :create_products - assert_added_route_for :products - end - - def test_scaffold_skip_migration_skips_migration - run_generator('scaffold', %w(Product name:string --skip-migration)) - - assert_generated_model_for :product - assert_generated_functional_test_for :products - assert_generated_unit_test_for :product - assert_generated_fixtures_for :products - assert_generated_helper_for :products - assert_generated_helper_test_for :products - assert_generated_stylesheet :scaffold - assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" - assert_skipped_migration :create_products - assert_added_route_for :products - end - - def test_scaffold_generates_resources_with_attributes - run_generator('scaffold', %w(Product name:string supplier_id:integer created_at:timestamp)) - - assert_generated_controller_for :products do |f| - - assert_has_method f, :index do |name, m| - assert_match /@products = Product\.all/, m, "#{name} should query products table" - end - - assert_has_method f, :show, :edit, :update, :destroy do |name, m| - assert_match /@product = Product\.find\(params\[:id\]\)/, m, "#{name.to_s} should query products table" - end - - assert_has_method f, :new do |name, m| - assert_match /@product = Product\.new/, m, "#{name.to_s} should instantiate a product" - end - - assert_has_method f, :create do |name, m| - assert_match /@product = Product\.new\(params\[:product\]\)/, m, "#{name.to_s} should instantiate a product" - assert_match /format.xml \{ render :xml => @product.errors, :status => :unprocessable_entity \}/, m, "#{name.to_s} should set status to :unprocessable_entity code for xml" - end - - end - - assert_generated_model_for :product - assert_generated_functional_test_for :products - assert_generated_unit_test_for :product - assert_generated_fixtures_for :products - assert_generated_helper_for :products - assert_generated_helper_test_for :products - assert_generated_stylesheet :scaffold - assert_generated_views_for :products, "index.html.erb", "new.html.erb", "edit.html.erb", "show.html.erb" - - assert_generated_migration :create_products do |t| - assert_generated_column t, :name, :string - assert_generated_column t, :supplier_id, :integer - assert_generated_column t, :created_at, :timestamp - end - - assert_added_route_for :products - end - - def test_scaffolded_plural_names - Rails::Generator::Base.logger.expects(:warning) - g = Rails::Generator::Base.instance('scaffold', %w(ProductLines)) - assert_equal "ProductLines", g.controller_name - assert_equal "ProductLines", g.controller_class_name - assert_equal "ProductLine", g.controller_singular_name - assert_equal "product_lines", g.controller_plural_name - assert_equal "product_lines", g.controller_file_name - assert_equal "product_lines", g.controller_table_name - end - - def test_scaffold_plural_model_name_without_force_plural_generates_singular_model - run_generator('scaffold', %w(Products name:string)) - - assert_generated_model_for :product - assert_generated_functional_test_for :products - assert_generated_unit_test_for :product - assert_generated_fixtures_for :products - assert_generated_helper_for :products - assert_generated_helper_test_for :products - assert_generated_stylesheet :scaffold - assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" - assert_skipped_migration :create_products - assert_added_route_for :products - end - - def test_scaffold_plural_model_name_with_force_plural_forces_plural_model - run_generator('scaffold', %w(Products name:string --force-plural)) - - assert_generated_model_for :products - assert_generated_functional_test_for :products - assert_generated_unit_test_for :products - assert_generated_fixtures_for :products - assert_generated_helper_for :products - assert_generated_helper_test_for :products - assert_generated_stylesheet :scaffold - assert_generated_views_for :products, "index.html.erb","new.html.erb","edit.html.erb","show.html.erb" - assert_skipped_migration :create_products - assert_added_route_for :products - end -end diff --git a/railties/test/generators/rails_template_runner_test.rb b/railties/test/generators/rails_template_runner_test.rb deleted file mode 100644 index 2da6bd59b5..0000000000 --- a/railties/test/generators/rails_template_runner_test.rb +++ /dev/null @@ -1,216 +0,0 @@ -require 'abstract_unit' -require 'generators/generator_test_helper' - -class RailsTemplateRunnerTest < GeneratorTestCase - def setup - Rails::Generator::Base.use_application_sources! - run_generator('app', [RAILS_ROOT]) - # generate empty template - @template_path = File.join(RAILS_ROOT, 'template.rb') - File.open(File.join(@template_path), 'w') {|f| f << '' } - - @git_plugin_uri = 'git://github.com/technoweenie/restful-authentication.git' - @svn_plugin_uri = 'svn://svnhub.com/technoweenie/restful-authentication/trunk' - end - - def teardown - super - rm_rf "#{RAILS_ROOT}/README" - rm_rf "#{RAILS_ROOT}/Rakefile" - rm_rf "#{RAILS_ROOT}/doc" - rm_rf "#{RAILS_ROOT}/lib" - rm_rf "#{RAILS_ROOT}/log" - rm_rf "#{RAILS_ROOT}/script" - rm_rf "#{RAILS_ROOT}/vendor" - rm_rf "#{RAILS_ROOT}/tmp" - rm_rf "#{RAILS_ROOT}/Capfile" - rm_rf @template_path - end - - def test_initialize_should_load_template - Rails::TemplateRunner.any_instance.expects(:load_template).with(@template_path) - silence_generator do - Rails::TemplateRunner.new(@template_path, RAILS_ROOT) - end - end - - def test_initialize_should_raise_error_on_missing_template_file - assert_raise(RuntimeError) do - silence_generator do - Rails::TemplateRunner.new('non/existent/path/to/template.rb', RAILS_ROOT) - end - end - end - - def test_file_should_write_data_to_file_path - run_template_method(:file, 'lib/test_file.rb', 'heres test data') - assert_generated_file_with_data 'lib/test_file.rb', 'heres test data' - end - - def test_file_should_write_block_contents_to_file_path - run_template_method(:file, 'lib/test_file.rb') { 'heres block data' } - assert_generated_file_with_data 'lib/test_file.rb', 'heres block data' - end - - def test_plugin_with_git_option_should_run_plugin_install - expects_run_ruby_script_with_command("script/plugin install #{@git_plugin_uri}") - run_template_method(:plugin, 'restful-authentication', :git => @git_plugin_uri) - end - - def test_plugin_with_svn_option_should_run_plugin_install - expects_run_ruby_script_with_command("script/plugin install #{@svn_plugin_uri}") - run_template_method(:plugin, 'restful-authentication', :svn => @svn_plugin_uri) - end - - def test_plugin_with_git_option_and_submodule_should_use_git_scm - Rails::Git.expects(:run).with("submodule add #{@git_plugin_uri} vendor/plugins/rest_auth") - run_template_method(:plugin, 'rest_auth', :git => @git_plugin_uri, :submodule => true) - end - - def test_plugin_with_no_options_should_skip_method - Rails::TemplateRunner.any_instance.expects(:run).never - run_template_method(:plugin, 'rest_auth', {}) - end - - def test_gem_should_put_gem_dependency_in_enviroment - run_template_method(:gem, 'will-paginate') - assert_rails_initializer_includes("config.gem 'will-paginate'") - end - - def test_gem_with_options_should_include_options_in_gem_dependency_in_environment - run_template_method(:gem, 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com') - assert_rails_initializer_includes("config.gem 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com'") - end - - def test_gem_with_env_string_should_put_gem_dependency_in_specified_environment - run_template_method(:gem, 'rspec', :env => 'test') - assert_generated_file_with_data('config/environments/test.rb', "config.gem 'rspec'", 'test') - end - - def test_gem_with_env_array_should_put_gem_dependency_in_specified_environments - run_template_method(:gem, 'quietbacktrace', :env => %w[ development test ]) - assert_generated_file_with_data('config/environments/development.rb', "config.gem 'quietbacktrace'") - assert_generated_file_with_data('config/environments/test.rb', "config.gem 'quietbacktrace'") - end - - def test_gem_with_lib_option_set_to_false_should_put_gem_dependency_in_enviroment_correctly - run_template_method(:gem, 'mislav-will-paginate', :lib => false, :source => 'http://gems.github.com') - assert_rails_initializer_includes("config.gem 'mislav-will-paginate', :lib => false, :source => 'http://gems.github.com'") - end - - def test_environment_should_include_data_in_environment_initializer_block - load_paths = 'config.load_paths += %w["#{RAILS_ROOT}/app/extras"]' - run_template_method(:environment, load_paths) - assert_rails_initializer_includes(load_paths) - end - - def test_environment_with_block_should_include_block_contents_in_environment_initializer_block - run_template_method(:environment) do - '# This wont be added' - '# This will be added' - end - assert_rails_initializer_includes('# This will be added') - end - - def test_git_with_symbol_should_run_command_using_git_scm - Rails::Git.expects(:run).once.with('init') - run_template_method(:git, :init) - end - - def test_git_with_hash_should_run_each_command_using_git_scm - Rails::Git.expects(:run).times(2) - run_template_method(:git, {:init => '', :add => '.'}) - end - - def test_vendor_should_write_data_to_file_in_vendor - run_template_method(:vendor, 'vendor_file.rb', '# vendor data') - assert_generated_file_with_data('vendor/vendor_file.rb', '# vendor data') - end - - def test_lib_should_write_data_to_file_in_lib - run_template_method(:lib, 'my_library.rb', 'class MyLibrary') - assert_generated_file_with_data('lib/my_library.rb', 'class MyLibrary') - end - - def test_rakefile_should_write_date_to_file_in_lib_tasks - run_template_method(:rakefile, 'myapp.rake', 'task :run => [:environment]') - assert_generated_file_with_data('lib/tasks/myapp.rake', 'task :run => [:environment]') - end - - def test_initializer_should_write_date_to_file_in_config_initializers - run_template_method(:initializer, 'constants.rb', 'MY_CONSTANT = 42') - assert_generated_file_with_data('config/initializers/constants.rb', 'MY_CONSTANT = 42') - end - - def test_generate_should_run_script_generate_with_argument_and_options - expects_run_ruby_script_with_command('script/generate model MyModel') - run_template_method(:generate, 'model', 'MyModel') - end - - def test_rake_should_run_rake_command_with_development_env - expects_run_with_command('rake log:clear RAILS_ENV=development') - run_template_method(:rake, 'log:clear') - end - - def test_rake_with_env_option_should_run_rake_command_in_env - expects_run_with_command('rake log:clear RAILS_ENV=production') - run_template_method(:rake, 'log:clear', :env => 'production') - end - - def test_rake_with_sudo_option_should_run_rake_command_with_sudo - expects_run_with_command('sudo rake log:clear RAILS_ENV=development') - run_template_method(:rake, 'log:clear', :sudo => true) - end - - def test_capify_should_run_the_capify_command - expects_run_with_command('capify .') - run_template_method(:capify!) - end - - def test_freeze_should_freeze_rails_edge - expects_run_with_command('rake rails:freeze:edge') - run_template_method(:freeze!) - end - - def test_route_should_add_data_to_the_routes_block_in_config_routes - route_command = "map.route '/login', :controller => 'sessions', :action => 'new'" - run_template_method(:route, route_command) - assert_generated_file_with_data 'config/routes.rb', route_command - end - - def test_run_ruby_script_should_add_ruby_to_command_in_win32_environment - ruby_command = RUBY_PLATFORM =~ /win32/ ? 'ruby ' : '' - expects_run_with_command("#{ruby_command}script/generate model MyModel") - run_template_method(:generate, 'model', 'MyModel') - end - - protected - def run_template_method(method_name, *args, &block) - silence_generator do - @template_runner = Rails::TemplateRunner.new(@template_path, RAILS_ROOT) - @template_runner.send(method_name, *args, &block) - end - end - - def expects_run_with_command(command) - Rails::TemplateRunner.any_instance.stubs(:run).once.with(command, false) - end - - def expects_run_ruby_script_with_command(command) - Rails::TemplateRunner.any_instance.stubs(:run_ruby_script).once.with(command,false) - end - - def assert_rails_initializer_includes(data, message = nil) - message ||= "Rails::Initializer should include #{data}" - assert_generated_file 'config/environment.rb' do |body| - assert_match(/#{Regexp.escape("Rails::Initializer.run do |config|")}.+#{Regexp.escape(data)}.+end/m, body, message) - end - end - - def assert_generated_file_with_data(file, data, message = nil) - message ||= "#{file} should include '#{data}'" - assert_generated_file(file) do |file| - assert_match(/#{Regexp.escape(data)}/,file, message) - end - end -end
\ No newline at end of file diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb new file mode 100644 index 0000000000..876ddbf84e --- /dev/null +++ b/railties/test/generators/resource_generator_test.rb @@ -0,0 +1,106 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/resource/resource_generator' + +class ResourceGeneratorTest < GeneratorsTestCase + + def setup + super + routes = Rails::Generators::ResourceGenerator.source_root + routes = File.join(routes, "..", "..", "app", "templates", "config", "routes.rb") + destination = File.join(destination_root, "config") + + FileUtils.mkdir_p(destination) + FileUtils.cp File.expand_path(routes), destination + end + + def test_help_with_inherited_options + content = run_generator ["--help"] + assert_match /ActiveRecord options:/, content + assert_match /TestUnit options:/, content + end + + def test_files_from_inherited_invocation + run_generator + + %w( + app/models/account.rb + test/unit/account_test.rb + test/fixtures/accounts.yml + ).each { |path| assert_file path } + + assert_migration "db/migrate/create_accounts.rb" + end + + def test_inherited_invocations_with_attributes + run_generator ["account", "name:string"] + assert_migration "db/migrate/create_accounts.rb", /t.string :name/ + end + + def test_resource_controller_with_pluralized_class_name + run_generator + assert_file "app/controllers/accounts_controller.rb", /class AccountsController < ApplicationController/ + assert_file "test/functional/accounts_controller_test.rb", /class AccountsControllerTest < ActionController::TestCase/ + + assert_file "app/helpers/accounts_helper.rb", /module AccountsHelper/ + assert_file "test/unit/helpers/accounts_helper_test.rb", /class AccountsHelperTest < ActionView::TestCase/ + end + + def test_resource_controller_with_actions + run_generator ["account", "--actions", "index", "new"] + + assert_file "app/controllers/accounts_controller.rb" do |controller| + assert_instance_method controller, :index + assert_instance_method controller, :new + end + + assert_file "app/views/accounts/index.html.erb" + assert_file "app/views/accounts/new.html.erb" + end + + def test_resource_routes_are_added + run_generator + + assert_file "config/routes.rb" do |route| + assert_match /map\.resources :accounts$/, route + end + end + + def test_singleton_resource + run_generator ["account", "--singleton"] + + assert_file "config/routes.rb" do |route| + assert_match /map\.resource :account$/, route + end + end + + def test_plural_names_are_singularized + content = run_generator ["accounts"] + assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/ + assert_file "test/unit/account_test.rb", /class AccountTest/ + assert_match /Plural version of the model detected, using singularized version. Override with --force-plural./, content + end + + def test_plural_names_can_be_forced + content = run_generator ["accounts", "--force-plural"] + assert_file "app/models/accounts.rb", /class Accounts < ActiveRecord::Base/ + assert_file "test/unit/accounts_test.rb", /class AccountsTest/ + assert_no_match /Plural version of the model detected/, content + end + + def test_route_is_removed_on_revoke + run_generator + run_generator ["account"], :behavior => :revoke + + assert_file "config/routes.rb" do |route| + assert_no_match /map\.resources :accounts$/, route + end + end + + protected + + def run_generator(args=["account"], config={}) + silence(:stdout) { Rails::Generators::ResourceGenerator.start args, config.merge(:destination_root => destination_root) } + end + +end diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb new file mode 100644 index 0000000000..024ea439ef --- /dev/null +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -0,0 +1,112 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/scaffold_controller/scaffold_controller_generator' + +class ScaffoldControllerGeneratorTest < GeneratorsTestCase + + def test_controller_skeleton_is_created + run_generator + + assert_file "app/controllers/users_controller.rb" do |content| + assert_match /class UsersController < ApplicationController/, content + + assert_instance_method content, :index do |m| + assert_match /@users = User\.all/, m + end + + assert_instance_method content, :show do |m| + assert_match /@user = User\.find\(params\[:id\]\)/, m + end + + assert_instance_method content, :new do |m| + assert_match /@user = User\.new/, m + end + + assert_instance_method content, :edit do |m| + assert_match /@user = User\.find\(params\[:id\]\)/, m + end + + assert_instance_method content, :create do |m| + assert_match /@user = User\.new\(params\[:user\]\)/, m + assert_match /@user\.save/, m + assert_match /@user\.errors/, m + end + + assert_instance_method content, :update do |m| + assert_match /@user = User\.find\(params\[:id\]\)/, m + assert_match /@user\.update_attributes\(params\[:user\]\)/, m + assert_match /@user\.errors/, m + end + + assert_instance_method content, :destroy do |m| + assert_match /@user = User\.find\(params\[:id\]\)/, m + assert_match /@user\.destroy/, m + end + end + end + + def test_helper_are_invoked_with_a_pluralized_name + run_generator + assert_file "app/helpers/users_helper.rb", /module UsersHelper/ + assert_file "test/unit/helpers/users_helper_test.rb", /class UsersHelperTest < ActionView::TestCase/ + end + + def test_views_are_generated + run_generator + + %w( + index + edit + new + show + ).each { |view| assert_file "app/views/users/#{view}.html.erb" } + assert_file "app/views/layouts/users.html.erb" + end + + def test_functional_tests + run_generator + + assert_file "test/functional/users_controller_test.rb" do |content| + assert_match /class UsersControllerTest < ActionController::TestCase/, content + assert_match /test "should get index"/, content + end + end + + def test_generates_singleton_controller + run_generator ["User", "name:string", "age:integer", "--singleton"] + + assert_file "app/controllers/users_controller.rb" do |content| + assert_no_match /def index/, content + end + + assert_file "test/functional/users_controller_test.rb" do |content| + assert_no_match /test "should get index"/, content + end + + assert_no_file "app/views/users/index.html.erb" + end + + def test_skip_helper_if_required + run_generator ["User", "name:string", "age:integer", "--no-helper"] + assert_no_file "app/helpers/users_helper.rb" + assert_no_file "test/unit/helpers/users_helper_test.rb" + end + + def test_skip_layout_if_required + run_generator ["User", "name:string", "age:integer", "--no-layout"] + assert_no_file "app/views/layouts/users.html.erb" + end + + def test_error_is_shown_if_orm_does_not_provide_interface + error = capture(:stderr){ run_generator ["User", "--orm=unknown"] } + assert_equal "Could not load Unknown::Generators::ActionORM, skipping controller. " << + "Error: no such file to load -- generators/unknown.\n", error + end + + protected + + def run_generator(args=["User", "name:string", "age:integer"]) + silence(:stdout) { Rails::Generators::ScaffoldControllerGenerator.start args, :destination_root => destination_root } + end + +end diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb new file mode 100644 index 0000000000..05eadd3460 --- /dev/null +++ b/railties/test/generators/scaffold_generator_test.rb @@ -0,0 +1,129 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/scaffold/scaffold_generator' + +class ScaffoldGeneratorTest < GeneratorsTestCase + + def setup + super + routes = Rails::Generators::ResourceGenerator.source_root + routes = File.join(routes, "..", "..", "app", "templates", "config", "routes.rb") + destination = File.join(destination_root, "config") + + FileUtils.mkdir_p(destination) + FileUtils.cp File.expand_path(routes), destination + end + + def test_scaffold_on_invoke + run_generator + + # Model + assert_file "app/models/product_line.rb", /class ProductLine < ActiveRecord::Base/ + assert_file "test/unit/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/ + assert_file "test/fixtures/product_lines.yml" + assert_migration "db/migrate/create_product_lines.rb" + + # Route + assert_file "config/routes.rb" do |route| + assert_match /map\.resources :product_lines$/, route + end + + # Controller + assert_file "app/controllers/product_lines_controller.rb" do |content| + assert_match /class ProductLinesController < ApplicationController/, content + + assert_instance_method content, :index do |m| + assert_match /@product_lines = ProductLine\.all/, m + end + + assert_instance_method content, :show do |m| + assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m + end + + assert_instance_method content, :new do |m| + assert_match /@product_line = ProductLine\.new/, m + end + + assert_instance_method content, :edit do |m| + assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m + end + + assert_instance_method content, :create do |m| + assert_match /@product_line = ProductLine\.new\(params\[:product_line\]\)/, m + assert_match /@product_line\.save/, m + assert_match /@product_line\.errors/, m + end + + assert_instance_method content, :update do |m| + assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m + assert_match /@product_line\.update_attributes\(params\[:product_line\]\)/, m + assert_match /@product_line\.errors/, m + end + + assert_instance_method content, :destroy do |m| + assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m + assert_match /@product_line\.destroy/, m + end + end + + assert_file "test/functional/product_lines_controller_test.rb", + /class ProductLinesControllerTest < ActionController::TestCase/ + + # Views + %w( + index + edit + new + show + ).each { |view| assert_file "app/views/product_lines/#{view}.html.erb" } + assert_file "app/views/layouts/product_lines.html.erb" + + # Helpers + assert_file "app/helpers/product_lines_helper.rb" + assert_file "test/unit/helpers/product_lines_helper_test.rb" + + # Stylesheets + assert_file "public/stylesheets/scaffold.css" + end + + def test_scaffold_on_revoke + run_generator + run_generator :behavior => :revoke + + # Model + assert_no_file "app/models/product_line.rb" + assert_no_file "test/unit/product_line_test.rb" + assert_no_file "test/fixtures/product_lines.yml" + assert_no_migration "db/migrate/create_product_lines.rb" + + # Route + assert_file "config/routes.rb" do |route| + assert_no_match /map\.resources :product_lines$/, route + end + + # Controller + assert_no_file "app/controllers/product_lines_controller.rb" + assert_no_file "test/functional/product_lines_controller_test.rb" + + # Views + assert_no_file "app/views/product_lines" + assert_no_file "app/views/layouts/product_lines.html.erb" + + # Helpers + assert_no_file "app/helpers/product_lines_helper.rb" + assert_no_file "test/unit/helpers/product_lines_helper_test.rb" + + # Stylesheets (should not be removed) + assert_file "public/stylesheets/scaffold.css" + end + + protected + + def run_generator(config={}) + silence(:stdout) do + Rails::Generators::ScaffoldGenerator.start ["product_line", "title:string", "price:integer"], + config.merge(:destination_root => destination_root) + end + end + +end diff --git a/railties/test/generators/session_migration_generator_test.rb b/railties/test/generators/session_migration_generator_test.rb new file mode 100644 index 0000000000..f83109800b --- /dev/null +++ b/railties/test/generators/session_migration_generator_test.rb @@ -0,0 +1,23 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/session_migration/session_migration_generator' + +class SessionMigrationGeneratorTest < GeneratorsTestCase + + def test_session_migration_with_default_name + run_generator + assert_migration "db/migrate/add_sessions_table.rb", /class AddSessionsTable < ActiveRecord::Migration/ + end + + def test_session_migration_with_given_name + run_generator ["create_session_table"] + assert_migration "db/migrate/create_session_table.rb", /class CreateSessionTable < ActiveRecord::Migration/ + end + + protected + + def run_generator(args=[]) + silence(:stdout) { Rails::Generators::SessionMigrationGenerator.start args, :destination_root => destination_root } + end + +end diff --git a/railties/test/generators/stylesheets_generator_test.rb b/railties/test/generators/stylesheets_generator_test.rb new file mode 100644 index 0000000000..02082d5cc8 --- /dev/null +++ b/railties/test/generators/stylesheets_generator_test.rb @@ -0,0 +1,24 @@ +require 'abstract_unit' +require 'generators/generators_test_helper' +require 'generators/rails/stylesheets/stylesheets_generator' + +class StylesheetsGeneratorTest < GeneratorsTestCase + + def test_copy_stylesheets + run_generator + assert_file "public/stylesheets/scaffold.css" + end + + def test_stylesheets_are_not_deleted_on_revoke + run_generator + run_generator :behavior => :revoke + assert_file "public/stylesheets/scaffold.css" + end + + protected + + def run_generator(config={}) + silence(:stdout) { Rails::Generators::StylesheetsGenerator.start [], config.merge(:destination_root => destination_root) } + end + +end diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb new file mode 100644 index 0000000000..89d52dd170 --- /dev/null +++ b/railties/test/generators_test.rb @@ -0,0 +1,156 @@ +require File.join(File.dirname(__FILE__), 'generators', 'generators_test_helper') +require 'generators/rails/model/model_generator' +require 'generators/test_unit/model/model_generator' +require 'mocha' + +class GeneratorsTest < GeneratorsTestCase + def test_invoke_when_generator_is_not_found + output = capture(:stdout){ Rails::Generators.invoke :unknown } + assert_equal "Could not find generator unknown.\n", output + end + + def test_help_when_a_generator_with_required_arguments_is_invoked_without_arguments + output = capture(:stdout){ Rails::Generators.invoke :model, [] } + assert_match /Description:/, output + end + + def test_invoke_with_default_values + Rails::Generators::ModelGenerator.expects(:start).with(["Account"], {}) + Rails::Generators.invoke :model, ["Account"] + end + + def test_invoke_with_config_values + Rails::Generators::ModelGenerator.expects(:start).with(["Account"], :behavior => :skip) + Rails::Generators.invoke :model, ["Account"], :behavior => :skip + end + + def test_find_by_namespace_without_base_or_context + assert_nil Rails::Generators.find_by_namespace(:model) + end + + def test_find_by_namespace_with_base + klass = Rails::Generators.find_by_namespace(:model, :rails) + assert klass + assert_equal "rails:generators:model", klass.namespace + end + + def test_find_by_namespace_with_context + klass = Rails::Generators.find_by_namespace(:test_unit, nil, :model) + assert klass + assert_equal "test_unit:generators:model", klass.namespace + end + + def test_find_by_namespace_add_generators_to_raw_lookups + klass = Rails::Generators.find_by_namespace("test_unit:model") + assert klass + assert_equal "test_unit:generators:model", klass.namespace + end + + def test_find_by_namespace_lookup_to_the_rails_root_folder + klass = Rails::Generators.find_by_namespace(:fixjour) + assert klass + assert_equal "fixjour", klass.namespace + end + + def test_find_by_namespace_lookup_to_deep_rails_root_folders + klass = Rails::Generators.find_by_namespace(:fixjour, :active_record) + assert klass + assert_equal "active_record:generators:fixjour", klass.namespace + end + + def test_find_by_namespace_lookup_traverse_folders + klass = Rails::Generators.find_by_namespace(:javascripts, :rails) + assert klass + assert_equal "rails:generators:javascripts", klass.namespace + end + + def test_find_by_namespace_lookup_to_vendor_folders + klass = Rails::Generators.find_by_namespace(:mspec) + assert klass + assert_equal "mspec", klass.namespace + end + + def test_builtin_generators + assert Rails::Generators.builtin.include? %w(rails model) + end + + def test_rails_generators_help_with_builtin_information + output = capture(:stdout){ Rails::Generators.help } + assert_match /model/, output + assert_match /scaffold_controller/, output + end + + def test_rails_generators_with_others_information + output = capture(:stdout){ Rails::Generators.help }.split("\n").last + assert_equal "Others: active_record:fixjour, fixjour, mspec, rails:javascripts, wrong.", output + end + + def test_warning_is_shown_if_generator_cant_be_loaded + output = capture(:stderr){ Rails::Generators.find_by_namespace(:wrong) } + assert_match /\[WARNING\] Could not load generator at/, output + assert_match /Error: uninitialized constant Rails::Generator/, output + end + + def test_no_color_sets_proper_shell + Rails::Generators.no_color! + assert_equal Thor::Shell::Basic, Thor::Base.shell + ensure + Thor::Base.shell = Thor::Shell::Color + end + + def test_rails_root_templates + template = File.join(RAILS_ROOT, "lib", "templates", "active_record", "model", "model.rb") + + # Create template + mkdir_p(File.dirname(template)) + File.open(template, 'w'){ |f| f.write "empty" } + + output = capture(:stdout) do + Rails::Generators.invoke :model, ["user"], :destination_root => destination_root + end + + assert_file "app/models/user.rb" do |content| + assert_equal "empty", content + end + ensure + rm_rf File.dirname(template) + end + + def test_fallbacks_for_generators_on_find_by_namespace + Rails::Generators.fallbacks[:remarkable] = :test_unit + klass = Rails::Generators.find_by_namespace(:plugin, :remarkable) + assert klass + assert_equal "test_unit:generators:plugin", klass.namespace + end + + def test_fallbacks_for_generators_on_invoke + Rails::Generators.fallbacks[:shoulda] = :test_unit + TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {}) + Rails::Generators.invoke "shoulda:model", ["Account"] + end + + def test_nested_fallbacks_for_generators + Rails::Generators.fallbacks[:super_shoulda] = :shoulda + TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {}) + Rails::Generators.invoke "super_shoulda:model", ["Account"] + end + + def test_developer_options_are_overwriten_by_user_options + Rails::Generators.options[:new_generator] = { :generate => false } + + klass = Class.new(Rails::Generators::Base) do + def self.name + "NewGenerator" + end + + class_option :generate, :default => true + end + + assert_equal false, klass.class_options[:generate].default + end + + def test_source_paths_for_not_namespaced_generators + mspec = Rails::Generators.find_by_namespace :mspec + assert mspec.source_paths.include?(File.join(RAILS_ROOT, "lib", "templates", "mspec")) + end +end diff --git a/railties/test/initializer_test.rb b/railties/test/initializer_test.rb index 550cb7de76..1fecd62995 100644 --- a/railties/test/initializer_test.rb +++ b/railties/test/initializer_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'initializer' +require 'generators' require 'action_view' require 'action_mailer' @@ -10,8 +11,19 @@ module Rails def self.configuration Rails::Configuration.new end + + module Generators + def self.clear_aliases! + @aliases = nil + end + + def self.clear_options! + @@options = nil + end + end end + class ConfigurationMock < Rails::Configuration attr_reader :environment_path @@ -278,6 +290,87 @@ class InitializerPluginLoadingTests < Test::Unit::TestCase end end +class InitializerGeneratorsTests < Test::Unit::TestCase + + def setup + @configuration = Rails::Configuration.new + @initializer = Rails::Initializer.default + @initializer.config = @configuration + end + + def test_generators_default_values + assert_equal(true, @configuration.generators.colorize_logging) + assert_equal({}, @configuration.generators.aliases) + assert_equal({}, @configuration.generators.options) + end + + def test_generators_set_rails_options + @configuration.generators.orm = :datamapper + @configuration.generators.test_framework = :rspec + expected = { :rails => { :orm => :datamapper, :test_framework => :rspec } } + assert_equal expected, @configuration.generators.options + end + + def test_generators_set_rails_aliases + @configuration.generators.aliases = { :rails => { :test_framework => "-w" } } + expected = { :rails => { :test_framework => "-w" } } + assert_equal expected, @configuration.generators.aliases + end + + def test_generators_aliases_and_options_on_initialization + @configuration.generators.rails :aliases => { :test_framework => "-w" } + @configuration.generators.orm :datamapper + @configuration.generators.test_framework :rspec + + @initializer.run(:initialize_generators) + + assert_equal :rspec, Rails::Generators.options[:rails][:test_framework] + assert_equal "-w", Rails::Generators.aliases[:rails][:test_framework] + end + + def test_generators_no_color_on_initialization + @configuration.generators.colorize_logging = false + @initializer.run(:initialize_generators) + assert_equal Thor::Base.shell, Thor::Shell::Basic + end + + def test_generators_with_hashes_for_options_and_aliases + @configuration.generators do |g| + g.orm :datamapper, :migration => false + g.plugin :aliases => { :generator => "-g" }, + :generator => true + end + + expected = { + :rails => { :orm => :datamapper }, + :plugin => { :generator => true }, + :datamapper => { :migration => false } + } + + assert_equal expected, @configuration.generators.options + assert_equal({ :plugin => { :generator => "-g" } }, @configuration.generators.aliases) + end + + def test_generators_with_hashes_are_deep_merged + @configuration.generators do |g| + g.orm :datamapper, :migration => false + g.plugin :aliases => { :generator => "-g" }, + :generator => true + end + @initializer.run(:initialize_generators) + + assert Rails::Generators.aliases.size >= 1 + assert Rails::Generators.options.size >= 1 + end + + protected + + def teardown + Rails::Generators.clear_aliases! + Rails::Generators.clear_options! + end +end + class InitializerSetupI18nTests < Test::Unit::TestCase def test_no_config_locales_dir_present_should_return_empty_load_path File.stubs(:exist?).returns(false) diff --git a/railties/test/rails_generator_test.rb b/railties/test/rails_generator_test.rb deleted file mode 100644 index 38bd90dcc1..0000000000 --- a/railties/test/rails_generator_test.rb +++ /dev/null @@ -1,148 +0,0 @@ -require 'test/unit' - -# Optionally load RubyGems. -begin - require 'rubygems' -rescue LoadError -end - -# Mock out what we need from AR::Base. -module ActiveRecord - class Base - class << self - attr_accessor :pluralize_table_names - end - self.pluralize_table_names = true - end -end - -# And what we need from ActionView -module ActionView - module Helpers - module ActiveRecordHelper; end - class InstanceTag; end - end -end - - -# Must set before requiring generator libs. -if defined?(RAILS_ROOT) - RAILS_ROOT.replace "#{File.dirname(__FILE__)}/fixtures" -else - RAILS_ROOT = "#{File.dirname(__FILE__)}/fixtures" -end - -$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" -require 'initializer' - -# Mocks out the configuration -module Rails - def self.configuration - Rails::Configuration.new - end -end - -require 'rails_generator' - -class RailsGeneratorTest < Test::Unit::TestCase - BUILTINS = %w(controller integration_test mailer migration model observer plugin resource scaffold session_migration) - CAPITALIZED_BUILTINS = BUILTINS.map { |b| b.capitalize } - - def setup - ActiveRecord::Base.pluralize_table_names = true - @initializer = Rails::Initializer.default - @initializer.config = Rails.configuration - @initializer.run(:set_root_path) - end - - def test_sources - expected = [:lib, :vendor, - "plugins (vendor/plugins)".to_sym, # <plugin>/generators and <plugin>/rails_generators - :user, - :RubyGems, :RubyGems, # gems named <x>_generator, gems containing /rails_generator/ folder - :builtin] - expected.delete(:RubyGems) unless Object.const_defined?(:Gem) - assert_equal expected, Rails::Generator::Base.sources.map { |s| s.label } - end - - def test_lookup_builtins - (BUILTINS + CAPITALIZED_BUILTINS).each do |name| - assert_nothing_raised do - spec = Rails::Generator::Base.lookup(name) - assert_not_nil spec - assert_kind_of Rails::Generator::Spec, spec - - klass = spec.klass - assert klass < Rails::Generator::Base - assert_equal spec, klass.spec - end - end - end - - def test_autolookup - assert_nothing_raised { ControllerGenerator } - assert_nothing_raised { ModelGenerator } - end - - def test_lookup_missing_generator - assert_raise Rails::Generator::GeneratorError do - Rails::Generator::Base.lookup('missing').klass - end - end - - def test_lookup_missing_class - spec = nil - assert_nothing_raised { spec = Rails::Generator::Base.lookup('missing_class') } - assert_not_nil spec - assert_kind_of Rails::Generator::Spec, spec - assert_raise(NameError) { spec.klass } - end - - def test_generator_usage - (BUILTINS - ["session_migration"]).each do |name| - assert_raise(Rails::Generator::UsageError, "Generator '#{name}' should raise an error without arguments") { - Rails::Generator::Base.instance(name) - } - end - end - - def test_generator_spec - spec = Rails::Generator::Base.lookup('working') - assert_equal 'working', spec.name - assert_match(/#{spec.path}$/, "#{RAILS_ROOT}/lib/generators/working") - assert_equal :lib, spec.source - assert_nothing_raised { assert_match(/WorkingGenerator$/, spec.klass.name) } - end - - def test_named_generator_attributes - g = Rails::Generator::Base.instance('working', %w(admin/foo bar baz)) - assert_equal 'admin/foo', g.name - assert_equal %w(admin), g.class_path - assert_equal 'Admin', g.class_nesting - assert_equal 'Admin::Foo', g.class_name - assert_equal 'foo', g.singular_name - assert_equal 'foos', g.plural_name - assert_equal g.singular_name, g.file_name - assert_equal "admin_#{g.plural_name}", g.table_name - assert_equal %w(bar baz), g.args - end - - def test_named_generator_attributes_without_pluralized - ActiveRecord::Base.pluralize_table_names = false - g = Rails::Generator::Base.instance('working', %w(admin/foo bar baz)) - assert_equal "admin_#{g.singular_name}", g.table_name - end - - def test_session_migration_generator_with_pluralization - g = Rails::Generator::Base.instance('session_migration') - assert_equal 'session'.pluralize, g.send(:default_session_table_name) - ActiveRecord::Base.pluralize_table_names = false - assert_equal 'session', g.send(:default_session_table_name) - end - - def test_scaffold_controller_name - # Default behaviour is use the model name - g = Rails::Generator::Base.instance('scaffold', %w(Product)) - assert_equal "Products", g.controller_name - end -end diff --git a/railties/test/secret_key_generation_test.rb b/railties/test/secret_key_generation_test.rb deleted file mode 100644 index 2c7c3d5dfe..0000000000 --- a/railties/test/secret_key_generation_test.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'abstract_unit' - -# Must set before requiring generator libs. -if defined?(RAILS_ROOT) - RAILS_ROOT.replace "#{File.dirname(__FILE__)}/fixtures" -else - RAILS_ROOT = "#{File.dirname(__FILE__)}/fixtures" -end - -$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" - -require 'initializer' - -# Mocks out the configuration -module Rails - def self.configuration - Rails::Configuration.new - end -end - -require 'rails_generator' -require 'rails_generator/secret_key_generator' -require 'rails_generator/generators/applications/app/app_generator' - -class SecretKeyGenerationTest < ActiveSupport::TestCase - SECRET_KEY_MIN_LENGTH = 128 - APP_NAME = "foo" - - def setup - @generator = Rails::SecretKeyGenerator.new(APP_NAME) - end - - def test_secret_key_generation - assert_deprecated /ActiveSupport::SecureRandom\.hex\(64\)/ do - assert @generator.generate_secret.length >= SECRET_KEY_MIN_LENGTH - end - end -end |