1  require 'singleton'
  2  require 'active_record'
  3  require 'action_controller'
  4  require 'action_mailer'
  5  require 'action_view'
  6  require 'yaml'
  7  require 'needle'
  8
  9  require File.join(File.dirname(__FILE__), 'base-enhance')
 10
 11  module Rails
 12
 13    # A simple subclass of the Needle registry class, to make it easier to add
 14    # component-specific namespaces.
 15    class Registry < Needle::Registry
 16
 17      # If a namespace does not exist with the given name, create it. Regardless,
 18      # return the namespace.
 19      def component_space( name )
 20        unless has_key?( name )
 21          namespace name
 22        end
 23
 24        self[ name ]
 25      end
 26
 27    end
 28
 29    # A singleton class encapsulating the management of environments in Rails.
 30    class Environment
 31      include Singleton
 32
 33      # The array of additional load paths to add.
 34      ADDITIONAL_LOAD_PATHS =
 35        %w{app/models app/controllers app/helpers config lib vendor}
 36
 37      # A reference to the registry to use.
 38      attr_reader :registry
 39
 40      # Create a new Environment. Since this is a singleton class, this cannot
 41      # be called directly, and will only be invoked once when the singleton
 42      # instance is created.
 43      def initialize
 44        ADDITIONAL_LOAD_PATHS.each do |dir|
 45          $:.unshift "#{File.dirname(__FILE__)}/../../#{dir}"
 46        end
 47
 48        ActionController::Base.template_root =
 49          ActionMailer::Base.template_root =
 50            File.dirname(__FILE__) + '/../../app/views/'
 51
 52        @registry = Rails::Registry.new :logs => { :device => STDOUT }
 53
 54        # Load the database configurations into a service, so they can be
 55        # referenced and rereferenced without reloading the file. This also allows
 56        # clients to override how database configurations are defined, simply by
 57        # redefining the database_configurations service.
 58        @registry.register :database_configurations do
 59          YAML::load(
 60            File.open(
 61              File.dirname(__FILE__) + "/../../config/database.yml"))
 62        end
 63
 64        # return the current database configuration. Use the 'prototype' model,
 65        # so that the block is executed on every request. Otherwise, if the
 66        # current_environment changed, the database_configuration service would
 67        # never reflect the change.
 68        @registry.register( :database_configuration, :model => :prototype ) do
 69          @registry.database_configurations[ @registry.current_environment ]
 70        end
 71
 72        # return the current system log file location. Use the 'prototype' model,
 73        # so that the block is executed on every request. Otherwise, if the
 74        # current_environment changed, the system_log_file service would
 75        # never reflect the change.
 76        #
 77        # this allows clients to change where logs are written to, simply by
 78        # registering a system_log_file service that replaces this one.
 79        @registry.register( :system_log_file, :model => :prototype ) do
 80          File.dirname(__FILE__) + "/../../log/#{@registry.current_environment}.log"
 81        end
 82
 83        ActiveRecord::Base.registry     = @registry.component_space :model
 84        ActionController::Base.registry = @registry.component_space :controller
 85        ActionMailer::Base.registry     = @registry.component_space :mailer
 86        ActionView::Base.registry       = @registry.component_space :view
 87
 88        ActiveRecord::Base.logger = @registry.logs.get "[active-record]"
 89        ActionController::Base.logger = @registry.logs.get "[action-controller]"
 90        ActionMailer::Base.logger = @registry.logs.get "[action-mailer]"
 91      end
 92
 93      # Select a new environment, by name. The only restriction is that, by default,
 94      # there must be an identically named database configuration, or this will
 95      # fail.
 96      def set( environment_name )
 97        @registry.register( :current_environment ) { environment_name }
 98        @registry.logs.write_to(@registry.system_log_file)
 99        ActiveRecord::Base.establish_connection(@registry.database_configuration)
100        true
101      end
102
103      # A convenience method for setting the environment. This allows you to do:
104      #
105      #   Environment.set "production"
106      #
107      # instead of
108      # 
109      #   Environment.instance.set "production"
110      def self.set( environment_name )
111        instance.set( environment_name )
112      end
113
114      # A convenience accessor for accessing the Rails registry. This allows you
115      # to do:
116      #
117      #   Environment.registry
118      #
119      # instead of
120      # 
121      #   Environment.instance.registry
122      def self.registry
123        instance.registry
124      end
125
126    end
127
128  end