Actually it's probably better to post here so that others can see it too. Here goes.
First up, you should be specifying the path like: gem 'your_engine', :path => "vendor/engines/your_engine" As you're telling Bundler the exact location of your engine. You could have multiple engines. An engine should be considered a miniature application. It can contain controllers, models, views, initializers, unicorns, files in the lib directory, assets, etc. just like a real application can. However, it should be treated like Yet Another Gem Dependency to your application. The reason for this is that Bundler would have a great deal of difficulty (at least, I think it would) determining which would be the canonical Gemfile. Should the Rails 3.1.0 exact dependency that you have in your engine's Gemfile be used, or should the 3.0.6 dependency in the application? What about the "~> 3.0.8" dependency required by one of the gems that you use? This is why your engine should be a gem. Making it a gem is simple. The files that "rails plugin new [name] --mountable" come out with contains a gemspec. This file and this file only should be used to specify the runtime dependencies of your engine. Why? Well... In your application you will want to (well, you are wanting to) use an engine. Now, Rails must find out about this engine somehow, but how? The same way a Rails application would find out about any other gem, because there's a line in the Gemfile for it! For forem (https://github.com/radar/forem) I recommend that people put this line in their Gemfile: gem 'forem', :git => "git://github.com/radar/forem.git" When this gem is specified then all the dependencies specified as runtime dependencies for the gem are added as dependencies for the application as well, and treated just as if they were specified right there in the Gemfile. When you run `bundle install` it will do its thing and install it just as a gem. Then when your application is initialized, these lovely lines near the top of config/application.rb will execute: if defined?(Bundler) # If you precompile assets before deploying to production, use this line Bundler.require *Rails.groups(:assets => %w(development test)) # If you want your assets lazily compiled in production, use this line # Bundler.require(:default, :assets, Rails.env) end This will require all the gems in the "default" group (that is, all the gems not in any group, as well as all the gems in the "assets" group and finally all the gems in a group matching the current name of the environment for Rails. Boom. What does this do for an engine? Well, Bundler treats it just like any other gem: it loads the file at lib/forem.rb (https://github.com/radar/forem/blob/master/lib/forem.rb). This file is the "entrypoint" into the engine. It should set up the base for the engine, but not the engine itself! That is what forem/engine.rb is for (https://github.com/radar/forem/blob/master/lib/forem/engine.rb). Maybe I am a little OCD about the separation of different things into different files, but this is, in my mind, how it should be done. Now, the fun begins. At this point in the initialization cycle, Rails has done its thing (i.e. Rails::Engine is loaded, as is Rails::Application), and so we can inherit from Rails::Engine as we please. You can see this happening in lib/forem/engine.rb if you look hard enough. BONUS SIDE TANGENT TIME: The isolate_namespace method call here will, er, isolate the namespace. Of the engine. Yeah. That. No really. It will: 1. Namespace your model's tables (i.e. forem's posts table is called "forem_posts" and the model respects that without us having to tell it to) 2. Imply that all the controllers, models, helpers, unicorns and views for this engine are namespaced too, rather than living at the root of the app directory. 3. "Shield" the routing helpers from the application and the engine, allowing the engine and application to have routing helpers named the same but do different things depending on the context. To use the main application's routing helpers in an engine you would call it like "main_app.root_path". To use an engine's in the application, "engine.root_path". Magic! And some other things that I can't recall right now. Namespacing your engine is SUPER HANDY and has a 100% guaranteed success rate of me not face-palming when I attempt to use it. Please do it! Namespace your engine for the same reason people namespace their gem code already: so you do not pollute the environment! BE GREEN! END BONUS SIDE TANGENT TIME. By inheriting from Rails::Engine, the engine will let Rails know it is there. It will add the app/controllers, app/models, app/helpers and app/views for the engine to the load paths *after* the similar locations of the application. This, my friend(s) is why you can override the engine's <whatever> inside your application without Rails saying "oh no you just didn't", shaking its head side to side and snapping its fingers. Now, this is great! You can override stuff in the engine that you don't like with stuff in the application that you like more. But what if you want to patch the application from the engine? Well, you could call `class_eval` on ApplicationController and treat that block that you'll inevitably pass it as if it were inside the *real* ApplicationController itself. Some people would have you do this in `config/initializers` and I currently have bounties on their heads. Instead, I would recommend you put a file like this in `app/controllers/application_controller_decorator.rb` and have that define a module called `ApplicationControllerDecorator` which is then included into `ApplicationController` by using an initializer. That idea was stolen from Spree, which covers how to do the same, but patching engine functionality vs application functionality, here: http://spreecommerce.com/documentation/customization.html#logic-customization. Same forest, different tree. I can't come up with the code right now to do it, but I know it is possible as I have seen it done. And that concludes this lengthy post on how engines work. If you have any further questions I would be more than happy to answer them, just not in such a long form as this. Until next, Ryan Bigg On Sunday, 4 September 2011 at 6:33 PM, [email protected] wrote: > > > > On 04/09/2011, at 11:26, Tim Uckun <[email protected] > > (mailto:[email protected])> wrote: > > > Ideally rails would process the Gemfile in the engine(s) and then the > > > Gemfile in the parent app but it doesn't seem to be doing that. > > > > No, this is never going to be the case. You must specify your dependencies > > in your engine's gemspec file which will be loaded by Bundler because your > > application is treating your engine as if it were a gem. Or at least, it > > should be doing that. > > > > If you've NOT specified your engine as a GEM DEPENDENCY of your application > > then things will not be loaded as normal. > > > > If you'd like a better explanation why I can give you one over IM later > > this afternoon or I could write a blog post? :) > > > I am always eager to learn so if you have the inclination please feel > free to ping me I am @timuckun almost everywhere. > > BTW I am specifying the engine as a gem in the gemfile > > gem 'engine_name', :path => 'vendor/engines' > > -- > You received this message because you are subscribed to the Google Groups > "Ruby or Rails Oceania" group. > To post to this group, send email to [email protected] > (mailto:[email protected]). > To unsubscribe from this group, send email to > [email protected] > (mailto:[email protected]). > For more options, visit this group at > http://groups.google.com/group/rails-oceania?hl=en. -- You received this message because you are subscribed to the Google Groups "Ruby or Rails Oceania" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/rails-oceania?hl=en.
