Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
The Rails Engine That Could - In Motion
1.
2. Problem
Difficulty reusing functionality cutting across:
Models
Views
Controllers
Assets (JS, CSS, Images)
Duplication across all web application layers.
4. Solution
Break common behavior into Rails Engines
Customize models/controllers/helpers in each
project where needed by reopening classes
Customize Rails views in each project as needed
by overriding templates
Link to Rails Engines in Gemfile via Git repo
5. Example
Common
Domain
Rails Engine
Search Map
Rails Engine
High School Public Athlete
Recruiting Profiles Recruiting
Rails App Rails App Rails App
9. Engine Definition
An engine structure is similar to a Rails app
having app, config, lib, spec, features, etc…
lib/engine_name.rb (read online instructions)
lib/engine_name/engine.rb (read online
instructions)
To reuse engine, use “jeweler” gem to generate
gemspec (read online instructions)
13. Load Order
Typically Rails app files load first before Engine
files.
Strongly recommended to reverse (by patching
“active_support/dependencies.rb”) so that
engine’s Ruby code is overrideable in app (see
next slide)
ERB files can be overridden in Rails app
14.
15. Ruby Code Customization
Model/Helper/Controller behavior can be
customized be redefining .rb files in Rails app:
Add new methods/behavior
Replace existing methods
Extend existing methods via alias_method_chain
16. View Customization
View files (erb, haml, etc…) and Asset files (js,
css, and images) can be redefined in Rails app to
override completely for customization purposes
18. Typical Development
Process
1. Make changes in engine, rake, and commit
obtaining a new git ref
2. Update Gemfile in app with new git ref, run
“bundle install” (getting ride of symlink)
3. Rake and commit changes in app.
4. If more changes in engine are needed go back
to step 1
19. Improved Productivity via
Symlinking
Multiple engine dependencies can hamper
productivity when frequently going back and
forth between engines and app
Engines gems installed via bundler can be
symlinked to allow continuous development until
done with both app and
engine:http://andymaleh.blogspot.com/2011/09/
more-productive-rails-engine.html
20. Improved Development
Process
1. Open Rails app and symlink all engines “rake
engine:symlink[engine_name]”
2. Work in app and engine until done WITHOUT running
“bundle install”
3. Rake and commit changes in engine obtaining a new git
ref
4. Update Gemfile in app with git ref, run “bundle install”
(getting ride of symlink)
5. Rake and commit changes in app
21. Engines Reuse Engines
Rails engines can reuse other Rails engines
When multiple levels of depth are involved (e.g.
App => Engine 1 => Engine 2), commit repos and
update Gemfile from the bottom up (e.g. Engine
2 => Engine 1 => App)
22. Engine Configuration
Engines can be configured to customize rack
middleware, load paths, generators, and Rails
component paths. More details at:
http://edgeapi.rubyonrails.org/classes/Rails/Engi
ne.html
23. Isolated Engines
To avoid Ruby namespace clash with
Models/Helpers/Controllers, you can define an
isolated namespaced engine:
24. Rails Engine Patterns
Goals:
Keep engine code agnostic of app customizations
Prevent bi-directional coupling to simplify
reasoning about code
Avoid app dependent conditionals to improve code
maintainability
25. Pattern - Common Domain
Problem: Multiple applications need to share a
basic domain model but want to customize
behavior without mixing concerns across apps
Solution:
In engine, please basic domain model definitions
and common associations only
In each app, define specialized behavior and extra
associations for domain models
26. Pattern - Expose Helper
Problem: need to customize presentation logic
for a view in one app only, but keep the same
logic in others
Solution:
In engine, extract helper logic that needs
customization into its own helper.
In app, redefine that new helper with
customizations.
27. Pattern - Expose Partial
Problem: need to customize a part of the view in
one app only, but keep it the same in others
Solution:
In engine, extract view part that needs
customization as a partial.
In app, redefine that partial with customizations.
28. Pattern - Extension Partial
Problem: One app needs to add content to a
view that is not needed in other apps
Solution:
In engine, introduce a new partial with empty
contents in area that needs extension for the one
app.
In app, define that partial with the required
content.
30. Pattern - Extension Point
Problem: different apps need to contribute data to a
view in different places (e.g. contribute
columns/rows in different spots)
Solution:
In engine, add logic that looks up partials in a specific
ext directory, and based on file name (e.g.
row_7.html.erb), determine index on where to insert it in
the view.
In app, define these partials with the right file names
and locations.
32. Pattern - Configurable
Features
Problem: different apps need different features from
an engine in different combinations
Solution:
In engine, add logic that looks up configuration options
from a constant hash (e.g. ENGINE_XYZ_CONFIG =
{:header => “visible”, :footer => “visible”, …}).
In app, configure engine by overriding configuration
options (e.g. ENGINE_XYZ_CONFIG[:header] =
“hidden”)
33. Rails Engine Benefits
Code reuse across all application layers
Better maintainability due to:
Independent project codebases
Cleanly defined boundaries between projects and
reusable components (engines)
Project tests get smaller and run faster
34. Rails Engine Costs
Overhead in establishing a new Rails Engine
gem project
Continuous switching between projects and
engines to get work done
Upgrade of ref numbers in Gemfile on every
commit (minimized with symlinking)
35. More Info
http://edgeapi.rubyonrails.org/classes/Rails/Engi
ne.html
http://andymaleh.blogspot.com/2011/09/more-
productive-rails-engine.html
http://stackoverflow.com/questions/2964050/rail
s-engines-extending-
functionality/2990539#2990539
36. Contact
Andy Maleh
Code Painter Blog http://andymaleh.blogspot.com
Twitter: @AndyMaleh