aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/command.rb
blob: 13f3b90b6d3171e22366d15e695babb58e952b38 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
require "active_support"
require "active_support/dependencies/autoload"
require "active_support/core_ext/enumerable"
require "active_support/core_ext/object/blank"
require "active_support/core_ext/hash/transform_values"

require "thor"

module Rails
  module Command
    extend ActiveSupport::Autoload

    autoload :Behavior
    autoload :Base

    include Behavior

    HELP_MAPPINGS = %w(-h -? --help)

    class << self
      def hidden_commands # :nodoc:
        @hidden_commands ||= []
      end

      def environment # :nodoc:
        ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
      end

      # Receives a namespace, arguments and the behavior to invoke the command.
      def invoke(namespace, args = [], **config)
        namespace = namespace.to_s
        namespace = "help" if namespace.blank? || HELP_MAPPINGS.include?(namespace)
        namespace = "version" if %w( -v --version ).include? namespace

        if command = find_by_namespace(namespace)
          command.perform(namespace, args, config)
        else
          find_by_namespace("rake").perform(namespace, args, config)
        end
      end

      # Rails finds namespaces similar to Thor, it only adds one rule:
      #
      # Command names must end with "_command.rb". This is required because Rails
      # looks in load paths and loads the command just before it's going to be used.
      #
      #   find_by_namespace :webrat, :rails, :integration
      #
      # Will search for the following commands:
      #
      #   "rails:webrat", "webrat:integration", "webrat"
      #
      # Notice that "rails:commands:webrat" could be loaded as well, what
      # Rails looks for is the first and last parts of the namespace.
      def find_by_namespace(name) # :nodoc:
        lookups = [ name, "rails:#{name}" ]

        lookup(lookups)

        namespaces = subclasses.index_by(&:namespace)
        namespaces[(lookups & namespaces.keys).first]
      end

      # Returns the root of the Rails engine or app running the command.
      def root
        if defined?(ENGINE_ROOT)
          Pathname.new(ENGINE_ROOT)
        elsif defined?(APP_PATH)
          Pathname.new(File.expand_path("../..", APP_PATH))
        end
      end

      def print_commands # :nodoc:
        sorted_groups.each { |b, n| print_list(b, n) }
      end

      def sorted_groups # :nodoc:
        lookup!

        groups = (subclasses - hidden_commands).group_by { |c| c.namespace.split(":").first }
        groups.transform_values! { |commands| commands.flat_map(&:printing_commands).sort }

        rails = groups.delete("rails")
        [[ "rails", rails ]] + groups.sort.to_a
      end

      private
        def command_type # :doc:
          @command_type ||= "command"
        end

        def lookup_paths # :doc:
          @lookup_paths ||= %w( rails/commands commands )
        end

        def file_lookup_paths # :doc:
          @file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_command.rb" ]
        end
    end
  end
end