Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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."

lazyatom

April 14, 2006
Tweet

More Decks by lazyatom

Other Decks in Programming

Transcript

  1. rails engines
    rails-engines.org
    James Adam

    View Slide

  2. “Hello”

    View Slide

  3. a bit of background

    View Slide

  4. for many clients
    we build applications
    simultaneously

    View Slide

  5. common
    functional
    aspects

    View Slide

  6. User Control
    Auditing
    Reporting
    Job Scheduling
    Charts & Graphs
    Data Storage
    Help Systems
    Content Management

    View Slide

  7. many applications
    many aspects
    lots of code

    lots of maintenance

    View Slide

  8. i am lazy
    i am a
    conscientious
    developer

    View Slide

  9. Reuse is overrated

    View Slide

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

    View Slide

  11. e.g.
    a simple
    help system

    View Slide

  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

    View Slide

  13. models/help_page.rb
    class HelpPage < ActiveRecord::Base
    end

    View Slide

  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

    View Slide

  15. and in our application:
    MeatSword :: Basket

    Please find your basket contents below.
    <%= help_link %>

    <% @cart.items.each do |item| %>
    <%= render :partial => ‘cart/item’ %>
    <%= link_to(‘[X]’, :action => ‘remove’,
    :id => item) %>
    <%= help_link ‘remove_item’ %>
    <% end %>

    View Slide

  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;
    }

    View Slide

  17. generators

    View Slide

  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

    View Slide

  19. a package of
    code templates
    processed by ERb
    generators

    View Slide

  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
    $

    View Slide

  21. generators
    complete
    subsystem

    View Slide

  22. generators
    share with
    rubygems

    View Slide

  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

    View Slide

  24. generators
    difficult to
    maintain

    View Slide

  25. plugins

    View Slide

  26. plugins
    init.rb
    start-up

    View Slide

  27. plugins
    install.rb
    post-install

    View Slide

  28. plugins
    lib/
    added to $LOAD_PATH

    View Slide

  29. plugins
    shareable

    View Slide

  30. sharing with
    svn:externals
    plugins

    View Slide

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

    View Slide

  32. View Slide

  33. $ svn update vendor/plugins
    A vendor/plugins/my_plugin/monkey.rb

    View Slide

  34. monkey.rb monkey.rb monkey.rb

    View Slide

  35. plugins
    maintainable

    View Slide

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

    View Slide

  37. plugins
    only code

    View Slide

  38. engines

    View Slide

  39. a login system?

    View Slide

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

    View Slide

  41. easy way to annoy
    DHH?

    View Slide

  42. plugins.succ

    View Slide

  43. plugins + 1

    View Slide

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

    View Slide

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

    View Slide

  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


    <% for member in fellowship do %>

    <%= member.name %>
    <%= member.gift_from_galadrial
    <%= member.ringbearer? %>

    <% end %>

    New member?
    <%= 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 %>

    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

    View Slide

  47. intuitive
    layout

    View Slide

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

    View Slide

  49. shared
    public assets


    <%= engine_stylesheet :help %>
    <%= engine_javascript :user_control %>
    MeatSword.com


    MeatSword :: Meat beyond Meat.

    View Slide

  50. migrations
    rake db:migrate:engines
    $
    Migrated help
    Migrated meat_sword
    Migrated south

    View Slide

  51. ... or just use them like
    plugins

    View Slide

  52. developing
    engines

    View Slide

  53. ....guess
    where
    the files go

    View Slide

  54. init.rb
    init_engine.rb

    View Slide

  55. generating an engine
    $ script/generate engine Help
    Author's name: James Adam
    Author's email: [email protected]
    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

    View Slide

  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

    View Slide

  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

    View Slide

  58. /app/views/help/show.rhtml
    overriding views
    src=’public/images/logo.jpg />
    NameOfThisParticularClient.com
    Help »
    <%= @page.title %>

    <%= render :partial => ‘index_links’ %>

    <%= @page.content %>

    View Slide

  59. WARNING:
    INCOMING
    CUSS-WORD
    WARNING:
    INCOMING
    CUSS-WORD

    View Slide

  60. WHAT
    THE HELL
    IS THIS
    BLACK MAGIC
    VOODOO?

    View Slide

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

    View Slide

  62. dependencies_extensions.rb
    overriding code

    View Slide

  63. action_view_extensions.rb
    action_mailer_extensions.rb
    overriding views

    View Slide

  64. action_view_extensions.rb
    public assets

    View Slide

  65. migration_extensions.rb
    migrations in plugins

    View Slide

  66. testing_extensions.rb
    flexible fixtures

    View Slide

  67. ruby_extensions.rb
    cheap configuration

    View Slide

  68. engines.rb
    plugin management

    View Slide

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

    View Slide

  70. where from here?

    View Slide

  71. bundles


    <%= require_bundle :help %>
    MeatSword.org


    More meat than you can
    shake a stick at.

    View Slide

  72. happy coexistence
    with Rails Core

    View Slide

  73. feeding back to
    plugin system

    View Slide

  74. opinionated
    open source

    View Slide

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

    View Slide