Added the RoR Login-Engine and activated it on the site.
[selectricity-live] / 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 (file)
index 0000000..749ccb2
--- /dev/null
@@ -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 <something>_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 <<EOS
+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 <engine_name>/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/<something>_engine dir if it doesn't exist
+      new_engine_dir = File.join(RAILS_ROOT, "public", public_dir)
+      if !File.exists?(new_engine_dir)
+        # Create <something>_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)

Benjamin Mako Hill || Want to submit a patch?