Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #6726: add_elective_gem_plugin_support.diff

File add_elective_gem_plugin_support.diff, 9.6 kB (added by wbruce, 2 years ago)

Patch to add support for gem plugins

  • railties/test/plugin_test.rb

    old new  
    3131    assert !@init.send(:plugin_path?, "#{File.dirname(__FILE__)}/fixtures/plugins/default/jalskdjflkas") 
    3232  end 
    3333 
    34   def test_find_plugin
     34  def test_find_plugin_path
    3535    base    = "#{File.dirname(__FILE__)}/fixtures/plugins" 
    3636    default = "#{base}/default" 
    3737    alt     = "#{base}/alternate" 
    3838    acts    = "#{default}/acts" 
    39     assert_equal ["#{acts}/acts_as_chunky_bacon"], @init.send(:find_plugins, acts) 
    40     assert_equal ["#{acts}/acts_as_chunky_bacon", "#{default}/stubby"], @init.send(:find_plugins, default).sort 
    41     assert_equal ["#{alt}/a", "#{acts}/acts_as_chunky_bacon", "#{default}/stubby"], @init.send(:find_plugins, base).sort 
     39    assert_equal ["#{acts}/acts_as_chunky_bacon"], @init.send(:find_plugin_paths, acts) 
     40    assert_equal ["#{acts}/acts_as_chunky_bacon", "#{default}/stubby"], @init.send(:find_plugin_paths, default).sort 
     41    assert_equal ["#{alt}/a", "#{acts}/acts_as_chunky_bacon", "#{default}/stubby"], @init.send(:find_plugin_paths, base).sort 
    4242  end 
    4343 
    44   def test_load_plugin 
     44  def test_load_plugin_path 
    4545    stubby = "#{File.dirname(__FILE__)}/fixtures/plugins/default/stubby" 
    4646    expected = Set.new(['stubby']) 
    4747 
    48     assert @init.send(:load_plugin, stubby) 
     48    assert @init.send(:load_plugin_path, stubby) 
    4949    assert_equal expected, @init.loaded_plugins 
    5050 
    51     assert !@init.send(:load_plugin, stubby) 
     51    assert !@init.send(:load_plugin_path, stubby) 
    5252    assert_equal expected, @init.loaded_plugins 
    5353 
    54     assert_raise(LoadError) { @init.send(:load_plugin, 'lakjsdfkasljdf') } 
     54    assert_raise(LoadError) { @init.send(:load_plugin_path, 'lakjsdfkasljdf') } 
    5555    assert_equal expected, @init.loaded_plugins 
    5656  end 
    5757 
  • railties/lib/initializer.rb

    old new  
    9999      # could overwrite anything set from the defaults/global through 
    100100      # the individual base class configurations. 
    101101      load_environment 
     102       
     103      load_plugins 
    102104 
    103105      add_support_load_paths 
    104106 
    105       load_plugins 
    106  
    107107      # Observers are loaded after plugins in case Observers or observed models are modified by plugins. 
    108108      load_observers 
    109109 
     
    166166    def add_support_load_paths 
    167167    end 
    168168 
    169     # Loads all plugins in <tt>config.plugin_paths</tt>.  <tt>plugin_paths</tt> 
     169    # Loads all plugins in <tt>config.plugin_paths</tt>, then attempts to load 
     170    # any RubyGem plugins listed in <tt>config.plugins</tt>.   
     171    #  
     172    # <tt>plugin_paths</tt> 
    170173    # defaults to <tt>vendor/plugins</tt> but may also be set to a list of 
    171174    # paths, such as 
    172175    #   config.plugin_paths = ['lib/plugins', 'vendor/plugins'] 
    173176    # 
    174     # Each plugin discovered in <tt>plugin_paths</tt> is initialized: 
    175     # * add its +lib+ directory, if present, to the beginning of the load path 
    176     # * evaluate <tt>init.rb</tt> if present 
     177    # <tt>plugins</tt> is the list of plugins to enable (either in in <tt>plugin_paths</tt> 
     178    #  or from gems).  For RubyGem plugins to be loaded, they need to be in this list (incidently, 
     179    #  if you're mixing plugins from <tt>plugin_paths</tt> as well, you'll need to include them in 
     180    #  this list also if you want them loaded) 
    177181    # 
     182    #  To include version requirements for a gem plugin, use an array (like the arguments you'd pass 
     183    #  to <tt>require_gem</tt>), such as 
     184    #    config.plugins = ['foo', ['bar', '>1.0.7', '<=1.0.9'], 'baz'] 
     185    # 
    178186    # After all plugins are loaded, duplicates are removed from the load path. 
    179     # Plugins are loaded in alphabetical order. 
     187    # Plugins are loaded in alphabetical order; plugins from <tt>plugin_paths</tt> first, then 
     188    # gem plugins. 
    180189    def load_plugins 
    181       find_plugins(configuration.plugin_paths).sort.each { |path| load_plugin path } 
     190      find_plugin_paths(configuration.plugin_paths).sort.each { |path| load_plugin_path(path) } 
     191      load_paths_before_gems = configuration.load_paths.size 
     192      find_gem_plugins.sort_by{|p| p.last.name }.each do |plugin, gemspec| 
     193        load_gem_plugin(plugin, gemspec) 
     194      end 
     195      # Add gem load paths, if changed 
     196      if configuration.load_paths.size > load_paths_before_gems 
     197        set_load_path 
     198        set_autoload_paths 
     199      end 
    182200      $LOAD_PATH.uniq! 
    183201    end 
    184202 
     
    323341    end 
    324342 
    325343    protected 
     344     
    326345      # Return a list of plugin paths within base_path.  A plugin path is 
    327346      # a directory that contains either a lib directory or an init.rb file. 
    328347      # This recurses into directories which are not plugin paths, so you 
    329348      # may organize your plugins within the plugin path. 
    330       def find_plugins(*base_paths) 
     349      def find_plugin_paths(*base_paths) 
    331350        base_paths.flatten.inject([]) do |plugins, base_path| 
    332351          Dir.glob(File.join(base_path, '*')).each do |path| 
    333352            if plugin_path?(path) 
    334               plugins << path if plugin_enabled?(path) 
     353              plugins << path if plugin_path_enabled?(path) 
    335354            elsif File.directory?(path) 
    336               plugins += find_plugins(path) 
     355              plugins += find_plugin_paths(path) 
    337356            end 
    338357          end 
    339358          plugins 
    340359        end 
    341360      end 
     361       
     362      # Return a list of plugins and related gemspecs for plugins set in <tt>configuration.plugins</tt> 
     363      # and available as RubyGems (when not already loaded from <tt>find_plugins_paths</tt>). 
     364      def find_gem_plugins 
     365        configuration.plugins.inject([]) do |plugins,plugin| 
     366          next plugins if loaded_plugins.include?(plugin) 
     367          name, version_requirements = plugin.to_a 
     368          gem = Gem::Dependency.new(name, version_requirements || []) 
     369          if gemspec = Gem.source_index.find_name(gem.name, gem.version_requirements).last 
     370            plugins.push([plugin, gemspec]) 
     371          end 
     372          plugins 
     373        end 
     374      end 
    342375 
    343376      def plugin_path?(path) 
    344377        File.directory?(path) and (File.directory?(File.join(path, 'lib')) or File.file?(File.join(path, 'init.rb'))) 
    345378      end 
    346379 
    347       def plugin_enabled?(path) 
     380      def plugin_path_enabled?(path) 
    348381        configuration.plugins.empty? || configuration.plugins.include?(File.basename(path)) 
    349382      end 
    350383 
     
    357390      # Returns <tt>true</tt> if the plugin is successfully loaded or 
    358391      # <tt>false</tt> if it is already loaded (similar to Kernel#require). 
    359392      # Raises <tt>LoadError</tt> if the plugin is not found. 
    360       def load_plugin(directory) 
     393      def load_plugin_path(directory) 
    361394        name = File.basename(directory) 
     395        # Check this just in case, even though <tt>find_gem_plugins</tt> does when used normally 
    362396        return false if loaded_plugins.include?(name) 
    363397 
    364398        # Catch nonexistent and empty plugins. 
     
    366400 
    367401        lib_path  = File.join(directory, 'lib') 
    368402        init_path = File.join(directory, 'init.rb') 
    369         has_lib   = File.directory?(lib_path) 
    370         has_init  = File.file?(init_path) 
    371  
    372         # Add lib to load path *after* the application lib, to allow 
    373         # application libraries to override plugin libraries. 
    374         if has_lib 
     403         
     404        if File.directory?(lib_path) 
    375405          application_lib_index = $LOAD_PATH.index(File.join(RAILS_ROOT, "lib")) || 0 
    376406          $LOAD_PATH.insert(application_lib_index + 1, lib_path) 
    377407        end 
    378  
    379         # Allow plugins to reference the current configuration object 
    380         config = configuration 
    381          
     408                 
    382409        # Add to set of loaded plugins before 'name' collapsed in eval. 
    383410        loaded_plugins << name 
    384411 
    385         # Evaluate init.rb. 
    386         silence_warnings { eval(IO.read(init_path), binding, init_path) } if has_init 
    387  
     412        if File.file?(init_path) 
     413          # Allow plugins to reference the current configuration object 
     414          config = configuration 
     415          # Evaluate init.rb. 
     416          silence_warnings { eval(IO.read(init_path), binding, init_path) } 
     417        end 
     418                         
    388419        true 
    389420      end 
     421       
     422      # Load plugin from RubyGem unless already loaded. 
     423      # 
     424      # Each plugin is initialized: 
     425      # * the gem is required (and its <tt>autorequire</tt>s automatically, too) 
     426      # * add its +lib+ directory, if present, to the beginning of the load path 
     427      # * evaluate <tt>init.rb</tt> if present 
     428      # 
     429      # Returns <tt>true</tt> if the plugin is successfully loaded or 
     430      # <tt>false</tt> if it is already loaded (similar to Kernel#require). 
     431      # Raises <tt>LoadError</tt> if the plugin is not found. 
     432      def load_gem_plugin(plugin, gemspec) 
     433        return false if loaded_plugins.include?(plugin) 
     434        require_gem gemspec.name, gemspec.version.version 
     435        directory = gemspec.full_gem_path 
     436        lib_path  = File.join(directory, 'lib') 
     437        init_path = File.join(directory, 'init.rb') 
     438         
     439        if File.directory?(lib_path) 
     440          configuration.load_paths << lib_path 
     441          # Circumvent freeze 
     442          configuration.load_once_paths = configuration.load_once_paths.dup + [lib_path] 
     443        end 
     444         
     445        if File.file?(init_path) 
     446          # Allow plugins to reference the current configuration object 
     447          config = configuration 
     448          # Evaluate init.rb. 
     449          silence_warnings { eval(IO.read(init_path), binding, init_path) } 
     450        end 
     451         
     452      rescue LoadError => e 
     453        raise LoadError, "No such gem plugin: #{plugin.inspect} (#{e.message})" 
     454      end 
     455                   
    390456  end 
    391457 
    392458  # The Configuration class holds all the parameters for the Initializer and