This Presentation was built to be viewed with GistDeck
801.946.1510
"I didn't want rails to succumb to the lure of high-level components like login systems, forums, content management, and the likes." - DHH (Apr 07)
Devise
Forem
Refinery
Spree
"But the goal of Rails is to create a world where [engines] are neither needed or strongly desired. Obviously, we are not quite there yet." -DHH
"Engines have not received the blessing of the RoR core team, and I wouldn't expect any different, because it would be madness to include them in core Rails." - James Adam, 2005
http://article.gmane.org/gmane.comp.lang.ruby.rails/29166
James Adam: Created first 'engines' plugin
10/31/2005 (Rails 0.14.2)
It was not really that well received because it broke every time a minor (and often tiny) release was pushed.
(~Mar 2009)
Use James' engines plugin http://github.com/lazyatom/engines
(~Aug 2010)
Actually, engines are not looking all that bad. Thanks to good guys like Piotr Sarnacki and the Rails core team.
(~May 2011)
Ya'll ever head of the Asset Pipeline? Engines just became awesome(r).
(~Jan 2012)
Not a lot has really changed, in general better namespacing practices are being followed in the community
(extracted from rails guide on engines )
Engines can be considered miniature applications that provide functionality to their host applications.
A Rails application is actually just a “supercharged” engine, with the Rails::Application class inheriting a lot of its behaviour from Rails::Engine.
In Rails 3.0, a Rails::Application object was introduced which is nothing more than an Engine but with the responsibility of coordinating the whole boot process.
Engines are also closely related to plugins where the two share a common lib directory structure and are both generated using the rails plugin new generator.
The difference being that an engine is considered a “full plugin” by Rails as indicated by the —full option that’s passed to the generator command.
This means that an application is able to have a path provided by a routing helper such as posts_path and use an engine also that provides a path also called posts_path, and the two would not clash.
<%= link_to 'App posts', posts_path %>
<%= link_to 'Engine posts', engine.posts_path %>
It’s important to keep in mind at all times that the application should always take precedence over its engines.
Finally, engines would not have be possible without the work of James Adam, Piotr Sarnacki, the Rails Core Team, and a number of other people.
Plugin is essentially foundation for engine
rails plugin new urug_testimonials --full --mountable
- --full --mountable give routes, assets directories
- as of 3.1 plugins are also a Gem by default, meaning pushing to github is simple
- App/ directory (assets/controllers/helpers/models/views all namespaced)
- config: routes.rb (initializers | locales | generators & templates would go here)
- lib: namespaced_dirs: engine modules, version | rake tasks
- script/ directory
- test/ directory
- Rakefile (tasks go into lib/tasks)
- Gemfile, .gemspec, .gitignore, MIT-LICENSE
- No readme file is generated
Gemfile should only source rubygems and gemspec
source "http://rubygems.org"
gemspec
s.add_dependency "paperclip"
s.add_development_dependency "rspec"
In lib/urug_testimonials.rb
require 'urug_testimonials/engine'
module UrugTestimonials
end
In lib/urug_testimonials/engine.rb
module UrugTestimonials
class Engine < Rails::Engine
isolate_namespace UrugTestimonials
end
end
-
Rails engine tells rails app there is an engine here and to add it to the load paths
-
If constants exist, great; if not unitialized constant error; restart or define constants appropriately.
Avoid double names:
app/models/post.rb ≠ path/to/engine/app/models/post.rb => Conflict!
well, just call it something different?
isolate_namespace urug app/models/urug/post.rb
UrugTestimonials::Post
also namespaces tables as:
urug_testimonials_posts
also namespaces app/{assets/controllers/helpers/views/models}
Works so that you could replace any of the predefined methods by simply creating an app/controllers/urug_testimonials/application_controller.rb file
- This can be assets/controllers/helpers/models/views
Rails.application.routes.draw:
Rails.application.routes.draw do
namespace :urug_testimonials do
resources :testimonials
end
end
Drawing on application routes, is generally bad.
urug_testimonials/config/routes.rb
UrugTestimonials::Engine.routes.draw do
resources :forums
...
end
urug/config/routes.rb
mount UrugTestimonials::Engine, :at => "/testimonials"
- Routes will not interfere, at all
within an engine, one can call testimonials_path
outside of engine, prefix with engine's isolated_namespace
urug_testimonials.testimonials_path
access app paths from within engine by prefixing with main_app
main_app.users_path
You can remove this and make an engine load seamlessly into an existing app
1.) In your engine's application controller
require :layout => 'desired_layout'
You can specify whatever layout you want.
2.) (Better way) Remove applications layout from engine and adjust the application_controller class to inherit from the Application rather than ActionController::Base.
module UrugTestimonials
class ApplicationController < ActionController::Base
end
end
becomes =>
class UrugTestimonials::ApplicationController < ApplicationController
end
The layout will be passed through from the application.
I prefer namespacing my engines (or any classes within modules really) like this:
class UrugTestimonials::ApplicationController < ApplicationController
end
rather than
module UrugTestimonials
class ApplicationController < ActionController::Base
end
end
because it allows me to more naturally have my classes inherit from objects outside the scope of the given module (without tampering with helpers or monkey patching a solution).
It wasn't always that way; it was just a standalone application you would customize to fit your ecommerce needs.
Spree::Core::Engine.routes.draw
Spree::Auth::Engine.routes.draw
Spree::API::Engine.routes.draw
Spree::Dash::Engine.routes.draw
Spree::Promo::Engine.routes.draw
mount Spree::Core::Engine, at => '/'
mount Spree::Auth::Engine, at => '/'
mount Spree::API::Engine, at => '/'
mount Spree::Dash::Engine, at => '/'
mount Spree::Promo::Engine, at => '/'
mount Spree::YourExtension1::Engine, at => '/'
mount Spree::YourExtension2::Engine, at => '/'
mount Spree::YourExtension3::Engine, at => '/'
spree_core.root_path
spree_auth.login_path
mount Spree::Core::Engine, at: '/'
Draw all routes onto core, using prepend
Spree::Core::Engine.routes.prepend do
...
end
Mount core onto app
Fix everything, lots of broken tests mainly pass { :use_route => :spree }
def spree_user
current_user
end
config/initializers/spree.rb
Spree.user_class = "User"
Ben Eggett | Director of Software Dev @ Savvi
[email protected] | 801.946.1510
(prelude to @blowmage)