aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/railtie.rb
blob: 2dc2ba10023f92a81ff437f596df848fce8eb448 (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
require 'rails/initializable'
require 'rails/configuration'

module Rails
  # Railtie is the core of the Rails Framework and provides all the hooks and
  # methods you need to link your plugin into Rails.
  # 
  # What Railtie does is make every component of Rails a "plugin" and creates 
  # an API that exposes all the powers that the builtin components need
  # to any plugin author.
  # 
  # In fact, every major component of Rails (Action Mailer, Action Controller,
  # Action View, Active Record and Active Resource) are all now just plain
  # old plugins, so anything they can do, your plugin can do.
  # 
  # Developing a plugin for Rails does not _require_ any implementation of
  # Railtie, there is no fixed rule, but as a guideline, if your plugin works
  # by just being required before Rails boots, then there is no need for you
  # to hook into Railtie, but if you need to interact with the Rails framework
  # during boot, or after boot, then Railtie is what you need to do that
  # interaction.
  # 
  # For example, the following would need you to implement Railtie in your
  # plugin:
  # 
  # * creating initializers (including route insertion)
  # * modifying the render path (think HAML et al)
  # * adding Rails config.* keys to the environment
  # * setting up a subscriber to the Rails +ActiveSupport::Notifications+
  # * adding global Rake tasks into rails
  # * setting up a default configuration for the Application
  # 
  # Railtie gives you a central place to connect into the Rails framework.  If you
  # find yourself writing plugin code that is having to monkey patch parts of the
  # Rails framework to achieve something, there is probably a better, more elegant
  # way to do it through Railtie, if there isn't, then you have found a lacking
  # feature of Railtie, please lodge a ticket.
  # 
  # Implementing Railtie in your plugin is by creating a class Railtie in your
  # application that has your plugin name and making sure that this gets loaded
  # durng boot time of the Rails stack.
  # 
  # You can do this however you wish, but three straight forward ways are:
  # 
  # == For gems or plugins that are not used outside of Rails
  # 
  # * Create a Railtie subclass within your lib/my_plugin.rb file:
  #   
  #   # lib/my_plugin.rb
  #   module MyPlugin
  #     class Railtie < Rails::Railtie
  #     end
  #   end
  # 
  # * Pass in your plugin name
  # 
  #   # lib/my_plugin.rb
  #   module MyPlugin
  #     class Railtie < Rails::Railtie
  #       plugin_name :my_plugin
  #     end
  #   end
  #   
  # == For gems that could be used without Rails
  # 
  # * Create a file (say, lib/my_gem/railtie.rb) which contains class Railtie inheriting from
  #   Rails::Railtie and is namespaced to your gem:
  #
  #   # lib/my_gem/railtie.rb
  #   module MyGem
  #     class Railtie < Rails::Railtie
  #     end
  #   end
  # 
  # * Require your own gem as well as rails in this file:
  # 
  #   # lib/my_gem/railtie.rb
  #   require 'my_gem'
  #   require 'rails'
  # 
  #   module MyGem
  #     class Railtie < Rails::Railtie
  #     end
  #   end
  #   
  # * Give your gem a unique name:
  # 
  #   # lib/my_gem/railtie.rb
  #   require 'my_gem'
  #   require 'rails'
  # 
  #   module MyGem
  #     class Railtie < Rails::Railtie
  #       plugin_name :my_gem
  #     end
  #   end
  # 
  # * Make sure your Gem loads the railtie.rb file if Rails is loaded first, an easy
  #   way to check is by checking for the Rails constant which will exist if Rails
  #   has started:
  # 
  #   # lib/my_gem.rb
  #   module MyGem
  #     require 'lib/railtie' if defined?(Rails)
  #   end
  # 
  # * Or instead of doing the require automatically, you can ask your users to require
  #   it for you in their Gemfile:
  # 
  #   # #{USER_RAILS_ROOT}/Gemfile
  #   gem "my_gem", :require_as => ["my_gem", "my_gem/railtie"]
  #
  class Railtie
    autoload :Configurable,  "rails/railtie/configurable"
    autoload :Configuration, "rails/railtie/configuration"

    include Initializable

    ABSTRACT_RAILTIES = %w(Rails::Plugin Rails::Engine Rails::Application)

    class << self
      def subclasses
        @subclasses ||= []
      end

      def inherited(base)
        unless abstract_railtie?(base)
          base.send(:include, self::Configurable)
          subclasses << base
        end
      end

      def railtie_name(railtie_name = nil)
        @railtie_name ||= name.demodulize.underscore
        @railtie_name = railtie_name if railtie_name
        @railtie_name
      end

      def railtie_names
        subclasses.map { |p| p.railtie_name }
      end

      def subscriber(subscriber)
        Rails::Subscriber.add(railtie_name, subscriber)
      end

      def rake_tasks(&blk)
        @rake_tasks ||= []
        @rake_tasks << blk if blk
        @rake_tasks
      end

      def generators(&blk)
        @generators ||= []
        @generators << blk if blk
        @generators
      end

    protected

      def abstract_railtie?(base)
        ABSTRACT_RAILTIES.include?(base.name)
      end
    end

    def rake_tasks
      self.class.rake_tasks
    end

    def generators
      self.class.generators
    end

    def load_tasks
      rake_tasks.each { |blk| blk.call }
    end

    def load_generators
      generators.each { |blk| blk.call }
    end
  end
end