mirror of https://github.com/apache/lucene.git
323 lines
13 KiB
Ruby
323 lines
13 KiB
Ruby
|
# This file contains the Engines module, which holds most of the logic regarding
|
||
|
# the startup and management of plugins. See Engines for more details.
|
||
|
#
|
||
|
# The engines plugin adds to Rails' existing behaviour by producing the +Rails.plugins+
|
||
|
# PluginList, a list of all loaded plugins in a form which can be easily queried
|
||
|
# and manipulated. Each instance of Plugin has methods which are used to enhance
|
||
|
# their behaviour, including mirroring public assets, add controllers, helpers
|
||
|
# and views and even migration.
|
||
|
#
|
||
|
# = init.rb
|
||
|
#
|
||
|
# When the engines plugin loads, it first includes the
|
||
|
# Engines::RailsExtensions::RailsInitializer module into Rails::Initializer,
|
||
|
# overriding a number of the methods used to load plugins.
|
||
|
#
|
||
|
# Once this is loaded, Engines.init is called to prepare the application
|
||
|
# and create the relevant new datastructures (including <tt>Rails.plugins</tt>).
|
||
|
#
|
||
|
# Finally, each of the extension modules from Engines::RailsExtensionsis
|
||
|
# loaded and included into the relevant Rails classes and modules, enhancing
|
||
|
# their behaviour to work better with files from plugins.
|
||
|
|
||
|
require "engines/plugin_list"
|
||
|
require "engines/plugin"
|
||
|
|
||
|
# TODO: define a better logger.
|
||
|
def logger
|
||
|
RAILS_DEFAULT_LOGGER
|
||
|
end
|
||
|
|
||
|
# The Engines module contains most of the methods used during the enhanced
|
||
|
# startup of Rails plugins.
|
||
|
#
|
||
|
# When the Engines plugin loads (its <tt>init.rb</tt> file is evaluated), the
|
||
|
# Engines.init method is called. This kickstarts the plugins hooks into
|
||
|
# the initialization process.
|
||
|
#
|
||
|
# == Parameters
|
||
|
#
|
||
|
# The Engines module has a number of public configuration parameters:
|
||
|
#
|
||
|
# [+public_directory+] The directory into which plugin assets should be
|
||
|
# mirrored. Defaults to <tt>RAILS_ROOT/public/plugin_assets</tt>.
|
||
|
# [+schema_info_table+] The table to use when storing plugin migration
|
||
|
# version information. Defaults to +plugin_schema_info+.
|
||
|
# [+rails_initializer+] A reference of the Rails initializer instance that
|
||
|
# was used to startup Rails. This is often useful
|
||
|
# when working with the startup process; see
|
||
|
# Engines::RailsExtensions::RailsInitializer for more
|
||
|
# information
|
||
|
#
|
||
|
# Additionally, there are a few flags which control the behaviour of
|
||
|
# some of the features the engines plugin adds to Rails:
|
||
|
#
|
||
|
# [+disable_application_view_loading+] A boolean flag determining whether
|
||
|
# or not views should be loaded from
|
||
|
# the main <tt>app/views</tt> directory.
|
||
|
# Defaults to false; probably only
|
||
|
# useful when testing your plugin.
|
||
|
# [+disable_application_code_loading+] A boolean flag determining whether
|
||
|
# or not to load controllers/helpers
|
||
|
# from the main +app+ directory,
|
||
|
# if corresponding code exists within
|
||
|
# a plugin. Defaults to false; again,
|
||
|
# probably only useful when testing
|
||
|
# your plugin.
|
||
|
# [+disable_code_mixing+] A boolean flag indicating whether all plugin
|
||
|
# copies of a particular controller/helper should
|
||
|
# be loaded and allowed to override each other,
|
||
|
# or if the first matching file should be loaded
|
||
|
# instead. Defaults to false.
|
||
|
#
|
||
|
module Engines
|
||
|
# The name of the public directory to mirror public engine assets into.
|
||
|
# Defaults to <tt>RAILS_ROOT/public/plugin_assets</tt>.
|
||
|
mattr_accessor :public_directory
|
||
|
self.public_directory = File.join(RAILS_ROOT, 'public', 'plugin_assets')
|
||
|
|
||
|
# The table in which to store plugin schema information. Defaults to
|
||
|
# "plugin_schema_info".
|
||
|
mattr_accessor :schema_info_table
|
||
|
self.schema_info_table = "plugin_schema_info"
|
||
|
|
||
|
# A reference to the current Rails::Initializer instance
|
||
|
mattr_accessor :rails_initializer
|
||
|
|
||
|
|
||
|
#--
|
||
|
# These attributes control the behaviour of the engines extensions
|
||
|
#++
|
||
|
|
||
|
# Set this to true if views should *only* be loaded from plugins
|
||
|
mattr_accessor :disable_application_view_loading
|
||
|
self.disable_application_view_loading = false
|
||
|
|
||
|
# Set this to true if controller/helper code shouldn't be loaded
|
||
|
# from the application
|
||
|
mattr_accessor :disable_application_code_loading
|
||
|
self.disable_application_code_loading = false
|
||
|
|
||
|
# Set this ti true if code should not be mixed (i.e. it will be loaded
|
||
|
# from the first valid path on $LOAD_PATH)
|
||
|
mattr_accessor :disable_code_mixing
|
||
|
self.disable_code_mixing = false
|
||
|
|
||
|
|
||
|
private
|
||
|
|
||
|
# A memo of the bottom of Rails' default load path
|
||
|
mattr_accessor :rails_final_load_path
|
||
|
# A memo of the bottom of Rails Dependencies load path
|
||
|
mattr_accessor :rails_final_dependency_load_path
|
||
|
|
||
|
public
|
||
|
|
||
|
# Initializes the engines plugin and prepares Rails to start loading
|
||
|
# plugins using engines extensions. Within this method:
|
||
|
#
|
||
|
# 1. Copies of the Rails configuration and initializer are stored;
|
||
|
# 2. The Rails.plugins PluginList instance is created;
|
||
|
# 3. Any plugins which were loaded before the engines plugin are given
|
||
|
# the engines treatment via #enhance_loaded_plugins.
|
||
|
# 4. The base public directory (into which plugin assets are mirrored)
|
||
|
# is created, if necessary - #initialize_base_public_directory
|
||
|
# 5. <tt>config.plugins</tt> is checked to see if a wildcard was present -
|
||
|
# #check_for_star_wildcard
|
||
|
#
|
||
|
def self.init(rails_configuration, rails_initializer)
|
||
|
# First, determine if we're running in legacy mode
|
||
|
@legacy_support = self.const_defined?(:LegacySupport) && LegacySupport
|
||
|
|
||
|
# Store some information about the plugin subsystem
|
||
|
Rails.configuration = rails_configuration
|
||
|
|
||
|
# We need a hook into this so we can get freaky with the plugin loading itself
|
||
|
self.rails_initializer = rails_initializer
|
||
|
|
||
|
@load_all_plugins = false
|
||
|
|
||
|
store_load_path_markers
|
||
|
|
||
|
Rails.plugins ||= PluginList.new
|
||
|
enhance_loaded_plugins # including this one, as it happens.
|
||
|
|
||
|
initialize_base_public_directory
|
||
|
|
||
|
check_for_star_wildcard
|
||
|
|
||
|
logger.debug "engines has started."
|
||
|
end
|
||
|
|
||
|
# You can enable legacy support by defining the LegacySupport constant
|
||
|
# in the Engines module before Rails loads, i.e. at the *top* of environment.rb,
|
||
|
# add:
|
||
|
#
|
||
|
# module Engines
|
||
|
# LegacySupport = true
|
||
|
# end
|
||
|
#
|
||
|
# Legacy Support doesn't actually do anything at the moment. If necessary
|
||
|
# we may support older-style 'engines' using this flag.
|
||
|
def self.legacy_support?
|
||
|
@legacy_support
|
||
|
end
|
||
|
|
||
|
# A reference to the currently-loading/loaded plugin. This is present to support
|
||
|
# legacy engines; it's preferred to use Rails.plugins[name] in your plugin's
|
||
|
# init.rb file in order to get your Plugin instance.
|
||
|
def self.current
|
||
|
Rails.plugins.last
|
||
|
end
|
||
|
|
||
|
# This is set to true if a "*" widlcard is present at the end of
|
||
|
# the config.plugins array.
|
||
|
def self.load_all_plugins?
|
||
|
@load_all_plugins
|
||
|
end
|
||
|
|
||
|
# Stores a record of the last paths which Rails added to each of the load path
|
||
|
# attributes ($LOAD_PATH, Dependencies.load_paths and
|
||
|
# ActionController::Routing.controller_paths) that influence how code is loaded
|
||
|
# We need this to ensure that we place our additions to the load path *after*
|
||
|
# all Rails' defaults
|
||
|
def self.store_load_path_markers
|
||
|
self.rails_final_load_path = $LOAD_PATH.last
|
||
|
logger.debug "Rails final load path: #{self.rails_final_load_path}"
|
||
|
self.rails_final_dependency_load_path = ::Dependencies.load_paths.last
|
||
|
logger.debug "Rails final dependency load path: #{self.rails_final_dependency_load_path}"
|
||
|
end
|
||
|
|
||
|
# Create Plugin instances for plugins loaded before the engines plugin was.
|
||
|
# Once a Plugin instance is created, the Plugin#load method is then called
|
||
|
# to fully load the plugin. See Plugin#load for more details about how a
|
||
|
# plugin is started once engines is involved.
|
||
|
def self.enhance_loaded_plugins
|
||
|
Engines.rails_initializer.loaded_plugins.each do |name|
|
||
|
plugin_path = File.join(self.find_plugin_path(name), name)
|
||
|
unless Rails.plugins[name]
|
||
|
plugin = Plugin.new(name, plugin_path)
|
||
|
logger.debug "enginizing plugin: #{plugin.name} from #{plugin_path}"
|
||
|
plugin.load # injects the extra directories into the load path, and mirrors public files
|
||
|
Rails.plugins << plugin
|
||
|
end
|
||
|
end
|
||
|
logger.debug "plugins is now: #{Rails.plugins.map { |p| p.name }.join(", ")}"
|
||
|
end
|
||
|
|
||
|
# Ensure that the plugin asset subdirectory of RAILS_ROOT/public exists, and
|
||
|
# that we've added a little warning message to instruct developers not to mess with
|
||
|
# the files inside, since they're automatically generated.
|
||
|
def self.initialize_base_public_directory
|
||
|
if !File.exist?(self.public_directory)
|
||
|
# create the public/engines directory, with a warning message in it.
|
||
|
logger.debug "Creating public engine files directory '#{self.public_directory}'"
|
||
|
FileUtils.mkdir(self.public_directory)
|
||
|
message = %{Files in this directory are automatically generated from your Rails Engines.
|
||
|
They are copied from the 'public' directories of each engine into this directory
|
||
|
each time Rails starts (server, console... any time 'start_engine' is called).
|
||
|
Any edits you make will NOT persist across the next server restart; instead you
|
||
|
should edit the files within the <plugin_name>/assets/ directory itself.}
|
||
|
target = File.join(public_directory, "README")
|
||
|
File.open(target, 'w') { |f| f.puts(message) } unless File.exist?(target)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Check for a "*" at the end of the plugins list; if one is found, note that
|
||
|
# we should load all other plugins once Rails has finished initializing, and
|
||
|
# remove the "*".
|
||
|
def self.check_for_star_wildcard
|
||
|
if Rails.configuration.plugins && Rails.configuration.plugins.last == "*"
|
||
|
Rails.configuration.plugins.pop
|
||
|
@load_all_plugins = true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
#-
|
||
|
# The following code is called once all plugins are loaded, and Rails is almost
|
||
|
# finished initialization
|
||
|
#+
|
||
|
|
||
|
# Once the Rails Initializer has finished, the engines plugin takes over
|
||
|
# and performs any post-processing tasks it may have, including:
|
||
|
#
|
||
|
# * Loading any remaining plugins if config.plugins ended with a '*'.
|
||
|
# * Updating Rails::Info with version information, if possible.
|
||
|
#
|
||
|
def self.after_initialize
|
||
|
if self.load_all_plugins?
|
||
|
logger.debug "loading remaining plugins from #{Rails.configuration.plugin_paths.inspect}"
|
||
|
# this will actually try to load ALL plugins again, but any that have already
|
||
|
# been loaded will be ignored.
|
||
|
rails_initializer.load_all_plugins
|
||
|
update_rails_info_with_loaded_plugins
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Updates Rails::Info with the list of loaded plugins, and version information for
|
||
|
# each plugin. This information is then available via script/about, or through
|
||
|
# the builtin rails_info controller.
|
||
|
def self.update_rails_info_with_loaded_plugins
|
||
|
if defined?(Rails::Info) # since it may not be available by default in some environments...
|
||
|
# don't do anything if it's not there.
|
||
|
Rails::Info.property("Loaded plugins") { Rails.plugins.map { |p| p.name }.join(", ") }
|
||
|
Rails.plugins.each do |plugin|
|
||
|
Rails::Info.property("#{plugin.name} version") { plugin.version.blank? ? "(unknown)" : plugin.version }
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
#-
|
||
|
# helper methods to find and deal with plugin paths and names
|
||
|
#+
|
||
|
|
||
|
# Returns the path within +Rails.configuration.plugin_paths+ which includes
|
||
|
# a plugin with the given name.
|
||
|
def self.find_plugin_path(name)
|
||
|
Rails.configuration.plugin_paths.find do |path|
|
||
|
File.exist?(File.join(path, name.to_s))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Returns the name for the plugin at the given path.
|
||
|
# (Note this method also appears in Rails::Initializer extensions)
|
||
|
def self.plugin_name(path)
|
||
|
File.basename(path)
|
||
|
end
|
||
|
|
||
|
# A general purpose method to mirror a directory (+source+) into a destination
|
||
|
# directory, including all files and subdirectories. Files will not be mirrored
|
||
|
# if they are identical already (checked via FileUtils#identical?).
|
||
|
def self.mirror_files_from(source, destination)
|
||
|
return unless File.directory?(source)
|
||
|
|
||
|
# TODO: use Rake::FileList#pathmap?
|
||
|
|
||
|
source_files = Dir[source + "/**/*"]
|
||
|
source_dirs = source_files.select { |d| File.directory?(d) }
|
||
|
source_files -= source_dirs
|
||
|
|
||
|
source_dirs.each do |dir|
|
||
|
# strip down these paths so we have simple, relative paths we can
|
||
|
# add to the destination
|
||
|
target_dir = File.join(destination, dir.gsub(source, ''))
|
||
|
begin
|
||
|
FileUtils.mkdir_p(target_dir)
|
||
|
rescue Exception => e
|
||
|
raise "Could not create directory #{target_dir}: \n" + e
|
||
|
end
|
||
|
end
|
||
|
|
||
|
source_files.each do |file|
|
||
|
begin
|
||
|
target = File.join(destination, file.gsub(source, ''))
|
||
|
unless File.exist?(target) && FileUtils.identical?(file, target)
|
||
|
FileUtils.cp(file, target)
|
||
|
end
|
||
|
rescue Exception => e
|
||
|
raise "Could not copy #{file} to #{target}: \n" + e
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|