83
Extending Rails: Understanding and Building Plugins Clinton R. Nixon

Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Embed Size (px)

Citation preview

Page 1: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Extending Rails:Understanding and Building Plugins

Clinton R. Nixon

Page 2: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Welcome!

Welcoming robin by Ian-S (http://flickr.com/photos/ian-s/2301022466/)

Page 3: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

What are we going to talk about?

The short

‣ How plugins work with Ruby on Rails

‣ How to find and install plugins

The long

‣ Types of plugins

‣ How to build plugins

Page 4: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Ruby on Rails and plugins

History of plugins:

‣ Introduced with Rails 1.0 as a way to extract functionality

‣ Made it easy to distribute functionality

‣ In Rails 2.0, some core features were pulled into plugins and some plugins pulled into core

‣ In Rails 2.1, gem dependencies introduced

Page 5: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Gem dependencies

Fulfills same role as a plugin

‣ Major disadvantage of plugins: no dependencies

‣ Your app can now depend on a gem

‣ That depends on other gems

Same techniques apply as a standard plugin

‣ Differences will be pointed out

Page 6: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Where do you find plugins?

Unfortunately, no simple answer

‣ Rails wiki

‣ Giant list of plugins

‣ Used by script/plugindiscover

‣ Lots of out of date information

Page 7: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Where do you find plugins?

My recommendations

‣ Agile Web Development

‣ Core Rails Plugins

‣ Technoweenie (Rick Olson)

Page 8: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you install plugins?

Install from a URL:

‣ script/plugininstallhttp://example.com/plugins/make_foo

‣ script/plugininstallsvn://code.mondu.org/svn/atom_fu/trunk

‣ script/plugininstallgit://github.com/bnl/acts_as_replicator.git

Page 9: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you install plugins?

Install by name:

‣ script/pluginsourcehttp://example.com/plugins/

‣ script/plugininstallmake_foo

Page 10: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Plugin installation sources

See list of sources:

‣ script/pluginsources

Remove source:

‣ script/pluginunsourcehttp://example.com/plugins/

Page 11: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Autodiscovering plugin sources

Scrape Rails wiki for sources:

‣ script/plugindiscover

Page 12: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

vendor/plugins trivia

Plugins are installed, by default, in vendor/plugins. However:

‣ “plugins can be nested arbitrarily deep within an unspecified number of intermediary directories” - railties/lib/rails/plugin/locator.rb

‣ So, vendor/plugins/my_organization/acts_as/acts_as_long_dir_name/ is fine.

Page 13: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Additional plugin paths

More plugin paths can be defined as configuration.plugin_pathsinenvironment.rb‣ Overwrites default plugin paths

Page 15: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Questions

Baby monkey (http://flickr.com/photos/7971389@N03/504227772/)

Page 16: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

What types of plugins are there?

‣ acts_as...

‣ ...fu

‣ controller and view helpers

‣ testing helpers

‣ resourceful plugins

‣ piggyback plugins

Page 17: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

acts_as... plugins

Adds capabilities to ActiveRecord models

‣ acts_as_versioned

‣ acts_as_paranoid

‣ acts_as_state_machine

‣ acts_as_taggable_on

Page 18: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

...fu plugins

Adds new controller capabilities and back-end processing

‣ attachment_fu

‣ GeoKit

‣ BackgrounDRb

‣ Active Merchant

‣ Exception Notification

Page 19: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Helper plugins

Automate frequently repeated or complicated tasks; some crossover with ...fu plugins

‣ will_paginate

‣ Stickies

‣ jRails

‣ ssl_requirement

‣ TinyMCE for Rails

‣ permalink_fu

Page 20: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Testing plugins

Adds capabilities to testing in Rails

‣ Shoulda

‣ Factory Girl

‣ Test::Spec on Rails

‣ RSpec on Rails

Page 21: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Resourceful plugins

Plugins which contain a mini-app

‣ Savage Beast

‣ Comatose

‣ RESTful Authentication

‣ Bloget

‣ Sandstone

Page 22: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Piggyback plugins

Plugins that alter the behavior of other plugins

‣ Also known as “evil twin plugin”

‣ Usually not published

‣ But increasingly found on GitHub

Page 23: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Questions

Shinji the Hedgehog by Narisa (http://flickr.com/photos/narisa/508277874/)

Page 24: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

What are the parts of a plugin?

‣ README

‣ about.yml

‣ install.rb

‣ uninstall.rb

‣ init.rb

‣ lib/

‣ Rakefile

‣ tasks/

‣ generators/

‣ test/‣ anything else you want to add

Page 25: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

README and about.yml

about.yml:author: Clinton R. Nixonsummary: Adds ability to set foreign key constraints.description: "Adds ability to set foreign key constraints in the database through ActiveRecord migrations. Only works currently with MySQL, PostgreSQL, and SQLite."homepage: http://www.extendviget.com/plugin: git://github.com/vigetlabs/foreign_key_migrations.gitlicense: MITversion: 0.9rails_version: 2.0+

You can see this information with: script/plugininfoPLUGIN.

Page 26: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

install.rb & uninstall.rb

Run automatically

‣ script/plugininstall

‣ script/pluginuninstall

Usually contains code to display instructions or move files

Often not found

puts IO.read(File.join(File.dirname(__FILE__), 'README'))

Page 27: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

init.rb

Always run at Rails startup

Arbitrary Ruby code

Usually injects plugin code

ActionView::Helpers::AssetTagHelper::JAVASCRIPT_DEFAULT_SOURCES = \ ['jquery','jquery-ui','jrails']ActionView::Helpers::AssetTagHelper::reset_javascript_include_defaultrequire 'jrails'

Page 28: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

lib/

Arbitrary Ruby code to be loaded

‣ models

‣ controllers

‣ modules

Added to require path

Because of Rails’ autoloading, all properly named files here will be available without require statements

Page 29: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Rakefile and tasks/

Rakefile contains tasks internal to the plugin

‣ Only executed from plugin directory

tasks/ contains tasks external to the plugin

‣ Available throughout the Rails environment

‣ in .rake files

Page 30: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

generators/

Contains new generators that can be run in Rails

‣ script/generate and script/destroy

Both generator definitions and generator assets

Used to automate creation of models, controllers, views, migrations, and tests

Page 31: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

test/

Tests for plugin

‣ raketest:plugins

‣ raketest:pluginsPLUGIN=plugin_name

‣ plugin Rakefile test task

Page 32: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Anything else you want to add

License files

Further instructions

Changelog

Contribution guidelines

Gemspecs

Todo lists

Asset files (JavaScript, images)

Page 33: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Questions

Baby Hippo by phalinn (http://flickr.com/photos/phalinn)

Page 34: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you create a plugin?

script/generateplugin createvendor/plugins/test_plugin/libcreatevendor/plugins/test_plugin/taskscreatevendor/plugins/test_plugin/testcreatevendor/plugins/test_plugin/READMEcreatevendor/plugins/test_plugin/MIT‐LICENSEcreatevendor/plugins/test_plugin/Rakefilecreatevendor/plugins/test_plugin/init.rbcreatevendor/plugins/test_plugin/install.rbcreatevendor/plugins/test_plugin/uninstall.rbcreatevendor/plugins/test_plugin/lib/test_plugin.rbcreatevendor/plugins/test_plugin/tasks/test_plugin_tasks.rakecreatevendor/plugins/test_plugin/test/test_plugin_test.rb

Page 35: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Plugins and metaprogramming

Modules

‣ include

‣ extend

alias_method and alias_method_chain

Page 36: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Using modules

Allows you to namespace your code

‣ Common idiom: YourName::YourPlugin::ModuleName

includeYourModule‣ adds methods to class instances

extendYourModule‣ adds methods to class

Page 37: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Common module inclusion idiommodule YourName::YourPlugin::YourModule def self.included(base) base.extend ClassMethods end def foo ... end module ClassMethods def bar ... end endend

User.send(:include, YourName::YourPlugin::YourModule)

>> user = User.new>> user.foo>> User.bar

Page 38: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Aliasing methods

Hook onto any method using this technique

def awesome_find ... old_find(params)end

alias_method :old_find, :findalias_method :find, :awesome_find

Kind of messy and unsustainable

Page 39: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

alias_method_chain

def find_with_awesome(params) ... find_without_awesome(params)end

alias_method_chain :find, :awesome

# Equivalent to:# alias_method :find_without_awesome, :find# alias_method :find, :find_with_awesome

Page 40: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Multiple aliasingclass Finder def find puts "found" end

def find_with_awesome puts "AWESOME" find_without_awesome end

def find_with_humility find_without_humility puts "nothing, really" end

alias_method :find_without_awesome, :find alias_method :find, :find_with_awesome alias_method :find_without_humility, :find alias_method :find, :find_with_humilityend

>>> Finder.new.findAWESOMEfoundnothing, really

Page 41: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Multiple aliasingclass Finder def find puts "found" end

def find_with_awesome puts "AWESOME" find_without_awesome end

def find_with_humility find_without_humility puts "nothing, really" end

alias_method_chain :find, :awesome alias_method_chain :find, :humilityend

>>> Finder.new.findAWESOMEfoundnothing, really

Page 42: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Plugin initialization order

‣ Framework is initializated (This typo was too great to leave out)

‣ Environment is loaded

‣ Gem dependencies are loaded

‣ Plugins are loaded

‣ config/initializers/*.rb (application initializers) loaded

‣ after_initialize callback executed

‣ Routes and observers loaded

Page 43: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Plugin best practices

‣ Namespace your code

‣ Enhance, not override

‣ Leave choices to plugin users

‣ Do as little as possible

‣ Don’t do anything unexpected

Page 44: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Questions

Chloe’s Baby #2 by mdprovost (http://flickr.com/photos/anderani/2617606614/)

Page 45: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you setup a plugin?

install.rb - on installation

‣ only works when script/plugininstall used

init.rb - on application load

Page 46: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

init.rb

Run every time the Rails environment is loaded

‣ script/server

‣ script/console

‣ script/runner

Page 47: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

init.rb

For a small plugin, may be all you need

Should kick off most of your metaprogramming

Sometimes used to copy assets

‣ Don’t do this unless absolutely necessary

Should only do things you need to do every time your plugin is loaded

Page 48: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

init.rb binding

def evaluate_init_rb(initializer) if has_init_file? silence_warnings do # Allow plugins to reference the current configuration object config = initializer.configuration eval(IO.read(init_path), binding, init_path) end endend

Page 49: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Gem plugins and init.rb

init.rb found under rails/ directory in a gem plugin

lib/ still added to load path

Page 50: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you work with models?

New models dropped in lib/ will automatically get picked up and be accessible - but are not reloaded in development.

Downside: user cannot easily extend model.

Page 51: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Model behavior in lib/

A solution:

‣ Put a module in lib/ to be included in a model class in app/models/

We can get assistance from generators if we require a class for our plugin to work.

‣ Example: acts_as_taggable_on vs. Bloget

Page 52: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Single table inheritance as a solution

You can drop a model in lib/ intended to be inherited from.

‣ DB table will need a type column

‣ STI has its own downsides

Page 54: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Granting ability to add behavior

AKA acts_as...

Include module in ActiveRecord::Base, but only add one method to trigger behaviormodule ActiveRecord::Acts::Versioned def self.included(base) base.extend ClassMethods end

module ClassMethods def acts_as_versioned(options = {}, &extension) ... end endendActiveRecord::Base.send :include, ActiveRecord::Acts::Versioned

Page 56: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you work with controllers?

Like models, you can drop a controller in lib/ and it will work. Again, this has the drawback that the user cannot easily edit it.

Wrapping behavior in a module and including it is smart. Inheritance also works well.

Page 57: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Classes involved with controllers

ActionController::Base and ActionView::Base are the two classes to change.

ActionController::Base.send(:include, Stickies::ControllerActions)ActionController::Base.send(:include, Stickies::AccessHelpers)ActionView::Base.send(:include, Stickies::AccessHelpers)ActionView::Base.send(:include, Stickies::RenderHelpers)

Page 58: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

A better way to handle helpers

Since Rails 0.8.5, there’s been a better way to add helpers to controllers and views.

ActionController::Base.send(:include, Stickies::AccessHelpers)ActionController::Base.helper(Stickies::AccessHelpers)ActionController::Base.helper(Stickies::RenderHelpers)

Page 59: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Adding behavior to controllers

Included modules are the most common way to add controller behavior.

Page 61: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you work with views?

Working with views is much like working with controllers.

Use ActionController::Base.helper to add new helpers.

Page 62: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Changing template root

class FooController < ActionController::Base self.template_root = \

File.join(File.dirname(__FILE__), '..', 'views') end

One problem with this: the controller will expect all views - including templates and partials - to be found under this directory.

Page 63: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Questions

Baby tiger (http://flickr.com/photos/modu_li/1788817738/)

Page 64: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you work with generators?

Generators can let you make models, controllers, or anything else to stick directly into the user’s Rails app.

Put assets and generation script in generators/.

Thanks to Brian Landau

Page 65: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Structure of generators/ directory

• generators/• plugin_name/

• plugin_name_generator.rb• templates/

• a_model.rb• a_controller.rb• some_views/

• view_file.html.erb• USAGE

Page 66: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Two types of generators

Rails::Generator::Base‣ No required argument

‣ More basic

Rails::Generator::NamedBase‣ First argument is a class name

‣ Extra attributes available in generation

‣ Use when you're creating a specific named object

Page 67: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Generator class structure

Should inherit from Base or NamedBase

Must define a manifest method

Can have a banner method

Can have an add_options! method

Page 68: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

NamedBase attributes

name Blog::Commentorblog/comment

class_nesting

class_nesting_depth

class_path

file_path

class_name

plural_name

singular_name

table_name

Blog

1

['blog']

blog/comment

Blog::Comment

comments

comment

blog_comments

Page 69: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Manifest directives

‣ class_collisions

‣ directory

‣ file

‣ template

‣ migration_template

‣ route_resources

‣ readme

‣ dependency

Page 70: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

USAGE

Description:Thecomatosegeneratorcreatesamigrationforthecomatosemodel.

Thegeneratortakesamigrationnameasitsargument.ThemigrationnamemaybegiveninCamelCaseorunder_score.'add_comatose_support'isthedefault.

Thegeneratorcreatesamigrationclassindb/migrateprefixedbyitsnumberinthequeue.

Example:./script/generatecomatoseadd_comatose_support

With4existingmigrations,thiswillcreateanComatosemigrationinthefiledb/migrate/005_add_comatose_support.rb

Page 71: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Generator templates

Use ERB to customize

‣ For ERB generating ERB: <%%[email protected]%>

‣ Generator methods available

‣ Unlike Rails’ views, generator instance variable not available

Page 72: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Programmatically running generators

Rails::Generator::Scripts::Generate.new.run( ['authenticated', 'user', 'sessions'])

Page 73: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you add new tasks?

No different from Rails

‣ Place Rake files in tasks/ named whatever.rake

The plugin Rakefile is only for Rake tasks run in the plugin directory.

‣ raketest

‣ rakerdoc

‣ rakererdoc

‣ rakeclobber_rdoc

Page 74: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you deal with other plugins?

Simple trick: all plugins in vendor/plugins are loaded in alphabetical order.

Page 75: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you test a plugin?

One way: require that your plugin is in a Rails app

‣ Easy

‣ Ugly

‣ Cannot specify Rails version

‣ May interfere with app classes

‣ Requires your plugin’s setup to be run

Page 76: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Running plugin tests

From plugin dir: raketest

‣ Does not automatically load Rails environment

‣ To load Rails env:

‣ require File.join(File.dirname(__FILE__), '../../../../test/test_helper')

Page 77: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Running plugin tests

From Rails app dir

‣ raketest:plugins

‣ raketest:pluginsPLUGIN=plugin_name

Running from Rails app dir does not run plugin’s Rakefile, so dependencies in there are not executed.

Page 78: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Standalone testing of plugins

Helper plugins may not need a Rails app

For other plugins, you can mock out a Rails app

‣ Generators may need a full directory

‣ Model plugins can get by with a test database

‣ Controller plugins may need very little

‣ But mocking out routing can be very hard

Page 79: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

How do you package your plugin?

A public Subversion or Git repository lets users install with script/plugin.

To make a gem plugin, try Mr Bones.

PROJ.name = 'friend-feed'PROJ.authors = 'Clinton R. Nixon'PROJ.email = '[email protected]'PROJ.url = 'friend-feed.rubyforge.org'PROJ.dependencies = ['json']PROJ.version = FriendFeed::VERSION

Page 81: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Foreign Key Migrations walkthrough

Baby duck (http://flickr.com/photos/dizzygirl/437988363/)

Page 82: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

RESTful Authentication walkthrough

Uganda Mbeya (http://flickr.com/photos/youngrobv/2347565498/)

Page 83: Extending Rails: Understanding and Building Pluginsassets.en.oreilly.com/1/event/12/Extending Rails_ Understanding and...Ruby on Rails and plugins History of plugins: ‣ Introduced

Resources

‣ Peepcode’s Plugin Patterns by Andrew Stewart

‣ Addison-Wesley’s Shortcut Rails Plugins by James Adam

‣ Rick Olson: techno-weenie.net - tons of plugins and plugin building blog posts

‣ This talk: http://crnixon.org/talks/rails-plugins