A Rails 3 Engine-Gem which is Also an Application wants to share a DRY configuration via Mixin
I have a number of engines which are also gems and also applications (Rails3). They are gems so they can be easily installed and the dependencies managed via bundler in more than one application (its a whole stack upon which multiple applications are built). They are engines to take advantage of Rails resources - models and such. They are 开发者_开发问答applications for two reasons: 1) to provide a full testing environment which is isolated from their including applications so you can do 'rails c' for example and 2) in order to run things like 'rake db:migrate' and seed and more.
I want both my engine and my application to inject some mixins into lower level dependencies. Here is the solution I came up with. It works fine - I am just wondering if anyone has any criticisms of the approach OR a best practice to share regarding the sharing issue OR the overall idea of engine-gem-applications:
The engine:
#my_engine/lib/my_engine.rb
require 'my_engine/config.rb'
module MyEngine
class Engine < Rails::Engine
config.to_prepare do
MyEngine.inject_mixins
end
end
end
The application:
#my_engine/config/application.rb
require 'my_engine/config'
module MyEngine
class Application < Rails::Application
config.to_prepare do
MyEngine.inject_mixins
end
end
end
The mixin:
#my_engine/lib/my_engine/config.rb
module MyEngine
module CLASSMETHODS
def inject_mixins
::ApplicationHelper.send(:include, MyEngine)
::SomeDependency::SomeClass.send(:include, MyEngine::SomeClassMixin)
end
#root should be defined as the root of this engine, ie relative to this file
def root
File.join(File.dirname(__FILE__), '..','..')
end
end
extend CLASS_METHODS
end
(Update: I edited the above to wrap the module in my_engine module, otherwise more than one engine using this pattern simultaneously could have unpredictable effects, like MyEngine.root == SomeOtherEngine.root)
There's no rhyme or rule to this, but you have a couple different options.
Your gem's tests can contain a dummy application for testing. Devise does this, for example. This is accepted practice for gems that are heavily Rails-dependent.
You can also keep it separate. In the past I've set up a testing application with a Gemfile that points to the gem via path (gem 'mygem', :path => 'some/path'
), which makes testing relatively easy. This can double as a sample application that you can provide in a separate repository (keep in mind when tagging the gem you should change the sample application's :path parameter to a specific version). The benefit here is that your sample application is always kept up to date.
If you're simply talking about unit testing models, you can skip the above and just add a testing dependency on Active Record and SQLite. Keep fixture data with the gem.
Since you have several of these engines and they will be mixed and matched in different applications, my suggestion is to set up an application that uses all of these gems and serves as your functional testbed. Keep unit tests with the individual gems, of course. This has the added benefit of integration testing between all engines, to ensure there are no conflicts.
精彩评论