Skip to content
This repository
Browse code

Added app/[models|controllers|helpers] to the load path for plugins t…

…hat has an app directory (go engines ;)) [DHH]
  • Loading branch information...
commit 63d8f56774dcb1ea601928c3eb6c119d359fae10 1 parent 133c349
David Heinemeier Hansson authored November 26, 2008
2  railties/CHANGELOG
... ...
@@ -1,5 +1,7 @@
1 1
 *2.3.0 [Edge]*
2 2
 
  3
+* Added app/[models|controllers|helpers] to the load path for plugins that has an app directory (go engines ;)) [DHH]
  4
+
3 5
 * Add config.preload_frameworks to load all frameworks at startup. Default to false so Rails autoloads itself as it's used. Turn this on for Passenger and JRuby. Also turned on by config.threadsafe!  [Jeremy Kemper]
4 6
 
5 7
 * Add a rake task to generate dispatchers : rake rails:generate_dispatchers [Pratik]
25  railties/lib/rails/plugin.rb
@@ -28,13 +28,17 @@ def initialize(directory)
28 28
     end
29 29
     
30 30
     def valid?
31  
-      File.directory?(directory) && (has_lib_directory? || has_init_file?)
  31
+      File.directory?(directory) && (has_app_directory? || has_lib_directory? || has_init_file?)
32 32
     end
33 33
   
34 34
     # Returns a list of paths this plugin wishes to make available in <tt>$LOAD_PATH</tt>.
35 35
     def load_paths
36 36
       report_nonexistant_or_empty_plugin! unless valid?
37  
-      has_lib_directory? ? [lib_path] : []
  37
+      
  38
+      returning [] do |load_paths|
  39
+        load_paths << lib_path  if has_lib_directory?
  40
+        load_paths << app_paths if has_app_directory?
  41
+      end.flatten
38 42
     end
39 43
 
40 44
     # Evaluates a plugin's init.rb file.
@@ -68,7 +72,16 @@ def load_about_information
68 72
 
69 73
       def report_nonexistant_or_empty_plugin!
70 74
         raise LoadError, "Can not find the plugin named: #{name}"
71  
-      end      
  75
+      end
  76
+
  77
+      
  78
+      def app_paths
  79
+        [ 
  80
+          File.join(directory, 'app', 'models'), 
  81
+          File.join(directory, 'app', 'controllers'),
  82
+          File.join(directory, 'app', 'helpers')
  83
+        ]
  84
+      end
72 85
     
73 86
       def lib_path
74 87
         File.join(directory, 'lib')
@@ -86,6 +99,11 @@ def init_path
86 99
         File.file?(gem_init_path) ? gem_init_path : classic_init_path
87 100
       end
88 101
 
  102
+
  103
+      def has_app_directory?
  104
+        File.directory?(File.join(directory, 'app'))
  105
+      end
  106
+
89 107
       def has_lib_directory?
90 108
         File.directory?(lib_path)
91 109
       end
@@ -94,6 +112,7 @@ def has_init_file?
94 112
         File.file?(init_path)
95 113
       end
96 114
 
  115
+
97 116
       def evaluate_init_rb(initializer)
98 117
         if has_init_file?
99 118
           silence_warnings do
10  railties/lib/rails/plugin/loader.rb
@@ -33,6 +33,7 @@ def load_plugins
33 33
           plugin.load(initializer)
34 34
           register_plugin_as_loaded(plugin)
35 35
         end
  36
+
36 37
         ensure_all_registered_plugins_are_loaded!
37 38
       end
38 39
       
@@ -45,12 +46,15 @@ def add_plugin_load_paths
45 46
         plugins.each do |plugin|
46 47
           plugin.load_paths.each do |path|
47 48
             $LOAD_PATH.insert(application_lib_index + 1, path)
48  
-            ActiveSupport::Dependencies.load_paths      << path
  49
+
  50
+            ActiveSupport::Dependencies.load_paths << path
  51
+
49 52
             unless Rails.configuration.reload_plugins?
50 53
               ActiveSupport::Dependencies.load_once_paths << path
51 54
             end
52 55
           end
53 56
         end
  57
+
54 58
         $LOAD_PATH.uniq!
55 59
       end      
56 60
       
@@ -59,9 +63,9 @@ def add_plugin_load_paths
59 63
         # The locate_plugins method uses each class in config.plugin_locators to
60 64
         # find the set of all plugins available to this Rails application.
61 65
         def locate_plugins
62  
-          configuration.plugin_locators.map { |locator|
  66
+          configuration.plugin_locators.map do |locator|
63 67
             locator.new(initializer).plugins
64  
-          }.flatten
  68
+          end.flatten
65 69
           # TODO: sorting based on config.plugins
66 70
         end
67 71
 
8  railties/test/initializer_test.rb
@@ -209,7 +209,7 @@ def test_only_the_specified_plugins_are_located_in_the_order_listed
209 209
     def test_all_plugins_are_loaded_when_registered_plugin_list_is_untouched
210 210
       failure_tip = "It's likely someone has added a new plugin fixture without updating this list"
211 211
       load_plugins!
212  
-      assert_plugins [:a, :acts_as_chunky_bacon, :gemlike, :plugin_with_no_lib_dir, :stubby], @initializer.loaded_plugins, failure_tip
  212
+      assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @initializer.loaded_plugins, failure_tip
213 213
     end
214 214
 
215 215
     def test_all_plugins_loaded_when_all_is_used
@@ -217,7 +217,7 @@ def test_all_plugins_loaded_when_all_is_used
217 217
       only_load_the_following_plugins! plugin_names
218 218
       load_plugins!
219 219
       failure_tip = "It's likely someone has added a new plugin fixture without updating this list"
220  
-      assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :gemlike, :plugin_with_no_lib_dir], @initializer.loaded_plugins, failure_tip
  220
+      assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :engine, :gemlike, :plugin_with_no_lib_dir], @initializer.loaded_plugins, failure_tip
221 221
     end
222 222
 
223 223
     def test_all_plugins_loaded_after_all
@@ -225,7 +225,7 @@ def test_all_plugins_loaded_after_all
225 225
       only_load_the_following_plugins! plugin_names
226 226
       load_plugins!
227 227
       failure_tip = "It's likely someone has added a new plugin fixture without updating this list"
228  
-      assert_plugins [:stubby, :a, :gemlike, :plugin_with_no_lib_dir, :acts_as_chunky_bacon], @initializer.loaded_plugins, failure_tip
  228
+      assert_plugins [:stubby, :a, :engine, :gemlike, :plugin_with_no_lib_dir, :acts_as_chunky_bacon], @initializer.loaded_plugins, failure_tip
229 229
     end
230 230
 
231 231
     def test_plugin_names_may_be_strings
@@ -299,7 +299,7 @@ def test_config_defaults_and_settings_should_be_added_to_i18n_defaults
299 299
        File.expand_path("./test/../../activesupport/lib/active_support/locale/en.yml"),
300 300
        File.expand_path("./test/../../actionpack/lib/action_view/locale/en.yml"),
301 301
        "my/test/locale.yml",
302  
-       "my/other/locale.yml" ], I18n.load_path
  302
+       "my/other/locale.yml" ], I18n.load_path.collect { |path| path =~ /^\./ ? File.expand_path(path) : path }
303 303
     end
304 304
     
305 305
     def test_setting_another_default_locale
25  railties/test/plugin_loader_test.rb
@@ -48,16 +48,16 @@ def test_should_return_empty_array_if_configuration_plugins_is_empty
48 48
     end
49 49
 
50 50
     def test_should_find_all_availble_plugins_and_return_as_all_plugins
51  
-      assert_plugins [:stubby, :plugin_with_no_lib_dir, :gemlike, :acts_as_chunky_bacon, :a], @loader.all_plugins.reverse, @failure_tip
  51
+      assert_plugins [ :engine, :stubby, :plugin_with_no_lib_dir, :gemlike, :acts_as_chunky_bacon, :a], @loader.all_plugins.reverse, @failure_tip
52 52
     end
53 53
 
54 54
     def test_should_return_all_plugins_as_plugins_when_registered_plugin_list_is_untouched
55  
-      assert_plugins [:a, :acts_as_chunky_bacon, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip
  55
+      assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip
56 56
     end
57 57
 
58 58
     def test_should_return_all_plugins_as_plugins_when_registered_plugin_list_is_nil
59 59
       @configuration.plugins = nil
60  
-      assert_plugins [:a, :acts_as_chunky_bacon, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip
  60
+      assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip
61 61
     end
62 62
 
63 63
     def test_should_return_specific_plugins_named_in_config_plugins_array_if_set
@@ -74,17 +74,17 @@ def test_should_respect_the_order_of_plugins_given_in_configuration
74 74
 
75 75
     def test_should_load_all_plugins_in_natural_order_when_all_is_used
76 76
       only_load_the_following_plugins! [:all]
77  
-      assert_plugins [:a, :acts_as_chunky_bacon, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip
  77
+      assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip
78 78
     end
79 79
 
80 80
     def test_should_load_specified_plugins_in_order_and_then_all_remaining_plugins_when_all_is_used
81 81
       only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon, :all]
82  
-      assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :gemlike, :plugin_with_no_lib_dir], @loader.plugins, @failure_tip
  82
+      assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :engine, :gemlike, :plugin_with_no_lib_dir], @loader.plugins, @failure_tip
83 83
     end
84 84
 
85 85
     def test_should_be_able_to_specify_loading_of_plugins_loaded_after_all
86 86
       only_load_the_following_plugins!  [:stubby, :all, :acts_as_chunky_bacon]
87  
-      assert_plugins [:stubby, :a, :gemlike, :plugin_with_no_lib_dir, :acts_as_chunky_bacon], @loader.plugins, @failure_tip
  87
+      assert_plugins [:stubby, :a, :engine, :gemlike, :plugin_with_no_lib_dir, :acts_as_chunky_bacon], @loader.plugins, @failure_tip
88 88
     end
89 89
 
90 90
     def test_should_accept_plugin_names_given_as_strings
@@ -112,6 +112,19 @@ def test_should_add_plugin_load_paths_to_Dependencies_load_paths
112 112
       assert ActiveSupport::Dependencies.load_paths.include?(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib'))
113 113
     end
114 114
 
  115
+
  116
+    def test_should_add_engine_load_paths_to_Dependencies_load_paths
  117
+      only_load_the_following_plugins! [:engine]
  118
+
  119
+      @loader.add_plugin_load_paths
  120
+
  121
+      %w( models controllers helpers ).each do |app_part|
  122
+        assert ActiveSupport::Dependencies.load_paths.include?(
  123
+          File.join(plugin_fixture_path('engines/engine'), 'app', app_part)
  124
+        ), "Couldn't find #{app_part} in load path"
  125
+      end
  126
+    end
  127
+
115 128
     def test_should_add_plugin_load_paths_to_Dependencies_load_once_paths
116 129
       only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon]
117 130
 

25 notes on commit 63d8f56

Sven Fuchs

wohoooooo :)

James Adam

Nice ;)

Andreas Neuhaus

Great. I wonder if/how an engine plugin can add routes.

Virtuaffinity

Good to see this making it into core, nice stuff!

Tekin Suleyman

hallelujah!

Jan De Poorter

@zargony 16:28 :: DHH:: It gets better shortly ;) 16:28 :: DHH:: almost have config/routes.rb working for plugins too

Yaroslav Markin

yay!

Max Lapshin

Cool!!! Waiting for Django-like apps!

Andrew Roth

I’m happy to see this change, but I am curious — what made you change your mind?

Matthew Rudy Jacobs

need to find a good way to extend routing from a plugin.

James Adam

@matthewrudy: it’s being taken care of.

Dan Pickett

how would migrations work for models like this? they’d probably still have to be written through a generator, right?

Bob Martens

If I knew exactly what this did, I’d be excited.

Samuel Williams

DHH: How does this integrate into GEM plugins? GEMs have a directory called “rails” so shouldn’t “app” and so on be in that directory to keep things in the correct namespace? How about rake tasks? You might want to check out my fork of engines which solves these problems and others: http://github.com/ioquatix/engines/tree/master

Stephan Kaag

How about views in a plugin?

Roman Le Négrate

+1 for <plugin-root>/rails/app instead of <plugin-root>/app (following ioquatix’s comment above).

Matthew Rudy Jacobs

you’re on top of everything @lazyatom.

Binary Logic

I thought rails was starting to move away from plugins and more towards gems. That seems to make the most sense, since gems have a versioning / update system, can require dependencies, etc. Plugins seem somewhat redundant to the gems system. That being said, it would be nice to do something like this for gems and encourage people to write gems instead of plugins. Now that github makes creating a gem so easy, it seems like it would actually be easier to do it this way.

Diego Algorta

+1 on engines-like gems support, still supporting rails plugins, but discouraging them.

Michael Christenson II

-1 for /rails/app instead of /app +1 for bringing engines back

I love having all of the above and the gem dependency options open. I love where rails is headed. Keep it up core.

Samuel Williams

m3talsmith: can you clarify your -1 comment? Since everything else for rails is in a subdirectory, why not put app there as well. Its much cleaner overall.

Michael Christenson II

@ioquatix:

I’m more concerned about keeping a pure directory structure in the vendor/plugins/your_plugin_or_engine_name area. When I made the minus comment I hadn’t was in a rush on my way out the door. I overlooked the GEM’s part of the comment. Given that I take my -1 back because it does make sense to keep it in the same namespace. So ….

+1 for /rails/app for the GEMS directory -2 for m3talsmith firing off a comment on the run.

Will this be like extensions in Radiant or Spree? Both of these projects already support views, controllers, routes, migrations, etc. in your extensions.

Todd Willey

Is there an easy way to run migrations from plugins? Maybe being able to set the schema migrations table name from within the migrations would be enough. I’ve been doing it with monkeypatching and a custom rake task so far.

David Heinemeier Hansson
Owner

xtoddx, we’re working on getting a good story for both migrations and files in public in before this is released.

Please sign in to comment.
Something went wrong with that request. Please try again.