File: lib/radiant/extension_path.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: Radiant#1
  class: ExtensionPath#2
inherits from
  Object ( Builtin-Module )
has properties
attribute: name [RW] #23
attribute: path [RW] #23
method: initialize / 1 #26
method: required #31
method: to_s #35
class method: from_path / 2 #46
class method: clear_paths! #55
method: load_paths #72
method: plugin_paths #79
method: locale_paths #87
method: helper_paths #96
method: model_paths #103
method: controller_paths #110
method: view_paths #118
method: metal_paths #125
method: rake_task_paths #131
method: eager_load_paths #143
class method: find / 1 #150
class method: for / 1 #157
class method: enabled #167
class method: enabled_paths #174
method: check_subdirectory / 1 #194

Class Hierarchy

Object ( Builtin-Module )
  ExtensionPath ( Radiant ) #2

Code

   1  module Radiant
   2    class ExtensionPath
   3      # This class holds information about extensions that may be loaded. It has two roles: to remember the 
   4      # location of the extension so that we don't have to search for it again, and to look within that path
   5      # for significant application subdirectories.
   6      #
   7      # We can't just retrieve this information from the Extension class because the initializer sets up 
   8      # most of the application load_paths before plugins (including extensions) are loaded. You can think
   9      # of this as a sort of pre-extension class preparing the way for extension loading.
  10      #
  11      # You can use instances of this class to retrieve information about a particular extension:
  12      #
  13      #   ExtensionPath.new(:name, :path)
  14      #   ExtensionPath.find(:name)               #=> ExtensionPath instance
  15      #   ExtensionPath.find(:name).plugin_paths  #=> "path/vendor/plugins" if it exists and is a directory
  16      #   ExtensionPath.for(:name)                #=> "path"
  17      #
  18      # The initializer calls class methods to get overall lists (in configured order) of enabled load paths:
  19      #
  20      #   ExtensionPath.enabled                   #=> ["path", "path", "path", "path"]
  21      #   ExtensionPath.plugin_paths              #=> ["path/vendor/plugins", "path/vendor/plugins"]
  22 
  23      attr_accessor :name, :path
  24      @@known_paths = {}
  25      
  26      def initialize(options = {}) #:nodoc
  27        @name, @path = options[:name], options[:path]
  28        @@known_paths[@name.to_sym] = self
  29      end
  30      
  31      def required
  32        File.join(path, "#{name}_extension")
  33      end
  34      
  35      def to_s
  36        path
  37      end
  38      
  39      # Builds a new ExtensionPath object from the supplied path, working out the name of the extension by 
  40      # stripping the extra bits from radiant-something-extension-1.0.0 to leave just 'something'. The object
  41      # is returned, and also remembered here for later use by the initializer (to find load paths) and the
  42      # ExtensionLoader, to load and activate the extension.
  43      #
  44      # If two arguments are given, the second is taken to be the full extension name.
  45      #
  46      def self.from_path(path, name=nil)
  47        name = path if name.blank?
  48        name = File.basename(name).gsub(/^radiant-|-extension(-[\d\.a-z]+|-[a-z\d]+)*$/, '')
  49        new(:name => name, :path => path)
  50      end
  51      
  52      # Forgets all recorded extension paths.
  53      # Currently only used in testing.
  54      #
  55      def self.clear_paths!
  56        @@known_paths = {}
  57      end
  58      
  59      # Returns a list of all the likely load paths found within this extension root. It includes all of these 
  60      # that exist and are directories:
  61      #
  62      # * path
  63      # * path/lib 
  64      # * path/app/models 
  65      # * path/app/controllers
  66      # * path/app/metal
  67      # * path/app/helpers
  68      # * path/test/helpers
  69      #
  70      # You can call the class method ExtensionPath.load_paths to get a flattened list of all the load paths in all the enabled extensions.
  71      #
  72      def load_paths
  73        %w(lib app/models app/controllers app/metal app/helpers test/helpers).collect { |d| check_subdirectory(d) }.push(path).flatten.compact
  74      end
  75 
  76      # Returns a list of all the +vendor/plugin+ paths found within this extension root.
  77      # Call the class method ExtensionPath.plugin_paths to get a list of the plugin paths found in all enabled extensions.
  78      #
  79      def plugin_paths
  80        check_subdirectory("vendor/plugins")
  81      end
  82 
  83      # Returns a list of names of all the locale files found within this extension root.
  84      # Call the class method ExtensionPath.locale_paths to get a list of the locale files found in all enabled extensions
  85      # in reverse order so that locale definitions override one another correctly.
  86      #
  87      def locale_paths
  88        if check_subdirectory("config/locales")
  89          Dir[File.join("#{path}","config/locales","*.{rb,yml}")] 
  90        end
  91      end
  92 
  93      # Returns the app/helpers path if it is found within this extension root.
  94      # Call the class method ExtensionPath.helper_paths to get a list of the helper paths found in all enabled extensions.
  95      #
  96      def helper_paths
  97        check_subdirectory("app/helpers")
  98      end
  99 
 100      # Returns the app/models path if it is found within this extension root.
 101      # Call the class method ExtensionPath.model_paths to get a list of the model paths found in all enabled extensions.
 102      #
 103      def model_paths
 104        check_subdirectory("app/models")
 105      end
 106 
 107      # Returns the app/controllers path if it is found within this extension root.
 108      # Call the class method ExtensionPath.controller_paths to get a list of the controller paths found in all enabled extensions.
 109      #
 110      def controller_paths
 111        check_subdirectory("app/controllers")
 112      end
 113 
 114      # Returns the app/views path if it is found within this extension root. 
 115      # Call the class method ExtensionPath.view_paths to get a list of the view paths found in all enabled extensions
 116      # in reverse order so that views override one another correctly.
 117      #
 118      def view_paths
 119        check_subdirectory("app/views")
 120      end
 121 
 122      # Returns the app/metal path if it is found within this extension root.
 123      # Call the class method ExtensionPath.metal_paths to get a list of the metal paths found in all enabled extensions.
 124      #
 125      def metal_paths
 126        check_subdirectory("app/metal")
 127      end
 128 
 129      # Returns a list of all the rake task files found within this extension root.
 130      #
 131      def rake_task_paths
 132        if check_subdirectory("lib/tasks")
 133          Dir[File.join("#{path}","lib/tasks/**","*.rake")] 
 134        end
 135      end
 136 
 137      # Returns a list of extension subdirectories that should be marked for eager loading. At the moment that
 138      # includes all the controller, model and helper paths. The main purpose here is to ensure that extension
 139      # controllers are loaded before running cucumber features, and there may be a better way to achieve that.
 140      #
 141      # Call the class method ExtensionPath.eager_load_paths to get a list for all enabled extensions.
 142      #
 143      def eager_load_paths
 144        [controller_paths, model_paths, helper_paths].flatten.compact
 145      end
 146      
 147      class << self
 148        # Returns the ExtensionPath object for the given extension name.
 149        #
 150        def find(name)
 151          raise LoadError, "Cannot return path for unknown extension: #{name}" unless @@known_paths[name.to_sym]
 152          @@known_paths[name.to_sym]
 153        end
 154        
 155        # Returns the root path recorded for the given extension name.
 156        #
 157        def for(name)
 158          find(name).path
 159        end
 160 
 161        # Returns a list of path objects for all the enabled extensions in the configured order. 
 162        # If a configured extension has not been found during initialization, a LoadError will be thrown here.
 163        #
 164        # Note that at this stage, in line with the usage of config.extensions = [], the extension names
 165        # are being passed around as symbols.
 166        #
 167        def enabled
 168          enabled_extensions = Radiant.configuration.enabled_extensions
 169          @@known_paths.values_at(*enabled_extensions).compact
 170        end
 171        
 172        # Returns a list of the root paths to all the enabled extensions, in the configured order.
 173        #
 174        def enabled_paths
 175          enabled.map(&:path)
 176        end
 177        
 178        [:load_paths, :plugin_paths, :helper_paths, :model_paths, :controller_paths, :eager_load_paths].each do |m|
 179          define_method(m) do
 180            enabled.map{|ep| ep.send(m)}.flatten.compact
 181          end
 182        end
 183        [:locale_paths, :view_paths, :metal_paths, :rake_task_paths].each do |m|
 184          define_method(m) do
 185            enabled.map{|ep| ep.send(m)}.flatten.compact.reverse
 186          end
 187        end
 188      end
 189 
 190    private
 191 
 192      # If the supplied path within the extension root exists and is a directory, its absolute path is returned. Otherwise, nil.
 193      #
 194      def check_subdirectory(subpath)
 195        subdirectory = File.join(path, subpath)
 196        subdirectory if File.directory?(subdirectory)
 197      end
 198      
 199    end
 200  en