Engines: Team Development on Rails

Engines: Team Development on Rails

(Canada on Rails, 2005)

"Rails Engines provide a means to share common functionality across many projects, in a package that's easy to both update *and* override.

"This presentation will explain the concepts behind Engines and demonstrate how they work within a Rails application. I will also discuss how Engines has dramatically enhanced collaboration within our own development team, and how such collaboration can be extended into the Rails community as a whole."



April 14, 2006


  1. rails engines rails-engines.org James Adam

  2. “Hello”

  3. a bit of background

  4. for many clients we build applications simultaneously

  5. common functional aspects

  6. User Control Auditing Reporting Job Scheduling Charts & Graphs Data

    Storage Help Systems Content Management
  7. many applications many aspects lots of code ✕ lots of

  8. i am lazy i am a conscientious developer

  9. Reuse is overrated

  10. Reuse is essential ... it just depends on the context

  11. e.g. a simple help system

  12. db/migrate/001_add_help.rb class AddHelp < ActiveRecord::Migration def self.up create_table :help_pages do

    |t| t.column :key, :string t.column :title, :string t.column :content, :text end end def self.down drop_table :help_pages end end
  13. models/help_page.rb class HelpPage < ActiveRecord::Base end

  14. helpers/help_helper.rb module HelpHelper def help_link(key=nil) key ||= params[:controller] + ‘/’

    + params[:action] page = HelpPage.find_by_key(key) unless page.nil? link_to(‘[help]’, {:controller => ‘help’, :action => ‘show’, :id => page}, {:class => ‘help_link’}) end end end
  15. and in our application: <h1>MeatSword :: Basket</h1> <p> Please find

    your basket contents below. <%= help_link %> </p> <% @cart.items.each do |item| %> <%= render :partial => ‘cart/item’ %> <%= link_to(‘[X]’, :action => ‘remove’, :id => item) %> <%= help_link ‘remove_item’ %> <% end %>
  16. public/stylesheets/help.css a.help_link { text-decoration: underline; color: #0F0; font-size: 0.8em; }

    a.help_link:hover { text-decoration: none; color: #000; background-color: #0F0; }
  17. generators

  18. rails generators $ script/generate model HelpPage create app/models/help_page.rb create test/fixtures/help_pages.yml

    create test/unit/help_page_test.rb $ script/generate controller Help create app/controllers/help_controller.rb create app/helpers/help_helper.rb create test/functional/... # and so on $ script/generate scaffold HelpPage Help create app/views/help/show.rhtml create app/views/help/edit.rhtml # ... etc generators
  19. a package of code templates processed by ERb generators

  20. script/generate my_help_system our help generator generators Text create app/models/help_page.rb create

    app/controllers/help_controller.rb create app/helpers/help_helper.rb create app/views/help/show.rhtml create app/views/help/edit.rhtml create app/views/help/list.rhtml create app/views/help/_form.rhtml create db/migrations/001_add_help.rb create lib/help_system.rb create lib/tasks/help.rake create public/stylesheets/help.css create test/unit/help_test.rb create test/fixtures/help_pages.yml $
  21. generators complete subsystem

  22. generators share with rubygems

  23. meanwhile, in our app: class HelpPage < ActiveRecord::Base # only

    words containing the # letter ‘x’ are important def summarize(limit=20) content.split.select { |word| word.include?(’x’) }.flatten[0..limit].join end end
  24. generators difficult to maintain

  25. plugins

  26. plugins init.rb start-up

  27. plugins install.rb post-install

  28. plugins lib/ added to $LOAD_PATH

  29. plugins shareable

  30. sharing with svn:externals plugins

  31. $ svn propset svn:externals \ ‘my_plugin svn://server/my_plugin’ \ vendor/plugins property

    'svn:externals' set
  32. None
  33. $ svn update vendor/plugins A vendor/plugins/my_plugin/monkey.rb

  34. monkey.rb monkey.rb monkey.rb

  35. plugins maintainable

  36. help plugin help.rb help_helper.rb help_controller.rb show.rhtml help.css 001_add_help.rb

  37. plugins only code

  38. engines

  39. a login system?

  40. huge, monolithic, apocalyptic high-level components?

  41. easy way to annoy DHH?

  42. plugins.succ

  43. plugins + 1

  44. plugins.succ (plus a little bit more that we found REALLY

  45. a mechanism for sharing MVC ‘slices’ between your apps

  46. class ArnoldController < ApplicationController def terminator @u = User.find_by_name(’Sarah Conner’)

    @u.destroy end def total_recall @quote = 'Get your ass to Mars!' render :text => @quote end def twins render :partial => ‘de_vito’, :locals => { :julius => User.find end def sixth_day User.find(:first).clone end end ruby code public assets </div> <table> <% for member in fellowship do %> <tr> <td><%= member.name %></td> <td><%= member.gift_from_galadrial <td><%= member.ringbearer? %></td> </tr> <% end %> </table> <h1>New member?</h1> <%= form_tag :action => ‘add_dude’, :m <%= text_field :new_guy, :species %> <%= text_area :new_guy, :biography % <%= check_box :new_guy, :pawn_of_sau <%= end_form_tag %> <div class=’map_of_middle_earth’> views, partials class JazzMigration < ActiveRecord::Mi def self.up create_table :instruments do |t| t.column :name, :string t.column :type_id, :integer t.column :family, :string t.column :range, :integer, :defa t.column :position_id, :integer, end Instrument.create_default_instrume JazzPlayers.connect_to_instruments end def self.down JazzPlayers.disconnect_from_instru drop_table :instruments end database migrations
  47. intuitive layout

  48. control your environment Engines.start :help, :reporting, :user_basics, :user_interface

  49. shared public assets <html> <head> <%= engine_stylesheet :help %> <%=

    engine_javascript :user_control %> <title>MeatSword.com</title> </head> <body> <h1>MeatSword :: Meat beyond Meat.</h1>
  50. migrations rake db:migrate:engines $ Migrated help Migrated meat_sword Migrated south

  51. ... or just use them like plugins

  52. developing engines

  53. ....guess where the files go

  54. init.rb init_engine.rb

  55. generating an engine $ script/generate engine Help Author's name: James

    Adam Author's email: james.adam@gmail.com We can automatically generate a license for you: 0) MIT 1) GPL 2) LGPL 3) None Please select a license: 0 create vendor/plugins/help create vendor/plugins/help/README create vendor/plugins/help/install.rb create vendor/plugins/help/init_engine.rb create vendor/plugins/help/app create vendor/plugins/help/app/models create vendor/plugins/help/app/controllers create vendor/plugins/help/app/helpers create vendor/plugins/help/app/views create vendor/plugins/help/db
  56. client customisation module HelpHelper def help_link(key=nil) # overrides method in

    engine key ||= params[:controller] + ‘/’ + params[:action] page = HelpPage.find_by_key(key) if page.nil? link_to(’Add help page?’, {:controller => ‘help’, :action => ‘create’, :key => key}) else link_to(‘More information...’, # different text {:controller => ‘help’, :action => ‘show’, :id => page}, {:class => ‘info_link’}) end end end /app/helpers/help_helper.rb
  57. extending & overriding class HelpController < ApplicationController def create #

    method NOT in engine if request.post? page = HelpPage.new(params[:new_page]) unless page.save flash[:message] = ‘Oh noes!’ end end end end /app/controllers/help_controller.rb
  58. /app/views/help/show.rhtml overriding views <img class=’logo’ src=’public/images/logo.jpg /> <h1>NameOfThisParticularClient.com</h1> <h2>Help &raquo;

    <%= @page.title %> </h2> <%= render :partial => ‘index_links’ %> <div class=’help_content’> <%= @page.content %> </div>


  61. a set of small extensions to Rails http://svn.rails-engines.org/plugins/engines/

  62. dependencies_extensions.rb overriding code

  63. action_view_extensions.rb action_mailer_extensions.rb overriding views

  64. action_view_extensions.rb public assets

  65. migration_extensions.rb migrations in plugins

  66. testing_extensions.rb flexible fixtures

  67. ruby_extensions.rb cheap configuration

  68. engines.rb plugin management

  69. engines are a mechanism for sharing maintainable code between your

  70. where from here?

  71. bundles <html> <head> <%= require_bundle :help %> <title>MeatSword.org</title> </head> <body>

    <h1>More meat than you can shake a stick at.</h1>
  72. happy coexistence with Rails Core

  73. feeding back to plugin system

  74. opinionated open source

  75. rails engines rails-engines.org James Adam dev.rails-engines.org www.rails-engines.org api.rails-engines.org issues/patches: propaganda: