X-Git-Url: https://projects.mako.cc/source/selectricity-live/blobdiff_plain/a12d4f62752f546f57421244e370e79965706ffb..f7aee769411a893c1059c529a220c0d25c72974f:/vendor/plugins/engines/lib/engines.rb diff --git a/vendor/plugins/engines/lib/engines.rb b/vendor/plugins/engines/lib/engines.rb new file mode 100644 index 0000000..749ccb2 --- /dev/null +++ b/vendor/plugins/engines/lib/engines.rb @@ -0,0 +1,467 @@ +require 'logger' + +# We need to know the version of Rails that we are running before we +# can override any of the dependency stuff, since Rails' own behaviour +# has changed over the various releases. We need to explicily make sure +# that the Rails::VERSION constant is loaded, because such things could +# not automatically be achieved prior to 1.1, and the location of the +# file moved in 1.1.1! +def load_rails_version + # At this point, we can't even rely on RAILS_ROOT existing, so we have to figure + # the path to RAILS_ROOT/vendor/rails manually + rails_base = File.expand_path( + File.join(File.dirname(__FILE__), # RAILS_ROOT/vendor/plugins/engines/lib + '..', # RAILS_ROOT/vendor/plugins/engines + '..', # RAILS_ROOT/vendor/plugins + '..', # RAILS_ROOT/vendor + 'rails', 'railties', 'lib')) # RAILS_ROOT/vendor/rails/railties/lib + begin + load File.join(rails_base, 'rails', 'version.rb') + #puts 'loaded 1.1.1+ from vendor: ' + File.join(rails_base, 'rails', 'version.rb') + rescue MissingSourceFile # this means they DON'T have Rails 1.1.1 or later installed in vendor + begin + load File.join(rails_base, 'rails_version.rb') + #puts 'loaded 1.1.0- from vendor: ' + File.join(rails_base, 'rails_version.rb') + rescue MissingSourceFile # this means they DON'T have Rails 1.1.0 or previous installed in vendor + begin + # try and load version information for Rails 1.1.1 or later from the $LOAD_PATH + require 'rails/version' + #puts 'required 1.1.1+ from load path' + rescue LoadError + # try and load version information for Rails 1.1.0 or previous from the $LOAD_PATH + require 'rails_version' + #puts 'required 1.1.0- from load path' + end + end + end +end + +# Actually perform the load +load_rails_version +#puts "Detected Rails version: #{Rails::VERSION::STRING}" + +require 'engines/ruby_extensions' +# ... further files are required at the bottom of this file + +# Holds the Rails Engine loading logic and default constants +module Engines + + class << self + # Return the version string for this plugin + def version + "#{Version::Major}.#{Version::Minor}.#{Version::Release}" + end + + # For holding the rails configuration object + attr_accessor :rails_config + + # A flag to stop searching for views in the application + attr_accessor :disable_app_views_loading + + # A flag to stop code being mixed in from the application + attr_accessor :disable_app_code_mixing + end + + # The DummyLogger is a class which might pass through to a real Logger + # if one is assigned. However, it can gracefully swallow any logging calls + # if there is now Logger assigned. + class LoggerWrapper + def initialize(logger=nil) + set_logger(logger) + end + # Assign the 'real' Logger instance that this dummy instance wraps around. + def set_logger(logger) + @logger = logger + end + # log using the appropriate method if we have a logger + # if we dont' have a logger, ignore completely. + def method_missing(name, *args) + if @logger && @logger.respond_to?(name) + @logger.send(name, *args) + end + end + end + + LOGGER = Engines::LoggerWrapper.new + + class << self + # Create a new Logger instance for Engines, with the given outputter and level + def create_logger(outputter=STDOUT, level=Logger::INFO) + LOGGER.set_logger(Logger.new(outputter, level)) + end + # Sets the Logger instance that Engines will use to send logging information to + def set_logger(logger) + Engines::LOGGER.set_logger(logger) # TODO: no need for Engines:: part + end + # Retrieves the current Logger instance + def log + Engines::LOGGER # TODO: no need for Engines:: part + end + alias :logger :log + end + + # An array of active engines. This should be accessed via the Engines.active method. + ActiveEngines = [] + + # The root directory for engines + config :root, File.join(RAILS_ROOT, "vendor", "plugins") + + # The name of the public folder under which engine files are copied + config :public_dir, "engine_files" + + class << self + + # Initializes a Rails Engine by loading the engine's init.rb file and + # ensuring that any engine controllers are added to the load path. + # This will also copy any files in a directory named 'public' + # into the public webserver directory. Example usage: + # + # Engines.start :login + # Engines.start :login_engine # equivalent + # + # A list of engine names can be specified: + # + # Engines.start :login, :user, :wiki + # + # The engines will be loaded in the order given. + # If no engine names are given, all engines will be started. + # + # Options can include: + # * :copy_files => true | false + # + # Note that if a list of engines is given, the options will apply to ALL engines. + def start(*args) + + options = (args.last.is_a? Hash) ? args.pop : {} + + if args.empty? + start_all + else + args.each do |engine_name| + start_engine(engine_name, options) + end + end + end + + # Starts all available engines. Plugins are considered engines if they + # include an init_engine.rb file, or they are named _engine. + def start_all + plugins = Dir[File.join(config(:root), "*")] + Engines.log.debug "considering plugins: #{plugins.inspect}" + plugins.each { |plugin| + engine_name = File.basename(plugin) + if File.exist?(File.join(plugin, "init_engine.rb")) || # if the directory contains init_engine.rb + (engine_name =~ /_engine$/) || # or it engines in '_engines' + (engine_name =~ /_bundle$/) # or even ends in '_bundle' + + start(engine_name) # start the engine... + + end + } + end + + # Initialize the routing controller paths. + def initialize_routing + # See lib/engines/routing_extensions.rb for more information. + ActionController::Routing.controller_paths = Engines.rails_config.controller_paths + end + + def start_engine(engine_name, options={}) + + # Create a new Engine and put this engine at the front of the ActiveEngines list + current_engine = Engine.new(engine_name) + Engines.active.unshift current_engine + Engines.log.info "Starting engine '#{current_engine.name}' from '#{File.expand_path(current_engine.root)}'" + + # add the code directories of this engine to the load path + add_engine_to_load_path(current_engine) + + # add the controller & component path to the Dependency system + engine_controllers = File.join(current_engine.root, 'app', 'controllers') + engine_components = File.join(current_engine.root, 'components') + + + # This mechanism is no longer required in Rails trunk + if Rails::VERSION::STRING =~ /^1.0/ && !Engines.config(:edge) + Controllers.add_path(engine_controllers) if File.exist?(engine_controllers) + Controllers.add_path(engine_components) if File.exist?(engine_components) + else + ActionController::Routing.controller_paths << engine_controllers + ActionController::Routing.controller_paths << engine_components + end + + # copy the files unless indicated otherwise + if options[:copy_files] != false + current_engine.mirror_engine_files + end + + # load the engine's init.rb file + startup_file = File.join(current_engine.root, "init_engine.rb") + if File.exist?(startup_file) + eval(IO.read(startup_file), binding, startup_file) + # possibly use require_dependency? Hmm. + else + Engines.log.debug "No init_engines.rb file found for engine '#{current_engine.name}'..." + end + end + + # Adds all directories in the /app and /lib directories within the engine + # to the load path + def add_engine_to_load_path(engine) + + # remove the lib directory added by load_plugin, and place it in the corrent + # location *after* the application/lib. This can be removed when + # http://dev.rubyonrails.org/ticket/2910 is fixed. + app_lib_index = $LOAD_PATH.index(File.join(RAILS_ROOT, "lib")) + engine_lib = File.join(engine.root, "lib") + if app_lib_index + $LOAD_PATH.delete(engine_lib) + $LOAD_PATH.insert(app_lib_index+1, engine_lib) + end + + # Add ALL paths under the engine root to the load path + app_dirs = %w(controllers helpers models).collect { |d| + File.join(engine.root, 'app', d) + } + other_dirs = %w(components lib).collect { |d| + File.join(engine.root, d) + } + load_paths = (app_dirs + other_dirs).select { |d| File.directory?(d) } + + # Remove other engines from the $LOAD_PATH by matching against the engine.root values + # in ActiveEngines. Store the removed engines in the order they came off. + + old_plugin_paths = [] + # assumes that all engines are at the bottom of the $LOAD_PATH + while (File.expand_path($LOAD_PATH.last).index(File.expand_path(Engines.config(:root))) == 0) do + old_plugin_paths.unshift($LOAD_PATH.pop) + end + + + # add these LAST on the load path. + load_paths.reverse.each { |dir| + if File.directory?(dir) + Engines.log.debug "adding #{File.expand_path(dir)} to the load path" + #$LOAD_PATH.push(File.expand_path(dir)) + $LOAD_PATH.push dir + end + } + + # Add the other engines back onto the bottom of the $LOAD_PATH. Put them back on in + # the same order. + $LOAD_PATH.push(*old_plugin_paths) + $LOAD_PATH.uniq! + end + + # Returns the directory in which all engine public assets are mirrored. + def public_engine_dir + File.expand_path(File.join(RAILS_ROOT, "public", Engines.config(:public_dir))) + end + + # create the /public/engine_files directory if it doesn't exist + def create_base_public_directory + if !File.exists?(public_engine_dir) + # create the public/engines directory, with a warning message in it. + Engines.log.debug "Creating public engine files directory '#{public_engine_dir}'" + FileUtils.mkdir(public_engine_dir) + File.open(File.join(public_engine_dir, "README"), "w") do |f| + f.puts </public/ directory itself. +EOS + end + end + end + + # Returns the Engine object for the specified engine, e.g.: + # Engines.get(:login) + def get(name) + active.find { |e| e.name == name.to_s || e.name == "#{name}_engine" } + end + alias_method :[], :get + + # Returns the Engine object for the current engine, i.e. the engine + # in which the currently executing code lies. + def current + current_file = caller[0] + active.find do |engine| + File.expand_path(current_file).index(File.expand_path(engine.root)) == 0 + end + end + + # Returns an array of active engines + def active + ActiveEngines + end + + # Pass a block to perform an operation on each engine. You may pass an argument + # to determine the order: + # + # * :load_order - in the order they were loaded (i.e. lower precidence engines first). + # * :precidence_order - highest precidence order (i.e. last loaded) first + def each(ordering=:precidence_order, &block) + engines = (ordering == :load_order) ? active.reverse : active + engines.each { |e| yield e } + end + end +end + +# A simple class for holding information about loaded engines +class Engine + + # Returns the base path of this engine + attr_accessor :root + + # Returns the name of this engine + attr_reader :name + + # An attribute for holding the current version of this engine. There are three + # ways of providing an engine version. The simplest is using a string: + # + # Engines.current.version = "1.0.7" + # + # Alternatively you can set it to a module which contains Major, Minor and Release + # constants: + # + # module LoginEngine::Version + # Major = 1; Minor = 0; Release = 6; + # end + # Engines.current.version = LoginEngine::Version + # + # Finally, you can set it to your own Proc, if you need something really fancy: + # + # Engines.current.version = Proc.new { File.open('VERSION', 'r').readlines[0] } + # + attr_writer :version + + # Engine developers can store any information they like in here. + attr_writer :info + + # Creates a new object holding information about an Engine. + def initialize(name) + + @root = '' + suffixes = ['', '_engine', '_bundle'] + while !File.exist?(@root) && !suffixes.empty? + suffix = suffixes.shift + @root = File.join(Engines.config(:root), name.to_s + suffix) + end + + if !File.exist?(@root) + raise "Cannot find the engine '#{name}' in either /vendor/plugins/#{name}, " + + "/vendor/plugins/#{name}_engine or /vendor/plugins/#{name}_bundle." + end + + @name = File.basename(@root) + end + + # Returns the version string of this engine + def version + case @version + when Module + "#{@version::Major}.#{@version::Minor}.#{@version::Release}" + when Proc # not sure about this + @version.call + when NilClass + 'unknown' + else + @version + end + end + + # Returns a string describing this engine + def info + @info || '(none)' + end + + # Returns a string representation of this engine + def to_s + "Engine<'#{@name}' [#{version}]:#{root.gsub(RAILS_ROOT, '')}>" + end + + # return the path to this Engine's public files (with a leading '/' for use in URIs) + def public_dir + File.join("/", Engines.config(:public_dir), name) + end + + # Replicates the subdirectories under the engine's /public directory into + # the corresponding public directory. + def mirror_engine_files + + begin + Engines.create_base_public_directory + + source = File.join(root, "public") + Engines.log.debug "Attempting to copy public engine files from '#{source}'" + + # if there is no public directory, just return after this file + return if !File.exist?(source) + + source_files = Dir[source + "/**/*"] + source_dirs = source_files.select { |d| File.directory?(d) } + source_files -= source_dirs + + Engines.log.debug "source dirs: #{source_dirs.inspect}" + + # Create the engine_files/_engine dir if it doesn't exist + new_engine_dir = File.join(RAILS_ROOT, "public", public_dir) + if !File.exists?(new_engine_dir) + # Create _engine dir with a message + Engines.log.debug "Creating #{public_dir} public dir" + FileUtils.mkdir_p(new_engine_dir) + end + + # create all the directories, transforming the old path into the new path + source_dirs.uniq.each { |dir| + begin + # strip out the base path and add the result to the public path, i.e. replace + # ../script/../vendor/plugins/engine_name/public/javascript + # with + # engine_name/javascript + # + relative_dir = dir.gsub(File.join(root, "public"), name) + target_dir = File.join(Engines.public_engine_dir, relative_dir) + unless File.exist?(target_dir) + Engines.log.debug "creating directory '#{target_dir}'" + FileUtils.mkdir_p(target_dir) + end + rescue Exception => e + raise "Could not create directory #{target_dir}: \n" + e + end + } + + # copy all the files, transforming the old path into the new path + source_files.uniq.each { |file| + begin + # change the path from the ENGINE ROOT to the public directory root for this engine + target = file.gsub(File.join(root, "public"), + File.join(Engines.public_engine_dir, name)) + unless File.exist?(target) && FileUtils.identical?(file, target) + Engines.log.debug "copying file '#{file}' to '#{target}'" + FileUtils.cp(file, target) + end + rescue Exception => e + raise "Could not copy #{file} to #{target}: \n" + e + end + } + rescue Exception => e + Engines.log.warn "WARNING: Couldn't create the engine public file structure for engine '#{name}'; Error follows:" + Engines.log.warn e + end + end +end + + +# These files must be required after the Engines module has been defined. +require 'engines/dependencies_extensions' +require 'engines/routing_extensions' +require 'engines/action_view_extensions' +require 'engines/action_mailer_extensions' +require 'engines/migration_extensions' +require 'engines/active_record_extensions' + +# only load the testing extensions if we are in the test environment +require 'engines/testing_extensions' if %w(test).include?(RAILS_ENV)