diff --git a/railties/lib/generators.rb b/railties/lib/generators.rb index ee17242940ccf5a39e8fef5d24fbce5b938f7f92..a923a3339707c34ff902f92817761a749219a0fc 100644 --- a/railties/lib/generators.rb +++ b/railties/lib/generators.rb @@ -16,7 +16,6 @@ module Rails module Generators - mattr_accessor :load_path # Generators load paths. First search on generators in the RAILS_ROOT, then # look for them in rails generators. @@ -33,42 +32,6 @@ def self.load_path end load_path # Cache load paths - # Receives paths in an array and tries to find generators for it in the load - # path. - # - def self.lookup(attempts) - generators_path = [] - - # Traverse attempts into directory lookups. For example: - # - # rails:generators:model - # - # Becomes: - # - # generators/rails/model/model_generator.rb - # generators/rails/model_generator.rb - # generators/model_generator.rb - # - attempts.each do |attempt| - paths = attempt.gsub(':generators:', ':').split(':') - paths << "#{paths.last}_generator.rb" - - until paths.empty? - generators_path << File.join(*paths) - paths.delete_at(-1) unless paths.delete_at(-2) - end - end - - generators_path.uniq! - generators_path.each do |generator_path| - self.load_path.each do |path| - Dir[File.join(path, generator_path)].each do |file| - require file - end - end - end - end - # Keep builtin generators in an Array[Array[group, name]]. # def self.builtin @@ -114,11 +77,15 @@ def self.find_by_namespace(name, base=nil, context=nil) attempts << name.sub(':', ':generators:') if name.count(':') == 1 attempts << name - unless klass = find_many_by_namespace(attempts) - lookup(attempts) - klass = find_many_by_namespace(attempts) + unloaded = attempts - namespaces + lookup(unloaded) + + attempts.each do |namespace| + klass = Thor::Util.find_by_namespace(namespace) + return klass if klass end - klass + + nil end # Show help message with available generators. @@ -133,8 +100,23 @@ def self.help puts "Please select a generator." puts "Builtin: #{rails.join(', ')}." - # TODO Show others after lookup is implemented - # puts "Others: #{others.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 # Receives a namespace, arguments and the behavior to invoke the generator. @@ -152,12 +134,45 @@ def self.invoke(namespace, args=ARGV, behavior=:invoke) protected - def self.find_many_by_namespace(attempts) - attempts.each do |namespace| - klass = Thor::Util.find_by_namespace(namespace) - return klass if klass + # Return all defined namespaces. + # + def self.namespaces + Thor::Base.subclasses.map(&:namespace) + 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) + 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| + require file + end + end end - nil end end 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 0000000000000000000000000000000000000000..7a4edb8bcbc35414e212f9c329dd956aab8fbdb4 --- /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 0000000000000000000000000000000000000000..ef3e9edbed9b8731958cfc973a47bc9fc067fa26 --- /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/missing_class/missing_class_generator.rb b/railties/test/fixtures/lib/generators/missing_class/missing_class_generator.rb deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/railties/test/fixtures/lib/generators/missing_class/templates/.gitignore b/railties/test/fixtures/lib/generators/missing_class/templates/.gitignore deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/railties/test/fixtures/lib/generators/missing_generator/templates/.gitignore b/railties/test/fixtures/lib/generators/missing_generator/templates/.gitignore deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/railties/test/fixtures/lib/generators/missing_templates/.gitignore b/railties/test/fixtures/lib/generators/missing_templates/.gitignore deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 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 0000000000000000000000000000000000000000..cad5e9678483c24b80fc5771c187487bf58e8fbb --- /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 465b34319ae13d55f6d9a7937a86be477204e210..0000000000000000000000000000000000000000 --- 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/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb index 7f22197a5476df948ecea8003311aa5ad87f84d7..2ccedf814d48c5bd64959e6dcf2b6772975652a3 100644 --- a/railties/test/generators/generators_test_helper.rb +++ b/railties/test/generators/generators_test_helper.rb @@ -1,9 +1,6 @@ require 'test/unit' require 'fileutils' -$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib" -require 'generators' - fixtures = File.expand_path(File.join(File.dirname(__FILE__), '..', 'fixtures')) if defined?(RAILS_ROOT) RAILS_ROOT.replace fixtures @@ -11,6 +8,9 @@ RAILS_ROOT = fixtures end +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib" +require 'generators' + class GeneratorsTestCase < Test::Unit::TestCase include FileUtils diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index d076244abd0cd4e3fe34add110a5c8c0a7f8a1a8..30fc83783483938956d47f8371cf942fce2ae526 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -107,8 +107,8 @@ def test_fixture_is_skipped end def test_fixture_is_skipped_if_fixture_replacement_is_given - content = run_generator ["account", "-r", "fixjour"] - assert_match /Could not find and invoke 'fixjour'/, content + content = run_generator ["account", "-r", "factory_girl"] + assert_match /Could not find and invoke 'factory_girl'/, content assert_no_file "test/fixtures/accounts.yml" end diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb new file mode 100644 index 0000000000000000000000000000000000000000..ac290b10bb2801e5e2aa2ab391355810c9b1f51e --- /dev/null +++ b/railties/test/generators_test.rb @@ -0,0 +1,80 @@ +require File.join(File.dirname(__FILE__), 'generators', 'generators_test_helper') +require 'generators/rails/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_behavior + Rails::Generators::ModelGenerator.expects(:start).with(["Account"], :behavior => :invoke) + Rails::Generators.invoke :model, ["Account"] + end + + def test_invoke_with_given_behavior + Rails::Generators::ModelGenerator.expects(:start).with(["Account"], :behavior => :skip) + Rails::Generators.invoke :model, ["Account"], :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_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, rails:javascripts.", output + end +end